IIO firmware for the AD4110

Dependencies:   tempsensors sdp_k1_sdram

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