Forked repo of Platform Drivers- Analog Devices

Revision:
17:af1f2416dd26
Child:
18:5ae03a197e59
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/adc_data_capture.c	Tue Jul 13 13:58:07 2021 +0530
@@ -0,0 +1,453 @@
+/***************************************************************************//**
+ *   @file    adc_data_capture.c
+ *   @brief   ADC common data capture interface for IIO based applications
+ *   @details This module handles the ADC data capturing for IIO client
+********************************************************************************
+ * Copyright (c) 2021 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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "adc_data_capture.h"
+#include "error.h"
+
+/******************************************************************************/
+/********************** Macros and Constants Definition ***********************/
+/******************************************************************************/
+
+/* IIO buffer request size in bytes */
+#define IIO_BUF_READ_REQUEST_SIZE	(256)
+
+/* 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	(100000000)
+
+/******************************************************************************/
+/********************** Variables and User Defined Data Types *****************/
+/******************************************************************************/
+
+/* Extern declaration for data capture operations structure variable */
+extern struct data_capture_ops data_capture_ops;
+
+/*
+ *@enum		acq_buffer_state_e
+ *@details	Enum holding the data acquisition buffer states
+ **/
+typedef enum {
+	BUF_AVAILABLE,
+	BUF_EMPTY,
+	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 sample_cnt;  				// buffer data/sample counter
+	uint32_t data[DATA_BUFFER_SIZE];  	// buffer data (adc raw values)
+} acq_buf_t;
+
+/* ADC data acquisition buffers */
+static volatile acq_buf_t acq_buffer;
+
+/* Flag to indicate data capture status */
+static volatile bool start_adc_data_capture = false;
+
+/* Number of active channels in any data buffer read request */
+static volatile uint8_t num_of_active_channels = 0;
+
+/* Channel data alignment variables */
+static volatile uint8_t buff_chn_indx = 0;
+static volatile bool do_chn_alignment = false;
+
+/* Count to track number of actual samples requested by IIO client */
+static volatile uint16_t num_of_requested_samples = 0;
+
+/* Actual or max available size of acquisition buffer */
+static volatile uint16_t max_available_buffer_size = 0;
+
+/******************************************************************************/
+/************************ Functions Declarations ******************************/
+/******************************************************************************/
+
+/******************************************************************************/
+/************************ Functions Definitions *******************************/
+/******************************************************************************/
+
+/*!
+ * @brief	Function to read the ADC raw data for single channel
+ * @param	input_chn[in] - Input channel to sample and read data for
+ * @param	raw_data[in, out]- ADC raw data
+ * @return	SUCCESS in case of success, FAILURE otherwise
+ */
+int32_t single_data_read(uint8_t input_chn, uint32_t *raw_data)
+{
+	uint32_t read_adc_data = 0;
+	int32_t status = SUCCESS;
+
+	do {
+		if (data_capture_ops.save_prev_active_chns) {
+			/* Save previously active channels */
+			if (data_capture_ops.save_prev_active_chns() != SUCCESS) {
+				status = FAILURE;
+				break;
+			}
+		}
+
+		if (data_capture_ops.disable_all_chns) {
+			/* Disable all channels */
+			if (data_capture_ops.disable_all_chns() != SUCCESS) {
+				status = FAILURE;
+				break;
+			}
+		}
+
+		if (data_capture_ops.enable_curr_chn) {
+			/* Enable user input channel */
+			if (data_capture_ops.enable_curr_chn(input_chn) != SUCCESS) {
+				status = FAILURE;
+				break;
+			}
+		}
+
+		if (data_capture_ops.enable_single_read_conversion) {
+			/* Enable single data read conversion */
+			if (data_capture_ops.enable_single_read_conversion() != SUCCESS) {
+				status = FAILURE;
+				break;
+			}
+		}
+
+		/* Read the data */
+		if (data_capture_ops.wait_for_conv_and_read_data(&read_adc_data, input_chn) !=
+		    SUCCESS) {
+			status = FAILURE;
+			break;
+		}
+	} while (0);
+
+	if (data_capture_ops.restore_prev_active_chns) {
+		/* Enable back channels which were disabled prior to single data read */
+		data_capture_ops.restore_prev_active_chns();
+	}
+
+	if (data_capture_ops.disable_conversion) {
+		/* Exit from conversion mode */
+		data_capture_ops.disable_conversion();
+	}
+
+	*raw_data = read_adc_data;
+	return status;
+}
+
+
+/*!
+ * @brief	Function to store the number of actul requested samples from IIO client
+ * @param	bytes[in] - Number of bytes corresponding to requested samples
+ * @param	sample_size_in_bytes[in] - Size of each sample in bytes (eqv to ADC resolution)
+ * @return	none
+ */
+void store_requested_samples_count(size_t bytes, uint8_t sample_size_in_bytes)
+{
+	/* This gets the number of samples requested by IIO client for all active channels */
+	num_of_requested_samples = bytes / sample_size_in_bytes;
+
+	/* Get the actual available size of buffer by aligning with number of requested samples.
+	 * e.g. if requested samples are 400, the max available size of buffer is:
+	 * available size = (2048 / 400) * 400 = 5 * 400 = 2000.
+	 * The max samples to be requested should always be less than half the max size of buffer
+	 * (in this case: 2048 / 2 = 1024).
+	 * */
+	max_available_buffer_size = ((DATA_BUFFER_SIZE / num_of_requested_samples) *
+				     num_of_requested_samples);
+}
+
+
+/*!
+ * @brief	Function to read new samples into buffer without IIO request timeout
+ * @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
+ * @param	sample_size_in_bytes[in] - Size of each sample in bytes (eqv to ADC resolution)
+ * @return	none
+ */
+static void wait_and_read_new_samples(char *output_buffer,
+				      size_t samples_to_read,
+				      uint8_t sample_size_in_bytes)
+{
+	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 * sample_size_in_bytes;
+
+	/* 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;
+	}
+
+	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, uint8_t sample_size_in_bytes)
+{
+	size_t samples_to_read = bytes /
+				 sample_size_in_bytes;   	// 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_requested_samples >= max_available_buffer_size) {
+		memset(pbuf, 1, bytes);
+		return bytes;
+	}
+
+	wait_and_read_new_samples(pbuf, samples_to_read, sample_size_in_bytes);
+
+	/* 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.sample_cnt = 0;
+		acq_buffer.state = BUF_AVAILABLE;
+	}
+
+	return bytes;
+}
+
+
+/*!
+ * @brief	This is an ISR (Interrupt Service Routine) to monitor end of conversion event.
+ * @param	*ctx[in] - Callback context (unused)
+ * @param	event[in] - Callback event (unused)
+ * @param	extra[in] - Callback extra (unused)
+ * @return	none
+ * @details	This is an Interrupt callback function/ISR invoked in synchronous/asynchronous
+ *			manner depending upon the application implementation. The conversion results
+ *			are read into acquisition buffer and control continue to sample next channel.
+ *			This continues until conversion is stopped (through IIO client command)
+ * @note	This function also handles the logic to align the zeroth channel data after
+ *			every 'n' sample transmission. This is required to visualize data properly
+ *			on IIO client application (more details listed below).
+ */
+void data_capture_callback(void *ctx, uint32_t event, void *extra)
+{
+	uint32_t adc_sample;
+
+	if (start_adc_data_capture == true) {
+		do {
+			/* Read the data for channel which has been sampled recently */
+			if (data_capture_ops.read_sampled_data(&adc_sample, buff_chn_indx) != SUCCESS) {
+				break;
+			}
+
+			/* If next sample/data in acquisition buffer is required to be of the zeroth
+			 * channel from a list of enabled channels (i.e. chn data alignment needed), then
+			 * wait until a conversion end event is triggered for that zeroth enabled channel */
+			/* Note: As shown below, after nth sample read, next sample must be for the zeroth
+			 *       channel present in the list of enabled channels */
+			/*+-----------------------+-------------------------+---------------------------+
+			 *| 0 | 1 | 2 | ------| n | 0 | 1 | 2 | --------| n | 0 | 1 | 2 |-----------| n |
+			 *+-^-------------------^-+-^---------------------^-+-^-----------------------^-+
+			 *  |                   |   |                     |   |                       |
+			 * 0th chn data  last data  0th chn data   last data  0th chn data         last data
+			 * n = number of requested samples
+			 **/
+			/* Wait until conversion event for the zeroth channel is triggered */
+			if ((do_chn_alignment == true) && (buff_chn_indx != 0)) {
+				/* Track the count for recently sampled channel */
+				buff_chn_indx++;
+				if (buff_chn_indx >= num_of_active_channels) {
+					buff_chn_indx = 0;
+				}
+
+				/* If recent sampled channel is not a zeroth enabled channel
+				 * in the channels list, return without storing data into buffer */
+				break;
+			}
+
+			/* Track the count for recently sampled channel */
+			buff_chn_indx++;
+			if (buff_chn_indx >= num_of_active_channels) {
+				buff_chn_indx = 0;
+			}
+
+			if (acq_buffer.state == BUF_AVAILABLE) {
+				/* Reset channel data alignment flag if control reach here */
+				do_chn_alignment = false;
+
+				acq_buffer.data[acq_buffer.wr_indx++] = adc_sample;
+				acq_buffer.sample_cnt++;
+
+				/* Check if current buffer is full */
+				if (acq_buffer.wr_indx >= max_available_buffer_size) {
+					acq_buffer.state = BUF_FULL;
+				}
+
+				/* Once all requested number of samples are transmitted, make sure
+				 * next data to be loaded into acquisition buffer is for zeroth channel
+				 * present in the channels list */
+				if (acq_buffer.sample_cnt >= num_of_requested_samples) {
+					acq_buffer.sample_cnt = 0;
+					do_chn_alignment = true;
+				}
+			}
+		} while (0);
+
+		/* Trigger next continuous conversion (optional or device dependent) */
+		if (data_capture_ops.trigger_next_cont_conversion) {
+			data_capture_ops.trigger_next_cont_conversion();
+		}
+	}
+}
+
+
+/*!
+ * @brief	Reset the data capture specific variables
+ * @return	none
+ */
+static void reset_data_capture(void)
+{
+	/* Reset data capture flags */
+	start_adc_data_capture = false;
+	num_of_active_channels = 0;
+
+	/* Reset acquisition buffer states and clear old data */
+	acq_buffer.state = BUF_EMPTY;
+
+	acq_buffer.wr_indx = 0;
+	acq_buffer.rd_indx = 0;
+	acq_buffer.sample_cnt = 0;
+
+	buff_chn_indx = 0;
+	do_chn_alignment = true;
+}
+
+
+/*!
+ * @brief	Function to trigger ADC conversion for new READBUFF
+ *              request from IIO client (for active channels)
+ * @param	ch_mask[in] - Channels to enable for data capturing
+ * @param	num_of_chns[in] - ADC channel count
+ * @return	none
+ */
+void start_data_capture(uint32_t ch_mask, uint8_t num_of_chns)
+{
+	uint32_t mask = 0x1;
+
+	/* Make sure requested samples size is less than ADC buffer size */
+	if (num_of_requested_samples >= max_available_buffer_size) {
+		return;
+	}
+
+	if (data_capture_ops.disable_conversion) {
+		/* Stop any previous conversion by putting ADC into standby mode */
+		if (data_capture_ops.disable_conversion() != SUCCESS) {
+			return;
+		}
+	}
+
+	/* Reset data capture related flags and variables */
+	reset_data_capture();
+
+	if (data_capture_ops.save_prev_active_chns) {
+		/* Store the previous active channels */
+		if (data_capture_ops.save_prev_active_chns() != SUCCESS) {
+			return;
+		}
+	}
+
+	/* Enable/Disable channels based on channel mask set in the IIO client */
+	for (uint8_t chn = 0; chn < num_of_chns; chn++) {
+		if (ch_mask & mask) {
+			if (data_capture_ops.enable_curr_chn) {
+				/* Enable the selected channel */
+				if (data_capture_ops.enable_curr_chn(chn) != SUCCESS) {
+					return;
+				}
+			}
+
+			num_of_active_channels++;
+		} else {
+			if (data_capture_ops.disable_curr_chn) {
+				/* Disable the selected channel */
+				if (data_capture_ops.disable_curr_chn(chn) != SUCCESS) {
+					return;
+				}
+			}
+		}
+
+		mask <<= 1;
+	}
+
+	/* Make primary acquisition buffer available and start continuous conversion */
+	acq_buffer.state = BUF_AVAILABLE;
+	if (data_capture_ops.enable_continuous_read_conversion(ch_mask) == SUCCESS) {
+		start_adc_data_capture = true;
+	} else {
+		start_adc_data_capture = false;
+	}
+}
+
+
+/*!
+ * @brief	Function to stop ADC data capture
+ * @return	none
+ */
+void stop_data_capture(void)
+{
+	start_adc_data_capture = false;
+
+	if (data_capture_ops.disable_conversion) {
+		/* Stop conversion */
+		data_capture_ops.disable_conversion();
+	}
+
+	if (data_capture_ops.restore_prev_active_chns) {
+		/* Enabled all previously active channels (active during conversion start) */
+		data_capture_ops.restore_prev_active_chns();
+	}
+
+	reset_data_capture();
+}