Example Program for EVAL-AD7606
Dependencies: platform_drivers
app/ad7606_data_capture.c
- Committer:
- mahphalke
- Date:
- 2020-10-16
- Revision:
- 3:83b3133f544a
- Parent:
- 1:819ac9aa5667
- Child:
- 6:32de160dce43
File content as of revision 3:83b3133f544a:
/***************************************************************************//** * @file ad7606_data_capture.c * @brief Data capture interface for AD7606 IIO application * @details This module handles the AD7606 data capturing ******************************************************************************** * Copyright (c) 2020 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 <stdlib.h> #include "app_config.h" #include "ad7606_data_capture.h" #include "ad7606_support.h" #include "gpio_extra.h" /******************************************************************************/ /********************** Macros and Constants Definition ***********************/ /******************************************************************************/ #if (AD7606X_ADC_RESOLUTION > 16) #define BYTES_PER_SAMPLE sizeof(uint32_t) #else #define BYTES_PER_SAMPLE sizeof(uint16_t) #endif /* 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 (5000000) /******************************************************************************/ /********************** Variables and User Defined Data Types *****************/ /******************************************************************************/ /* Device instance for background adc conversion callback */ static volatile struct ad7606_dev *dev = NULL; /* *@enum acq_buffer_state_e *@details Enum holding the data acquisition buffer states **/ typedef enum { BUF_EMPTY, BUF_AVAILABLE, 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 align_cnt; // buffer alignment counter #if (AD7606X_ADC_RESOLUTION > 16) uint32_t data[DATA_BUFFER_SIZE]; #else uint16_t data[DATA_BUFFER_SIZE]; #endif } acq_buf_t; /* ADC data acquisition buffers */ static volatile acq_buf_t acq_buffer; /* Flag to indicate background data capture status */ static volatile bool start_adc_data_capture = false; /* Active channels to be captured */ static volatile uint32_t active_channels = 0; /* Number of active channels */ static uint8_t num_of_active_channels = 0; /* Minimum number of bytes to be read from device over digital interface */ static volatile uint8_t min_bytes_to_read = 0; /* Number of channels requested for read into acquisition buffer */ static volatile uint8_t chn_read_cnt = 0; /* Number of samples requested by IIO client */ static uint16_t num_of_samples = 0; /* Max available size of buffer */ static uint16_t max_available_buffer_size = 0; /******************************************************************************/ /************************ Functions Declarations ******************************/ /******************************************************************************/ static void adjust_buffer_offset(uint8_t *arr_indx, uint8_t *offset_end, uint8_t *mask); /******************************************************************************/ /************************ Functions Definitions *******************************/ /******************************************************************************/ /*! * @brief Function to init the data capture for AD7606 device * @param device[in]- Device instance * @return none */ int32_t iio_data_capture_init(struct ad7606_dev *device) { /* Save the device structure for background conversion */ dev = malloc(sizeof(struct ad7606_dev *)); if (!dev) { return FAILURE; } memcpy(&dev, &device, sizeof(dev)); return SUCCESS; } /*! * @brief Function to read the ADC raw data for single channel * @param device[in]- Device instance * @param chn[in] - Input channel * @return adc raw data/sample */ int32_t single_data_read(void *device, uint8_t chn, polarity_e polarity) { int32_t adc_data = 0; uint32_t adc_raw[AD7606X_ADC_RESOLUTION] = { 0 }; if (ad7606_read(device, adc_raw) != SUCCESS) { adc_raw[chn] = 0; } if (polarity == BIPOLAR) { /* Check for negative adc value for bipolar inputs */ if (adc_raw[chn] >= ADC_MAX_COUNT_BIPOLAR) { /* Take the 2s complement for the negative counts (>full scale value) */ adc_data = ADC_MAX_COUNT_UNIPOLAR - adc_raw[chn]; adc_data = 0 - adc_data; } else { adc_data = adc_raw[chn]; } } else { adc_data = adc_raw[chn]; } return adc_data; } /*! * @brief Function to store the number of requested samples from IIO client * @param bytes[in] - Number of bytes corresponding to requested samples * @return none */ void store_requested_samples_count(size_t bytes) { /* This gets the number of samples requested by IIO client for all active channels */ num_of_samples = bytes / BYTES_PER_SAMPLE; /* Get the actual available size of buffer by aligning with number of requested samples. * e.g. if requested samples are 1050, the max available size of buffer is: * available size = (8192 / 1050) * 1050 = 7 * 1050 = 7350. * The max samples to be requested should always be less than half the max size of buffer * (in this case: 8192 / 2 = 4096). * */ max_available_buffer_size = ((DATA_BUFFER_SIZE / num_of_samples) * num_of_samples); } /*! * @brief Function to read new samples into buffer without timeout IIO request * @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 * @return none */ static void wait_and_read_new_samples(volatile acq_buf_t *input_buffer, char *output_buffer, size_t samples_to_read) { 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 * BYTES_PER_SAMPLE; /* 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 = (int32_t)input_buffer->wr_indx - (int32_t)input_buffer->rd_indx; timeout--; } while (((buff_rd_wr_indx_offset < (int32_t)(samples_to_read)) || (input_buffer->wr_indx < input_buffer->rd_indx)) && (timeout > 0)); if ((timeout == 0) || (input_buffer->wr_indx <= 0)) { /* This returns the empty buffer */ return; } memcpy(output_buffer, (void const *)&input_buffer->data[input_buffer->rd_indx], bytes); input_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) { size_t samples_to_read = bytes / BYTES_PER_SAMPLE; // 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_samples >= max_available_buffer_size) { memset(pbuf, 1, bytes); return bytes; } wait_and_read_new_samples(&acq_buffer, pbuf, samples_to_read); /* 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.align_cnt = 0; acq_buffer.state = BUF_AVAILABLE; } return bytes; } /*! * @brief Function to perform background ADC conversion and data capture * @return none * @details This is an External Interrupt callback function/ISR, which is tied up to * falling edge trigger of BUSY pin. It is trigered when previous data * conversion is over and BUSY line goes low. Upon trigger, conversion * results are read into acquisition buffer and next conversion is triggered. * This continues until background conversion is stopped. */ void do_conversion_callback(void) { #if (AD7606X_ADC_RESOLUTION == 18) uint8_t arr_indx = 0; uint8_t offset_end = 2; uint8_t mask = 0xff; #else uint8_t mask = 0x01; #endif if (start_adc_data_capture == true) { /* Read the conversion result for required number of bytes */ if (ad7606_read_conversion_data(dev, min_bytes_to_read) == SUCCESS) { /* Extract the data based on the active channels selected in IIO client. * Note: The extraction of data based on active channel needs * to be done since its not possible to capture individual * channel in AD7606 devices */ for (uint8_t chn = 0; chn < chn_read_cnt; chn++) { if (active_channels & mask) { if (acq_buffer.state == BUF_AVAILABLE) { #if (AD7606X_ADC_RESOLUTION == 18) /* For AD7606C device (18-bit resolution) */ acq_buffer.data[acq_buffer.wr_indx++] = (((uint32_t)(dev->data[arr_indx] & mask) << 16) | // MSB ((uint32_t)dev->data[arr_indx + 1] << 8) | ((uint32_t)(dev->data[arr_indx + 2] >> (8 - offset_end)))); // LSB #else acq_buffer.data[acq_buffer.wr_indx++] = (uint16_t)(((uint16_t)dev->data[chn << 1] << 8) | // MSB dev->data[(chn << 1) + 1]); // LSB #endif acq_buffer.align_cnt++; /* Monitor primary buffer full state */ if (acq_buffer.wr_indx >= max_available_buffer_size) { acq_buffer.state = BUF_FULL; /* This aligns the buffer to first active channel once all requested samples are transmitted */ if (acq_buffer.align_cnt >= num_of_samples) { acq_buffer.align_cnt = 0; } #if (AD7606X_ADC_RESOLUTION == 18) adjust_buffer_offset(&arr_indx, &offset_end, &mask); #endif mask <<= 1; continue; } /* This aligns the buffer to first active channel once all requested samples are transmitted */ if (acq_buffer.align_cnt >= num_of_samples) { acq_buffer.align_cnt = 0; break; } } } #if (AD7606X_ADC_RESOLUTION == 18) adjust_buffer_offset(&arr_indx, &offset_end, &mask); #endif mask <<= 1; } /* Trigger next conversion */ if (ad7606_convst(dev) != SUCCESS) { start_adc_data_capture = false; } } else { start_adc_data_capture = false; } } } /*! * @brief Function to trigger bakground ADC conversion for new READBUFF * request from IIO client (for active channels) * @return Conversion start status */ void start_background_data_capture(uint32_t ch_mask, size_t bytes_count) { uint8_t mask = 0x1; /* Make sure requested samples size is less than ADC buffer size */ if (num_of_samples >= max_available_buffer_size) return; active_channels = ch_mask; stop_background_data_capture(); /* Find the minimum number of bytes to be read from device during * background conversion (Reading all bytes/channels adds extra overhead, * so restrict the minimum reads to whatever is possible). * Note: This happens only at the start of buffer read request. */ for (uint8_t chn = 1, indx = 0; chn <= AD7606X_ADC_CHANNELS; chn++, indx++) { if (ch_mask & mask) { /* 1 sample = 2 or 4 bytes */ min_bytes_to_read = chn * BYTES_PER_SAMPLE; if (min_bytes_to_read > AD7606X_ADC_RESOLUTION) { min_bytes_to_read = AD7606X_ADC_RESOLUTION; } /* Get the count for number of samples to be stored into acquisition buffer */ chn_read_cnt = chn; num_of_active_channels++; } mask <<= 1; } /* Make primary acquisition buffer available and start conversion */ acq_buffer.state = BUF_AVAILABLE; if (ad7606_convst(dev) == SUCCESS) { start_adc_data_capture = true; } else { start_adc_data_capture = false; } } /*! * @brief Function to stop background ADC data capture * @return none */ void stop_background_data_capture(void) { /* Reset data capture flags */ start_adc_data_capture = false; num_of_active_channels = 0; min_bytes_to_read = 0; chn_read_cnt = 0; /* Reset acquisition buffer states and clear old data */ acq_buffer.state = BUF_EMPTY; memset((void *)acq_buffer.data, 0, sizeof(acq_buffer.data)); acq_buffer.wr_indx = 0; acq_buffer.rd_indx = 0; acq_buffer.align_cnt = 0; } /*! * @brief Function to adjust the data buffer offset * @param *arr_indx[in, out]- Array index * @param *offset_end[out] - offset to extract LSB from 18-bit data * @param *mask[out] - channel select mask * @return none */ static void adjust_buffer_offset(uint8_t *arr_indx, uint8_t *offset_end, uint8_t *mask) { *arr_indx += 2; /* Track array to reach at the middle (9 bytes apart) to change the offsets */ if (*arr_indx == ((AD7606X_ADC_RESOLUTION / 2) - 1)) { (*arr_indx)++; *offset_end = 2; *mask = 0xff; } else { *mask = (0xff >> *offset_end); *offset_end += 2; } }