Example Program for EVAL-AD7606

Dependencies:   platform_drivers

Revision:
1:819ac9aa5667
Child:
3:83b3133f544a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/ad7606_data_capture.c	Mon Oct 05 13:45:15 2020 +0530
@@ -0,0 +1,429 @@
+/***************************************************************************//**
+ *   @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)
+
+/******************************************************************************/
+/********************** 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 = 5000000; 				// To avoid IIO data request timeout
+	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;
+	}
+}