IIO firmware for the AD4110
Dependencies: tempsensors sdp_k1_sdram
Diff: app/ad4110_iio.c
- Revision:
- 0:6ca37a8f8ba9
diff -r 000000000000 -r 6ca37a8f8ba9 app/ad4110_iio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/ad4110_iio.c Wed Jul 27 17:04:15 2022 +0530 @@ -0,0 +1,553 @@ +/***************************************************************************//** + * @file ad4110_iio.c + * @brief Implementation of AD4110-1 IIO application interfaces +******************************************************************************** + * Copyright (c) 2022 Analog Devices, Inc. + * All rights reserved. + * + * This software is proprietary to Analog Devices, Inc. and its licensors. + * By using this software you agree to the terms of the associated + * Analog Devices Software License Agreement. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include <stdlib.h> +#include "ad4110_iio.h" +#include "app_config.h" +#include "ad4110.h" +#include "no_os_error.h" +#include "no_os_util.h" +#include "ad4110_data_capture.h" +#include "ad4110_temperature_sensor.h" + +/* Forward declaration of getter/setter functions */ +static int ad4110_get_attribute(void *device, char *buf, uint32_t len, + const struct iio_ch_info *channel, intptr_t priv); + +static int ad4110_set_attribute(void *device, char *buf, uint32_t len, + const struct iio_ch_info *channel,intptr_t priv); + +/******************************************************************************/ +/************************ Macros/Constants ************************************/ +/******************************************************************************/ + +/* Bytes per sample (*Note: 4 bytes needed per sample for data range + * of 0 to 32-bit) */ +#define BYTES_PER_SAMPLE sizeof(uint32_t) + +/* Number of data storage bits (needed for IIO client) */ +#define CHN_STORAGE_BITS (BYTES_PER_SAMPLE * 8) + +/* Channel Attribute definition */ +#define AD4110_CH_ATTR(_name, _idx) {\ + .name = _name,\ + .priv = _idx,\ + .show = ad4110_get_attribute,\ + .store = ad4110_set_attribute\ +} + +/* Channel definition */ +#define AD4110_CHANNEL(_name, _idx) {\ + .name = _name # _idx, \ + .ch_type = IIO_CHANNEL_TYPE,\ + .ch_out = false,\ + .indexed = true,\ + .channel = _idx,\ + .scan_index = _idx,\ + .scan_type = &ad4110_scan_type,\ + .attributes = ad4110_channel_attributes\ +} + +#if (REGISTER_READ_SEL == AD4110_ADC) +#define REGISTER_MAX_VAL AD4110_ADC_GAIN3 +#else // AD4110_AFE +#define REGISTER_MAX_VAL AD4110_REG_AFE_NO_PWR_DEFAULT_STATUS +#endif + +/* SCale value for voltage and current mode configuration */ +#define AD4110_BIPOLAR_SCALE ((AD4110_REF_VOLTAGE / (ADC_MAX_COUNT_BIPOLAR)) * 1000) /AD4110_DEFAULT_PGA +#define AD4110_UNIPOLAR_SCALE ((AD4110_REF_VOLTAGE / (ADC_MAX_COUNT_UNIPOLAR)) * 1000) /AD4110_DEFAULT_PGA + +/* IIO Channel type for each demo mode config */ +#if (ACTIVE_DEMO_MODE_CONFIG == VOLTAGE_MODE_CONFIG) +#define IIO_CHANNEL_TYPE IIO_VOLTAGE +#elif (ACTIVE_DEMO_MODE_CONFIG == CURRENT_MODE_CONFIG) +#define IIO_CHANNEL_TYPE IIO_CURRENT +#elif (ACTIVE_DEMO_MODE_CONFIG == FIELD_POWER_SUPPLY_CONFIG) +#define IIO_CHANNEL_TYPE IIO_CURRENT +#elif (ACTIVE_DEMO_MODE_CONFIG == THERMOCOUPLE_CONFIG) +#define IIO_CHANNEL_TYPE IIO_TEMP +#elif (ACTIVE_DEMO_MODE_CONFIG == RTD_CONFIG) +#define IIO_CHANNEL_TYPE IIO_TEMP +#endif + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/* IIO interface descriptor */ +static struct iio_desc *ad4110_iio_desc; + +/* AD4110-1 Device Descriptor */ +struct ad4110_dev *ad4110_dev_inst; + +/* IIO Device name */ +static const char *dev_name = ACTIVE_DEVICE; + +/* Channel Attribute ID */ +enum ad4110_attribute_id { + RAW_ATTR_ID, + SCALE_ATTR_ID, + OFFSET_ATTR_ID, + SAMPLING_FREQ_ATTR_ID, + DEMO_CONFIG_ATTR_ID +}; + +struct scan_type ad4110_scan_type = { + .storagebits = CHN_STORAGE_BITS, + .shift = 0, + .is_big_endian = false +}; + +/* Global attributes */ +static struct iio_attribute ad4110_global_attributes[] = { + AD4110_CH_ATTR("sampling_frequency", SAMPLING_FREQ_ATTR_ID), + AD4110_CH_ATTR("demo_config", DEMO_CONFIG_ATTR_ID), + + END_ATTRIBUTES_ARRAY +}; + +/* Channel attributes */ +static struct iio_attribute ad4110_channel_attributes[] = { + AD4110_CH_ATTR("raw", RAW_ATTR_ID), + AD4110_CH_ATTR("scale", SCALE_ATTR_ID), + AD4110_CH_ATTR("offset", OFFSET_ATTR_ID), + + END_ATTRIBUTES_ARRAY +}; + +/* IIOD channels configurations for AD4110-1 */ +static struct iio_channel ad4110_iio_channels[] = { + AD4110_CHANNEL("Chn", 0), + AD4110_CHANNEL("Chn", 1), + AD4110_CHANNEL("Chn", 2), + AD4110_CHANNEL("Chn", 3) +}; + +/* IIO context attributes list */ +static struct iio_context_attribute ad4110_iio_context_attributes[] = { + { + .name = "hw_mezzanine", + .value = HW_MEZZANINE_NAME + }, + { + .name = "hw_carrier", + .value = HW_CARRIER_NAME + }, + { + .name = "hw_name", + .value = HW_NAME + }, + END_ATTRIBUTES_ARRAY +}; + +/* Scale attribute value per channel */ +float attr_scale_val[AD4110_NUM_CHANNELS]; + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/*! + * @brief Calculate the scale value + * @param chan_id[in] - ADC input channel + * @param adc_raw[in] - Raw ADC Data + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad4110_get_scale(uint8_t chan_id, int32_t adc_raw) +{ + float temperature = 0; + uint32_t cjc_raw_data; + float cjc_temp; + int32_t ret; + +#if (ACTIVE_DEMO_MODE_CONFIG == RTD_CONFIG) + temperature = get_rtd_temperature(adc_raw); + attr_scale_val[chan_id] = (temperature / adc_raw) * 1000.0; +#elif (ACTIVE_DEMO_MODE_CONFIG == THERMOCOUPLE_CONFIG) + /* Sample the CJC Channel. TC is already sampled through the get_adc_attribute() function */ + if (chan_id != CJC_CHANNEL) { + /* Disable the requested channel chan_id */ + ret = ad4110_set_channel_status(ad4110_dev_inst, chan_id, false); + if (ret) { + return ret; + } + + /* Enable the channel on which CJC has been incorporated */ + ret = ad4110_set_channel_status(ad4110_dev_inst, CJC_CHANNEL, true); + if (ret) { + return ret; + } + + /* Read the CJC raw data */ + if (ad4110_do_single_read(ad4110_dev_inst, &cjc_raw_data)) { + return ret; + } + + /* Disable the CJC channel */ + ret = ad4110_set_channel_status(ad4110_dev_inst, CJC_CHANNEL, false); + if (ret) { + return ret; + } + + } else { + /* For calculating CJC value, TC raw value does not matter */ + chan_id = 0; + cjc_raw_data = adc_raw; + adc_raw = 0; + } + + /* Calculate the TC and CJC temperature and update scale factor */ + temperature = get_tc_temperature(adc_raw, HV_CHANNEL, cjc_raw_data, CJC_CHANNEL, + &cjc_temp); + attr_scale_val[chan_id] = (temperature / adc_raw) * 1000; + attr_scale_val[CJC_CHANNEL] = (cjc_temp / cjc_raw_data) * 1000; +#endif + + return 0; +} + + +/*! + * @brief Getter/Setter function for ADC attributes + * @param device[in]- Pointer to IIO device instance + * @param buf[in]- IIO input data buffer + * @param len[in]- Number of input bytes + * @param channel[in] - ADC input channel + * @param priv[in] - Attribute private ID + * @return len in case of success, negative error code otherwise + */ +static int ad4110_get_attribute(void *device, char *buf, uint32_t len, + const struct iio_ch_info *channel, intptr_t priv) +{ + int ret; + uint32_t raw_data_read = 0 ; + int32_t offset = 0; + uint8_t ch_id; + float scale; + + switch (priv) { + case RAW_ATTR_ID: + /* Enable the requested channel, disable the rest */ + for (ch_id = 0; ch_id < AD4110_NUM_CHANNELS; ch_id++) { + if (ch_id == channel->ch_num) { + ret = ad4110_set_channel_status(device, ch_id, true); + } else { + ret = ad4110_set_channel_status(device, ch_id, false); + } + if (ret) { + return ret; + } + } + + /* Perform single conversion */ + ret = ad4110_do_single_read(device, &raw_data_read); + if (ret) { + return ret; + } + /* Update scale factor depending on the channel requested */ + ret = ad4110_get_scale(channel->ch_num, raw_data_read); + if (ret) { + return ret; + } + + return sprintf(buf, "%d", raw_data_read); + + case SCALE_ATTR_ID: + return sprintf(buf, "%g", attr_scale_val[channel->ch_num]); + + case OFFSET_ATTR_ID: +#if (ACTIVE_DEMO_MODE_CONFIG == VOLTAGE_MODE_CONFIG ||\ + ACTIVE_DEMO_MODE_CONFIG == CURRENT_MODE_CONFIG ||\ + ACTIVE_DEMO_MODE_CONFIG == FIELD_POWER_SUPPLY_CONFIG) + /* Offset values for other demo modes are incorporated as a part of the ad4110_get_scale() function */ + if (ad4110_dev_inst->bipolar) { + offset = -ADC_MAX_COUNT_BIPOLAR; + } else { + offset = 0; + } +#endif + return sprintf(buf, "%ld", offset); + + case SAMPLING_FREQ_ATTR_ID: + return sprintf(buf, "%d", AD4110_SAMPLING_RATE); + + case DEMO_CONFIG_ATTR_ID: +#if (ACTIVE_DEMO_MODE_CONFIG == VOLTAGE_MODE_CONFIG) + return sprintf(buf, "%s", "Voltage"); +#elif (ACTIVE_DEMO_MODE_CONFIG == CURRENT_MODE_CONFIG) + return sprintf(buf, "%s", "Current"); +#elif (ACTIVE_DEMO_MODE_CONFIG == RTD_CONFIG) + return sprintf(buf, "%s", "RTD"); +#elif (ACTIVE_DEMO_MODE_CONFIG == FIELD_POWER_SUPPLY_CONFIG) + return sprintf(buf, "%s", "Field Power Supply"); +#elif (ACTIVE_DEMO_MODE_CONFIG == THERMOCOUPLE_CONFIG) + return sprintf(buf, "%s", "Thermocouple"); +#endif + } + + return -EINVAL; +} + +static int ad4110_set_attribute(void *device, char *buf, uint32_t len, + const struct iio_ch_info *channel, intptr_t priv) +{ + switch (priv) { + case RAW_ATTR_ID: + case SCALE_ATTR_ID: + case SAMPLING_FREQ_ATTR_ID: + case DEMO_CONFIG_ATTR_ID: + default: + break; + } + + return len; +} + +/** + * @brief Transfer the device data into memory (optional) + * @param dev_instance[in] - IIO device instance + * @param ch_mask[in] - Channels select mask + * @return 0 in case of success or negative value otherwise + */ +static int32_t iio_ad4110_pre_enable(void *dev_instance, uint32_t chn_mask) +{ + return prepare_data_transfer(chn_mask, AD4110_NUM_CHANNELS, BYTES_PER_SAMPLE); +} + +/** + * @brief Read buffer data corresponding to AD4110 IIO device + * @param iio_dev_data[in] - Pointer to IIO device data structure + * @return 0 in case of success or negative value otherwise + */ +static int32_t iio_ad4110_submit_buffer(struct iio_device_data *iio_dev_data) +{ + int32_t ret; + + /* Read the samples counts equal to buffer size/block */ + ret = read_buffered_data(&iio_dev_data->buffer->buf->buff, + iio_dev_data->buffer->size); + + /* Increment the write spin count as buffer reads all 'n' samples + * in one go which is also the size of buffer block for a given instance. + * The read spin count is incremented from IIO library during buffer + * write/offloading into transmit buffer */ + if (iio_dev_data->buffer->buf->write.spin_count >= UINT32_MAX) { + iio_dev_data->buffer->buf->write.spin_count = 0; + } + + iio_dev_data->buffer->buf->write.spin_count += 1; + + return ret; +} + + +/** + * @brief Perform tasks before end of current data transfer + * @param dev[in] - IIO device instance + * @return 0 in case of success or negative value otherwise + */ +static int32_t iio_ad4110_post_disable(void *dev) +{ + return end_data_transfer(); +} + +/*! + * @brief Read the device register value + * @param dev[in]- Pointer to IIO device instance + * @param reg[in]- Register address to read from + * @param readval[out]- Pointer to variable to read data into + * @return 0 in case of success, negative error code otherwise + */ +static int32_t ad4110_read_register(void *dev, uint32_t reg, uint32_t *readval) +{ + int ret; + + if (reg > REGISTER_MAX_VAL) { + return -EINVAL; + } + + ret = ad4110_spi_int_reg_read(dev, REGISTER_READ_SEL, reg, readval); + if (ret < 0) { + return ret; + } + + return 0; +} + +/*! + * @brief Write into the device register + * @param dev[in] - Pointer to IIO device instance + * @param reg[in] - Register address to write into + * @param writeval[in] - Register value to write + * @return 0 in case of success, negative error code otherwise + */ +static int32_t ad4110_write_register(void *dev, uint32_t reg, uint32_t writeval) +{ + int ret; + + if (reg > REGISTER_MAX_VAL) { + return -EINVAL; + } + + ret = ad4110_spi_int_reg_write(dev, REGISTER_READ_SEL, reg, writeval); + if (ret < 0) { + return ret; + } + + return 0; +} + +/** + * @brief Init for reading/writing and parameterization of a AD4110-1 IIO device + * @param desc[in,out] - IIO device descriptor + * @return 0 in case of success, negative error code otherwise + */ +int32_t ad4110_iio_init(struct iio_device **desc) +{ + struct iio_device *ad4110_iio_device; + + ad4110_iio_device = (struct iio_device *)calloc(1, + sizeof(*ad4110_iio_device)); + if (!ad4110_iio_device) { + return -ENOMEM; + } + + ad4110_iio_device->num_ch = NO_OS_ARRAY_SIZE(ad4110_iio_channels); + ad4110_iio_device->channels = ad4110_iio_channels; + ad4110_iio_device->attributes = ad4110_global_attributes; + ad4110_iio_device->context_attributes = ad4110_iio_context_attributes; + ad4110_iio_device->pre_enable = iio_ad4110_pre_enable; + ad4110_iio_device->post_disable = iio_ad4110_post_disable; + ad4110_iio_device->submit = iio_ad4110_submit_buffer; + ad4110_iio_device->debug_reg_read = ad4110_read_register; + ad4110_iio_device->debug_reg_write = ad4110_write_register; + + *desc = ad4110_iio_device; + + return 0; +} + + +/** + * @brief Update channel attribute structure + * @param dev[in] - AD4110 device descriptor + * @return None + */ +static void update_channel_scan_parameters(struct ad4110_dev *dev) +{ + uint8_t channel_id; + + if (dev->bipolar) { + ad4110_scan_type.sign = 's'; + ad4110_scan_type.realbits = CHN_STORAGE_BITS; + + /* Update scale value in case of voltage or current mode */ + for (channel_id = 0; channel_id < AD4110_NUM_CHANNELS; channel_id++) { +#if (ACTIVE_DEMO_MODE_CONFIG == VOLTAGE_MODE_CONFIG) + attr_scale_val[channel_id] = AD4110_BIPOLAR_SCALE; +#elif (ACTIVE_DEMO_MODE_CONFIG == CURRENT_MODE_CONFIG) ||\ + (ACTIVE_DEMO_MODE_CONFIG == FIELD_POWER_SUPPLY_CONFIG) + attr_scale_val[channel_id] = AD4110_BIPOLAR_SCALE/AD4110_R_SENSE; + +#endif + } + } else { + ad4110_scan_type.sign = 'u'; + ad4110_scan_type.realbits = AD4110_RESOLUTION; + + for (channel_id = 0; channel_id < AD4110_NUM_CHANNELS; channel_id++) { +#if (ACTIVE_DEMO_MODE_CONFIG == VOLTAGE_MODE_CONFIG) + attr_scale_val[channel_id] = AD4110_UNIPOLAR_SCALE; +#elif (ACTIVE_DEMO_MODE_CONFIG == CURRENT_MODE_CONFIG) ||\ + (ACTIVE_DEMO_MODE_CONFIG == FIELD_POWER_SUPPLY_CONFIG) + attr_scale_val[channel_id] = AD4110_UNIPOLAR_SCALE / AD4110_R_SENSE; + +#endif + } + } +} + + +/** + * @brief Initialize the AD4110-1 IIO application + * @return 0 in case of success, negative error code otherwise + */ +int32_t ad4110_iio_initialize(void) +{ + int32_t ret_status; + + /* IIO Device Descriptor */ + struct iio_device *ad4110_iio_dev; + + /* IIO interface init parameters */ + struct iio_init_param iio_init_params = { + .phy_type = USE_UART, + .nb_devs = 1, + }; + + /* IIOD init parameters */ + struct iio_device_init iio_device_init_params = { + .name = (char *)dev_name, + .raw_buf = adc_data_buffer, + .raw_buf_len = DATA_BUFFER_SIZE + }; + + /* Initialize the system peripherals */ + ret_status = init_system(); + if (ret_status) { + return ret_status; + } + + /* Initialize the AD4110 Device */ + ret_status = ad4110_setup(&ad4110_dev_inst, ad4110_init_params); + if (ret_status) { + return ret_status; + } + + /* Update channel scan attribute parameters */ + update_channel_scan_parameters(ad4110_dev_inst); + + /* Initialize the AD4110-1 IIO application interface */ + ret_status = ad4110_iio_init(&ad4110_iio_dev); + if (ret_status) { + return ret_status; + } + + /* Update iio interface and IIOD init params */ + iio_init_params.uart_desc = uart_desc; + iio_device_init_params.dev = ad4110_dev_inst; + iio_device_init_params.dev_descriptor = ad4110_iio_dev; + iio_init_params.devs = &iio_device_init_params; + + /* Initialize the IIO interface */ + ret_status = iio_init(&ad4110_iio_desc, &iio_init_params); + if (ret_status) { + return ret_status; + } + + return 0; +} + + +/** + * @brief Run the AD4110-1 IIO event handler + * @return None + */ +void ad4110_iio_event_handler(void) +{ + (void)iio_step(ad4110_iio_desc); +} \ No newline at end of file