Analog Devices / Mbed OS EVAL-AD4110

Dependencies:   tempsensors sdp_k1_sdram

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ad4110_data_capture.c Source File

ad4110_data_capture.c

Go to the documentation of this file.
00001 /***************************************************************************//**
00002  * @file    ad4110_data_capture.c
00003  * @brief   Source file for AD4110 Data capture
00004 ********************************************************************************
00005 * Copyright (c) 2022 Analog Devices, Inc.
00006 * All rights reserved.
00007 *
00008 * This software is proprietary to Analog Devices, Inc. and its licensors.
00009 * By using this software you agree to the terms of the associated
00010 * Analog Devices Software License Agreement.
00011 *******************************************************************************/
00012 
00013 /******************************************************************************/
00014 /***************************** Include Files **********************************/
00015 /******************************************************************************/
00016 
00017 #include <string.h>
00018 #include "ad4110_data_capture.h"
00019 #include "ad4110_iio.h"
00020 #include "ad4110.h"
00021 #include "no_os_error.h"
00022 #include "app_config.h"
00023 
00024 /******************************************************************************/
00025 /********************* Macros and Constants Definition ************************/
00026 /******************************************************************************/
00027 
00028 /* Timeout count to avoid stuck into potential infinite loop while checking
00029  * for new data into an acquisition buffer. The actual timeout factor is determined
00030  * through 'sampling_frequency' attribute of IIO app, but this period here makes sure
00031  * we are not stuck into a forever loop in case data capture is interrupted
00032  * or failed in between.
00033  * Note: This timeout factor is dependent upon the MCU clock frequency. Below timeout
00034  * is tested for SDP-K1 platform @180Mhz default core clock */
00035 #define BUF_READ_TIMEOUT    0xffffffff
00036 
00037 /******************************************************************************/
00038 /******************** Variables and User Defined Data Types *******************/
00039 /******************************************************************************/
00040 
00041 /*
00042  *@enum  acq_buffer_state_e
00043  *@details Data acquisition buffer states
00044  **/
00045 typedef enum {
00046     BUF_AVAILABLE,
00047     BUF_EMPTY,
00048     BUF_FULL
00049 } acq_buffer_state_e;
00050 
00051 /*
00052  *@struct acq_buf_t
00053  *@details Data acquisition buffer parameters
00054  **/
00055 typedef struct {
00056     acq_buffer_state_e state;   // Buffer state
00057     uint32_t wr_indx;           // Buffer write index (incremented per sample read)
00058     uint32_t rd_indx;           // Buffer read index (incremented per sample read)
00059     int8_t *wr_pdata;           // Data buffer write pointer
00060     int8_t *rd_pdata;           // Data buffer read pointer
00061     bool reindex_buffer;        // Reindex buffer to 0th channel
00062 } acq_buf_t;
00063 
00064 /* ADC data acquisition buffers */
00065 static volatile acq_buf_t acq_buffer;
00066 
00067 /* Number of samples requested by IIO client */
00068 static volatile uint32_t num_of_requested_samples = 0;
00069 
00070 /* Number of active channels */
00071 static volatile uint8_t num_of_active_channels;
00072 
00073 /* ADC sample/raw data size in bytes */
00074 static volatile uint8_t sample_size_in_bytes;
00075 
00076 /* ADC data buffer */
00077 #if !defined(USE_SDRAM_CAPTURE_BUFFER)
00078 int8_t adc_data_buffer[DATA_BUFFER_SIZE] = { 0 };
00079 #endif
00080 
00081 /* List of input channels to be captured */
00082 static volatile uint8_t input_channels[AD4110_NUM_CHANNELS];
00083 
00084 /* Flag to indicate data capture status */
00085 static volatile bool start_cont_data_capture = false;
00086 
00087 /* Max available buffer size (after considering the data alignment with IIO buffer) */
00088 static volatile uint32_t max_buffer_sz;
00089 
00090 /* Current active channel index */
00091 static volatile uint8_t chn_indx;
00092 
00093 /******************************************************************************/
00094 /************************** Functions Declaration *****************************/
00095 /******************************************************************************/
00096 
00097 /******************************************************************************/
00098 /************************** Functions Definition ******************************/
00099 /******************************************************************************/
00100 
00101 /*!
00102  * @brief Reset the data capture specific variables
00103  * @return none
00104  */
00105 static void reset_data_capture(void)
00106 {
00107     /* Reset data capture flags */
00108     num_of_active_channels = 0;
00109     start_cont_data_capture = false;
00110 
00111     /* Reset acquisition buffer states and clear old data */
00112     acq_buffer.state = BUF_EMPTY;
00113     acq_buffer.wr_indx = 0;
00114     acq_buffer.rd_indx = 0;
00115     acq_buffer.reindex_buffer = false;
00116 
00117     acq_buffer.wr_pdata = adc_data_buffer;
00118     acq_buffer.rd_pdata = adc_data_buffer;
00119     max_buffer_sz = DATA_BUFFER_SIZE;
00120 }
00121 
00122 
00123 /*!
00124  * @brief Trigger a data capture in continuous/burst mode
00125  * @return 0 in case of success, negative error code otherwise
00126  */
00127 static int32_t adc_start_data_capture(void)
00128 {
00129     return ad4110_set_adc_mode(ad4110_dev_inst, AD4110_CONTINOUS_CONV_MODE);
00130 }
00131 
00132 
00133 /*!
00134  * @brief Stop a data capture operation
00135  * @return 0 in case of success, negative error code otherwise
00136  */
00137 static int32_t adc_stop_data_capture(void)
00138 {
00139     return ad4110_set_adc_mode(ad4110_dev_inst, AD4110_STANDBY_MODE);
00140 }
00141 
00142 
00143 /*!
00144  * @brief Function to prepare the data ADC capture for new READBUFF
00145  *      request from IIO client (for active channels)
00146  * @param chn_mask[in] - Channels to enable for data capturing
00147  * @param num_of_chns[in] - ADC channel count
00148  * @param sample_size[in] - Sample size in bytes
00149  * @return 0 in case of success, negative error code otherwise
00150  */
00151 int32_t prepare_data_transfer(uint32_t ch_mask, uint8_t num_of_chns,
00152                   uint8_t sample_size)
00153 {
00154     int32_t ret;
00155     uint8_t ch_id;
00156     uint8_t mask = 0x1;
00157 
00158     /* Reset the data capture module */
00159     reset_data_capture();
00160 
00161     sample_size_in_bytes = sample_size;
00162 
00163     /* Enable Active channels requested and Disable the remaining */
00164     for (ch_id = 0;
00165          ch_id < num_of_chns; ch_id++) {
00166         if (ch_mask & mask) {
00167             ret = ad4110_set_channel_status(ad4110_dev_inst, ch_id, true);
00168             if (ret) {
00169                 return ret;
00170             }
00171             num_of_requested_samples++;
00172         } else {
00173             ret = ad4110_set_channel_status(ad4110_dev_inst, ch_id, false);
00174             if (ret) {
00175                 return ret;
00176             }
00177         }
00178         mask <<= 1;
00179     }
00180 
00181     /* Trigger continuous data capture */
00182 #if (DATA_CAPTURE_MODE == CONTINUOUS_DATA_CAPTURE)
00183     ret = adc_start_data_capture();
00184     if (ret) {
00185         return ret;
00186     }
00187 
00188     acq_buffer.state = BUF_AVAILABLE;
00189     start_cont_data_capture = true;
00190 
00191     /* Pull the cs line low to detect the EOC bit during data capture */
00192     ret = no_os_gpio_set_value(csb_gpio, NO_OS_GPIO_LOW);
00193     if (ret) {
00194         return ret;
00195     }
00196 
00197     ret = no_os_irq_enable(external_int_desc, IRQ_INT_ID);
00198     if(ret) {
00199         return ret;
00200     }
00201 #endif
00202 
00203     return 0;
00204 }
00205 
00206 
00207 /*!
00208  * @brief Function to end data capture
00209  * @return 0 in case of success, negative error code otherwise
00210  */
00211 int32_t end_data_transfer(void)
00212 {
00213     start_cont_data_capture = false;
00214 
00215     /* Reset data capture module specific flags and variables */
00216     reset_data_capture();
00217 
00218     /* Stop ADC data capture */
00219     return adc_stop_data_capture();
00220 }
00221 
00222 
00223 /*!
00224  * @brief Capture requested number of ADC samples in burst mode
00225  * @param pbuf[out] - Pointer to ADC data buffer
00226  * @param nb_of_samples[in] - Number of samples to be read
00227  * @return 0 in case of success, negative error code otherwise
00228  */
00229 static int32_t read_burst_data(int8_t *pbuf, uint32_t nb_of_samples)
00230 {
00231     uint32_t sample_index = 0;
00232     uint32_t adc_raw_data = 0;
00233     int32_t ret;
00234 
00235     if (adc_start_data_capture()) {
00236         return -EIO;
00237     }
00238 
00239     while (sample_index < nb_of_samples) {
00240         /* Wait for the RDY Bit to go low to notify end of conversion */
00241         ret = ad4110_wait_for_rdy_low(ad4110_dev_inst, AD4110_ADC_CONV_TIMEOUT);
00242         if (ret) {
00243             return ret;
00244         }
00245 
00246         /* Read the converted data from the Data register */
00247         ret = ad4110_spi_int_data_reg_read(ad4110_dev_inst, &adc_raw_data);
00248         if (ret) {
00249             return ret;
00250         }
00251 
00252         /* Copy raw data to the buffer */
00253         memcpy((uint8_t*)pbuf, &adc_raw_data, sample_size_in_bytes);
00254         sample_index++;
00255         pbuf += sample_size_in_bytes;
00256     }
00257 
00258     /* Stop any active conversion */
00259     ret = adc_stop_data_capture();
00260     if (ret) {
00261         return ret;
00262     }
00263 
00264     return 0;
00265 }
00266 
00267 
00268 /*!
00269  * @brief Perform buffer read operations to read requested samples
00270  * @param nb_of_samples[in] - Requested number of samples to read
00271  * @return 0 in case of success, negative error code otherwise
00272  */
00273 static int32_t buffer_read_operations(uint32_t nb_of_samples)
00274 {
00275     uint32_t timeout = BUF_READ_TIMEOUT;
00276     int32_t offset;
00277 
00278     /* Wait until requested samples are available in the buffer to read */
00279     do {
00280         if (acq_buffer.wr_indx >= acq_buffer.rd_indx) {
00281             offset = acq_buffer.wr_indx - acq_buffer.rd_indx;
00282         } else {
00283             offset = max_buffer_sz + (acq_buffer.wr_indx - acq_buffer.rd_indx);
00284         }
00285 
00286         timeout--;
00287     } while ((offset < (int32_t)(nb_of_samples)) && (timeout > 0));
00288 
00289     if (timeout == 0) {
00290         /* This returns the empty buffer */
00291         return -EIO;
00292     }
00293 
00294     if (acq_buffer.rd_indx >= max_buffer_sz) {
00295         acq_buffer.rd_indx = 0;
00296     }
00297 
00298     return 0;
00299 }
00300 
00301 
00302 /*!
00303  * @brief Perform buffer write operations such as buffer full or empty
00304  *    check, resetting buffer index and pointers, etc
00305  * @return none
00306  */
00307 static void buffer_write_operations(void)
00308 {
00309     acq_buffer.wr_indx++;
00310 
00311     /* Perform buffer full check and operations */
00312     if (acq_buffer.wr_indx >= max_buffer_sz) {
00313         if ((acq_buffer.rd_indx >= num_of_requested_samples)
00314             && (acq_buffer.rd_indx != 0)) {
00315             /* Reset buffer write index and write pointer when enough
00316              * space available in the buffer to wrap to start */
00317             acq_buffer.wr_indx = 0;
00318 
00319             acq_buffer.wr_pdata = adc_data_buffer;
00320             if (acq_buffer.rd_indx >= max_buffer_sz) {
00321                 /* Wrap the read index and read pointer to start of buffer
00322                  * if buffer is completely read/emptied */
00323                 acq_buffer.rd_indx = 0;
00324                 acq_buffer.rd_pdata = adc_data_buffer;
00325             }
00326 
00327             acq_buffer.state = BUF_AVAILABLE;
00328         } else {
00329             /* Wait until enough space available to wrap buffer write index
00330              * at the start of buffer */
00331             acq_buffer.wr_indx = max_buffer_sz;
00332             acq_buffer.state = BUF_FULL;
00333             acq_buffer.reindex_buffer = true;
00334         }
00335     }
00336 }
00337 
00338 
00339 /*!
00340  * @brief Read requested number of ADC samples in continuous mode
00341  * @param pbuf[in] - Pointer to data buffer
00342  * @param nb_of_samples[in] - Number of samples to read
00343  * @return 0 in case of success, negative error code otherwise
00344  * @note The actual sample capturing happens through interrupt. This
00345  *          function tracks the buffer read pointer to read block of data
00346  */
00347 static int32_t read_continuous_conv_data(int8_t **pbuf, uint32_t nb_of_samples)
00348 {
00349     volatile int8_t *rd_addr;
00350     int32_t ret;
00351 
00352     /* Determine the max available buffer size based on the requested
00353      * samples count and actual avilable buffer size. Buffer should be
00354      * capable of holding all requested 'n' samples from previous write
00355      * index upto to the end of buffer, as data is read linearly
00356      * from adc buffer in IIO library.
00357      * E.g. If actual buffer size is 2048 samples and requested samples
00358      * are 1600, max available buffer size is actually 1600. So in given
00359      * iteration, only 1600 samples will be stored into buffer and after
00360      * that buffer indexes will be wrapped to a start of buffer. If index
00361      * is not wrapped, the next 1600 requested samples won't accomodate into
00362      * remaining 448 samples space. As buffer is read in linear fashion, the
00363      * read index can't be wrapped to start of buffer to read remaining samples.
00364      * So max available space in this case is 2048 but only utilized space
00365      * will be 1600 in single read buffer request from IIO client.
00366      **/
00367     max_buffer_sz = ((DATA_BUFFER_SIZE / sample_size_in_bytes) /
00368              nb_of_samples) * nb_of_samples;
00369 
00370     ret = buffer_read_operations(nb_of_samples);
00371     if (ret) {
00372         return ret;
00373     }
00374 
00375     /* Get the next read address */
00376     rd_addr = (volatile int8_t *)(acq_buffer.rd_pdata + (acq_buffer.rd_indx *
00377                       sample_size_in_bytes));
00378     acq_buffer.rd_indx += nb_of_samples;
00379 
00380     /* Update the IIO buffer pointer to point to next read start location */
00381     *pbuf = rd_addr;
00382 
00383     return 0;
00384 }
00385 
00386 
00387 /*!
00388  * @brief Read ADC raw data for recently sampled channel
00389  * @param adc_data[out] - Pointer to adc data read variable
00390  * @param input_chn[in] - Input channel
00391  * @return 0 in case of success, negative error code otherwise
00392  * @note This function is intended to call from the conversion end trigger
00393  *   event. Therefore, this function should just read raw ADC data
00394  *   without further monitoring conversion end event
00395  */
00396 static int32_t adc_read_converted_sample(uint32_t *adc_data, uint8_t input_chn)
00397 {
00398     int32_t ret;
00399 
00400     if (!adc_data) {
00401         return -EIO;
00402     }
00403 
00404     ret = ad4110_spi_int_data_reg_read(ad4110_dev_inst, adc_data);
00405     if (ret) {
00406         return ret;
00407     }
00408 
00409     /* Pull the cs line low to detect the EOC bit during data capture */
00410     ret = no_os_gpio_set_value(csb_gpio, NO_OS_GPIO_LOW);
00411     if (ret) {
00412         return ret;
00413     }
00414 
00415     ret = no_os_irq_enable(external_int_desc, IRQ_INT_ID);
00416     if (ret) {
00417         return ret;
00418     }
00419 
00420     return 0;
00421 }
00422 
00423 
00424 /*!
00425  * @brief This is an ISR (Interrupt Service Routine) to monitor end of conversion event.
00426  * @param ctx[in] - Callback context (unused)
00427  * @return none
00428  * @details This is an Interrupt callback function/ISR invoked in synchronous/asynchronous
00429  *          manner depending upon the application implementation. The conversion results
00430  *          are read into acquisition buffer and control continue to sample next channel.
00431  *          This continues until conversion is stopped (through IIO client command)
00432  */
00433 void data_capture_callback(void *ctx)
00434 {
00435     uint32_t adc_sample;
00436     volatile uint8_t *wr_addr;
00437 
00438     /* The callback function is triggered when the first falling edge is detected
00439      * on the MISO pin, indicating End of Conversion. Interrupt is disabled
00440      because any data transaction on the  SPI line could be interpreted
00441      by the MCU as a falling edge.
00442      */
00443 
00444     no_os_irq_disable(external_int_desc, IRQ_INT_ID);
00445 
00446     if (start_cont_data_capture == true) {
00447         /* Read the sample for channel which has been sampled recently */
00448         if (adc_read_converted_sample(&adc_sample,
00449                           input_channels[chn_indx]) == 0) {
00450             do {
00451                 if (acq_buffer.state == BUF_AVAILABLE) {
00452                     if (acq_buffer.reindex_buffer) {
00453                         /* Buffer refilling must start with first active channel data
00454                          * for IIO client to synchronize the buffered data */
00455                         if (chn_indx != 0) {
00456                             break;
00457                         }
00458                         acq_buffer.reindex_buffer = false;
00459                     }
00460 
00461                     /* Copy adc samples into acquisition buffer to transport over
00462                      * communication link */
00463                     wr_addr = (volatile uint8_t *)(acq_buffer.wr_pdata + (acq_buffer.wr_indx *
00464                                        sample_size_in_bytes));
00465                     memcpy((uint8_t *)wr_addr, &adc_sample, sample_size_in_bytes);
00466                 }
00467 
00468                 /* Perform buffer write operations */
00469                 buffer_write_operations();
00470             } while (0);
00471 
00472             /* Track the count for recently sampled channel */
00473             chn_indx++;
00474             if (chn_indx >= num_of_active_channels) {
00475                 chn_indx = 0;
00476             }
00477         }
00478     }
00479 }
00480 
00481 
00482 /*!
00483  * @brief Function to read the ADC buffered raw data requested by IIO client
00484  * @param pbuf[in] - Pointer to data buffer
00485  * @param nb_of_bytes[in] - Number of bytes to read
00486  * @return 0 in case of success, negative error code otherwise
00487  */
00488 int32_t read_buffered_data(int8_t **pbuf, uint32_t nb_of_bytes)
00489 {
00490     int32_t ret;
00491     num_of_requested_samples = nb_of_bytes / sample_size_in_bytes;
00492 
00493 #if (DATA_CAPTURE_MODE == BURST_DATA_CAPTURE)
00494     ret = read_burst_data(*pbuf, num_of_requested_samples);
00495 #else
00496     ret = read_continuous_conv_data(pbuf, num_of_requested_samples);
00497 #endif
00498     if (ret) {
00499         return ret;
00500     }
00501 
00502     return 0;
00503 }