Analog Devices / Mbed OS EVAL-AD717x-AD411x-IIO

Dependencies:   sdp_k1_sdram

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ad717x_data_capture.c Source File

ad717x_data_capture.c

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