Forked repository for pushing changes to EVAL-AD4696

Dependencies:   platform_drivers

app/iio_ad4696.c

Committer:
pmallick
Date:
2021-09-30
Revision:
1:8792acb5a039

File content as of revision 1:8792acb5a039:

/***************************************************************************//**
 *   @file    iio_ad4696.c
 *   @brief   Implementation of AD4696 IIO application interfaces
 *   @details This module acts as an interface for AD4696 IIO application
********************************************************************************
 * Copyright (c) 2021 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 <math.h>

#include "app_config.h"
#include "tinyiiod.h"
#include "iio_ad4696.h"
#include "adc_data_capture.h"
#include "ad4696_support.h"
#include "ad4696_user_config.h"
#include "error.h"

/******************************************************************************/
/************************ Macros/Constants ************************************/
/******************************************************************************/

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

/* 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

/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/

/* Pointer to the struct representing the AD4696 IIO device */
struct ad469x_dev *p_ad4696_dev = NULL;

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

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

/* 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,
	DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE,
	DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE
};

/* Flag to trigger new data capture */
static bool adc_data_capture_started = false;

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

/*!
 * @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
 * @return	Number of characters read/written
 */
static ssize_t get_adc_raw(void *device,
					char *buf,
					size_t len,
					const struct iio_ch_info *channel,
					intptr_t id)
{
	static uint32_t adc_data_raw = 0;
	int32_t offset = 0;

	switch (id) {
	case IIO_RAW_ATTR_ID:
		/* Capture the raw adc data */
		if (read_single_sample((uint32_t)channel->ch_num, &adc_data_raw) != FAILURE) {
			return (ssize_t) sprintf(buf, "%d", adc_data_raw);
		}
		break;
		
	case IIO_SCALE_ATTR_ID:
		return (ssize_t) sprintf(buf, "%f", attr_scale_val[channel->ch_num]);
		break;
		
	case IIO_OFFSET_ATTR_ID:
#if defined(PSEUDO_BIPOLAR_MODE)
		if (adc_data_raw >= ADC_MAX_COUNT_BIPOLAR) {
			offset = -ADC_MAX_COUNT_UNIPOLAR ;
		}
		else {
			offset = 0;
		}
#endif
		return (ssize_t) sprintf(buf, "%d", offset);
		break;
			
	default:
		break;
	}

	return -EINVAL;
}

static ssize_t set_adc_raw(void *device,
					char *buf,
					size_t len,
					const struct iio_ch_info *channel,
					intptr_t id)
{
	/* ADC raw value, offset factor and scale factor are fixed for given configurations set
	 * in the firmware */
	return len;
}

/*!
 * @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
 * @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 = 50SPS and requested samples = 400
 *			Timeout = (400 * 0.02) + 1 = 9sec
 */
ssize_t get_sampling_frequency(void *device,
			       char *buf,
			       size_t len,
			       const struct iio_ch_info *channel,
			       intptr_t id)
{
	return (ssize_t) sprintf(buf, "%d", SAMPLING_RATE);
}

ssize_t set_sampling_frequency(void *device,
			       char *buf,
			       size_t len,
			       const struct iio_ch_info *channel,
			       intptr_t id)
{
	/* Sampling frequency determines the IIO client timeout. It is defined in the
	* software and not allowed to change externally */
	return -EINVAL;
}

/*!
 * @brief	Read the debug register value
 * @param	dev- Pointer to IIO device instance
 * @param	reg- Register address to read from
 * @param	readval- Pointer to variable to read data into
 * @return	SUCCESS in case of success, negative value otherwise
 */
int32_t debug_reg_read(void *dev, uint32_t reg, uint8_t *readval)
{
	/* Read the data from device */
	if (reg <= NUM_OF_REGISTERS) {
		if (ad469x_spi_reg_read(dev, reg, readval) == SUCCESS) {
			return SUCCESS;
		}
	}

	return FAILURE;
}

/*!
 * @brief	Write into the debug register
 * @param	dev- Pointer to IIO device instance
 * @param	reg- Register address to write into
 * @param	writeval- Register value to write
 * @return	SUCCESS in case of success, negative value otherwise
 */
int32_t debug_reg_write(void *dev, uint32_t reg, uint32_t writeval)
{
	if (reg <= NUM_OF_REGISTERS) {
		if (ad469x_spi_reg_write(dev, reg, writeval) == SUCCESS) {
			return SUCCESS;
		}
	}

	return FAILURE;
}

/**
 * @brief	Read buffer data corresponding to AD4696 IIO device
 * @param	dev_instance[in] - IIO device instance
 * @param	pbuf[out] - Pointer to output data buffer
 * @param	offset[in] - Data buffer offset
 * @param	bytes_count[in] - Number of bytes to read
 * @param	ch_mask[in] - Channels select mask
 * @return	SUCCESS in case of success or negative value otherwise
 */
static ssize_t iio_ad4696_read_data(void *dev_instance,
				    char *pbuf,
				    size_t offset,
				    size_t bytes_count,
				    uint32_t ch_mask)
{
	if (adc_data_capture_started == false) {
		start_data_capture(ch_mask, AD469x_CHANNEL_NO);
		adc_data_capture_started = true;
	}

	/* Read the buffered data */
	return (ssize_t)read_buffered_data(pbuf, bytes_count, offset, ch_mask,
		BYTES_PER_SAMPLE);

}

/**
 * @brief	Transfer the device data into memory (optional)
 * @param	dev_instance[in] - IIO device instance
 * @param	bytes_count[in] - Number of bytes to read
 * @param	ch_mask[in] - Channels select mask
 * @return	SUCCESS in case of success or negative value otherwise
 */
static ssize_t iio_ad4696_transfer_dev_data(void *dev_instance,
		size_t bytes_count, uint32_t ch_mask)
{
	store_requested_samples_count(bytes_count, BYTES_PER_SAMPLE);
	return SUCCESS;
}

/**
 * @brief	Perform tasks before new data transfer
 * @param	dev_instance[in] - IIO device instance
 * @param	ch_mask[in] - Channels select mask
 * @return	SUCCESS in case of success or negative value otherwise
 */
static int32_t iio_ad4696_start_transfer(void *dev_instance, uint32_t ch_mask)
{
	return SUCCESS;
}


/**
 * @brief	Perform tasks before end of current data transfer
 * @param	dev_instance[in] - IIO device instance
 * @return	SUCCESS in case of success or negative value otherwise
 */
static int32_t iio_ad4696_stop_transfer(void *dev)
{
	adc_data_capture_started = false;
	stop_data_capture();

	return SUCCESS;
}

/*********************************************************
 *               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 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 debug attributes list */
static struct iio_attribute debug_attributes[] = {
	{
		.name = "direct_reg_access",
		.show = NULL,
		.store = NULL,
	},

	END_ATTRIBUTES_ARRAY
};

/* IIOD channels configurations */
struct scan_type chn_scan = {
#if defined(PSEUDO_BIPOLAR_MODE)
	.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_ad4696_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
	},
	{
		.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
	},
	{
		.name = "voltage8",
		.ch_type = IIO_VOLTAGE,
		.channel = 8,
		.scan_index = 8,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true,
	},
	{
		.name = "voltage9",
		.ch_type = IIO_VOLTAGE,
		.channel = 9,
		.scan_index = 9,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage10",
		.ch_type = IIO_VOLTAGE,
		.channel = 10,
		.scan_index = 10,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage11",
		.ch_type = IIO_VOLTAGE,
		.channel = 11,
		.scan_index = 11,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage12",
		.ch_type = IIO_VOLTAGE,
		.channel = 12,
		.scan_index = 12,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage13",
		.ch_type = IIO_VOLTAGE,
		.channel = 13,
		.scan_index = 13,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage14",
		.ch_type = IIO_VOLTAGE,
		.channel = 14,
		.scan_index = 14,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
	{
		.name = "voltage15",
		.ch_type = IIO_VOLTAGE,
		.channel = 15,
		.scan_index = 15,
		.scan_type = &chn_scan,
		.attributes = channel_input_attributes,
		.ch_out = false,
		.indexed = true
	},
};

/**
 * @brief	Init for reading/writing and parameterization of a
 * 			ad4696 IIO device
 * @param 	desc[in,out] - IIO device descriptor
 * @return	SUCCESS in case of success, FAILURE otherwise
 */
static int32_t iio_ad4696_init(struct iio_device **desc)
{
	struct iio_device *iio_ad4696_inst;

	iio_ad4696_inst = calloc(1, sizeof(struct iio_device));
	if (!iio_ad4696_inst) {
		return FAILURE;
	}

	iio_ad4696_inst->num_ch = sizeof(iio_ad4696_channels) / sizeof(
					  iio_ad4696_channels[0]);
	iio_ad4696_inst->channels = iio_ad4696_channels;
	iio_ad4696_inst->attributes = global_attributes;
	iio_ad4696_inst->debug_attributes = debug_attributes;

	iio_ad4696_inst->transfer_dev_to_mem = iio_ad4696_transfer_dev_data;
	iio_ad4696_inst->transfer_mem_to_dev = NULL;
	iio_ad4696_inst->read_data = iio_ad4696_read_data;
	iio_ad4696_inst->write_data = NULL;
	iio_ad4696_inst->prepare_transfer = iio_ad4696_start_transfer;
	iio_ad4696_inst->end_transfer = iio_ad4696_stop_transfer;
	iio_ad4696_inst->debug_reg_read = debug_reg_read;
	iio_ad4696_inst->debug_reg_write = debug_reg_write;

	*desc = iio_ad4696_inst;

	return SUCCESS;
}

/**
 * @brief Release resources allocated for IIO device
 * @param desc[in] - IIO device descriptor
 * @return SUCCESS in case of success, FAILURE otherwise
 */
static int32_t iio_ad4696_remove(struct iio_desc *desc)
{
	int32_t status;

	if (!desc) {
		return FAILURE;
	}

	status = iio_unregister(desc, (char *)dev_name);
	if (status != SUCCESS) {
		return FAILURE;
	}

	return SUCCESS;
}


/**
 * @brief	Initialize the IIO interface for AD4696 IIO device
 * @return	SUCCESS in case of success, FAILURE otherwise
 */
int32_t ad4696_iio_initialize(void)
{
	int32_t init_status;

	/* IIO device descriptor */
	struct iio_device *p_iio_ad4696_dev;

	/* IIO interface init parameter */
	struct iio_init_param iio_init_params = {
		.phy_type = USE_UART,
		{
			&uart_init_params
		}
	};

	/* Initialize AD4696 device and peripheral interface */
	init_status = ad469x_init(&p_ad4696_dev, &ad4696_init_str);
	if (init_status != SUCCESS) {
		return init_status;
	}

	/* Initialize the IIO interface */
	init_status = iio_init(&p_ad4696_iio_desc, &iio_init_params);
	if (init_status != SUCCESS) {
		return init_status;
	}

	/* Register and initialize the AD4696 device into IIO interface */
	init_status = iio_ad4696_init(&p_iio_ad4696_dev);
	if (init_status != SUCCESS) {
		return init_status;
	}

	/* Register AD4696 IIO interface */
	init_status = iio_register(p_ad4696_iio_desc, p_iio_ad4696_dev,
				   (char *)dev_name, p_ad4696_dev, NULL,
				   NULL);
	if (init_status != SUCCESS) {
		return init_status;
	}

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

	/* To enable manual trigger mode for operation */ 
	init_status = ad4696_enable_manual_trigger_mode(p_ad4696_dev);
	if (init_status != SUCCESS) {
		return init_status;
	}
	
	return SUCCESS;
}

/**
 * @brief 	Run the AD4696 IIO event handler
 * @return	none
 * @details	This function monitors the new IIO client event
 */
void ad4696_iio_event_handler(void)
{
	while(1) {
		(void)iio_step(p_ad4696_iio_desc);
	}
}