Mahesh Phalke / Mbed OS EVAL-AD7606

Dependencies:   platform_drivers

Revision:
7:94f36455773c
Parent:
6:75e922b3859a
--- a/app/ad7606_data_capture.c	Thu Jul 15 13:21:42 2021 +0530
+++ b/app/ad7606_data_capture.c	Mon Aug 02 16:11:58 2021 +0530
@@ -3,8 +3,7 @@
  *   @brief   Data capture interface for AD7606 IIO application
  *   @details This module handles the AD7606 data capturing
 ********************************************************************************
- * Copyright (c) 2020 Analog Devices, Inc.
- *
+ * Copyright (c) 2020-2021 Analog Devices, Inc.
  * All rights reserved.
  *
  * This software is proprietary to Analog Devices, Inc. and its licensors.
@@ -21,135 +20,88 @@
 
 #include "app_config.h"
 #include "ad7606_data_capture.h"
+#include "iio_ad7606.h"
 #include "ad7606_support.h"
-#include "gpio_extra.h"
+#include "error.h"
+#include "adc_data_capture.h"
 
 /******************************************************************************/
 /********************** Macros and Constants Definition ***********************/
 /******************************************************************************/
 
-#if (AD7606X_ADC_RESOLUTION > 16)
-#define		BYTES_PER_SAMPLE	sizeof(uint32_t)
+#if (AD7606X_ADC_RESOLUTION == 18)
+#define	SAMPLE_SIZE_IN_BYTE		3
 #else
-#define		BYTES_PER_SAMPLE	sizeof(uint16_t)
+#define	SAMPLE_SIZE_IN_BYTE		2
 #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 volatile 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 volatile uint16_t num_of_samples = 0;
-
-/* Max available size of buffer */
-static volatile uint16_t max_available_buffer_size = 0;
+/* Polarity of channels */
+static polarity_e polarity[AD7606X_ADC_CHANNELS];
 
 /******************************************************************************/
 /************************ Functions Declarations ******************************/
 /******************************************************************************/
 
-static void adjust_buffer_offset(uint8_t *arr_indx, uint8_t *offset_end,
-				 uint8_t *mask);
+/* Perform conversion on input channel and read single conversion sample */
+static int32_t ad7606_perform_conv_and_read_sample(uint32_t *adc_data,
+		uint8_t chn);
+
+/* Perform continuous sample read pre-operations */
+static int32_t ad7606_continuous_sample_read_start_ops(uint32_t ch_mask);
+
+/* Read ADC sampled/conversion data */
+static int32_t ad7606_read_converted_sample(uint32_t *adc_data,
+		uint8_t input_chn);
+
+/* Trigger next ADC conversion */
+static int32_t ad7606_trigger_next_conversion(void);
+
+/* Define the variable for data_capture_ops structure */
+struct data_capture_ops data_capture_ops = {
+	/* Point AD7606 data capture functions to generic ADC data capture functions */
+	.single_sample_read_start_ops = NULL,
+	.perform_conv_and_read_sample = ad7606_perform_conv_and_read_sample,
+	.single_sample_read_stop_ops = NULL,
+	.continuous_sample_read_start_ops = ad7606_continuous_sample_read_start_ops,
+	.read_converted_sample = ad7606_read_converted_sample,
+	.continuous_sample_read_stop_ops = NULL,
+	.trigger_next_conversion = ad7606_trigger_next_conversion
+};
 
 /******************************************************************************/
 /************************ 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)
-{
-	dev = *device;
-	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
+ * @brief	Remove the offset from data to change output data format to
+ *			normal or stright  binary representation (needed for IIO client)
+ * @param	adc_raw[out] - Pointer to adc data read variable
+ * @param	bipolar[in] - Channel polarity
+ * @return	SUCCESS in case of success, FAILURE otherwise
  */
-int32_t single_data_read(void *device, uint8_t chn, polarity_e polarity)
+static int32_t reformat_adc_raw_data(uint32_t adc_raw_data, 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;
-	}
+	int32_t adc_data;
 
+	/* Bipolar ADC Range:  (-FS) <-> 0 <-> (+FS) : 2^(ADC_RES-1) <-> 0 <-> 2^(ADC_RES-1)-1
+	   Unipolar ADC Range: 0 <-> (+FS) : 0 <-> 2^ADC_RES
+	 **/
 	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;
+		/* Data output format is 2's complement for bipolar mode */
+		if (adc_raw_data > ADC_MAX_COUNT_BIPOLAR) {
+			/* Remove the offset from result to convert into negative reading */
+			adc_data = ADC_MAX_COUNT_UNIPOLAR - adc_raw_data;
+			adc_data = -adc_data;
 		} else {
-			adc_data = adc_raw[chn];
+			adc_data = adc_raw_data;
 		}
 	} else {
-		adc_data = adc_raw[chn];
+		/* Data output format is straight binary for unipolar mode */
+		adc_data = adc_raw_data;
 	}
 
 	return adc_data;
@@ -157,270 +109,132 @@
 
 
 /*!
- * @brief	Function to store the number of requested samples from IIO client
- * @param	bytes[in] - Number of bytes corresponding to requested samples
- * @return	none
+ * @brief	Perform conversion and read conversion sample
+ * @param	adc_data[out] - Pointer to adc data read variable
+ * @param	chn[in] - Channel for which data is to read
+ * @return	SUCCESS in case of success, FAILURE otherwise
  */
-void store_requested_samples_count(size_t bytes)
+int32_t ad7606_perform_conv_and_read_sample(uint32_t *adc_data, uint8_t chn)
 {
-	/* 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);
-}
-
+	uint32_t adc_raw[AD7606X_ADC_CHANNELS] = { 0 };
+	uint8_t read_val;
+	uint8_t chn_range;
+	polarity_e polarity;
 
-/*!
- * @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(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 = 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;
+	/* Get input channel range */
+	if (ad7606_spi_reg_read(p_ad7606_dev_inst,
+				AD7606_REG_RANGE_CH_ADDR(chn),
+				&read_val) != SUCCESS) {
+		return FAILURE;
 	}
 
-	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)
-{
-	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;
+	if (((chn) % 2) != 0) {
+		read_val >>= CHANNEL_RANGE_MSK_OFFSET;
+		chn_range = read_val;
+	} else {
+		chn_range = (read_val & AD7606_RANGE_CH_MSK(chn));
 	}
 
-	wait_and_read_new_samples(pbuf, samples_to_read);
+	/* Get polarity based on input channel range */
+	polarity = ad7606_get_input_polarity(chn_range);
 
-	/* 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;
+	/* This function monitors BUSY line for EOC and read ADC result post that */
+	if (ad7606_read(p_ad7606_dev_inst, adc_raw) != SUCCESS) {
+		adc_raw[chn] = 0;
 	}
 
-	return bytes;
+	*adc_data = reformat_adc_raw_data(adc_raw[chn], polarity);
+
+	return SUCCESS;
 }
 
 
 /*!
- * @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.
+ * @brief	Perform the operations required before starting continuous sample read
+ * @param	chn_mask[in] - active channels list received from IIO client
+ * @return	SUCCESS in case of success, FAILURE otherwise
  */
-void do_conversion_callback(void *ctx, uint32_t event, void *extra)
+int32_t ad7606_continuous_sample_read_start_ops(uint32_t chn_mask)
 {
-#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
+	uint8_t read_val;
+	uint8_t chn_range;
 
-						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;
+	for (uint8_t chn = 0; chn < AD7606X_ADC_CHANNELS; chn++) {
+		/* Store the channels polarity */
+		if (ad7606_spi_reg_read(p_ad7606_dev_inst,
+					AD7606_REG_RANGE_CH_ADDR(chn),
+					&read_val) == SUCCESS) {
+			if (((chn) % 2) != 0) {
+				read_val >>= CHANNEL_RANGE_MSK_OFFSET;
+				chn_range = read_val;
+			} else {
+				chn_range = (read_val & AD7606_RANGE_CH_MSK(chn));
 			}
 
-			/* Trigger next conversion */
-			if (ad7606_convst(dev) != SUCCESS) {
-				start_adc_data_capture = false;
-			}
-		} else {
-			start_adc_data_capture = false;
+			polarity[chn] = ad7606_get_input_polarity(chn_range);
 		}
 	}
+
+	/* Trigger first conversion */
+	return ad7606_trigger_next_conversion();
 }
 
 
 /*!
- * @brief	Function to trigger bakground ADC conversion for new READBUFF
- *              request from IIO client (for active channels)
- * @return	Conversion start status
+ * @brief	Read ADC raw data for recently sampled channel
+ * @param	adc_data[out] - Pointer to adc data read variable
+ * @param	input_chn[in] - Input channel
+ * @return	SUCCESS in case of success, FAILURE otherwise
+ * @note	This function is intended to call from the conversion end trigger
+ *			event. Therefore, this function should just read raw ADC data
+ *			without further monitoring conversion end event
  */
-void start_background_data_capture(uint32_t ch_mask, size_t bytes_count)
+int32_t ad7606_read_converted_sample(uint32_t *adc_data,
+				     uint8_t input_chn)
 {
-	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();
+	uint32_t adc_raw;
+	uint8_t bytes_to_read;
+	uint8_t buffer_offset;
 
-	/* 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;
+	if (!adc_data) {
+		return FAILURE;
 	}
 
-	/* 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;
-	}
+	/* Get number of bytes to read count = chn_cnt * bytes per sample */
+	bytes_to_read = AD7606X_ADC_CHANNELS * SAMPLE_SIZE_IN_BYTE;
+	buffer_offset = input_chn * SAMPLE_SIZE_IN_BYTE;
+
+	/* Read data over spi interface for all ADC channels */
+	memset(p_ad7606_dev_inst->data, 0, sizeof(p_ad7606_dev_inst->data));
+	spi_write_and_read(p_ad7606_dev_inst->spi_desc,
+			   p_ad7606_dev_inst->data, bytes_to_read);
+
+#if (AD7606X_ADC_RESOLUTION == 18)
+	adc_raw =
+		(((uint32_t)p_ad7606_dev_inst->data[buffer_offset] << 16) |		// MSB
+		 ((uint32_t)p_ad7606_dev_inst->data[buffer_offset + 1] << 8) |
+		 ((uint32_t)p_ad7606_dev_inst->data[buffer_offset + 2]));		// LSB
+#else
+	adc_raw =
+		(uint16_t)(((uint16_t)p_ad7606_dev_inst->data[buffer_offset] << 8) | 	// MSB
+			   p_ad7606_dev_inst->data[buffer_offset + 1]); 		// LSB
+#endif
+
+	*adc_data = reformat_adc_raw_data(adc_raw, polarity[input_chn]);
+
+	return SUCCESS;
 }
 
 
 /*!
- * @brief	Function to stop background ADC data capture
- * @return	none
+ * @brief	Trigger next ADC conversion
+ * @return	SUCCESS in case of success, FAILURE otherwise
  */
-void stop_background_data_capture(void)
+int32_t ad7606_trigger_next_conversion(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;
-}
-
+	if (ad7606_convst(p_ad7606_dev_inst) != SUCCESS) {
+		return FAILURE;
+	}
 
-/*!
- * @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;
-	}
+	return SUCCESS;
 }