Mahesh Phalke / Mbed OS EVAL-AD7689

Dependencies:   sdp_k1_sdram

app/ad7689_iio.c

Committer:
MPhalke@MPHALKE-L02.ad.analog.com
Date:
2022-07-15
Revision:
2:8ce98fe471c7

File content as of revision 2:8ce98fe471c7:

/***************************************************************************//**
 *   @file    ad7689_iio.c
 *   @brief   Implementation of AD7689 IIO application interfaces
********************************************************************************
 * Copyright (c) 2021-22 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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "ad7689_iio.h"
#include "app_config.h"
#include "ad7689_user_config.h"
#include "ad7689_data_capture.h"
#include "no_os_error.h"

/******************************************************************************/
/********************* Macros and Constants Definition ************************/
/******************************************************************************/

/* ADC Raw to Voltage conversion default scale factor for IIO client */
#if defined(BIPOLAR)
/* Device supports only pseudo-bipolar mode. INX- = Vref / 2 */
#define ADC_DEFAULT_SCALE		(((ADC_DEFAULT_REF_VOLTAGE / 2) / ADC_MAX_COUNT_BIPOLAR) * 1000)
#else
#define ADC_DEFAULT_SCALE		((ADC_DEFAULT_REF_VOLTAGE / ADC_MAX_COUNT_UNIPOLAR) * 1000)
#endif

/* The output of temperature sensor is always unipolar (streight-binary) */
#define TEMPERATURE_SENSITIVITY		0.283	// 283mv
#define ROOM_TEMPERATURE			25.0
#define TEMPERATURE_CONV_SCALE		(ROOM_TEMPERATURE / TEMPERATURE_SENSITIVITY) * \
									((ADC_DEFAULT_REF_VOLTAGE / ADC_MAX_COUNT_UNIPOLAR) * 1000)

/* Bytes per sample. This count should divide the total 256 bytes into 'n' equivalent
 * ADC samples as IIO library requests only 256bytes of data at a time in a given
 * data read query.
 * For 1 to 8-bit ADC, bytes per sample = 1 (2^0)
 * For 9 to 16-bit ADC, bytes per sample = 2 (2^1)
 * For 17 to 32-bit ADC, bytes per sample = 4 (2^2)
 **/
#define	BYTES_PER_SAMPLE	sizeof(uint16_t)	// For ADC resolution of 16-bits

/* Number of data storage bits (needed for IIO client to plot ADC data) */
#define CHN_STORAGE_BITS	(BYTES_PER_SAMPLE * 8)

/* Private IDs for IIO attributes */
#define	IIO_RAW_ATTR_ID			0
#define	IIO_SCALE_ATTR_ID		1
#define	IIO_OFFSET_ATTR_ID		2

/******************************************************************************/
/******************** Variables and User Defined Data Types *******************/
/******************************************************************************/

/* IIO interface descriptor */
static struct iio_desc *p_ad7689_iio_desc;

/* Device name */
static const char dev_name[] = ACTIVE_DEVICE_NAME;

/* Pointer to the struct representing the AD7689 IIO device */
struct ad7689_dev *p_ad7689_dev_inst = NULL;

/* Device attributes with default values */

/* Scale attribute value per channel */
static float attr_scale_val[ADC_CHN_COUNT] = {
	ADC_DEFAULT_SCALE, ADC_DEFAULT_SCALE, ADC_DEFAULT_SCALE, ADC_DEFAULT_SCALE,
#if !defined(DEV_AD7682)
	ADC_DEFAULT_SCALE, ADC_DEFAULT_SCALE, ADC_DEFAULT_SCALE, ADC_DEFAULT_SCALE,
#endif
	TEMPERATURE_CONV_SCALE
};

/* AD7689 current configuration */
struct ad7689_config ad7689_current_config;

/******************************************************************************/
/************************** Functions Declarations ****************************/
/******************************************************************************/

/******************************************************************************/
/************************ Functions Definitions *******************************/
/******************************************************************************/

/*!
 * @brief	Getter/Setter for the sampling frequency attribute value
 * @param	device- pointer to IIO device structure
 * @param	buf- pointer to buffer holding attribute value
 * @param	len- length of buffer string data
 * @param	channel- pointer to IIO channel structure
 * @param	id- Attribute ID (optional)
 * @return	Number of characters read/written
 * @Note	This attribute is used to define the timeout period in IIO
 *			client during data capture.
 *			Timeout = (number of requested samples * (1/sampling frequency)) + 1sec
 *			e.g. if sampling frequency = 60KSPS and requested samples = 400
 *			Timeout = (400 * (1/60000)) + 1 = 1.0067sec = ~1sec
 */
static int get_sampling_frequency(void *device,
				  char *buf,
				  uint32_t len,
				  const struct iio_ch_info *channel,
				  intptr_t id)
{
	return sprintf(buf, "%d", SAMPLING_RATE);
}

static int set_sampling_frequency(void *device,
				  char *buf,
				  uint32_t len,
				  const struct iio_ch_info *channel,
				  intptr_t id)
{
	/* sampling frequency is read-only attribute */
	return len;
}


/*!
 * @brief	Getter/Setter for the raw, offset and 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
 * @param	id- Attribute ID (optional)
 * @return	Number of characters read/written
 */
static int get_adc_raw(void *device,
		       char *buf,
		       uint32_t len,
		       const struct iio_ch_info *channel,
		       intptr_t id)
{
	static uint32_t adc_data_raw = 0;
	int32_t offset = 0;
	int32_t ret;

	switch (id) {
	case IIO_RAW_ATTR_ID:
		/* Capture the raw adc data */
		ret = read_single_sample((uint8_t)channel->ch_num, &adc_data_raw);
		if (ret) {
			return sprintf(buf, " %s", "Error");
		}
		return sprintf(buf, "%d", adc_data_raw);

	case IIO_SCALE_ATTR_ID:
		return sprintf(buf, "%g", attr_scale_val[channel->ch_num]);

	case IIO_OFFSET_ATTR_ID:
#if defined(BIPOLAR)
		if (channel->ch_num == TEMPERATURE_CHN) {
			offset = 0;
		} else {
			if (adc_data_raw >= ADC_MAX_COUNT_BIPOLAR) {
				offset = -ADC_MAX_COUNT_UNIPOLAR;
			} else {
				offset = 0;
			}
		}
#endif
		return sprintf(buf, "%d", offset);

	default:
		break;
	}

	return len;
}

static int set_adc_raw(void *device,
		       char *buf,
		       uint32_t len,
		       const struct iio_ch_info *channel,
		       intptr_t id)
{
	/* ADC raw, offset and scale are read-only attributes */
	return len;
}

/**
 * @brief	Read buffer data corresponding to AD4170 IIO device
 * @param	iio_dev_data[in] - Pointer to IIO device data structure
 * @return	0 in case of success, negative error code otherwise
 */
static int32_t iio_ad7689_submit_buffer(struct iio_device_data *iio_dev_data)
{
	uint32_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	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, negative error code otherwise
 */
static int32_t iio_ad7689_prepare_transfer(void *dev_instance,
		uint32_t ch_mask)
{
	return prepare_data_transfer(ch_mask, ADC_CHN_COUNT, BYTES_PER_SAMPLE);
}

/**
 * @brief	Perform tasks before end of current data transfer
 * @param	dev[in] - IIO device instance
 * @return	0 in case of success, negative error code otherwise
 */
static int32_t iio_ad7689_end_transfer(void *dev)
{
	return end_data_transfer();
}

/*********************************************************
 *               IIO Attributes and Structures
 ********************************************************/

/* IIOD channels attributes list */
struct iio_attribute channel_input_attributes[] = {
	{
		.name = "raw",
		.show = get_adc_raw,
		.store = set_adc_raw,
		.priv = IIO_RAW_ATTR_ID
	},
	{
		.name = "scale",
		.show = get_adc_raw,
		.store = set_adc_raw,
		.priv = IIO_SCALE_ATTR_ID
	},
	{
		.name = "offset",
		.show = get_adc_raw,
		.store = set_adc_raw,
		.priv = IIO_OFFSET_ATTR_ID
	},

	END_ATTRIBUTES_ARRAY
};

/* IIOD context attributes list */
static struct iio_context_attribute 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
};

/* IIOD device (global) attributes list */
static struct iio_attribute global_attributes[] = {
	{
		.name = "sampling_frequency",
		.show = get_sampling_frequency,
		.store = set_sampling_frequency,
	},

	END_ATTRIBUTES_ARRAY
};

/* IIOD channels configurations */
struct scan_type chn_scan = {
#if defined(BIPOLAR)
	.sign = 's',
#else
	.sign = 'u',
#endif
	.realbits = CHN_STORAGE_BITS,
	.storagebits = CHN_STORAGE_BITS,
	.shift = 0,
	.is_big_endian = false
};

static struct iio_channel iio_ad7689_channels[] = {
	{
		.name = "voltage0",
		.ch_type = IIO_VOLTAGE,
		.channel = 0,
		.scan_index = 0,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true,
	},
	{
		.name = "voltage1",
		.ch_type = IIO_VOLTAGE,
		.channel = 1,
		.scan_index = 1,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage2",
		.ch_type = IIO_VOLTAGE,
		.channel = 2,
		.scan_index = 2,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage3",
		.ch_type = IIO_VOLTAGE,
		.channel = 3,
		.scan_index = 3,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
#if !defined(DEV_AD7682)
	{
		.name = "voltage4",
		.ch_type = IIO_VOLTAGE,
		.channel = 4,
		.scan_index = 4,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage5",
		.ch_type = IIO_VOLTAGE,
		.channel = 5,
		.scan_index = 5,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage6",
		.ch_type = IIO_VOLTAGE,
		.channel = 6,
		.scan_index = 6,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage7",
		.ch_type = IIO_VOLTAGE,
		.channel = 7,
		.scan_index = 7,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
#endif
	{
		.name = "temperature",
		.ch_type = IIO_TEMP,
#if !defined(DEV_AD7682)
		.channel = 8,
		.scan_index = 8,
#else
		.channel = 4,
		.scan_index = 4,
#endif
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
};


/**
 * @brief	Init for reading/writing and parameterization of a
 * 			ad7689 IIO device
 * @param 	desc[in,out] - IIO device descriptor
 * @return	0 in case of success, negative error code otherwise
 */
static int32_t iio_ad7689_init(struct iio_device **desc)
{
	struct iio_device *iio_ad7689_inst;

	iio_ad7689_inst = calloc(1, sizeof(struct iio_device));
	if (!iio_ad7689_inst) {
		return -EINVAL;
	}

	iio_ad7689_inst->num_ch = sizeof(iio_ad7689_channels) / sizeof(
					  iio_ad7689_channels[0]);
	iio_ad7689_inst->channels = iio_ad7689_channels;
	iio_ad7689_inst->attributes = global_attributes;
	iio_ad7689_inst->context_attributes = iio_context_attributes;

	iio_ad7689_inst->submit = iio_ad7689_submit_buffer;
	iio_ad7689_inst->pre_enable = iio_ad7689_prepare_transfer;
	iio_ad7689_inst->post_disable = iio_ad7689_end_transfer;

	iio_ad7689_inst->debug_reg_read = NULL;
	iio_ad7689_inst->debug_reg_write = NULL;

	*desc = iio_ad7689_inst;

	return 0;
}

/**
 * @brief	Initialize the IIO interface for AD7689 IIO device
 * @return	none
 * @return	0 in case of success, negative error code otherwise
 */
int32_t ad7689_iio_initialize(void)
{
	int32_t init_status;

	/* IIO device descriptor */
	struct iio_device *p_iio_ad7689_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
	};

	/* Init the system peripherals */
	init_status = init_system();
	if (init_status) {
		return init_status;
	}

	/* Initialize AD7689 device and peripheral interface */
	init_status = ad7689_init(&p_ad7689_dev_inst, &ad7689_init_params);
	if (init_status) {
		return init_status;
	}

	/* Initialize the AD7689 IIO application interface */
	init_status = iio_ad7689_init(&p_iio_ad7689_dev);
	if (init_status) {
		return init_status;
	}

	/* Initialize the IIO interface */
	iio_init_params.uart_desc = uart_desc;
	iio_device_init_params.dev = p_ad7689_dev_inst;
	iio_device_init_params.dev_descriptor = p_iio_ad7689_dev;
	iio_init_params.devs = &iio_device_init_params;
	init_status = iio_init(&p_ad7689_iio_desc, &iio_init_params);
	if (init_status) {
		return init_status;
	}

	/* Load the init config into current configuration */
	memcpy(&ad7689_current_config, &ad7689_init_params.config,
	       sizeof(ad7689_current_config));

	return 0;
}

/**
 * @brief 	Run the AD7689 IIO event handler
 * @return	none
 * @details	This function monitors the new IIO client event
 */
void ad7689_iio_event_handler(void)
{
	(void)iio_step(p_ad7689_iio_desc);
}