Platform drivers for Mbed.

Dependents:   EVAL-CN0535-FMCZ EVAL-CN0535-FMCZ EVAL-AD568x-AD569x EVAL-AD7606 ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers adc_data_capture.c Source File

adc_data_capture.c

Go to the documentation of this file.
00001 /***************************************************************************//**
00002  *   @file    adc_data_capture.c
00003  *   @brief   ADC common data capture interface for IIO based applications
00004  *   @details This module handles the ADC data capturing for IIO client
00005 ********************************************************************************
00006  * Copyright (c) 2021 Analog Devices, Inc.
00007  * All rights reserved.
00008  *
00009  * This software is proprietary to Analog Devices, Inc. and its licensors.
00010  * By using this software you agree to the terms of the associated
00011  * Analog Devices Software License Agreement.
00012 *******************************************************************************/
00013 
00014 /******************************************************************************/
00015 /***************************** Include Files **********************************/
00016 /******************************************************************************/
00017 
00018 #include <stdint.h>
00019 #include <stdbool.h>
00020 #include <string.h>
00021 
00022 #include "adc_data_capture.h"
00023 #include "error.h"
00024 
00025 /******************************************************************************/
00026 /********************** Macros and Constants Definition ***********************/
00027 /******************************************************************************/
00028 
00029 /* Max available channels for continuous data capture. Actual number of channels
00030  * to be captured are supplied from an application */
00031 #define MAX_AVAILABLE_CHANNELS      (16)
00032 
00033 /* Max size of the acquisition buffer (in terms of bytes) */
00034 #define DATA_BUFFER_SIZE    (32768)     // 32Kbytes
00035 
00036 /* Timeout count to avoid stuck into potential infinite loop while checking
00037  * for new data into an acquisition buffer. The actual timeout factor is determined
00038  * through 'sampling_frequency' attribute of IIO app, but this period here makes sure
00039  * we are not stuck into a forever loop in case data capture is interrupted
00040  * or failed in between.
00041  * Note: This timeout factor is dependent upon the MCU clock frequency. Below timeout
00042  * is tested for SDP-K1 platform @180Mhz default core clock */
00043 #define BUF_READ_TIMEOUT    (100000000)
00044 
00045 /******************************************************************************/
00046 /********************** Variables and User Defined Data Types *****************/
00047 /******************************************************************************/
00048 
00049 /* Extern declaration for device specific data capture operations structure
00050  * (actual definition should be present in an application) */
00051 extern struct data_capture_ops data_capture_ops;
00052 
00053 /*
00054  *@enum     acq_buffer_state_e
00055  *@details  Enum holding the data acquisition buffer states
00056  **/
00057 typedef enum {
00058     BUF_AVAILABLE,
00059     BUF_EMPTY,
00060     BUF_FULL
00061 } acq_buffer_state_e;
00062 
00063 /*
00064  *@struct   acq_buf_t
00065  *@details  Structure holding the data acquisition buffer parameters
00066  **/
00067 typedef struct {
00068     acq_buffer_state_e state;           // Buffer state
00069     bool refill_buffer;                 // Flag to start refilling acquisition buffer
00070     uint32_t rd_indx;                   // Buffer read index (incremented per sample transmit)
00071     uint32_t wr_indx;                   // Buffer write index (incremented per sample read)
00072     uint8_t sample_size;                // ADC sample/raw data size received from application
00073     uint8_t chn_indx;                   // ADC channel index into acquisition buffer
00074     uint8_t active_chn[MAX_AVAILABLE_CHANNELS];     // Active channel number sequence
00075     uint8_t data[DATA_BUFFER_SIZE];                 // buffer data (adc raw values)
00076     uint8_t *pdata;                                 // Pointer to data buffer
00077 } acq_buf_t;
00078 
00079 /* ADC data acquisition buffers */
00080 static volatile acq_buf_t acq_buffer;
00081 
00082 /* Flag to indicate data capture status */
00083 static volatile bool start_adc_data_capture = false;
00084 
00085 /* Number of active channels in any data buffer read request */
00086 static volatile uint8_t num_of_active_channels = 0;
00087 
00088 /* Count to track number of actual samples requested by IIO client */
00089 static volatile uint16_t num_of_requested_samples = 0;
00090 
00091 /* Channel alignment offset */
00092 static volatile uint8_t chn_alignment_offset = 0;
00093 
00094 /* Actual or max available size of acquisition buffer */
00095 static volatile uint16_t max_available_buffer_size = 0;
00096 
00097 /******************************************************************************/
00098 /************************ Functions Declarations ******************************/
00099 /******************************************************************************/
00100 
00101 /******************************************************************************/
00102 /************************ Functions Definitions *******************************/
00103 /******************************************************************************/
00104 
00105 /*!
00106  * @brief   Function to read the single ADC sample (raw data) for input channel
00107  * @param   input_chn[in] - Input channel to sample and read data for
00108  * @param   raw_data[in, out]- ADC raw data
00109  * @return  SUCCESS in case of success, FAILURE otherwise
00110  */
00111 int32_t read_single_sample(uint8_t input_chn, uint32_t *raw_data)
00112 {
00113     uint32_t read_adc_data = 0;
00114     int32_t status = SUCCESS;
00115 
00116     do {
00117         /* Perform operations required before single sample conversion read */
00118         if (data_capture_ops.single_sample_read_start_ops) {
00119             if (data_capture_ops.single_sample_read_start_ops(input_chn) != SUCCESS) {
00120                 status = FAILURE;
00121                 break;
00122             }
00123         }
00124 
00125         /* Perform ADC conversion and read the converted sample after EOC */
00126         if (data_capture_ops.perform_conv_and_read_sample(&read_adc_data, input_chn) !=
00127             SUCCESS) {
00128             status = FAILURE;
00129             break;
00130         }
00131     } while (0);
00132 
00133     /* Perform operations required post single sample conversion read */
00134     if (data_capture_ops.single_sample_read_stop_ops) {
00135         if (data_capture_ops.single_sample_read_stop_ops(input_chn) != SUCCESS) {
00136             status = FAILURE;
00137         }
00138     }
00139 
00140     *raw_data = read_adc_data;
00141     return status;
00142 }
00143 
00144 
00145 /*!
00146  * @brief   Function to store the number of actul requested ADC samples from IIO client
00147  * @param   bytes[in] - Number of bytes corresponding to requested samples
00148  * @param   sample_size_in_bytes[in] - Size of each sample in bytes (eqv to ADC resolution)
00149  * @return  none
00150  * @note    The information about sample and buffer size is required for continuous
00151  *          data acquisition
00152  */
00153 void store_requested_samples_count(size_t bytes, uint8_t sample_size_in_bytes)
00154 {
00155     /* This gets the number of samples requested by IIO client for all active channels */
00156     num_of_requested_samples = bytes / sample_size_in_bytes;
00157 
00158     /* Store the ADC sample size */
00159     acq_buffer.sample_size = sample_size_in_bytes;
00160 
00161     /* Get the actual available size of buffer by aligning with number of requested samples.
00162      * e.g. if requested samples are 400 and sample size is 2 bytes, the max available
00163      * size of buffer is: available size = ((32768 / 2) / 400) * 400 = 40 * 400 = 16000.
00164      * The max samples to be requested should always be less than half the max size of buffer
00165      * (in this case: (32768/2) / 2 = 8192).
00166      * */
00167     max_available_buffer_size = ((DATA_BUFFER_SIZE / sample_size_in_bytes) /
00168                      num_of_requested_samples) * num_of_requested_samples;
00169 }
00170 
00171 
00172 /*!
00173  * @brief   Function to read acquired samples into buffer without IIO request timeout
00174  * @param   input_buffer[in] - Input data acquisition buffer
00175  * @param   output_buffer[in, out] - Output data buffer
00176  * @param   samples_to_read[in] - Number of samples to read
00177  * @param   sample_size_in_bytes[in] - Size of each sample in bytes (eqv to ADC resolution)
00178  * @return  none
00179  */
00180 static void read_acquired_samples(char *output_buffer,
00181                   size_t samples_to_read,
00182                   uint8_t sample_size_in_bytes)
00183 {
00184     int32_t buff_rd_wr_indx_offset;             // Offset b/w buffer read and write indexes
00185     uint32_t timeout = BUF_READ_TIMEOUT;    // Buffer new data read timeout count
00186     size_t bytes =  samples_to_read * sample_size_in_bytes;
00187 
00188     /* Copy the bytes into buffer provided there is enough offset b/w read and write counts.
00189      * If there is overlap b/w read and write indexes (read is faster than write), empty buffer
00190      * should be returned to IIO client to avoid IIO request timeout */
00191     do {
00192         buff_rd_wr_indx_offset = (acq_buffer.wr_indx - acq_buffer.rd_indx);
00193         timeout--;
00194     } while (((buff_rd_wr_indx_offset < (int32_t)(samples_to_read))
00195           || (acq_buffer.wr_indx < acq_buffer.rd_indx)) && (timeout > 0)
00196          && start_adc_data_capture);
00197 
00198     if ((timeout == 0) || (acq_buffer.wr_indx <= 0)) {
00199         /* This returns the empty buffer */
00200         return;
00201     }
00202 
00203     memcpy(output_buffer,
00204            (void const *)&acq_buffer.data[acq_buffer.rd_indx * sample_size_in_bytes],
00205            bytes);
00206     acq_buffer.rd_indx += samples_to_read;
00207 }
00208 
00209 
00210 /*!
00211  * @brief   Function to read and align the ADC buffered raw data
00212  * @param   device[in]- Device instance
00213  * @param   pbuf[out] - Buffer to load ADC raw data
00214  * @param   bytes[in] - Number of bytes to be read
00215  * @param   active_chns_mask[in] - Active channels mask
00216  * @return  Number of bytes read
00217  */
00218 size_t read_buffered_data(char *pbuf,
00219               size_t bytes,
00220               size_t offset,
00221               uint32_t active_chns_mask,
00222               uint8_t sample_size_in_bytes)
00223 {
00224     size_t samples_to_read = bytes /
00225                  sample_size_in_bytes;      // Bytes to sample conversion
00226 
00227     /* Make sure requested samples size is less than ADC buffer size. Return constant
00228      * value to avoid IIO client getting timed-out */
00229     if(num_of_requested_samples >= max_available_buffer_size) {
00230         memset(pbuf, 1, bytes);
00231         return bytes;
00232     }
00233 
00234     /* Increment read counter to point to next acquired data of 1st active channel */
00235     if ((offset == 0) && (acq_buffer.rd_indx > 0)) {
00236         acq_buffer.rd_indx += chn_alignment_offset;
00237     }
00238 
00239     read_acquired_samples(pbuf, samples_to_read, sample_size_in_bytes);
00240 
00241     /* Make buffer available again once it is read/transmited completely */
00242     if (acq_buffer.rd_indx >= max_available_buffer_size) {
00243         acq_buffer.rd_indx = 0;
00244         acq_buffer.wr_indx = 0;
00245         acq_buffer.pdata = acq_buffer.data;
00246         acq_buffer.state = BUF_AVAILABLE;
00247         acq_buffer.refill_buffer = true;
00248     }
00249 
00250     return bytes;
00251 }
00252 
00253 
00254 /*!
00255  * @brief   This is an ISR (Interrupt Service Routine) to monitor end of conversion event.
00256  * @param   *ctx[in] - Callback context (unused)
00257  * @param   event[in] - Callback event (unused)
00258  * @param   extra[in] - Callback extra (unused)
00259  * @return  none
00260  * @details This is an Interrupt callback function/ISR invoked in synchronous/asynchronous
00261  *          manner depending upon the application implementation. The conversion results
00262  *          are read into acquisition buffer and control continue to sample next channel.
00263  *          This continues until conversion is stopped (through IIO client command)
00264  * @note    This function also handles the logic to align the first channel data after
00265  *          every 'n' sample transmission. This is required to visualize data properly
00266  *          on IIO client application.
00267  */
00268 void data_capture_callback(void *ctx, uint32_t event, void *extra)
00269 {
00270     uint32_t adc_sample;
00271 
00272     if (start_adc_data_capture == true) {
00273         /* Read the sample(s) for channel(s) which has/have been sampled recently and
00274          * get the number of samples read count */
00275         if (data_capture_ops.read_converted_sample(&adc_sample,
00276                 acq_buffer.active_chn[acq_buffer.chn_indx]) != FAILURE) {
00277             do {
00278                 if (acq_buffer.state == BUF_AVAILABLE) {
00279                     if (acq_buffer.refill_buffer) {
00280                         /* Buffer refilling must start with first active channel data
00281                          * for IIO client to synchronize the buffered data */
00282                         if (acq_buffer.chn_indx != 0) {
00283                             break;
00284                         }
00285                         acq_buffer.refill_buffer = false;
00286                     }
00287 
00288                     /* Copy adc samples into acquisition buffer to transport over
00289                      * communication link */
00290                     memcpy(acq_buffer.pdata, &adc_sample, acq_buffer.sample_size);
00291                     acq_buffer.pdata += acq_buffer.sample_size;
00292 
00293                     /* Check for acquisition buffer full condition */
00294                     acq_buffer.wr_indx++;
00295                     if (acq_buffer.wr_indx >= max_available_buffer_size) {
00296                         acq_buffer.state = BUF_FULL;
00297                     }
00298                 }
00299             } while (0);
00300 
00301             /* Keep tracking channel index as it is needed to refill the buffer
00302              * starting with first channel data */
00303             acq_buffer.chn_indx++;
00304             if (acq_buffer.chn_indx >= num_of_active_channels) {
00305                 acq_buffer.chn_indx = 0;
00306             }
00307         }
00308 
00309         /* Trigger next continuous conversion (optional or device dependent) */
00310         if (data_capture_ops.trigger_next_conversion) {
00311             data_capture_ops.trigger_next_conversion();
00312         }
00313     }
00314 }
00315 
00316 
00317 /*!
00318  * @brief   Reset the data capture specific variables
00319  * @return  none
00320  */
00321 static void reset_data_capture(void)
00322 {
00323     /* Reset data capture flags */
00324     start_adc_data_capture = false;
00325     num_of_active_channels = 0;
00326 
00327     /* Reset acquisition buffer states and clear old data */
00328     acq_buffer.state = BUF_EMPTY;
00329 
00330     acq_buffer.wr_indx = 0;
00331     acq_buffer.rd_indx = 0;
00332     acq_buffer.chn_indx = 0;
00333     acq_buffer.refill_buffer = false;
00334     acq_buffer.pdata = acq_buffer.data;
00335 }
00336 
00337 
00338 /*!
00339  * @brief   Function to trigger ADC conversion for new READBUFF
00340  *              request from IIO client (for active channels)
00341  * @param   ch_mask[in] - Channels to enable for data capturing
00342  * @param   num_of_chns[in] - ADC channel count
00343  * @return  none
00344  */
00345 void start_data_capture(uint32_t ch_mask, uint8_t num_of_chns)
00346 {
00347     uint32_t mask = 0x1;
00348     uint8_t index = 0;
00349 
00350     /* Make sure requested samples size is less than max available buffer size */
00351     if (num_of_requested_samples >= max_available_buffer_size) {
00352         return;
00353     }
00354 
00355     /* Reset data capture module specific flags and variables */
00356     reset_data_capture();
00357 
00358     /* Count active channels based on channel mask set in the IIO client */
00359     for (uint8_t chn = 0; chn < num_of_chns; chn++) {
00360         if (ch_mask & mask) {
00361             acq_buffer.active_chn[index++] = chn;
00362             num_of_active_channels++;
00363         }
00364 
00365         mask <<= 1;
00366     }
00367 
00368     /* Note: As shown below, after nth sample read, next sample must be for the first
00369      *       channel present in the list of enabled channels */
00370     /*+-----------------------+-------------------------+---------------------------+
00371      *| 0 | 1 | 2 | ------| n | 0 | 1 | 2 | --------| n | 0 | 1 | 2 |-----------| l |
00372      *+-^-------------------^-+-^---------------------^-+-^-----------------------^-+
00373      *  |                   |   |                     |   |                       |
00374      * 1st chn data   nth data  1st chn data    nth data  1st chn data         last data
00375      * n = number of requested samples. l = last data (channel unknown)
00376      * To achieve this, offset value is determined based on the requested samples count
00377      * and number of active channels. The read index is then incremented by offset value
00378      * to read the data for first enabled channel by mapping into acquisition buffer.
00379      **/
00380     if (num_of_requested_samples % num_of_active_channels) {
00381         chn_alignment_offset = (num_of_requested_samples - ((num_of_requested_samples /
00382                     num_of_active_channels) * num_of_active_channels)) + 1;
00383     } else {
00384         chn_alignment_offset = 0;
00385     }
00386 
00387     /* Make acquisition buffer available and start continuous conversion */
00388     acq_buffer.state = BUF_AVAILABLE;
00389     if (data_capture_ops.continuous_sample_read_start_ops) {
00390         if (data_capture_ops.continuous_sample_read_start_ops(ch_mask) != FAILURE) {
00391             start_adc_data_capture = true;
00392         }
00393     } else {
00394         start_adc_data_capture = true;
00395     }
00396 }
00397 
00398 
00399 /*!
00400  * @brief   Function to stop ADC data capture
00401  * @return  none
00402  */
00403 void stop_data_capture(void)
00404 {
00405     start_adc_data_capture = false;
00406 
00407     /* Enable operations required post continuous sample read */
00408     if (data_capture_ops.continuous_sample_read_stop_ops) {
00409         data_capture_ops.continuous_sample_read_stop_ops();
00410     }
00411 
00412     /* Reset data capture module specific flags and variables */
00413     reset_data_capture();
00414 }