IIO firmware for the AD4110

Dependencies:   tempsensors sdp_k1_sdram

app/ad4110_iio.c

Committer:
Janani Sunil
Date:
2022-08-01
Revision:
1:a78dbaa4b05d
Parent:
0:6ca37a8f8ba9

File content as of revision 1:a78dbaa4b05d:

/***************************************************************************//**
 *   @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);
}