Example Program for EVAL-AD7606

Dependencies:   platform_drivers

Revision:
3:83b3133f544a
Parent:
1:819ac9aa5667
Child:
6:32de160dce43
--- a/app/iio_ad7606.c	Mon Oct 05 09:39:06 2020 +0000
+++ b/app/iio_ad7606.c	Fri Oct 16 21:37:35 2020 +0530
@@ -45,15 +45,41 @@
 /* ADC data to Voltage conversion scale factor for IIO client */
 #define DEFAULT_SCALE		((DEFAULT_CHN_RANGE / ADC_MAX_COUNT_BIPOLAR) * 1000)
 
-/* LSB Threshold to entry into open wire detection as per datasheet */
-#define OPEN_DETECT_ENTRY_THRESHOLD		350
+/* LSB Threshold to entry into open circuit detection as per datasheet */
+#define MANUAL_OPEN_DETECT_ENTRY_TRHLD			350
+
+/* Manual open circuit detect LSB threshold @50K Rpd as per datasheet */
+#define MANUAL_OPEN_DETECT_THRESHOLD_RPD50K		20
+
+/* Number of consecutive conversions (N) in manual open circuit detection */
+#define MANUAL_OPEN_DETECT_CONV_CNTS			10
 
-/* Open detect LSB threshold @50K Rpd as per datasheet */
-#define OPEN_DETECT_THRESHOLD_RPD50K	15
+/* LSB Threshold b/w consecutive N conversions */
+#define MANUAL_OPEN_DETECT_CONV_TRSHLD			10
+
+/* Number of common mode conversions in manual open circuit detect */
+#define MANUAL_OPEN_DETECT_CM_CNV_CNT			3
+
+/* Max number of queue counts for auto mode open circuit detection */
+#define AUTO_OPEN_DETECT_QUEUE_MAX_CNT			128
+#define AUTO_OPEN_DETECT_QUEUE_EXTRA_CONV_CNT	15
 
 /* Maximum ADC calibration gain value */
 #define ADC_CALIBRATION_GAIN_MAX		64.0
 
+#if defined(DEV_AD7606C_18)
+#define	OFFSET_REG_RESOLUTION		4
+#else
+#define	OFFSET_REG_RESOLUTION		1
+#endif
+
+/* Default sampling frequency for AD7606 (in SPS) to define IIO client timeout period.
+ * Note: The actual sampling frequncy is much higher (~30KSPS per channel), however the
+ * data transmission back to IIO client is limited by the serial (UART) link, which
+ * can provide max transmission rate of ~3-4KSPS. Hence sampling frequency is set to 1Khz
+ * for safer side */
+#define AD7606_DEFLT_SAMPLING_FREQEUNCY		(1000)
+
 /******************************************************************************/
 /*************************** Types Declarations *******************************/
 /******************************************************************************/
@@ -98,8 +124,8 @@
 	"8  (+/-10.0V SE)", "9  (+/-10.0V SE)", "10  (+/-10.0V SE)", "11  (+/-10.0V SE)",
 #elif defined(DEV_AD7606C_18) || defined(DEV_AD7606C_16)
 	"0  (+/-2.5V SE)", "1  (+/-5.0V SE)", "2  (+/-6.25V SE)", "3  (+/-10.0V SE)",
-	"4  (+/-12.5V SE)", "5  (+5.0V SE)", "6  (+10.0V SE)", "7  (+12.5V SE)",
-	"8  (+/-5.0V DE)", "9  (+/-10.0V DE)", "10  (+/-12.5V DE)", "11  (+/-20.0V DE)"
+	"4  (+/-12.5V SE)", "5  (0 to 5V SE)", "6  (0 to 10V SE)", "7  (0 to 12.5V SE)",
+	"8  (+/-5.0V Diff)", "9  (+/-10.0V Diff)", "10  (+/-12.5V Diff)", "11  (+/-20.0V Diff)"
 #elif defined(DEV_AD7609)
 	"0  (+/-10.0V SE)", "1  (+/-20.0V SE)"
 #else
@@ -171,6 +197,17 @@
 /* Flag to trigger new background conversion and capture when READBUFF command is issued */
 static bool adc_background_data_capture_started = false;
 
+/* Gain calibration status */
+static bool gain_calibration_done = false;
+
+/* Open circuit mode detection flags */
+static bool open_circuit_detection_done = false;
+static bool open_circuit_detection_error = false;
+static bool open_circuit_detect_read_done = false;
+
+/* Sampling frequency of device */
+static uint16_t sampling_frequency = AD7606_DEFLT_SAMPLING_FREQEUNCY;
+
 /******************************************************************************/
 /************************ Functions Prototypes ********************************/
 /******************************************************************************/
@@ -217,6 +254,37 @@
 
 
 /*!
+ * @brief	Getter/Setter for the sampling frequency attribute value
+ * @param	device- pointer to IIO device structure
+ * @param	buf- pointer to buffer holding attribute value
+ * @param	len- length of buffer string data
+ * @param	channel- pointer to IIO channel structure
+ * @return	Number of characters read/written
+ * @Note	This attribute is used to define the timeout period in IIO
+ *			client during data capture.
+ *			Timeout = (number of requested samples * (1/sampling frequency)) + 1sec
+ *			e.g. if sampling frequency = 1KSPS and requested samples = 400
+ *			Timeout = (400 * 0.001) + 1 = 1.4sec
+ */
+ssize_t get_sampling_frequency(void *device,
+			       char *buf,
+			       size_t len,
+			       const struct iio_ch_info *channel)
+{
+	return (ssize_t) sprintf(buf, "%d", sampling_frequency);
+}
+
+ssize_t set_sampling_frequency(void *device,
+			       char *buf,
+			       size_t len,
+			       const struct iio_ch_info *channel)
+{
+	/* NA- Can't set sampling frequency value */
+	return len;
+}
+
+
+/*!
  * @brief	Getter/Setter for the raw attribute value
  * @param	device- pointer to IIO device structure
  * @param	buf- pointer to buffer holding attribute value
@@ -1057,70 +1125,114 @@
 
 
 /*!
- * @brief	Getter/Setter for the channel open wire detect manual attribute value
+ * @brief	Getter/Setter for the channel open circuit detect manual attribute value
  * @param	device- pointer to IIO device structure
  * @param	buf- pointer to buffer holding attribute value
  * @param	len- length of buffer string data
  * @param	channel- pointer to IIO channel structure
  * @return	Number of characters read/written
  */
-ssize_t get_chn_open_wire_detect_manual(void *device,
-					char *buf,
-					size_t len,
-					const struct iio_ch_info *channel)
+ssize_t get_chn_open_circuit_detect_manual(void *device,
+		char *buf,
+		size_t len,
+		const struct iio_ch_info *channel)
 {
-	static volatile int32_t data[2];
-	uint8_t open_detect_flag = false;
-	int32_t rw_status = FAILURE;
+	int32_t prev_adc_code, curr_adc_code;
+	bool open_detect_flag = false;
+	bool open_detect_done = false;
+	uint8_t cnt;
 
-	/* Read the ADC on selected channel (before open wire detection start) */
-	data[0] = single_data_read(device,
-				   channel->ch_num - 1,
-				   attr_polarity_val[channel->ch_num - 1]);
+	/* Enter into manual open circuit detection mode */
+	do {
+		if (ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE, 1) == SUCCESS) {
+			/* Read the ADC on selected chnnel (first reading post open circuit detection start) */
+			prev_adc_code = single_data_read(device,
+							 channel->ch_num - 1,
+							 attr_polarity_val[channel->ch_num - 1]);
 
-	/* Enter into manual open wire detect mode */
-	if (ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE, 1) == SUCCESS) {
-		/* Set common mode high (enabling open wire detect on selected channel) */
-		if (ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_ENABLE,
-					 (1 << ((channel->ch_num) - 1))) == SUCCESS) {
-			/* Read the ADC on selected chnnel (post open wire detection) */
-			data[1] = single_data_read(device, channel->ch_num - 1,
-						   attr_polarity_val[channel->ch_num - 1]);
+			/* Perform N conversions and monitor the code delta */
+			for (cnt = 0; cnt < MANUAL_OPEN_DETECT_CONV_CNTS; cnt++) {
+				/* Check if code is within 350LSB (nearest ZS code) */
+				if (prev_adc_code >= 0 && prev_adc_code < MANUAL_OPEN_DETECT_ENTRY_TRHLD) {
+					/* Perform next conversion and read the result */
+					curr_adc_code = single_data_read(device,
+									 channel->ch_num - 1,
+									 attr_polarity_val[channel->ch_num - 1]);
 
-			/* Check for up shift in common mode output voltage */
-			if ((data[1] - data[0]) > OPEN_DETECT_THRESHOLD_RPD50K) {
-				/* Set common mode low (disabling open wire detect on channels) */
-				if (ad7606_spi_reg_write(device,
-							 AD7606_REG_OPEN_DETECT_ENABLE,
-							 0) == SUCCESS) {
-
-					/* Let channel settle down */
-					mdelay(1);
+					/* Check if delta b/w current and previus reading is within 10 LSB code */
+					if (abs(curr_adc_code - prev_adc_code) > MANUAL_OPEN_DETECT_CONV_TRSHLD) {
+						open_detect_done = true;
+						break;
+					}
 
-					/* Read the ADC output after open wire detection */
-					data[1] = single_data_read(device,
-								   channel->ch_num - 1,
-								   attr_polarity_val[channel->ch_num - 1]);
-
-					/* Check if code returns back to original (with some threshold) */
-					if (abs(data[1] - data[0]) <= OPEN_DETECT_THRESHOLD_RPD50K) {
-						open_detect_flag = true;
-					}
+					/* Get the previous code */
+					prev_adc_code = curr_adc_code;
+				} else {
+					open_detect_done = true;
+					break;
 				}
 			}
 
-			rw_status = SUCCESS;
+			/* Break if open circuit detection aborted (in case above conditions not met) */
+			if (open_detect_done)
+				break;
+
+			/* Set common mode high (enabling open circuit detect on selected channel) */
+			if (ad7606_spi_reg_write(device,
+						 AD7606_REG_OPEN_DETECT_ENABLE,
+						 (1 << ((channel->ch_num) - 1))) == SUCCESS) {
+
+				/* Perform next conversions (~2-3) and read the result (with common mode set high) */
+				for (cnt = 0; cnt < MANUAL_OPEN_DETECT_CM_CNV_CNT; cnt++) {
+					udelay(100);
+					curr_adc_code = single_data_read(device,
+									 channel->ch_num - 1,
+									 attr_polarity_val[channel->ch_num - 1]);
+				}
+
+				/* Check if delta b/w common mode high code and previous N conversion code is > threshold */
+				if ((curr_adc_code - prev_adc_code) < MANUAL_OPEN_DETECT_THRESHOLD_RPD50K) {
+					open_detect_done = true;
+					break;
+				}
+			} else {
+				return -EINVAL;
+			}
+
+			/* Break if open circuit detection aborted (in case above conditions not met) */
+			if (open_detect_done)
+				break;
+
+			/* Set common mode low (disabling open circuit detect on channel) */
+			if (ad7606_spi_reg_write(device,
+						 AD7606_REG_OPEN_DETECT_ENABLE,
+						 0) == SUCCESS) {
+				/* Perform next conversion and read the result (with common mode set low) */
+				curr_adc_code = single_data_read(device,
+								 channel->ch_num - 1,
+								 attr_polarity_val[channel->ch_num - 1]);
+
+				/* Check if delta b/w common mode low code and previous N conversion code is < threshold */
+				if (abs(curr_adc_code - prev_adc_code) < MANUAL_OPEN_DETECT_THRESHOLD_RPD50K) {
+					open_detect_flag = true;
+					open_detect_done = true;
+				}
+			} else {
+				return -EINVAL;
+			}
+		} else {
+			return -EINVAL;
 		}
-	}
+	} while (0);
 
 	/* Disable open detect mode */
 	(void)ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE, 0);
 
-	if (rw_status == SUCCESS) {
+	if (open_detect_done) {
 		if (open_detect_flag) {
-			strcpy(buf, "Open Wire Detected");
+			strcpy(buf, "Open Circuit Detected");
 		} else {
-			strcpy(buf, "Open Wire Not Detected");
+			strcpy(buf, "Open Circuit Not Detected");
 		}
 
 		return len;
@@ -1129,105 +1241,126 @@
 	return -EINVAL;
 }
 
-ssize_t set_chn_open_wire_detect_manual(void *device,
-					char *buf,
-					size_t len,
-					const struct iio_ch_info *channel)
+ssize_t set_chn_open_circuit_detect_manual(void *device,
+		char *buf,
+		size_t len,
+		const struct iio_ch_info *channel)
 {
-	// NA- Can't set open wire detect
+	// NA- Can't set open circuit detect
 	return - EINVAL;
 }
 
 
 /*!
- * @brief	Getter/Setter for the channel open wire detect auto attribute value
+ * @brief	Getter/Setter for the channel open circuit detect auto attribute value
  * @param	device- pointer to IIO device structure
  * @param	buf- pointer to buffer holding attribute value
  * @param	len- length of buffer string data
  * @param	channel- pointer to IIO channel structure
  * @return	Number of characters read/written
  */
-ssize_t get_chn_open_wire_detect_auto(void *device,
-				      char *buf,
-				      size_t len,
-				      const struct iio_ch_info *channel)
+ssize_t get_chn_open_circuit_detect_auto(void *device,
+		char *buf,
+		size_t len,
+		const struct iio_ch_info *channel)
 {
-	uint8_t open_detect_flag = false;
-	int32_t rw_status = FAILURE;
-	uint8_t conv_cnts;
+	if (open_circuit_detect_read_done) {
+		open_circuit_detect_read_done = false;
+
+		if (open_circuit_detection_error) {
+			strcpy(buf, "Error!!");
+		}
 
-	if (open_detect_queue_cnts[channel->ch_num-1] <= 1) {
-		strcpy(buf, "Err: OPEN_DETECT_QUEUE Invalid");
+		if (open_circuit_detection_done) {
+			strcpy(buf, "Open Circuit Detected");
+		} else {
+			strcpy(buf, "Open Circuit Not Detected");
+		}
+
 		return len;
 	}
 
-	/* Enter into auto open detect mode */
-	if (ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE,
-				 open_detect_queue_cnts[channel->ch_num-1]) == SUCCESS) {
-		/* Enable open wire detection on selected channel */
-		if (ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_ENABLE,
-					 (1 << ((channel->ch_num) - 1))) == SUCCESS) {
-			/* Monitor the open detect flag for max N (open detect queue count) conversions.
-			 * Note: In ideal scenario, the open detect flash should be monitored continuously while
-			 * background N conversions are in progress */
-			for (conv_cnts = 0; conv_cnts < open_detect_queue_cnts[channel->ch_num - 1];
-			     conv_cnts++) {
-				if (ad7606_convst(device) == SUCCESS) {
-					udelay(100);
+	return (ssize_t)sprintf(buf, "OPEN_DETECT_QUEUE: %d",
+				open_detect_queue_cnts[channel->ch_num - 1]);
+}
+
+ssize_t set_chn_open_circuit_detect_auto(void *device,
+		char *buf,
+		size_t len,
+		const struct iio_ch_info *channel)
+{
+	uint8_t data;
+	uint8_t open_detect_flag = false;
+	int32_t rw_status = FAILURE;
+	uint16_t conv_cnts;
+
+	(void)sscanf(buf, "%d", &data);
+	open_circuit_detection_error = false;
+
+	if ((data > 1 && data <= AUTO_OPEN_DETECT_QUEUE_MAX_CNT) && (buf[0] >= '0'
+			&& buf[0] <= '9')) {
+		open_detect_queue_cnts[channel->ch_num - 1] = data;
 
-					/* Monitor the open detect flag */
-					if (ad7606_spi_reg_read(device,
-								AD7606_REG_OPEN_DETECTED,
-								&open_detect_flag) == SUCCESS) {
-						open_detect_flag >>= (channel->ch_num - 1);
-						open_detect_flag &= 0x1;
+		/* Enter into open circuit auto open detect mode */
+		if (ad7606_spi_reg_write(device,
+					 AD7606_REG_OPEN_DETECT_QUEUE,
+					 open_detect_queue_cnts[channel->ch_num - 1]) == SUCCESS) {
+			/* Enable open circuit detection on selected channel */
+			if (ad7606_spi_reg_write(device,
+						 AD7606_REG_OPEN_DETECT_ENABLE,
+						 (1 << ((channel->ch_num) - 1))) == SUCCESS) {
+				/* Monitor the open detect flag for max N+15 (open detect queue count) conversions.
+				 * Note: In ideal scenario, the open detect flash should be monitored continuously while
+				 * background N conversions are in progress */
+				for (conv_cnts = 0;
+				     conv_cnts < (open_detect_queue_cnts[channel->ch_num - 1] +
+						  AUTO_OPEN_DETECT_QUEUE_EXTRA_CONV_CNT);
+				     conv_cnts++) {
+					if (ad7606_convst(device) == SUCCESS) {
+						udelay(100);
 
-						rw_status = SUCCESS;
-						if (open_detect_flag) {
+						/* Monitor the open detect flag */
+						if (ad7606_spi_reg_read(device,
+									AD7606_REG_OPEN_DETECTED,
+									&open_detect_flag) == SUCCESS) {
+							open_detect_flag >>= (channel->ch_num - 1);
+							open_detect_flag &= 0x1;
+
+							rw_status = SUCCESS;
+							if (open_detect_flag) {
+								break;
+							}
+						} else {
+							rw_status = FAILURE;
 							break;
 						}
 					} else {
 						rw_status = FAILURE;
 						break;
 					}
-				} else {
-					rw_status = FAILURE;
-					break;
 				}
 			}
 		}
+
+		/* Disable open detect mode and clear open detect flag */
+		(void)ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE, 0);
+		(void)ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECTED, 0xFF);
+
+		open_detect_queue_cnts[channel->ch_num - 1] = 0;
+
+		if (rw_status == SUCCESS) {
+			if (open_detect_flag) {
+				open_circuit_detection_done = true;
+			} else {
+				open_circuit_detection_done = false;
+			}
+
+			open_circuit_detect_read_done = true;
+			return len;
+		}
 	}
 
-	/* Disable open detect mode */
-	(void)ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE, 0);
-
-	if (rw_status == SUCCESS) {
-		if (open_detect_flag) {
-			strcpy(buf, "Open Wire Detected");
-		} else {
-			strcpy(buf, "Open Wire Not Detected");
-		}
-
-		return len;
-	}
-
-	return -EINVAL;
-}
-
-ssize_t set_chn_open_wire_detect_auto(void *device,
-				      char *buf,
-				      size_t len,
-				      const struct iio_ch_info *channel)
-{
-	uint8_t data;
-
-	(void)sscanf(buf, "%d", &data);
-
-	if (data > 1) {
-		open_detect_queue_cnts[channel->ch_num - 1] = data;
-		return len;
-	}
-
+	open_circuit_detection_error = true;
 	return -EINVAL;
 }
 
@@ -1268,7 +1401,7 @@
 			attr_scale_val[channel->ch_num - 1]);
 
 	/* Calculate the channel offset and write it to offset register */
-	chn_offset = -(adc_voltage / lsb_voltage);
+	chn_offset = -(adc_voltage / lsb_voltage / OFFSET_REG_RESOLUTION);
 
 	if (ad7606_set_ch_offset(device, channel->ch_num - 1,
 				 chn_offset) == SUCCESS) {
@@ -1283,7 +1416,7 @@
 				     size_t len,
 				     const struct iio_ch_info *channel)
 {
-	// NA- Can't set open wire detect
+	// NA- Can't set open circuit detect
 	return - EINVAL;
 }
 
@@ -1301,15 +1434,22 @@
 				   size_t len,
 				   const struct iio_ch_info *channel)
 {
-	uint8_t chn_gain;
-
-	/* Perform the gain calibration */
-	chn_gain = gain_calibration_reg_val[channel->ch_num - 1];
+	uint8_t read_val;
 
-	if (ad7606_set_ch_gain(device,
-			       channel->ch_num - 1,
-			       chn_gain) == SUCCESS) {
-		return sprintf(buf, "Calibration Done (Rfilter=%d K)", chn_gain);
+	if (gain_calibration_done) {
+		/* Get calibration status for previous gain value write event */
+		gain_calibration_done = false;
+		return sprintf(buf, "Calibration Done (Rfilter=%d K)",
+			       gain_calibration_reg_val[channel->ch_num - 1]);
+	}
+
+	/* Return gain value when normal read event is triggered */
+	if (ad7606_spi_reg_read(device,
+				AD7606_REG_GAIN_CH(channel->ch_num - 1),
+				&read_val) == SUCCESS) {
+		gain_calibration_reg_val[channel->ch_num - 1] = (read_val & AD7606_GAIN_MSK);
+		return sprintf(buf, "Rfilter= %d K",
+			       gain_calibration_reg_val[channel->ch_num - 1]);
 	}
 
 	return -EINVAL;
@@ -1322,12 +1462,21 @@
 {
 	float data;
 
-	(void)sscanf(buf, "%f", &data);
+	if (buf[0] >= '0' && buf[0] <= '9') {
+		(void)sscanf(buf, "%f", &data);
+
+		if (data >= 0 && data < ADC_CALIBRATION_GAIN_MAX) {
+			/* Get the nearest value of unsigned integer */
+			gain_calibration_reg_val[channel->ch_num - 1] = (uint8_t)(round(data));
 
-	if (data < ADC_CALIBRATION_GAIN_MAX) {
-		/* Get the nearest value of unsigned integer */
-		gain_calibration_reg_val[channel->ch_num - 1] = (uint8_t)(round(data));
-		return len;
+			/* Perform the gain calibration by writing gain value into gain register */
+			if (ad7606_set_ch_gain(device,
+					       channel->ch_num - 1,
+					       gain_calibration_reg_val[channel->ch_num - 1]) == SUCCESS) {
+				gain_calibration_done = true;
+				return len;
+			}
+		}
 	}
 
 	return -EINVAL;