IIO firmware for the AD4110
Dependencies: tempsensors sdp_k1_sdram
Diff: app/ad4110_data_capture.c
- Revision:
- 0:6ca37a8f8ba9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/ad4110_data_capture.c Wed Jul 27 17:04:15 2022 +0530 @@ -0,0 +1,503 @@ +/***************************************************************************//** + * @file ad4110_data_capture.c + * @brief Source file for AD4110 Data capture +******************************************************************************** +* Copyright (c) 2022 Analog Devices, Inc. +* All rights reserved. +* +* This software is proprietary to Analog Devices, Inc. and its licensors. +* By using this software you agree to the terms of the associated +* Analog Devices Software License Agreement. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include <string.h> +#include "ad4110_data_capture.h" +#include "ad4110_iio.h" +#include "ad4110.h" +#include "no_os_error.h" +#include "app_config.h" + +/******************************************************************************/ +/********************* Macros and Constants Definition ************************/ +/******************************************************************************/ + +/* Timeout count to avoid stuck into potential infinite loop while checking + * for new data into an acquisition buffer. The actual timeout factor is determined + * through 'sampling_frequency' attribute of IIO app, but this period here makes sure + * we are not stuck into a forever loop in case data capture is interrupted + * or failed in between. + * Note: This timeout factor is dependent upon the MCU clock frequency. Below timeout + * is tested for SDP-K1 platform @180Mhz default core clock */ +#define BUF_READ_TIMEOUT 0xffffffff + +/******************************************************************************/ +/******************** Variables and User Defined Data Types *******************/ +/******************************************************************************/ + +/* + *@enum acq_buffer_state_e + *@details Data acquisition buffer states + **/ +typedef enum { + BUF_AVAILABLE, + BUF_EMPTY, + BUF_FULL +} acq_buffer_state_e; + +/* + *@struct acq_buf_t + *@details Data acquisition buffer parameters + **/ +typedef struct { + acq_buffer_state_e state; // Buffer state + uint32_t wr_indx; // Buffer write index (incremented per sample read) + uint32_t rd_indx; // Buffer read index (incremented per sample read) + int8_t *wr_pdata; // Data buffer write pointer + int8_t *rd_pdata; // Data buffer read pointer + bool reindex_buffer; // Reindex buffer to 0th channel +} acq_buf_t; + +/* ADC data acquisition buffers */ +static volatile acq_buf_t acq_buffer; + +/* Number of samples requested by IIO client */ +static volatile uint32_t num_of_requested_samples = 0; + +/* Number of active channels */ +static volatile uint8_t num_of_active_channels; + +/* ADC sample/raw data size in bytes */ +static volatile uint8_t sample_size_in_bytes; + +/* ADC data buffer */ +#if !defined(USE_SDRAM_CAPTURE_BUFFER) +int8_t adc_data_buffer[DATA_BUFFER_SIZE] = { 0 }; +#endif + +/* List of input channels to be captured */ +static volatile uint8_t input_channels[AD4110_NUM_CHANNELS]; + +/* Flag to indicate data capture status */ +static volatile bool start_cont_data_capture = false; + +/* Max available buffer size (after considering the data alignment with IIO buffer) */ +static volatile uint32_t max_buffer_sz; + +/* Current active channel index */ +static volatile uint8_t chn_indx; + +/******************************************************************************/ +/************************** Functions Declaration *****************************/ +/******************************************************************************/ + +/******************************************************************************/ +/************************** Functions Definition ******************************/ +/******************************************************************************/ + +/*! + * @brief Reset the data capture specific variables + * @return none + */ +static void reset_data_capture(void) +{ + /* Reset data capture flags */ + num_of_active_channels = 0; + start_cont_data_capture = false; + + /* Reset acquisition buffer states and clear old data */ + acq_buffer.state = BUF_EMPTY; + acq_buffer.wr_indx = 0; + acq_buffer.rd_indx = 0; + acq_buffer.reindex_buffer = false; + + acq_buffer.wr_pdata = adc_data_buffer; + acq_buffer.rd_pdata = adc_data_buffer; + max_buffer_sz = DATA_BUFFER_SIZE; +} + + +/*! + * @brief Trigger a data capture in continuous/burst mode + * @return 0 in case of success, negative error code otherwise + */ +static int32_t adc_start_data_capture(void) +{ + return ad4110_set_adc_mode(ad4110_dev_inst, AD4110_CONTINOUS_CONV_MODE); +} + + +/*! + * @brief Stop a data capture operation + * @return 0 in case of success, negative error code otherwise + */ +static int32_t adc_stop_data_capture(void) +{ + return ad4110_set_adc_mode(ad4110_dev_inst, AD4110_STANDBY_MODE); +} + + +/*! + * @brief Function to prepare the data ADC capture for new READBUFF + * request from IIO client (for active channels) + * @param chn_mask[in] - Channels to enable for data capturing + * @param num_of_chns[in] - ADC channel count + * @param sample_size[in] - Sample size in bytes + * @return 0 in case of success, negative error code otherwise + */ +int32_t prepare_data_transfer(uint32_t ch_mask, uint8_t num_of_chns, + uint8_t sample_size) +{ + int32_t ret; + uint8_t ch_id; + uint8_t mask = 0x1; + + /* Reset the data capture module */ + reset_data_capture(); + + sample_size_in_bytes = sample_size; + + /* Enable Active channels requested and Disable the remaining */ + for (ch_id = 0; + ch_id < num_of_chns; ch_id++) { + if (ch_mask & mask) { + ret = ad4110_set_channel_status(ad4110_dev_inst, ch_id, true); + if (ret) { + return ret; + } + num_of_requested_samples++; + } else { + ret = ad4110_set_channel_status(ad4110_dev_inst, ch_id, false); + if (ret) { + return ret; + } + } + mask <<= 1; + } + + /* Trigger continuous data capture */ +#if (DATA_CAPTURE_MODE == CONTINUOUS_DATA_CAPTURE) + ret = adc_start_data_capture(); + if (ret) { + return ret; + } + + acq_buffer.state = BUF_AVAILABLE; + start_cont_data_capture = true; + + /* Pull the cs line low to detect the EOC bit during data capture */ + ret = no_os_gpio_set_value(csb_gpio, NO_OS_GPIO_LOW); + if (ret) { + return ret; + } + + ret = no_os_irq_enable(external_int_desc, IRQ_INT_ID); + if(ret) { + return ret; + } +#endif + + return 0; +} + + +/*! + * @brief Function to end data capture + * @return 0 in case of success, negative error code otherwise + */ +int32_t end_data_transfer(void) +{ + start_cont_data_capture = false; + + /* Reset data capture module specific flags and variables */ + reset_data_capture(); + + /* Stop ADC data capture */ + return adc_stop_data_capture(); +} + + +/*! + * @brief Capture requested number of ADC samples in burst mode + * @param pbuf[out] - Pointer to ADC data buffer + * @param nb_of_samples[in] - Number of samples to be read + * @return 0 in case of success, negative error code otherwise + */ +static int32_t read_burst_data(int8_t *pbuf, uint32_t nb_of_samples) +{ + uint32_t sample_index = 0; + uint32_t adc_raw_data = 0; + int32_t ret; + + if (adc_start_data_capture()) { + return -EIO; + } + + while (sample_index < nb_of_samples) { + /* Wait for the RDY Bit to go low to notify end of conversion */ + ret = ad4110_wait_for_rdy_low(ad4110_dev_inst, AD4110_ADC_CONV_TIMEOUT); + if (ret) { + return ret; + } + + /* Read the converted data from the Data register */ + ret = ad4110_spi_int_data_reg_read(ad4110_dev_inst, &adc_raw_data); + if (ret) { + return ret; + } + + /* Copy raw data to the buffer */ + memcpy((uint8_t*)pbuf, &adc_raw_data, sample_size_in_bytes); + sample_index++; + pbuf += sample_size_in_bytes; + } + + /* Stop any active conversion */ + ret = adc_stop_data_capture(); + if (ret) { + return ret; + } + + return 0; +} + + +/*! + * @brief Perform buffer read operations to read requested samples + * @param nb_of_samples[in] - Requested number of samples to read + * @return 0 in case of success, negative error code otherwise + */ +static int32_t buffer_read_operations(uint32_t nb_of_samples) +{ + uint32_t timeout = BUF_READ_TIMEOUT; + int32_t offset; + + /* Wait until requested samples are available in the buffer to read */ + do { + if (acq_buffer.wr_indx >= acq_buffer.rd_indx) { + offset = acq_buffer.wr_indx - acq_buffer.rd_indx; + } else { + offset = max_buffer_sz + (acq_buffer.wr_indx - acq_buffer.rd_indx); + } + + timeout--; + } while ((offset < (int32_t)(nb_of_samples)) && (timeout > 0)); + + if (timeout == 0) { + /* This returns the empty buffer */ + return -EIO; + } + + if (acq_buffer.rd_indx >= max_buffer_sz) { + acq_buffer.rd_indx = 0; + } + + return 0; +} + + +/*! + * @brief Perform buffer write operations such as buffer full or empty + * check, resetting buffer index and pointers, etc + * @return none + */ +static void buffer_write_operations(void) +{ + acq_buffer.wr_indx++; + + /* Perform buffer full check and operations */ + if (acq_buffer.wr_indx >= max_buffer_sz) { + if ((acq_buffer.rd_indx >= num_of_requested_samples) + && (acq_buffer.rd_indx != 0)) { + /* Reset buffer write index and write pointer when enough + * space available in the buffer to wrap to start */ + acq_buffer.wr_indx = 0; + + acq_buffer.wr_pdata = adc_data_buffer; + if (acq_buffer.rd_indx >= max_buffer_sz) { + /* Wrap the read index and read pointer to start of buffer + * if buffer is completely read/emptied */ + acq_buffer.rd_indx = 0; + acq_buffer.rd_pdata = adc_data_buffer; + } + + acq_buffer.state = BUF_AVAILABLE; + } else { + /* Wait until enough space available to wrap buffer write index + * at the start of buffer */ + acq_buffer.wr_indx = max_buffer_sz; + acq_buffer.state = BUF_FULL; + acq_buffer.reindex_buffer = true; + } + } +} + + +/*! + * @brief Read requested number of ADC samples in continuous mode + * @param pbuf[in] - Pointer to data buffer + * @param nb_of_samples[in] - Number of samples to read + * @return 0 in case of success, negative error code otherwise + * @note The actual sample capturing happens through interrupt. This + * function tracks the buffer read pointer to read block of data + */ +static int32_t read_continuous_conv_data(int8_t **pbuf, uint32_t nb_of_samples) +{ + volatile int8_t *rd_addr; + int32_t ret; + + /* Determine the max available buffer size based on the requested + * samples count and actual avilable buffer size. Buffer should be + * capable of holding all requested 'n' samples from previous write + * index upto to the end of buffer, as data is read linearly + * from adc buffer in IIO library. + * E.g. If actual buffer size is 2048 samples and requested samples + * are 1600, max available buffer size is actually 1600. So in given + * iteration, only 1600 samples will be stored into buffer and after + * that buffer indexes will be wrapped to a start of buffer. If index + * is not wrapped, the next 1600 requested samples won't accomodate into + * remaining 448 samples space. As buffer is read in linear fashion, the + * read index can't be wrapped to start of buffer to read remaining samples. + * So max available space in this case is 2048 but only utilized space + * will be 1600 in single read buffer request from IIO client. + **/ + max_buffer_sz = ((DATA_BUFFER_SIZE / sample_size_in_bytes) / + nb_of_samples) * nb_of_samples; + + ret = buffer_read_operations(nb_of_samples); + if (ret) { + return ret; + } + + /* Get the next read address */ + rd_addr = (volatile int8_t *)(acq_buffer.rd_pdata + (acq_buffer.rd_indx * + sample_size_in_bytes)); + acq_buffer.rd_indx += nb_of_samples; + + /* Update the IIO buffer pointer to point to next read start location */ + *pbuf = rd_addr; + + return 0; +} + + +/*! + * @brief Read ADC raw data for recently sampled channel + * @param adc_data[out] - Pointer to adc data read variable + * @param input_chn[in] - Input channel + * @return 0 in case of success, negative error code otherwise + * @note This function is intended to call from the conversion end trigger + * event. Therefore, this function should just read raw ADC data + * without further monitoring conversion end event + */ +static int32_t adc_read_converted_sample(uint32_t *adc_data, uint8_t input_chn) +{ + int32_t ret; + + if (!adc_data) { + return -EIO; + } + + ret = ad4110_spi_int_data_reg_read(ad4110_dev_inst, adc_data); + if (ret) { + return ret; + } + + /* Pull the cs line low to detect the EOC bit during data capture */ + ret = no_os_gpio_set_value(csb_gpio, NO_OS_GPIO_LOW); + if (ret) { + return ret; + } + + ret = no_os_irq_enable(external_int_desc, IRQ_INT_ID); + if (ret) { + return ret; + } + + return 0; +} + + +/*! + * @brief This is an ISR (Interrupt Service Routine) to monitor end of conversion event. + * @param ctx[in] - Callback context (unused) + * @return none + * @details This is an Interrupt callback function/ISR invoked in synchronous/asynchronous + * manner depending upon the application implementation. The conversion results + * are read into acquisition buffer and control continue to sample next channel. + * This continues until conversion is stopped (through IIO client command) + */ +void data_capture_callback(void *ctx) +{ + uint32_t adc_sample; + volatile uint8_t *wr_addr; + + /* The callback function is triggered when the first falling edge is detected + * on the MISO pin, indicating End of Conversion. Interrupt is disabled + because any data transaction on the SPI line could be interpreted + by the MCU as a falling edge. + */ + + no_os_irq_disable(external_int_desc, IRQ_INT_ID); + + if (start_cont_data_capture == true) { + /* Read the sample for channel which has been sampled recently */ + if (adc_read_converted_sample(&adc_sample, + input_channels[chn_indx]) == 0) { + do { + if (acq_buffer.state == BUF_AVAILABLE) { + if (acq_buffer.reindex_buffer) { + /* Buffer refilling must start with first active channel data + * for IIO client to synchronize the buffered data */ + if (chn_indx != 0) { + break; + } + acq_buffer.reindex_buffer = false; + } + + /* Copy adc samples into acquisition buffer to transport over + * communication link */ + wr_addr = (volatile uint8_t *)(acq_buffer.wr_pdata + (acq_buffer.wr_indx * + sample_size_in_bytes)); + memcpy((uint8_t *)wr_addr, &adc_sample, sample_size_in_bytes); + } + + /* Perform buffer write operations */ + buffer_write_operations(); + } while (0); + + /* Track the count for recently sampled channel */ + chn_indx++; + if (chn_indx >= num_of_active_channels) { + chn_indx = 0; + } + } + } +} + + +/*! + * @brief Function to read the ADC buffered raw data requested by IIO client + * @param pbuf[in] - Pointer to data buffer + * @param nb_of_bytes[in] - Number of bytes to read + * @return 0 in case of success, negative error code otherwise + */ +int32_t read_buffered_data(int8_t **pbuf, uint32_t nb_of_bytes) +{ + int32_t ret; + num_of_requested_samples = nb_of_bytes / sample_size_in_bytes; + +#if (DATA_CAPTURE_MODE == BURST_DATA_CAPTURE) + ret = read_burst_data(*pbuf, num_of_requested_samples); +#else + ret = read_continuous_conv_data(pbuf, num_of_requested_samples); +#endif + if (ret) { + return ret; + } + + return 0; +} \ No newline at end of file