From faea03f1fdedc87c6b783ce89f231a3b2e3e910f Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sun, 1 Sep 2024 11:38:23 +0200 Subject: [PATCH] add system/adcscope - streaming tool for ADC add adcscope app allowing to stream data from ADC device with logging/nxscope. Useful for testing ADC drivers. Signed-off-by: raiden00pl --- system/adcscope/CMakeLists.txt | 31 ++ system/adcscope/Kconfig | 109 ++++++++ system/adcscope/Make.defs | 23 ++ system/adcscope/Makefile | 34 +++ system/adcscope/adcscope_main.c | 482 ++++++++++++++++++++++++++++++++ 5 files changed, 679 insertions(+) create mode 100644 system/adcscope/CMakeLists.txt create mode 100644 system/adcscope/Kconfig create mode 100644 system/adcscope/Make.defs create mode 100644 system/adcscope/Makefile create mode 100644 system/adcscope/adcscope_main.c diff --git a/system/adcscope/CMakeLists.txt b/system/adcscope/CMakeLists.txt new file mode 100644 index 000000000..38c85d7d9 --- /dev/null +++ b/system/adcscope/CMakeLists.txt @@ -0,0 +1,31 @@ +# ############################################################################## +# apps/system/adcscope/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_ADCSCOPE) + nuttx_add_application( + NAME + ${CONFIG_SYSTEM_ADCSCOPE_PROGNAME} + SRCS + adcscope_main.c + STACKSIZE + ${CONFIG_SYSTEM_ADCSCOPE_STACKSIZE} + PRIORITY + ${CONFIG_SYSTEM_ADCSCOPE_PRIORITY}) +endif() diff --git a/system/adcscope/Kconfig b/system/adcscope/Kconfig new file mode 100644 index 000000000..803fe83a9 --- /dev/null +++ b/system/adcscope/Kconfig @@ -0,0 +1,109 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_ADCSCOPE + bool "NxScope ADC stream app" + default n + depends on ADC && LOGGING_NXSCOPE + +if SYSTEM_ADCSCOPE + +config SYSTEM_ADCSCOPE_PROGNAME + string "Program name" + default "adcscope" + +config SYSTEM_ADCSCOPE_PRIORITY + int "adcscope task priority" + default 100 + +config SYSTEM_ADCSCOPE_STACKSIZE + int "adcscope stack size" + default DEFAULT_TASK_STACKSIZE + +config SYSTEM_ADCSCOPE_SERIAL_PATH + string "adcscope serial device path" + default "/dev/ttyUSB0" + +config SYSTEM_ADCSCOPE_SERIAL_BAUD + int "adcscope serial baud" + default 115200 + ---help--- + Ignored if set to 0 (for example for RTT interface) + +config SYSTEM_ADCSCOPE_CDCACM + bool "adcscope CDCACM device support" + depends on CDCACM + default n + +config SYSTEM_ADCSCOPE_FORCE_ENABLE + bool "adcscope force enable" + default n + +config SYSTEM_ADCSCOPE_STREAMBUF_LEN + int "adcscope stream buffer length" + default 512 + +config SYSTEM_ADCSCOPE_RXBUF_LEN + int "adcscope RX buffer length" + default 32 + +config SYSTEM_ADCSCOPE_RX_PADDING + int "adcscope RX padding" + default 0 + ---help--- + This value is returned by NxScope protocol to the client and can + be used to optimize RX transfers when the target supports DMA transfers. + In this case this value should be equal to the DMA RX buffer length. + +config SYSTEM_ADCSCOPE_MAIN_INTERVAL + int "adcscope main interval (microseconds)" + default 100000 + ---help--- + This value is responsible for the frequency at which stream + frames will be sent and incoming frames will be received. + +config SYSTEM_ADCSCOPE_FETCH_INTERVAL + int "adcscope fetch interval (microseconds)" + default 1000 + ---help--- + This value is responsible for the frequency at which the ADC + samples are read. If set to 0, then samples are read at maximum rate. + +config SYSTEM_ADCSCOPE_ADC_PATH + string "adcscope ADC device path" + default "/dev/adc0" + +config SYSTEM_ADCSCOPE_ADCBUF_LEN + int "adcscope buffer length" + default ADC_FIFOSIZE + ---help--- + The number of samples in the buffer used to read from the ADC device. + This is the maximum number of samples that can be read from the ADC + device during one read() operation. + +config SYSTEM_ADCSCOPE_SWTRIG + bool "adcscope uses software trigger" + default n + ---help--- + When this option is set, all read() operations on the ADC device will + be preceded by an ioctl(ANIOC_TRIGGER) call. + +choice + prompt "adcscope data type" + default SYSTEM_ADCSCOPE_INT16 + +config SYSTEM_ADCSCOPE_INT16 + bool "pack ADC data as int16_t" + ---help--- + NxScope send ADC samples as int16_t data. + +config SYSTEM_ADCSCOPE_INT32 + bool "pack ADC data as int32_t" + ---help--- + NxScope send ADC samples as int32_t data. + +endchoice # "adcscope data type" + +endif # SYSTEM_ADCSCOPE diff --git a/system/adcscope/Make.defs b/system/adcscope/Make.defs new file mode 100644 index 000000000..1e16190e4 --- /dev/null +++ b/system/adcscope/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/systen/adcscope/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_ADCSCOPE),) +CONFIGURED_APPS += $(APPDIR)/system/adcscope +endif diff --git a/system/adcscope/Makefile b/system/adcscope/Makefile new file mode 100644 index 000000000..663a95ef8 --- /dev/null +++ b/system/adcscope/Makefile @@ -0,0 +1,34 @@ +############################################################################ +# apps/system/adcscope/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# adcscope built-in application info + +PROGNAME = $(CONFIG_SYSTEM_ADCSCOPE_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_ADCSCOPE_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_ADCSCOPE_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_ADCSCOPE) + +MAINSRC = adcscope_main.c + +CSRCS = + +include $(APPDIR)/Application.mk diff --git a/system/adcscope/adcscope_main.c b/system/adcscope/adcscope_main.c new file mode 100644 index 000000000..3413c6b52 --- /dev/null +++ b/system/adcscope/adcscope_main.c @@ -0,0 +1,482 @@ +/**************************************************************************** + * apps/system/adcscope/adcscope_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "logging/nxscope/nxscope.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Channel name length */ + +#define CHNAME_MAX 8 + +/* ADC data type */ + +#ifdef CONFIG_SYSTEM_ADCSCOPE_INT16 +# define ADATA_PUT nxscope_put_int16 +# define ADATA_DTYPE NXSCOPE_TYPE_INT16 +#elif defined(CONFIG_SYSTEM_ADCSCOPE_INT32) +# define ADATA_PUT nxscope_put_int32 +# define ADATA_DTYPE NXSCOPE_TYPE_INT32 +#else +# error ADC data type not selected +#endif + +/**************************************************************************** + * Private Type Definition + ****************************************************************************/ + +struct adcscope_thr_env_s +{ + /* NxScope data */ + + struct nxscope_s nxs; + struct nxscope_cfg_s nxs_cfg; + struct nxscope_intf_s intf; + struct nxscope_proto_s proto; + struct nxscope_callbacks_s cbs; + struct nxscope_ser_cfg_s nxs_ser_cfg; + + /* ADC data */ + + int fd; + size_t channels; + size_t data_size; + FAR struct adc_msg_s *data; + FAR char *chname; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: adcscope_env_init + ****************************************************************************/ + +static int adcscope_env_init(FAR struct adcscope_thr_env_s *envp) +{ + int ret = OK; + + /* Open ADC device */ + + envp->fd = open(CONFIG_SYSTEM_ADCSCOPE_ADC_PATH, O_RDWR); + if (envp->fd <= 0) + { + printf("ERROR: failed to open %s\n", CONFIG_SYSTEM_ADCSCOPE_ADC_PATH); + ret = -errno; + goto errout; + } + + /* Get number of channels */ + + envp->channels = ioctl(envp->fd, ANIOC_GET_NCHANNELS, 0); + if (envp->channels <= 0) + { + printf("ANIOC_GET_NCHANNELS ioctl failed: %d\n", envp->channels); + ret = -errno; + goto errout; + } + + /* Allocate data buffer */ + + envp->data_size = sizeof(struct adc_msg_s) * envp->channels * + CONFIG_SYSTEM_ADCSCOPE_ADCBUF_LEN; + envp->data = zalloc(envp->data_size); + if (!envp->data) + { + printf("ERROR: zalloc failed %d\n", -errno); + ret = -errno; + goto errout; + } + + /* Allocate channels name buffer */ + + envp->chname = zalloc(envp->channels * CHNAME_MAX); + if (!envp->chname) + { + printf("ERROR: zalloc failed %d\n", -errno); + ret = -errno; + goto errout; + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: adcscope_env_clean + ****************************************************************************/ + +static void adcscope_env_clean(FAR struct adcscope_thr_env_s *envp) +{ + /* Free buffers */ + + free(envp->data); + free(envp->chname); +} + +/**************************************************************************** + * Name: nxscope_cb_start + ****************************************************************************/ + +static int nxscope_cb_start(FAR void *priv, bool start) +{ + UNUSED(priv); + + printf("--> nxscope_cb_start: start=%d\n", start); + + return OK; +} + +/**************************************************************************** + * Name: adcscope_nxscope_init + ****************************************************************************/ + +static int adcscope_nxscope_init(FAR struct adcscope_thr_env_s *envp) +{ + int ret; + + /* Default serial protocol */ + + ret = nxscope_proto_ser_init(&envp->proto, NULL); + if (ret < 0) + { + printf("ERROR: nxscope_proto_ser_init failed %d\n", ret); + goto errout_noproto; + } + + /* Configuration */ + + envp->nxs_ser_cfg.path = CONFIG_SYSTEM_ADCSCOPE_SERIAL_PATH; + envp->nxs_ser_cfg.nonblock = true; + envp->nxs_ser_cfg.baud = CONFIG_SYSTEM_ADCSCOPE_SERIAL_BAUD; + + /* Initialize serial interface */ + + ret = nxscope_ser_init(&envp->intf, &envp->nxs_ser_cfg); + if (ret < 0) + { + printf("ERROR: nxscope_ser_init failed %d\n", ret); + goto errout_nointf; + } + + /* Connect callbacks */ + + envp->cbs.start_priv = NULL; + envp->cbs.start = nxscope_cb_start; + + /* Initialize nxscope */ + + envp->nxs_cfg.intf_cmd = &envp->intf; + envp->nxs_cfg.intf_stream = &envp->intf; + envp->nxs_cfg.proto_cmd = &envp->proto; + envp->nxs_cfg.proto_stream = &envp->proto; + envp->nxs_cfg.callbacks = &envp->cbs; + envp->nxs_cfg.channels = envp->channels; + envp->nxs_cfg.streambuf_len = CONFIG_SYSTEM_ADCSCOPE_STREAMBUF_LEN; + envp->nxs_cfg.rxbuf_len = CONFIG_SYSTEM_ADCSCOPE_RXBUF_LEN; + envp->nxs_cfg.rx_padding = CONFIG_SYSTEM_ADCSCOPE_RX_PADDING; + + ret = nxscope_init(&envp->nxs, &envp->nxs_cfg); + if (ret < 0) + { + printf("ERROR: nxscope_init failed %d\n", ret); + goto errout_nonxscope; + } + + return OK; + +errout_nonxscope: + nxscope_proto_ser_deinit(&envp->proto); +errout_nointf: + nxscope_ser_deinit(&envp->intf); +errout_noproto: + return ret; +} + +/**************************************************************************** + * Name: adcscope_nxscope_clean + ****************************************************************************/ + +static void adcscope_nxscope_clean(FAR struct adcscope_thr_env_s *envp) +{ + /* Deinit nxscope */ + + nxscope_deinit(&envp->nxs); + + /* Deinit protocol */ + + nxscope_proto_ser_deinit(&envp->proto); + + /* Deinit interface */ + + nxscope_ser_deinit(&envp->intf); +} + +/**************************************************************************** + * Name: adcscope_samples_thr + ****************************************************************************/ + +static FAR void *adcscope_samples_thr(FAR void *arg) +{ + FAR struct adcscope_thr_env_s *envp = arg; + int ret; + size_t ptr; + size_t i; + + DEBUGASSERT(envp); + + printf("adcscope_samples_thr\n"); + + while (1) + { +#ifdef CONFIG_SYSTEM_ADCSCOPE_SWTRIG + /* Issue the software trigger to start ADC conversion */ + + ret = ioctl(envp->fd, ANIOC_TRIGGER, 0); + if (ret < 0) + { + printf("ERROR: ANIOC_TRIGGER ioctl failed: %d\n", -errno); + return NULL; + } +#endif + + /* Read data from ADC */ + + ret = read(envp->fd, envp->data, envp->data_size); + if (ret < 0) + { + printf("ERROR: read failed %d\n", -errno); + } + else + { + /* Feed data to nxscope */ + + for (ptr = 0; ptr < ret / sizeof(struct adc_msg_s); ) + { + for (i = 0; i < envp->channels; i++) + { + ADATA_PUT(&envp->nxs, i, envp->data[ptr].am_data); + ptr += 1; + } + } + } + +#if CONFIG_SYSTEM_ADCSCOPE_FETCH_INTERVAL > 0 + usleep(CONFIG_SYSTEM_ADCSCOPE_FETCH_INTERVAL); +#endif + } + + return NULL; +} + +#ifdef CONFIG_SYSTEM_ADCSCOPE_CDCACM +/**************************************************************************** + * Name: adcscope_cdcacm_init + ****************************************************************************/ + +static int adcscope_cdcacm_init(void) +{ + struct boardioc_usbdev_ctrl_s ctrl; + FAR void *handle; + int ret; + + ctrl.usbdev = BOARDIOC_USBDEV_CDCACM; + ctrl.action = BOARDIOC_USBDEV_CONNECT; + ctrl.instance = 0; + ctrl.handle = &handle; + + ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl); + if (ret < 0) + { + printf("ERROR: BOARDIOC_USBDEV_CONTROL failed %d\n", ret); + goto errout; + } + +errout: + return ret; +} +#endif + +/**************************************************************************** + * Name: adcscope_channels + ****************************************************************************/ + +static int adcscope_channels(FAR struct adcscope_thr_env_s *envp) +{ + union nxscope_chinfo_type_u u; + FAR char *chname; + size_t i; + + for (i = 0; i < envp->channels; i++) + { + /* Get channel name */ + + chname = &envp->chname[i * CHNAME_MAX]; + snprintf(chname, CHNAME_MAX, "chan%d", i); + + /* Register nxscope channel */ + + u.s.dtype = ADATA_DTYPE; + u.s._res = 0; + u.s.cri = 0; + + nxscope_chan_init(&envp->nxs, i, chname, u.u8, 1, 0); + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: adcscope_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct adcscope_thr_env_s env; + pthread_t thread; + int ret; + +#ifndef CONFIG_NSH_ARCHINIT + /* Perform architecture-specific initialization (if configured) */ + + boardctl(BOARDIOC_INIT, 0); + +# ifdef CONFIG_BOARDCTL_FINALINIT + /* Perform architecture-specific final-initialization (if configured) */ + + boardctl(BOARDIOC_FINALINIT, 0); +# endif +#endif + +#ifdef CONFIG_SYSTEM_ADCSCOPE_CDCACM + /* Initialize the USB CDCACM device */ + + ret = adcscope_cdcacm_init(); + if (ret < 0) + { + printf("ERROR: adcscope_cdcacm_init failed %d\n", ret); + goto errout_noenv; + } +#endif + + /* Initialize environment */ + + ret = adcscope_env_init(&env); + if (ret < 0) + { + printf("ERROR: failed to open %s\n", CONFIG_SYSTEM_ADCSCOPE_ADC_PATH); + goto errout_nonxscope; + } + + /* Initialize NxScope */ + + ret = adcscope_nxscope_init(&env); + if (ret < 0) + { + printf("ERROR: failed to init nxscope\n"); + goto errout_nonxscope; + } + + /* Create channels */ + + ret = adcscope_channels(&env); + if (ret != OK) + { + printf("ERROR: adcscope_channels failed %d\n", ret); + goto errout; + } + + /* Create samples thread */ + + ret = pthread_create(&thread, NULL, adcscope_samples_thr, &env); + if (ret != OK) + { + printf("ERROR: pthread_create failed %d\n", ret); + goto errout; + } + +#ifdef CONFIG_SYSTEM_ADCSCOPE_FORCE_ENABLE + /* Enable channels and enable stream */ + + nxscope_chan_all_en(&nxs, true); + nxscope_stream_start(&nxs, true); +#endif + + /* Main loop */ + + while (1) + { + /* Flush stream data */ + + ret = nxscope_stream(&env.nxs); + if (ret < 0) + { + printf("ERROR: nxscope_stream failed %d\n", ret); + } + + /* Handle recv data */ + + ret = nxscope_recv(&env.nxs); + if (ret < 0) + { + printf("ERROR: nxscope_recv failed %d\n", ret); + } + + usleep(CONFIG_SYSTEM_ADCSCOPE_MAIN_INTERVAL); + } + +errout: + adcscope_nxscope_clean(&env); +errout_nonxscope: + adcscope_env_clean(&env); +#ifdef CONFIG_SYSTEM_ADCSCOPE_CDCACM +errout_noenv: +#endif + return 0; +}