Example Program for EVAL-AD7606
Dependencies: platform_drivers
Diff: app/iio_ad7606.c
- 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;