Forked repo of Platform Drivers- Analog Devices

Revision:
18:5ae03a197e59
Parent:
17:af1f2416dd26
diff -r af1f2416dd26 -r 5ae03a197e59 adc_data_capture.c
--- a/adc_data_capture.c	Tue Jul 13 13:58:07 2021 +0530
+++ b/adc_data_capture.c	Mon Aug 02 16:03:08 2021 +0530
@@ -26,24 +26,28 @@
 /********************** Macros and Constants Definition ***********************/
 /******************************************************************************/
 
-/* IIO buffer request size in bytes */
-#define IIO_BUF_READ_REQUEST_SIZE	(256)
+/* Max available channels for continuous data capture. Actual number of channels
+ * to be captured are supplied from an application */
+#define MAX_AVAILABLE_CHANNELS		(16)
 
-/* Max size of the acquisition buffer (in terms of samples) */
-#define DATA_BUFFER_SIZE	(8192)
+/* Max size of the acquisition buffer (in terms of bytes) */
+#define DATA_BUFFER_SIZE	(32768)		// 32Kbytes
 
 /* 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 */
+ * through 'sampling_frequency' attribute of IIO app, but this period here makes sure
+ * we are not stuck into a forever loop in case data capture is interrupted
+ * or failed in between.
+ * Note: This timeout factor is dependent upon the MCU clock frequency. Below timeout
+ * is tested for SDP-K1 platform @180Mhz default core clock */
 #define BUF_READ_TIMEOUT	(100000000)
 
 /******************************************************************************/
 /********************** Variables and User Defined Data Types *****************/
 /******************************************************************************/
 
-/* Extern declaration for data capture operations structure variable */
+/* Extern declaration for device specific data capture operations structure
+ * (actual definition should be present in an application) */
 extern struct data_capture_ops data_capture_ops;
 
 /*
@@ -61,11 +65,15 @@
  *@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_buffer_state_e state;         	// Buffer state
+	bool refill_buffer;        			// Flag to start refilling acquisition buffer
+	uint32_t rd_indx;          			// Buffer read index (incremented per sample transmit)
+	uint32_t wr_indx;          			// Buffer write index (incremented per sample read)
+	uint8_t sample_size;        		// ADC sample/raw data size received from application
+	uint8_t chn_indx;        			// ADC channel index into acquisition buffer
+	uint8_t active_chn[MAX_AVAILABLE_CHANNELS];   	// Active channel number sequence
+	uint8_t data[DATA_BUFFER_SIZE];   				// buffer data (adc raw values)
+	uint8_t *pdata;   								// Pointer to data buffer
 } acq_buf_t;
 
 /* ADC data acquisition buffers */
@@ -77,13 +85,12 @@
 /* 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;
 
+/* Channel alignment offset */
+static volatile uint8_t chn_alignment_offset = 0;
+
 /* Actual or max available size of acquisition buffer */
 static volatile uint16_t max_available_buffer_size = 0;
 
@@ -96,65 +103,38 @@
 /******************************************************************************/
 
 /*!
- * @brief	Function to read the ADC raw data for single channel
+ * @brief	Function to read the single ADC sample (raw data) for input 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)
+int32_t read_single_sample(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) {
+		/* Perform operations required before single sample conversion read */
+		if (data_capture_ops.single_sample_read_start_ops) {
+			if (data_capture_ops.single_sample_read_start_ops(input_chn) != 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) !=
+		/* Perform ADC conversion and read the converted sample after EOC */
+		if (data_capture_ops.perform_conv_and_read_sample(&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();
+	/* Perform operations required post single sample conversion read */
+	if (data_capture_ops.single_sample_read_stop_ops) {
+		if (data_capture_ops.single_sample_read_stop_ops(input_chn) != SUCCESS) {
+			status = FAILURE;
+		}
 	}
 
 	*raw_data = read_adc_data;
@@ -163,41 +143,46 @@
 
 
 /*!
- * @brief	Function to store the number of actul requested samples from IIO client
+ * @brief	Function to store the number of actul requested ADC 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
+ * @note	The information about sample and buffer size is required for continuous
+ *			data acquisition
  */
 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;
 
+	/* Store the ADC sample size */
+	acq_buffer.sample_size = 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.
+	 * e.g. if requested samples are 400 and sample size is 2 bytes, the max available
+	 * size of buffer is: available size = ((32768 / 2) / 400) * 400 = 40 * 400 = 16000.
 	 * The max samples to be requested should always be less than half the max size of buffer
-	 * (in this case: 2048 / 2 = 1024).
+	 * (in this case: (32768/2) / 2 = 8192).
 	 * */
-	max_available_buffer_size = ((DATA_BUFFER_SIZE / num_of_requested_samples) *
-				     num_of_requested_samples);
+	max_available_buffer_size = ((DATA_BUFFER_SIZE / sample_size_in_bytes) /
+				     num_of_requested_samples) * num_of_requested_samples;
 }
 
 
 /*!
- * @brief	Function to read new samples into buffer without IIO request timeout
+ * @brief	Function to read acquired 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)
+static void read_acquired_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
+	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.
@@ -216,7 +201,7 @@
 	}
 
 	memcpy(output_buffer,
-	       (void const *)&acq_buffer.data[acq_buffer.rd_indx],
+	       (void const *)&acq_buffer.data[acq_buffer.rd_indx * sample_size_in_bytes],
 	       bytes);
 	acq_buffer.rd_indx += samples_to_read;
 }
@@ -230,27 +215,36 @@
  * @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 read_buffered_data(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
+				 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) {
+	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);
+	/* Increment read counter to point to next acquired data of 1st active channel */
+	if ((offset == 0) && (acq_buffer.rd_indx > 0)) {
+		acq_buffer.rd_indx += chn_alignment_offset;
+	}
 
-	/* Make buffer available again once read completely */
+	read_acquired_samples(pbuf, samples_to_read, sample_size_in_bytes);
+
+	/* Make buffer available again once it is read/transmited 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.pdata = acq_buffer.data;
 		acq_buffer.state = BUF_AVAILABLE;
+		acq_buffer.refill_buffer = true;
 	}
 
 	return bytes;
@@ -267,77 +261,54 @@
  *			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
+ * @note	This function also handles the logic to align the first channel data after
  *			every 'n' sample transmission. This is required to visualize data properly
- *			on IIO client application (more details listed below).
+ *			on IIO client application.
  */
 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;
-				}
+		/* Read the sample(s) for channel(s) which has/have been sampled recently and
+		 * get the number of samples read count */
+		if (data_capture_ops.read_converted_sample(&adc_sample,
+				acq_buffer.active_chn[acq_buffer.chn_indx]) != FAILURE) {
+			do {
+				if (acq_buffer.state == BUF_AVAILABLE) {
+					if (acq_buffer.refill_buffer) {
+						/* Buffer refilling must start with first active channel data
+						 * for IIO client to synchronize the buffered data */
+						if (acq_buffer.chn_indx != 0) {
+							break;
+						}
+						acq_buffer.refill_buffer = false;
+					}
 
-				/* 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;
+					/* Copy adc samples into acquisition buffer to transport over
+					 * communication link */
+					memcpy(acq_buffer.pdata, &adc_sample, acq_buffer.sample_size);
+					acq_buffer.pdata += acq_buffer.sample_size;
 
-				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;
+					/* Check for acquisition buffer full condition */
+					acq_buffer.wr_indx++;
+					if (acq_buffer.wr_indx >= max_available_buffer_size) {
+						acq_buffer.state = BUF_FULL;
+					}
 				}
+			} while (0);
 
-				/* 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;
-				}
+			/* Keep tracking channel index as it is needed to refill the buffer
+			 * starting with first channel data */
+			acq_buffer.chn_indx++;
+			if (acq_buffer.chn_indx >= num_of_active_channels) {
+				acq_buffer.chn_indx = 0;
 			}
-		} 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();
+		if (data_capture_ops.trigger_next_conversion) {
+			data_capture_ops.trigger_next_conversion();
 		}
 	}
 }
@@ -358,10 +329,9 @@
 
 	acq_buffer.wr_indx = 0;
 	acq_buffer.rd_indx = 0;
-	acq_buffer.sample_cnt = 0;
-
-	buff_chn_indx = 0;
-	do_chn_alignment = true;
+	acq_buffer.chn_indx = 0;
+	acq_buffer.refill_buffer = false;
+	acq_buffer.pdata = acq_buffer.data;
 }
 
 
@@ -375,58 +345,53 @@
 void start_data_capture(uint32_t ch_mask, uint8_t num_of_chns)
 {
 	uint32_t mask = 0x1;
+	uint8_t index = 0;
 
-	/* Make sure requested samples size is less than ADC buffer size */
+	/* Make sure requested samples size is less than max available 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 module specific 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 */
+	/* Count active 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;
-				}
-			}
-
+			acq_buffer.active_chn[index++] = chn;
 			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 */
+	/* Note: As shown below, after nth sample read, next sample must be for the first
+	 *       channel present in the list of enabled channels */
+	/*+-----------------------+-------------------------+---------------------------+
+	 *| 0 | 1 | 2 | ------| n | 0 | 1 | 2 | --------| n | 0 | 1 | 2 |-----------| l |
+	 *+-^-------------------^-+-^---------------------^-+-^-----------------------^-+
+	 *  |                   |   |                     |   |                       |
+	 * 1st chn data   nth data  1st chn data    nth data  1st chn data         last data
+	 * n = number of requested samples. l = last data (channel unknown)
+	 * To achieve this, offset value is determined based on the requested samples count
+	 * and number of active channels. The read index is then incremented by offset value
+	 * to read the data for first enabled channel by mapping into acquisition buffer.
+	 **/
+	if (num_of_requested_samples % num_of_active_channels) {
+		chn_alignment_offset = (num_of_requested_samples - ((num_of_requested_samples /
+					num_of_active_channels) * num_of_active_channels)) + 1;
+	} else {
+		chn_alignment_offset = 0;
+	}
+
+	/* Make acquisition buffer available and start continuous conversion */
 	acq_buffer.state = BUF_AVAILABLE;
-	if (data_capture_ops.enable_continuous_read_conversion(ch_mask) == SUCCESS) {
+	if (data_capture_ops.continuous_sample_read_start_ops) {
+		if (data_capture_ops.continuous_sample_read_start_ops(ch_mask) != FAILURE) {
+			start_adc_data_capture = true;
+		}
+	} else {
 		start_adc_data_capture = true;
-	} else {
-		start_adc_data_capture = false;
 	}
 }
 
@@ -439,15 +404,11 @@
 {
 	start_adc_data_capture = false;
 
-	if (data_capture_ops.disable_conversion) {
-		/* Stop conversion */
-		data_capture_ops.disable_conversion();
+	/* Enable operations required post continuous sample read */
+	if (data_capture_ops.continuous_sample_read_stop_ops) {
+		data_capture_ops.continuous_sample_read_stop_ops();
 	}
 
-	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 module specific flags and variables */
 	reset_data_capture();
 }