Pratyush Mallick
/
testing
this is testing
app/src/iio_ad7606.c
- Committer:
- pmallick
- Date:
- 2021-01-14
- Revision:
- 0:3afcd581558d
File content as of revision 0:3afcd581558d:
/***************************************************************************//** * @file iio_ad7606.c * @brief Implementation of AD7606 IIO application interfaces * @details This module acts as an interface for AD7606 IIO application ******************************************************************************** * Copyright (c) 2020 Analog Devices, Inc. * * 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 <inttypes.h> #include <string.h> #include <errno.h> #include "app_config.h" #include "tinyiiod.h" #include "iio_ad7606.h" #include "iio_ad7606_attr.h" #include "iio_app.h" #include "iio_transport.h" #include "platform_support.h" #include "spi_extra.h" #include "gpio_extra.h" #include "uart_extra.h" #include "irq_extra.h" #include "ad7606.h" #include "ad7606_data_capture.h" #include "ad7606_support.h" #include "ad7606_user_config.h" /******************************************************************************/ /************************ Macros/Constants ************************************/ /******************************************************************************/ /* ADC data to Voltage conversion scale factor for IIO client */ #define DEFAULT_SCALE ((DEFAULT_CHN_RANGE / ADC_MAX_COUNT_BIPOLAR) * 1000) /* Select open wire detection mode (disable to select auto mode) */ #define MANUAL_OPEN_DETECT /* Open detect threshold @50K Rpd as per datasheet */ #define OPEN_DETECT_THRESHOLD_RPD50K 15 /* Number of conversions to be performed before detecting open wire */ #define OPEN_DETECT_AUTO_MODE_CNTS 10 /******************************************************************************/ /*************************** Types Declarations *******************************/ /******************************************************************************/ /** * IIO application instance descriptor. */ static struct iio_app_desc *iio_app_desc; /** * Device name. */ static const char dev_name[] = ACTIVE_DEVICE_NAME; /** * Pointer to the struct representing the AD7606 IIO device */ static struct ad7606_dev *p_ad7606_dev = NULL; /* Device attributes with default values */ /* Power down mode values string representation (possible values specified in datasheet) */ static char *operating_mode_str[] = { "0 (Normal Mode)", "1 (Standby Mode)", "2 (Auto Standby Mode)", "3 (Shutdown Mode)" }; /* Bandwidth values string */ static char *bandwidth_str[] = { "0 (Low)", "1 (High)" }; /* Channel range values string representation (possible values specified in datasheet) */ static char *chn_range_str[] = { #if defined(DEV_AD7606B) "0 (+/-2.5V SE)", "1 (+/-5.0V SE)", "2 (+/-10.0V SE)", "3 (+/-10.0V SE)", "4 (+/-10.0V SE)", "5 (+/-10.0V SE)", "6 (+/-10.0V SE)", "7 (+/-10.0V SE)", "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)" #elif defined(DEV_AD7609) "0 (+/-10.0V SE)", "1 (+/-20.0V SE)" #else "0 (+/-5.0V SE)", "1 (+/-10.0V SE)" #endif }; /* Oversampling values string representation (possible values specified in datasheet) */ static char *oversampling_val_str[] = { "0 (no oversampling)", "1 (oversampling by 2)", "2 (oversampling by 4)", "3 (oversampling by 8)", "4 (oversampling by 16)", "5 (oversampling by 32)", "6 (oversampling by 64)", "7 (oversampling by 128)", "8 (oversampling by 256)" }; /* Channel range values string representation (possible values specified in datasheet) */ static float chn_range_val[] = { #if defined(DEV_AD7606B) 2.5, 5.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0 #elif defined(DEV_AD7606C_18) || defined(DEV_AD7606C_16) 2.5, 5.0, 6.25, 10.0, 12.5, 5.0, 10.0, 12.5, 5.0, 10.0, 12.5, 20.0 #elif defined(DEV_AD7609) 10.0, 20.0 #else 5.0, 10.0 #endif }; /* Range value per channel */ static float attr_chn_range[NO_OF_CHANNELS] = { DEFAULT_CHN_RANGE, DEFAULT_CHN_RANGE, DEFAULT_CHN_RANGE, DEFAULT_CHN_RANGE, DEFAULT_CHN_RANGE, DEFAULT_CHN_RANGE, DEFAULT_CHN_RANGE, DEFAULT_CHN_RANGE }; /* Scale value per channel */ static float attr_scale_val[NO_OF_CHANNELS] = { DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE }; /* Scale value per channel */ static polarity_e attr_polarity_val[NO_OF_CHANNELS] = { BIPOLAR, BIPOLAR, BIPOLAR, BIPOLAR, BIPOLAR, BIPOLAR, BIPOLAR, BIPOLAR }; /* Channel range */ typedef enum { LOW, HIGH } range_e; /* Device register value */ static uint8_t device_reg_val; /* GPIO LED descriptor and init structure */ static struct gpio_desc *gpio_led; /******************************************************************************/ /************************ Functions Prototypes ********************************/ /******************************************************************************/ static float get_vltg_conv_scale_factor(float chn_range, polarity_e polarity); /******************************************************************************/ /************************ Functions Definitions *******************************/ /******************************************************************************/ /*! * @brief Getter/Setter for the scale 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_scale(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { return (ssize_t) sprintf(buf, "%f", attr_scale_val[channel->ch_num - 1]); } ssize_t set_chn_scale(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { float scale; (void)sscanf(buf, "%f", &scale); if (scale > 0.0) { attr_scale_val[channel->ch_num - 1] = scale; return len; } return -EINVAL; } /*! * @brief Getter/Setter for the raw 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_raw(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { int32_t adc_data_raw; /* Capture the raw adc data */ adc_data_raw = single_data_read(device, channel->ch_num - 1, attr_polarity_val[channel->ch_num - 1]); return (ssize_t) sprintf(buf, "%d", adc_data_raw); } ssize_t set_chn_raw(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { /* NA- Can't set raw value */ return len; } /*! * @brief Getter/Setter for the operating mode 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 Available only for AD7606B and AD7606C */ ssize_t get_operating_mode(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t read_val; uint8_t operating_mode_value; if (ad7606_spi_reg_read(device, AD7606_REG_CONFIG, &read_val) == SUCCESS) { operating_mode_value = (read_val & AD7606_CONFIG_OPERATION_MODE_MSK); if (operating_mode_value < sizeof(operating_mode_str) / sizeof( operating_mode_str[0])) { return (ssize_t)sprintf(buf, "%s", operating_mode_str[operating_mode_value]); } } return -EINVAL; } ssize_t set_operating_mode(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t operating_mode_value; (void)sscanf(buf, "%d", &operating_mode_value); if (operating_mode_value < sizeof(operating_mode_str) / sizeof( operating_mode_str[0])) { if (ad7606_spi_write_mask(device, AD7606_REG_CONFIG, AD7606_CONFIG_OPERATION_MODE_MSK, operating_mode_value) == SUCCESS) { return len; } } return -EINVAL; } /*! * @brief Getter/Setter for the power down mode 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 Available for all devices except AD7606B and AD7606C */ ssize_t get_power_down_mode(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t gpio_stby_val; uint8_t gpio_range_val; if (gpio_get_value(((struct ad7606_dev *)device)->gpio_stby_n, &gpio_stby_val) == SUCCESS) { if (gpio_get_value(((struct ad7606_dev *)device)->gpio_range, &gpio_range_val) == SUCCESS) { if (gpio_stby_val) { return sprintf(buf, "%s", operating_mode_str[AD7606_NORMAL]); } else { if (gpio_range_val) { return sprintf(buf, "%s", operating_mode_str[AD7606_STANDBY]); } else { return sprintf(buf, "%s", operating_mode_str[AD7606_SHUTDOWN]); } } } } return -EINVAL; } ssize_t set_power_down_mode(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t power_down_mode_value; static enum ad7606_op_mode prev_power_down_mode = AD7606_NORMAL; struct ad7606_config dev_config; sscanf(buf, "%d", &power_down_mode_value); if (power_down_mode_value < (sizeof(operating_mode_str) / sizeof( operating_mode_str[0]))) { dev_config.op_mode = power_down_mode_value; switch (power_down_mode_value) { case AD7606_NORMAL: if (ad7606_set_config(device, dev_config) == SUCCESS) { /* Reset the device if previous power down mode was either standby * or shutdown */ if (prev_power_down_mode != AD7606_NORMAL) { /* Power-up wait time */ mdelay(1); /* Toggle reset pin */ if (gpio_set_value(((struct ad7606_dev *)device)->gpio_reset, GPIO_HIGH) == SUCCESS) { mdelay(1); if (gpio_set_value(((struct ad7606_dev *)device)->gpio_reset, GPIO_LOW) == SUCCESS) { prev_power_down_mode = AD7606_NORMAL; return len; } } } } break; case AD7606_STANDBY: if (ad7606_set_config(device, dev_config) == SUCCESS) { prev_power_down_mode = AD7606_STANDBY; return len; } break; case AD7606_SHUTDOWN: if (ad7606_set_config(device, dev_config) == SUCCESS) { prev_power_down_mode = AD7606_SHUTDOWN; return len; } break; default: break; } } return -EINVAL; } /*! * @brief Getter/Setter for the range 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 Available for all devices except AD7606B and AD7606C */ ssize_t get_range(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t gpio_range_val; struct ad7606_dev *dev = device; if (gpio_get_value(dev->gpio_range, &gpio_range_val) == SUCCESS) { if (gpio_range_val) { return sprintf(buf, "%s", chn_range_str[HIGH]); } else { return sprintf(buf, "%s", chn_range_str[LOW]); } } return -EINVAL; } ssize_t set_range(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t range_value; struct ad7606_dev *dev = device; (void)sscanf(buf, "%d", &range_value); if (range_value < (sizeof(chn_range_str) / sizeof(chn_range_str[0]))) { if (range_value == LOW) { if (gpio_set_value(dev->gpio_range, GPIO_LOW) == SUCCESS) { return len; } } else { if (gpio_set_value(dev->gpio_range, GPIO_HIGH) == SUCCESS) { return len; } } } return -EINVAL; } /*! * @brief Getter/Setter for the oversampling 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 Available for all devices except AD7606B and AD7606C */ ssize_t get_oversampling(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t oversampling_value; uint8_t read_val; uint8_t gpio_osr0_val; uint8_t gpio_osr1_val; uint8_t gpio_osr2_val; struct ad7606_dev *dev = device; #if defined(DEV_AD7606B) || defined(DEV_AD7606C_18) || defined(DEV_AD7606C_16) if (ad7606_spi_reg_read(device, AD7606_REG_OVERSAMPLING, &read_val) == SUCCESS) { oversampling_value = (read_val & AD7606_OVERSAMPLING_MSK); if (oversampling_value < sizeof(oversampling_val_str) / sizeof( oversampling_val_str[0])) { return (ssize_t)sprintf(buf, "%s", oversampling_val_str[oversampling_value]); } } #else if (gpio_get_value(dev->gpio_os0, &gpio_osr0_val) == SUCCESS) { if (gpio_get_value(dev->gpio_os1, &gpio_osr1_val) == SUCCESS) { if (gpio_get_value(dev->gpio_os2, &gpio_osr2_val) == SUCCESS) { oversampling_value = (gpio_osr2_val << 2) | (gpio_osr1_val << 1) | gpio_osr0_val; if (oversampling_value < (sizeof(oversampling_val_str) / sizeof( oversampling_val_str[0]))) { return sprintf(buf, "%s", oversampling_val_str[oversampling_value]); } } } } #endif return -EINVAL; } ssize_t set_oversampling(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t oversampling_value; struct ad7606_oversampling oversampling_cfg; (void)sscanf(buf, "%d", &oversampling_value); if (oversampling_value < (sizeof(oversampling_val_str) / sizeof( oversampling_val_str[0]))) { oversampling_cfg.os_pad = 0; oversampling_cfg.os_ratio = oversampling_value; ad7606_set_oversampling(device, oversampling_cfg); return len; } return -EINVAL; } /*! * @brief Getter/Setter for the bandwidth 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 Available for only AD7606C */ ssize_t get_bandwidth(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t bw_value; uint8_t read_val; if (ad7606_spi_reg_read(device, AD7606_REG_BANDWIDTH, &read_val) == SUCCESS) { bw_value = (read_val >> (channel->ch_num - 1)) & 0x1; if (bw_value < sizeof(bandwidth_str) / sizeof( bandwidth_str[0])) { return (ssize_t)sprintf(buf, "%s", bandwidth_str[bw_value]); } } return -EINVAL; } ssize_t set_bandwidth(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t bw_value; uint8_t read_val; (void)sscanf(buf, "%d", &bw_value); if (bw_value < sizeof(bandwidth_str) / sizeof( bandwidth_str[0])) { if (ad7606_spi_reg_read(device, AD7606_REG_BANDWIDTH, &read_val) == SUCCESS) { if (bw_value) { read_val |= (1 << (channel->ch_num - 1)); } else { read_val &= (~(1 << (channel->ch_num - 1))); } if (ad7606_spi_reg_write(device, AD7606_REG_BANDWIDTH, read_val) == SUCCESS) { return len; } } } return -EINVAL; } /*! * @brief Getter/Setter for the channel range 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_range(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t read_val; uint8_t chn_range; if (ad7606_spi_reg_read(device, AD7606_REG_RANGE_CH_ADDR(channel->ch_num-1), &read_val) == SUCCESS) { if (((channel->ch_num - 1) % 2) != 0) { read_val >>= CHANNEL_RANGE_MSK_OFFSET; chn_range = read_val; } else { chn_range = (read_val & AD7606_RANGE_CH_MSK(channel->ch_num - 1)); } if (chn_range < sizeof(chn_range_str) / sizeof(chn_range_str[0])) { return (ssize_t)sprintf(buf, "%s", chn_range_str[chn_range]); } } return -EINVAL; } ssize_t set_chn_range(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t chn_range; (void)sscanf(buf, "%d", &chn_range); if (chn_range < sizeof(chn_range_val) / sizeof(chn_range_val[0])) { /* Get the polarity of channel */ attr_polarity_val[channel->ch_num - 1] = ad7606_get_input_polarity(chn_range); attr_chn_range[channel->ch_num - 1] = chn_range_val[chn_range]; attr_scale_val[channel->ch_num - 1] = get_vltg_conv_scale_factor( chn_range_val[chn_range], attr_polarity_val[channel->ch_num - 1]); if (((channel->ch_num - 1) % 2) != 0) { chn_range <<= CHANNEL_RANGE_MSK_OFFSET; } if (ad7606_spi_write_mask(device, AD7606_REG_RANGE_CH_ADDR(channel->ch_num-1), AD7606_RANGE_CH_MSK(channel->ch_num-1), chn_range) == SUCCESS) { return len; } } return -EINVAL; } /*! * @brief Getter/Setter for the channel gain 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_gain(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t read_val; uint8_t chn_gain_value; if (ad7606_spi_reg_read(device, AD7606_REG_GAIN_CH(channel->ch_num - 1), &read_val) == SUCCESS) { chn_gain_value = (read_val & AD7606_GAIN_MSK); return (ssize_t)sprintf(buf, "%d", chn_gain_value); } return -EINVAL; } ssize_t set_chn_gain(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t chn_gain_value; (void)sscanf(buf, "%d", &chn_gain_value); if (ad7606_set_ch_gain(device, channel->ch_num - 1, chn_gain_value) == SUCCESS) { return len; } return -EINVAL; } /*! * @brief Getter/Setter for the channel offset 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_offset(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t chn_offset_value; if (ad7606_spi_reg_read(device, AD7606_REG_OFFSET_CH(channel->ch_num-1), &chn_offset_value) == SUCCESS) { return (ssize_t)sprintf(buf, "%d", chn_offset_value); } return -EINVAL; } ssize_t set_chn_offset(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t chn_offset_value = 0; (void)sscanf(buf, "%d", &chn_offset_value); if (ad7606_set_ch_offset(device, channel->ch_num - 1, chn_offset_value) == SUCCESS) { return len; } return -EINVAL; } /*! * @brief Getter/Setter for the channel pahse offset 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_phase_offset(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t chn_phase_offset_value; if (ad7606_spi_reg_read(device, AD7606_REG_PHASE_CH(channel->ch_num - 1), &chn_phase_offset_value) == SUCCESS) { return (ssize_t)sprintf(buf, "%d", chn_phase_offset_value); } return -EINVAL; } ssize_t set_chn_phase_offset(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t chn_phase_offset_value = 0; (void)sscanf(buf, "%d", &chn_phase_offset_value); if (ad7606_set_ch_phase(device, channel->ch_num - 1, chn_phase_offset_value) == SUCCESS) { return len; } return -EINVAL; } /*! * @brief Getter/Setter for the channel temperature 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_temperature(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { int32_t adc_chn_data = 0; float temperature; float voltage; /* Configure the channel multiplexer to select temperature read */ if (ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), TEMPERATURE_MUX)) == SUCCESS) { /* Allow to settle Mux channel */ udelay(100); /* Sample the channel and read conversion result */ adc_chn_data = single_data_read(device, channel->ch_num - 1, attr_polarity_val[channel->ch_num - 1]); /* Convert ADC data into equivalent voltage */ voltage = convert_adc_raw_to_voltage(adc_chn_data, attr_scale_val[channel->ch_num - 1]); /* Obtain the temperature using equation specified in device datasheet */ temperature = ((voltage - 0.69068) / 0.019328) + 25.0; /* Change channel mux back to analog input */ (void)ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), ANALOG_INPUT_MUX)); return (ssize_t)sprintf(buf, "%f", temperature); } return -EINVAL; } ssize_t set_chn_temperature(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { // NA- Can't set temperature return -EINVAL; } /*! * @brief Getter/Setter for the channel Vref 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_vref(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { float vref_voltge; int32_t adc_chn_data; /* Configure the channel multiplexer to select Vref read */ if (ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), VREF_MUX)) == SUCCESS) { /* Allow to settle Mux channel */ udelay(100); /* Sample the channel and read conversion result */ adc_chn_data = single_data_read(device, channel->ch_num - 1, attr_polarity_val[channel->ch_num - 1]); /* Convert ADC data into equivalent voltage */ vref_voltge = convert_adc_raw_to_voltage(adc_chn_data, attr_scale_val[channel->ch_num - 1]); /* Divide by 4 since Vref Mux has 4x multiplier on it */ vref_voltge /= VREF_MUX_MULTIPLIER; /* Change channel mux back to analog input */ (void)ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), ANALOG_INPUT_MUX)); return (ssize_t)sprintf(buf, "%f", vref_voltge); } return -EINVAL; } ssize_t set_chn_vref(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { // NA- Can't set Vref return - EINVAL; } /*! * @brief Getter/Setter for the channel Vdrive 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_vdrive(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { float vdrive_voltge; int32_t adc_chn_data; /* Configure the channel multiplexer to select Vdrive read */ if (ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), VDRIVE_MUX)) == SUCCESS) { /* Allow to settle Mux channel */ udelay(100); /* Sample the channel and read conversion result */ adc_chn_data = single_data_read(device, channel->ch_num - 1, attr_polarity_val[channel->ch_num - 1]); /* Convert ADC data into equivalent voltage */ vdrive_voltge = convert_adc_raw_to_voltage(adc_chn_data, attr_scale_val[channel->ch_num - 1]); /* Change channel mux back to analog input */ (void)ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), ANALOG_INPUT_MUX)); return (ssize_t)sprintf(buf, "%f", vdrive_voltge); } return -EINVAL; } ssize_t set_chn_vdrive(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { // NA- Can't set Vdrive return - EINVAL; } /*! * @brief Getter/Setter for the channel ALDO 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_aldo(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { float aldo_voltge; int32_t adc_chn_data; /* Configure the channel multiplexer to select ALDO read */ if (ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), ALDO_MUX)) == SUCCESS) { /* Allow to settle Mux channel */ udelay(100); /* Sample the channel and read conversion result */ adc_chn_data = single_data_read(device, channel->ch_num - 1, attr_polarity_val[channel->ch_num - 1]); /* Convert ADC data into equivalent voltage */ aldo_voltge = convert_adc_raw_to_voltage(adc_chn_data, attr_scale_val[channel->ch_num - 1]); /* Divide by 4 since ALDO Mux has 4x multiplier on it */ aldo_voltge /= VREF_MUX_MULTIPLIER; /* Change channel mux back to analog input */ (void)ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), ANALOG_INPUT_MUX)); return (ssize_t)sprintf(buf, "%f", aldo_voltge); } return -EINVAL; } ssize_t set_chn_aldo(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { // NA- Can't set ALDO return - EINVAL; } /*! * @brief Getter/Setter for the channel DLDO 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 Available only for AD7606B and AD7606C */ ssize_t get_chn_dldo(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { float dldo_voltge; int32_t adc_chn_data; /* Configure the channel multiplexer to select DLDO read */ if (ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), DLDO_MUX)) == SUCCESS) { /* Allow to settle Mux channel */ udelay(100); /* Sample the channel and read conversion result */ adc_chn_data = single_data_read(device, channel->ch_num - 1, attr_polarity_val[channel->ch_num - 1]); /* Convert ADC data into equivalent voltage */ dldo_voltge = convert_adc_raw_to_voltage(adc_chn_data, attr_scale_val[channel->ch_num - 1]); /* Divide by 4 since ALDO Mux has 4x multiplier on it */ dldo_voltge /= VREF_MUX_MULTIPLIER; /* Change channel mux back to analog input */ (void)ad7606_spi_write_mask(device, AD7606_REG_DIAGNOSTIC_MUX_CH(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_MSK(channel->ch_num - 1), AD7606_DIAGN_MUX_CH_VAL((channel->ch_num - 1), ANALOG_INPUT_MUX)); return (ssize_t)sprintf(buf, "%f", dldo_voltge); } return -EINVAL; } ssize_t set_chn_dldo(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { // NA- Can't set DLDO return - EINVAL; } /*! * @brief Getter/Setter for the channel open wire detect 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(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { int32_t data[2]; uint8_t open_detect_flag = false; int32_t rw_status = FAILURE; #if defined MANUAL_OPEN_DETECT /* Read the ADC output voltage on channel */ data[0] = single_data_read(device, channel->ch_num - 1, attr_polarity_val[channel->ch_num - 1]); /* Enter into manual open detect mode */ if (ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE, 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) { /* 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 for shift in common mode output voltage */ if ((data[1] - data[0]) > OPEN_DETECT_THRESHOLD_RPD50K) { open_detect_flag = true; } rw_status = SUCCESS; } } #else /* Enter into auto open detect mode */ if (ad7606_spi_reg_write(device, AD7606_REG_OPEN_DETECT_QUEUE, OPEN_DETECT_AUTO_MODE_CNTS) == 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) { mdelay(1); if (ad7606_spi_reg_read(device, AD7606_REG_OPEN_DETECTED, &open_detect_flag) == SUCCESS) { rw_status = SUCCESS; open_detect_flag >>= (channel->ch_num -1); open_detect_flag &= 0x1; } } } #endif /* 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(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { // NA- Can't set open wire detect return - EINVAL; } /*! * @brief Getter/Setter for the adc calibration * @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_calibrate_adc(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { float lsb_voltage; float adc_voltage; polarity_e polarity = attr_polarity_val[channel->ch_num - 1]; int32_t adc_data; int8_t chn_offset; /* Perform the system offset calibration */ if (polarity == UNIPOLAR) { lsb_voltage = attr_chn_range[channel->ch_num - 1] / ADC_MAX_COUNT_UNIPOLAR; } else { lsb_voltage = attr_chn_range[channel->ch_num - 1] / ADC_MAX_COUNT_BIPOLAR; } /* Sample and read the ADC channel */ adc_data = single_data_read(device, channel->ch_num - 1, polarity); /* Get an equivalent ADC voltage */ adc_voltage = convert_adc_raw_to_voltage(adc_data, attr_scale_val[channel->ch_num - 1]); /* Calculate the channel offset and write it to offset register (0x80-chn_offset) */ chn_offset = (adc_voltage / lsb_voltage); if (ad7606_set_ch_offset(device, channel->ch_num - 1, chn_offset) == SUCCESS) { return sprintf(buf, "%s", "ADC Calibration Done"); } return -EINVAL; } ssize_t set_chn_calibrate_adc(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { // NA- Can't set open wire detect return - EINVAL; } /*! * @brief Getter/Setter for the direct register access 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_direct_reg_access(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { return (ssize_t)sprintf(buf, "%d", device_reg_val); } ssize_t set_direct_reg_access(void *device, char *buf, size_t len, const struct iio_ch_info *channel) { uint8_t reg_address; char *token; uint8_t offset = strlen("0x"); char str[10] = ""; uint8_t i=0; uint8_t reg_data; if (buf[1] == 'x') { /* Write a data to device */ /* Extract the register address from received string */ strcpy(str, buf); token = strtok(str, " "); (void)sscanf(token + offset, "%x", ®_address); /* Extract the register data from received string */ i = (strlen(str) + 1) + offset; (void)sscanf(str + i, "%x", ®_data); if (reg_address <= NUM_OF_REGISTERS) { if ((ad7606_spi_reg_write(device, reg_address, reg_data) == SUCCESS)) { return len; } } } else { /* Read the data from device */ (void)sscanf(buf, "%d", ®_address); if ((ad7606_spi_reg_read(device, reg_address, &device_reg_val) == SUCCESS)) { return len; } } return -EINVAL; } /** * @brief Get xml corresponding to an AD7606 device. * @param xml - Xml containing description of a device. * @param iio_dev - Structure describing a device, channels and attributes. * @return SUCCESS in case of success or negative value otherwise. */ static ssize_t iio_ad7606_get_xml(char **xml, struct iio_device *iio_dev) { *xml = (char*)calloc(1, strlen(ad7606_phy_xml) + 1); if (!(*xml)) { return -ENOMEM; } memcpy(*xml, ad7606_phy_xml, strlen(ad7606_phy_xml)); return SUCCESS; } /** * @brief Read buffer data corresponding to AD7606 IIO device * @param dev_instance[in] - IIO device instance * @param pbuf[out] - Pointer to output data buffer * @return SUCCESS in case of success or negative value otherwise */ static ssize_t iio_ad7606_read_data(void *dev_instance, char *pbuf, size_t offset, size_t bytes_count, uint32_t ch_mask) { /* Read the buffered data */ buffered_data_read(pbuf, bytes_count, offset, ch_mask); return bytes_count; } /** * @brief Create structure describing a device, channels and attributes * @param device_name[in] - Device name * @return iio_device or NULL, in case of failure */ static struct iio_device *iio_ad7606_create_device(const char *device_name) { struct iio_device *iio_ad7606_device; iio_ad7606_device = calloc(1, sizeof(struct iio_device)); if (!iio_ad7606_device) { return NULL; } iio_ad7606_device->name = device_name; iio_ad7606_device->num_ch = sizeof(iio_ad7606_channels) / sizeof( iio_ad7606_channels[0]); iio_ad7606_device->channels = iio_ad7606_channels; iio_ad7606_device->attributes = global_attributes; return iio_ad7606_device; } /** * @brief Delete IIO device. * @param iio_device - Structure describing a device, channels and attributes * @return SUCCESS in case of success or negative value otherwise */ static ssize_t iio_ad7606_delete_device(struct iio_device *iio_device) { if (!iio_device) { return FAILURE; } free(iio_device); return SUCCESS; } /** * @brief Init for reading/writing and parameterization of a * ad7606 IIO device * @param desc[in,out] - IIO device descriptor * @param init[in] - Configuration structure * @return SUCCESS in case of success, FAILURE otherwise */ int32_t iio_ad7606_init(struct iio_ad7606_desc **desc, struct iio_ad7606_init_param *init) { int32_t status; struct iio_interface *iio_interface; iio_interface = (struct iio_interface *)calloc(1, sizeof(struct iio_interface)); if (!iio_interface) { return -ENOMEM; } *iio_interface = (struct iio_interface) { .name = dev_name, .dev_instance = init->ad7606_phy, .iio = iio_ad7606_create_device(dev_name), .get_xml = iio_ad7606_get_xml, .transfer_dev_to_mem = NULL, .transfer_mem_to_dev = NULL, .read_data = iio_ad7606_read_data, .write_data = NULL, }; status = iio_register(iio_interface); if (status < 0) { free(iio_interface); return FAILURE; } *desc = calloc(1, sizeof(struct iio_ad7606_desc)); if (!(*desc)) { iio_unregister(iio_interface); free(iio_interface); return FAILURE; } (*desc)->iio_interface = iio_interface; return SUCCESS; } /** * @brief Release resources allocated for IIO device * @param desc[in] - IIO device descriptor * @return SUCCESS in case of success, FAILURE otherwise */ int32_t iio_ad7606_remove(struct iio_ad7606_desc *desc) { int32_t status; if (!desc) { return FAILURE; } status = iio_unregister(desc->iio_interface); if (status < 0) { return FAILURE; } status = iio_ad7606_delete_device(desc->iio_interface->iio); if (status < 0) { return FAILURE; } free(desc->iio_interface); free(desc); return SUCCESS; } /*! * @brief Get scale factor for adc data to voltage conversion for IIO client * @param chn_range[in] - Current channel voltage range * @param chn_range_bits[in] - Channel range register bits * @return scale * @details This function samples and capture the new data when previous data * is transmitted to IIO client */ static float get_vltg_conv_scale_factor(float chn_range, polarity_e polarity) { float scale; /* Get the scale factor for voltage conversion from range */ if (polarity == UNIPOLAR) { scale = (chn_range / ADC_MAX_COUNT_UNIPOLAR) * 1000; } else { scale = (chn_range / ADC_MAX_COUNT_BIPOLAR) * 1000; } return scale; } /** * @brief Initialize the IRQ contoller * @param uart_desc[in] - UART descriptor for UART Rx IRQ event init * @return none * @details This function initialize the interrupt controller */ static int32_t iio_interrupt_handler_init(mbed_uart_desc *uart_desc) { /* Pin to generate external interrupt */ PinName ext_int_pin = BUSY_PIN; /* External interrupt descriptor */ struct irq_ctrl_desc *external_int_desc; /* Define external interrupt platform specific parameters structure */ mbed_irq_init_param mbed_ext_int_init_param = { .int_mode = EXT_IRQ_FALL, .int_obj_type = &ext_int_pin }; /* External interrupt init parameters */ struct irq_init_param external_int_init_param = { .irq_ctrl_id = EXTERNAL_INT_ID, .extra = &mbed_ext_int_init_param }; /* External interrupt callback descriptor */ struct callback_desc external_int_callback_desc = { &do_conversion_callback, NULL, NULL }; /* UART Rx interrupt descriptor */ struct irq_ctrl_desc *uart_rx_int_desc; /* Define external interrupt platform specific parameters structure */ mbed_irq_init_param mbed_uart_rx_int_init_param = { .int_mode = 0, .int_obj_type = uart_desc->uart_port }; /* UART Rx interrupt init parameters */ struct irq_init_param uart_rx_int_init_param = { .irq_ctrl_id = UART_RX_INT_ID, .extra = &mbed_uart_rx_int_init_param }; /* UART Rx interrupt callback descriptor */ struct callback_desc uart_rx_int_callback_desc = { &iio_uart_rx_callback, NULL, NULL }; /* Init interrupt controller for external interrupt */ if (irq_ctrl_init(&external_int_desc, &external_int_init_param) == FAILURE) { return FAILURE; } /* Init interrupt controller for UART Rx interrupt */ if (irq_ctrl_init(&uart_rx_int_desc, &uart_rx_int_init_param) == FAILURE) { return FAILURE; } /* Register a callback function for external interrupt */ if (irq_register_callback(external_int_desc, EXTERNAL_INT_ID, &external_int_callback_desc) == FAILURE) { return FAILURE; } /* Register a callback function for UART Rx interrupt */ if (irq_register_callback(uart_rx_int_desc, UART_RX_INT_ID, &uart_rx_int_callback_desc) == FAILURE) { return FAILURE; } return SUCCESS; } /** * @brief Initialize the IIO interface for AD7606 IIO device * @return none * @return SUCCESS in case of success, FAILURE otherwise */ int32_t ad7606_iio_initialize(void) { int32_t init_status; /** * AD7606 IIO instance descriptor */ struct iio_ad7606_desc *piio_ad7606_desc; /** * AD7606 IIO init parameters */ struct iio_ad7606_init_param iio_ad7606_init_param; /** * IIO application init parameters */ struct iio_app_init_param iio_app_init_par; /** * UART serial interface read/write callback */ struct iio_server_ops uart_iio_server_ops; /* * UART Mbed init extra parameters structure */ mbed_uart_init_param uart_extra_init_param; /* * UART init parameters structure */ struct uart_init_param uart_init_param; /** * IIO application UART descriptor */ struct uart_desc *uart_desc = NULL; /* * GPIO LED Init parameters structure */ struct gpio_init_param gpio_init_led; iio_ad7606_init_param = (struct iio_ad7606_init_param) { .ad7606_phy = NULL, }; uart_extra_init_param = (mbed_uart_init_param) { .uart_tx_pin = UART_TX, .uart_rx_pin = UART_RX }; uart_init_param = (struct uart_init_param ) { .device_id = NULL, .baud_rate = IIO_UART_BAUD_RATE, .extra = &uart_extra_init_param }; uart_iio_server_ops = (struct iio_server_ops) { .read = iio_uart_read, .write = iio_uart_write, }; gpio_init_led = (struct gpio_init_param) { .number = LED_GREEN, .extra = NULL }; iio_app_init_par = (struct iio_app_init_param) { .iio_server_ops = &uart_iio_server_ops, }; /* Initialize AD7606 device and peripheral interface */ init_status = ad7606_init(&p_ad7606_dev, &ad7606_init_str); if (init_status != SUCCESS) { return init_status; } /* Initialize the LED GPIO descriptor */ init_status = gpio_get_optional(&gpio_led, &gpio_init_led); if (init_status != SUCCESS) { return init_status; } else { init_status = gpio_direction_output(gpio_led, GPIO_HIGH); if (init_status != SUCCESS) { return init_status; } } /* Get the AD7606 IIO device instance */ iio_ad7606_init_param.ad7606_phy = p_ad7606_dev; /* Initialize the UART interface for IIO application */ init_status = iio_uart_init(&uart_desc, &uart_init_param); if (init_status != SUCCESS) { return init_status; } /* Initialize the IIO application interface */ init_status = iio_app_init(&iio_app_desc, &iio_app_init_par); if (init_status != SUCCESS) { return init_status; } /* Register and initialize the AD7606 device into IIO interface */ init_status = iio_ad7606_init(&piio_ad7606_desc, &iio_ad7606_init_param); if (init_status != SUCCESS) { return init_status; } /* Init the data capture for AD7606 IIO app */ init_status = iio_data_capture_init(p_ad7606_dev); if (init_status != SUCCESS) { return init_status; } /* Init the interrupt event handler for AD7606 IIO app */ init_status = iio_interrupt_handler_init(uart_desc->extra); if (init_status != SUCCESS) { return init_status; } return init_status; } /** * @brief Run the AD7606 IIO event handler * @return none * @details This function monitors the new IIO client event */ void ad7606_iio_event_handler(void) { static bool background_conversion_started = false; /* Handle new IIO command */ if (is_new_iio_command_detected()) { /* Start/Stop the bakground conversion based on buffer read request */ if (!check_iio_cmd("READBUF", 7)) { stop_background_data_conversion(); background_conversion_started = false; } else { if (background_conversion_started == false) { start_background_data_conversion(); background_conversion_started = true; } } /* Run the IIO interface when new command is detected */ (void)iio_app(iio_app_desc); } }