Forked repo of Platform Drivers- Analog Devices
Diff: adc_data_capture.c
- Revision:
- 17:af1f2416dd26
- Child:
- 18:5ae03a197e59
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/adc_data_capture.c Tue Jul 13 13:58:07 2021 +0530 @@ -0,0 +1,453 @@ +/***************************************************************************//** + * @file adc_data_capture.c + * @brief ADC common data capture interface for IIO based applications + * @details This module handles the ADC data capturing for IIO client +******************************************************************************** + * Copyright (c) 2021 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 <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include "adc_data_capture.h" +#include "error.h" + +/******************************************************************************/ +/********************** Macros and Constants Definition ***********************/ +/******************************************************************************/ + +/* IIO buffer request size in bytes */ +#define IIO_BUF_READ_REQUEST_SIZE (256) + +/* Max size of the acquisition buffer (in terms of samples) */ +#define DATA_BUFFER_SIZE (8192) + +/* 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, but this period here makes sure + * we are not stuck into a loop forever in case data capture interrupted + * or failed in between */ +#define BUF_READ_TIMEOUT (100000000) + +/******************************************************************************/ +/********************** Variables and User Defined Data Types *****************/ +/******************************************************************************/ + +/* Extern declaration for data capture operations structure variable */ +extern struct data_capture_ops data_capture_ops; + +/* + *@enum acq_buffer_state_e + *@details Enum holding the data acquisition buffer states + **/ +typedef enum { + BUF_AVAILABLE, + BUF_EMPTY, + BUF_FULL +} acq_buffer_state_e; + +/* + *@struct acq_buf_t + *@details Structure holding the data acquisition buffer parameters + **/ +typedef struct { + acq_buffer_state_e state; // buffer state + uint32_t rd_indx; // buffer read index + uint32_t wr_indx; // buffer write index + uint32_t sample_cnt; // buffer data/sample counter + uint32_t data[DATA_BUFFER_SIZE]; // buffer data (adc raw values) +} acq_buf_t; + +/* ADC data acquisition buffers */ +static volatile acq_buf_t acq_buffer; + +/* Flag to indicate data capture status */ +static volatile bool start_adc_data_capture = false; + +/* Number of active channels in any data buffer read request */ +static volatile uint8_t num_of_active_channels = 0; + +/* Channel data alignment variables */ +static volatile uint8_t buff_chn_indx = 0; +static volatile bool do_chn_alignment = false; + +/* Count to track number of actual samples requested by IIO client */ +static volatile uint16_t num_of_requested_samples = 0; + +/* Actual or max available size of acquisition buffer */ +static volatile uint16_t max_available_buffer_size = 0; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/*! + * @brief Function to read the ADC raw data for single channel + * @param input_chn[in] - Input channel to sample and read data for + * @param raw_data[in, out]- ADC raw data + * @return SUCCESS in case of success, FAILURE otherwise + */ +int32_t single_data_read(uint8_t input_chn, uint32_t *raw_data) +{ + uint32_t read_adc_data = 0; + int32_t status = SUCCESS; + + do { + if (data_capture_ops.save_prev_active_chns) { + /* Save previously active channels */ + if (data_capture_ops.save_prev_active_chns() != SUCCESS) { + status = FAILURE; + break; + } + } + + if (data_capture_ops.disable_all_chns) { + /* Disable all channels */ + if (data_capture_ops.disable_all_chns() != SUCCESS) { + status = FAILURE; + break; + } + } + + if (data_capture_ops.enable_curr_chn) { + /* Enable user input channel */ + if (data_capture_ops.enable_curr_chn(input_chn) != SUCCESS) { + status = FAILURE; + break; + } + } + + if (data_capture_ops.enable_single_read_conversion) { + /* Enable single data read conversion */ + if (data_capture_ops.enable_single_read_conversion() != SUCCESS) { + status = FAILURE; + break; + } + } + + /* Read the data */ + if (data_capture_ops.wait_for_conv_and_read_data(&read_adc_data, input_chn) != + SUCCESS) { + status = FAILURE; + break; + } + } while (0); + + if (data_capture_ops.restore_prev_active_chns) { + /* Enable back channels which were disabled prior to single data read */ + data_capture_ops.restore_prev_active_chns(); + } + + if (data_capture_ops.disable_conversion) { + /* Exit from conversion mode */ + data_capture_ops.disable_conversion(); + } + + *raw_data = read_adc_data; + return status; +} + + +/*! + * @brief Function to store the number of actul requested samples from IIO client + * @param bytes[in] - Number of bytes corresponding to requested samples + * @param sample_size_in_bytes[in] - Size of each sample in bytes (eqv to ADC resolution) + * @return none + */ +void store_requested_samples_count(size_t bytes, uint8_t sample_size_in_bytes) +{ + /* This gets the number of samples requested by IIO client for all active channels */ + num_of_requested_samples = bytes / sample_size_in_bytes; + + /* Get the actual available size of buffer by aligning with number of requested samples. + * e.g. if requested samples are 400, the max available size of buffer is: + * available size = (2048 / 400) * 400 = 5 * 400 = 2000. + * The max samples to be requested should always be less than half the max size of buffer + * (in this case: 2048 / 2 = 1024). + * */ + max_available_buffer_size = ((DATA_BUFFER_SIZE / num_of_requested_samples) * + num_of_requested_samples); +} + + +/*! + * @brief Function to read new samples into buffer without IIO request timeout + * @param input_buffer[in] - Input data acquisition buffer + * @param output_buffer[in, out] - Output data buffer + * @param samples_to_read[in] - Number of samples to read + * @param sample_size_in_bytes[in] - Size of each sample in bytes (eqv to ADC resolution) + * @return none + */ +static void wait_and_read_new_samples(char *output_buffer, + size_t samples_to_read, + uint8_t sample_size_in_bytes) +{ + int32_t buff_rd_wr_indx_offset; // Offset b/w buffer read and write indexes + uint32_t timeout = BUF_READ_TIMEOUT; // Buffer new data read timeout count + size_t bytes = samples_to_read * sample_size_in_bytes; + + /* Copy the bytes into buffer provided there is enough offset b/w read and write counts. + * If there is overlap b/w read and write indexes (read is faster than write), empty buffer + * should be returned to IIO client to avoid IIO request timeout */ + do { + buff_rd_wr_indx_offset = (acq_buffer.wr_indx - acq_buffer.rd_indx); + timeout--; + } while (((buff_rd_wr_indx_offset < (int32_t)(samples_to_read)) + || (acq_buffer.wr_indx < acq_buffer.rd_indx)) && (timeout > 0) + && start_adc_data_capture); + + if ((timeout == 0) || (acq_buffer.wr_indx <= 0)) { + /* This returns the empty buffer */ + return; + } + + memcpy(output_buffer, + (void const *)&acq_buffer.data[acq_buffer.rd_indx], + bytes); + acq_buffer.rd_indx += samples_to_read; +} + + +/*! + * @brief Function to read and align the ADC buffered raw data + * @param device[in]- Device instance + * @param pbuf[out] - Buffer to load ADC raw data + * @param bytes[in] - Number of bytes to be read + * @param active_chns_mask[in] - Active channels mask + * @return Number of bytes read + */ +size_t buffered_data_read(char *pbuf, size_t bytes, + size_t offset, uint32_t active_chns_mask, uint8_t sample_size_in_bytes) +{ + size_t samples_to_read = bytes / + sample_size_in_bytes; // Bytes to sample conversion + + /* Make sure requested samples size is less than ADC buffer size. Return constant + * value to avoid IIO client getting timed-out */ + if (num_of_requested_samples >= max_available_buffer_size) { + memset(pbuf, 1, bytes); + return bytes; + } + + wait_and_read_new_samples(pbuf, samples_to_read, sample_size_in_bytes); + + /* Make buffer available again once read completely */ + if (acq_buffer.rd_indx >= max_available_buffer_size) { + acq_buffer.rd_indx = 0; + acq_buffer.wr_indx = 0; + acq_buffer.sample_cnt = 0; + acq_buffer.state = BUF_AVAILABLE; + } + + return bytes; +} + + +/*! + * @brief This is an ISR (Interrupt Service Routine) to monitor end of conversion event. + * @param *ctx[in] - Callback context (unused) + * @param event[in] - Callback event (unused) + * @param extra[in] - Callback extra (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) + * @note This function also handles the logic to align the zeroth channel data after + * every 'n' sample transmission. This is required to visualize data properly + * on IIO client application (more details listed below). + */ +void data_capture_callback(void *ctx, uint32_t event, void *extra) +{ + uint32_t adc_sample; + + if (start_adc_data_capture == true) { + do { + /* Read the data for channel which has been sampled recently */ + if (data_capture_ops.read_sampled_data(&adc_sample, buff_chn_indx) != SUCCESS) { + break; + } + + /* If next sample/data in acquisition buffer is required to be of the zeroth + * channel from a list of enabled channels (i.e. chn data alignment needed), then + * wait until a conversion end event is triggered for that zeroth enabled channel */ + /* Note: As shown below, after nth sample read, next sample must be for the zeroth + * channel present in the list of enabled channels */ + /*+-----------------------+-------------------------+---------------------------+ + *| 0 | 1 | 2 | ------| n | 0 | 1 | 2 | --------| n | 0 | 1 | 2 |-----------| n | + *+-^-------------------^-+-^---------------------^-+-^-----------------------^-+ + * | | | | | | + * 0th chn data last data 0th chn data last data 0th chn data last data + * n = number of requested samples + **/ + /* Wait until conversion event for the zeroth channel is triggered */ + if ((do_chn_alignment == true) && (buff_chn_indx != 0)) { + /* Track the count for recently sampled channel */ + buff_chn_indx++; + if (buff_chn_indx >= num_of_active_channels) { + buff_chn_indx = 0; + } + + /* If recent sampled channel is not a zeroth enabled channel + * in the channels list, return without storing data into buffer */ + break; + } + + /* Track the count for recently sampled channel */ + buff_chn_indx++; + if (buff_chn_indx >= num_of_active_channels) { + buff_chn_indx = 0; + } + + if (acq_buffer.state == BUF_AVAILABLE) { + /* Reset channel data alignment flag if control reach here */ + do_chn_alignment = false; + + acq_buffer.data[acq_buffer.wr_indx++] = adc_sample; + acq_buffer.sample_cnt++; + + /* Check if current buffer is full */ + if (acq_buffer.wr_indx >= max_available_buffer_size) { + acq_buffer.state = BUF_FULL; + } + + /* Once all requested number of samples are transmitted, make sure + * next data to be loaded into acquisition buffer is for zeroth channel + * present in the channels list */ + if (acq_buffer.sample_cnt >= num_of_requested_samples) { + acq_buffer.sample_cnt = 0; + do_chn_alignment = true; + } + } + } while (0); + + /* Trigger next continuous conversion (optional or device dependent) */ + if (data_capture_ops.trigger_next_cont_conversion) { + data_capture_ops.trigger_next_cont_conversion(); + } + } +} + + +/*! + * @brief Reset the data capture specific variables + * @return none + */ +static void reset_data_capture(void) +{ + /* Reset data capture flags */ + start_adc_data_capture = false; + num_of_active_channels = 0; + + /* 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.sample_cnt = 0; + + buff_chn_indx = 0; + do_chn_alignment = true; +} + + +/*! + * @brief Function to trigger ADC conversion for new READBUFF + * request from IIO client (for active channels) + * @param ch_mask[in] - Channels to enable for data capturing + * @param num_of_chns[in] - ADC channel count + * @return none + */ +void start_data_capture(uint32_t ch_mask, uint8_t num_of_chns) +{ + uint32_t mask = 0x1; + + /* Make sure requested samples size is less than ADC buffer size */ + if (num_of_requested_samples >= max_available_buffer_size) { + return; + } + + if (data_capture_ops.disable_conversion) { + /* Stop any previous conversion by putting ADC into standby mode */ + if (data_capture_ops.disable_conversion() != SUCCESS) { + return; + } + } + + /* Reset data capture related flags and variables */ + reset_data_capture(); + + if (data_capture_ops.save_prev_active_chns) { + /* Store the previous active channels */ + if (data_capture_ops.save_prev_active_chns() != SUCCESS) { + return; + } + } + + /* Enable/Disable channels based on channel mask set in the IIO client */ + for (uint8_t chn = 0; chn < num_of_chns; chn++) { + if (ch_mask & mask) { + if (data_capture_ops.enable_curr_chn) { + /* Enable the selected channel */ + if (data_capture_ops.enable_curr_chn(chn) != SUCCESS) { + return; + } + } + + num_of_active_channels++; + } else { + if (data_capture_ops.disable_curr_chn) { + /* Disable the selected channel */ + if (data_capture_ops.disable_curr_chn(chn) != SUCCESS) { + return; + } + } + } + + mask <<= 1; + } + + /* Make primary acquisition buffer available and start continuous conversion */ + acq_buffer.state = BUF_AVAILABLE; + if (data_capture_ops.enable_continuous_read_conversion(ch_mask) == SUCCESS) { + start_adc_data_capture = true; + } else { + start_adc_data_capture = false; + } +} + + +/*! + * @brief Function to stop ADC data capture + * @return none + */ +void stop_data_capture(void) +{ + start_adc_data_capture = false; + + if (data_capture_ops.disable_conversion) { + /* Stop conversion */ + data_capture_ops.disable_conversion(); + } + + if (data_capture_ops.restore_prev_active_chns) { + /* Enabled all previously active channels (active during conversion start) */ + data_capture_ops.restore_prev_active_chns(); + } + + reset_data_capture(); +}