this is testing

app/src/ad7606_data_capture.c

Committer:
pmallick
Date:
2021-01-14
Revision:
0:3afcd581558d

File content as of revision 0:3afcd581558d:

/***************************************************************************//**
 *   @file    ad7606_data_capture.c
 *   @brief   Data capture interface for AD7606 IIO application
 *   @details This module handles the AD7606 data capturing
********************************************************************************
 * Copyright (c) 2020 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 <string.h>
#include <stdlib.h>

#include "app_config.h"
#include "ad7606_data_capture.h"
#include "ad7606_support.h"
#include "gpio_extra.h"

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

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

/* Device instance for background adc conversion callback */
static volatile struct ad7606_dev *dev = NULL;

/*
 *@enum		acq_buffer_state_e
 *@details	Enum holding the data acquisition buffer states
 **/
typedef enum {
	BUF_AVAILABLE,
	BUF_FULL
} acq_buffer_state_e;

/*
 *@struct	acq_buf_t
 *@details	Structure holding the data acquisition buffer parameters
 **/
typedef struct {
	acq_buffer_state_e state;
#if (ADC_RESOLUTION > 16)
	uint32_t data[NO_OF_SAMPLES];
#else
	uint16_t data[NO_OF_SAMPLES];
#endif
} acq_buf_t;

/* ADC data acquisition buffers */
static volatile acq_buf_t acq_buffer[2];

/* Flag to indicate background data conversion status */
static volatile bool start_background_conversion = false;

/* Active channels to be captured */
static volatile uint32_t active_channels = 0;

/* Minimum number of bytes to be read from device */
static volatile uint8_t min_bytes_to_read = 0;

/* Minimum number of samples to be stored into acquisition buffer */
static volatile uint8_t min_samples_to_store = 0;

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

/*!
 * @brief	Function to init the data capture for AD7606 device
 * @param	device[in]- Device instance
 * @return	none
 */
int32_t iio_data_capture_init(struct ad7606_dev *device)
{
	/* Save the device structure for background conversion */
	dev = malloc(sizeof(struct ad7606_dev *));
	if (!dev) {
		return FAILURE;
	}

	memcpy(&dev, &device, sizeof(dev));

	return SUCCESS;
}


/*!
 * @brief	Function to read the ADC raw data for single channel
 * @param	device[in]- Device instance
 * @param	chn[in] - Input channel
 * @return	adc raw data/sample
 */
int32_t single_data_read(void *device, uint8_t chn, polarity_e polarity)
{
	int32_t adc_data = 0;
	uint32_t adc_raw[ADC_RESOLUTION] = { 0 };

	if (ad7606_read(device, adc_raw) != SUCCESS) {
		adc_raw[chn] = 0;
	}

	if (polarity == BIPOLAR) {
		/* Check for negative adc value for bipolar inputs */
		if (adc_raw[chn] >= ADC_MAX_COUNT_BIPOLAR) {
			/* Take the 2s complement for the negative counts (>full scale value) */
			adc_data = ADC_MAX_COUNT_UNIPOLAR - adc_raw[chn];
			adc_data = 0 - adc_data;
		} else {
			adc_data = adc_raw[chn];
		}
	} else {
		adc_data = adc_raw[chn];
	}

	return adc_data;
}


/*!
 * @brief	Function to read the ADC buffered raw data
 * @param	device[in]- Device instance
 * @param	pbuf[out] - Buffer to load ADC raw data
 * @param	bytes[in] - Number of bytes to be read
 * @param	chn_mask[in] - Active channels mask
 * @return	none
 * @details	This function reads the data from any available acquisition
 *			buffer (the buffer which is full). The intention here is to
 *			have most recent data fetched from the device.
 */
void buffered_data_read(char *pbuf, size_t bytes, size_t offset,
			uint32_t chn_mask)
{
	uint8_t mask = 0x1;

	if (acq_buffer[0].state == BUF_FULL) {
		/* Copy the acquisition buffer 0 into output data buffer */
		memcpy(pbuf, acq_buffer[0].data, bytes);
	} else if(acq_buffer[1].state == BUF_FULL) {
		/* Copy the acquisition buffer 1 into output data buffer */
		memcpy(pbuf, acq_buffer[1].data, bytes);
	} else {
		/* This case should never reach. At any instance, at least one
		 * of the buffer should be full */
	}

	/* Find the minimum number of bytes to be read from device during
	 * background conversion (Reading all channels adds extra overhead,
	 * so restrict the minimum reads to whatever is possible) */
	if (offset == 0) {
		active_channels = chn_mask;
		for (uint8_t chn = 1; chn <= NO_OF_CHANNELS; chn++) {
			if (active_channels & mask) {
#if (ADC_RESOLUTION == 18)
				/* 1 sample = 3 bytes (max 18 bytes) */
				min_bytes_to_read = chn * 3;
				if (min_bytes_to_read > ADC_RESOLUTION) {
					min_bytes_to_read = ADC_RESOLUTION;
				}
#else
				/* 1 sample = 2 bytes */
				min_bytes_to_read = chn * 2;
#endif
				min_samples_to_store = chn;
			}

			mask <<= 1;
		}
	}
}


/*!
 * @brief	Function to perform background ADC conversion
 * @return	none
 * @details	This is an External Interrupt callback function/ISR, which is tied up to
 *			falling edge trigger of BUSY pin. It is trigered when previous data
 *			conversion is over and BUSY line goes low. Upon trigger, conversion
 *			results are read into acquisition buffer and next conversion is triggered.
 *			This continues until background conversion is stopped.
 */
void do_conversion_callback(void)
{
	static uint16_t sample_cnt = 0;
#if (ADC_RESOLUTION == 18)
	uint8_t arr_indx = 0;
	uint8_t offset_end = 2;
	uint8_t mask = 0xff;
#else
	uint8_t mask = 0x01;
#endif

	if (start_background_conversion == true) {

		/* Read the conversion result for required number of bytes */
		(void)ad7606_read_conversion_data(dev, min_bytes_to_read);

		/* Extract the channels data based on the active channels
		 * Note: The extraction of data based on active channel needs
		 * to be done since its not possible to capture individual
		 * channel in AD7606 devices */
		for (uint8_t cnt = 0; cnt < min_samples_to_store; cnt++) {
			if (active_channels & mask) {
				/* Fill the buffer based on the availability */
				if (acq_buffer[0].state == BUF_AVAILABLE) {
#if (ADC_RESOLUTION == 18)
					acq_buffer[0].data[sample_cnt] =
						(((uint32_t)(dev->data[arr_indx] & mask) << 16) | 			// MSB
						 ((uint32_t)dev->data[arr_indx+1] << 8) |
						 ((uint32_t)(dev->data[arr_indx+2] >> (8-offset_end))));	// LSB
#else
					acq_buffer[0].data[sample_cnt] =
						(uint16_t)(((uint16_t)dev->data[cnt << 1] << 8) |	// MSB
							   dev->data[(cnt << 1) + 1]);					// LSB
#endif
				} else if (acq_buffer[1].state == BUF_AVAILABLE) {
#if (ADC_RESOLUTION == 18)
					acq_buffer[1].data[sample_cnt] =
						(((uint32_t)(dev->data[arr_indx] & mask) << 16) | 			// MSB
						 ((uint32_t)dev->data[arr_indx + 1] << 8) |
						 ((uint32_t)(dev->data[arr_indx + 2] >> (8 - offset_end))));// LSB
#else
					acq_buffer[1].data[sample_cnt] =
						(uint16_t)(((uint16_t)dev->data[cnt << 1] << 8) |	// MSB
							   dev->data[(cnt << 1) + 1]);					// LSB
#endif
				} else {
					/* This case should never reach */
				}

				sample_cnt++;
			}

#if (ADC_RESOLUTION == 18)
			arr_indx += 2;

			/* Track array to reach at the middle (9 bytes apart) to change the offsets */
			if (arr_indx == ((ADC_RESOLUTION / 2) -1)) {
				arr_indx++;
				offset_end = 2;
				mask = 0xff;
			} else {
				mask = (0xff >> offset_end);
				offset_end += 2;
			}
#endif

			if (sample_cnt >= NO_OF_SAMPLES) {
				break;
			}

			mask <<= 1;
		}

		/* Monitor buffer full status based on active buffer state */
		if (sample_cnt >= NO_OF_SAMPLES) {
			if (acq_buffer[0].state == BUF_AVAILABLE) {
				/* Buffer 0 is set FULL */
				acq_buffer[0].state =  BUF_FULL;

				/* Buffer 1 is set available to store next data samples */
				acq_buffer[1].state =  BUF_AVAILABLE;
			} else if (acq_buffer[1].state == BUF_AVAILABLE) {
				/* Buffer 1 is set FULL */
				acq_buffer[1].state =  BUF_FULL;

				/* Buffer 0 is set available to store next data samples */
				acq_buffer[0].state =  BUF_AVAILABLE;
			} else {
				/* This case should never reach */
			}

			sample_cnt = 0;
		}

		/* Trigger next conversion */
		(void)ad7606_convst(dev);
	}
}


/*!
 * @brief	Function to start bakground ADC conversion
 * @return	none
 */
void start_background_data_conversion(void)
{
	/* Start conversion */
	(void)ad7606_convst(dev);
	start_background_conversion = true;
}


/*!
 * @brief	Function to stop bakground ADC conversion
 * @return	none
 */
void stop_background_data_conversion(void)
{
	start_background_conversion = false;
}