Example Program for EVAL-AD5592R

Dependencies:   adi_console_menu platform_drivers

app/ad5592r_console_app.c

Committer:
EndaKilgarriff
Date:
2020-06-22
Revision:
3:ae77c589c81a
Parent:
1:e84d8d51cd51
Child:
4:9fa40df9bf93

File content as of revision 3:ae77c589c81a:

/*!
 *****************************************************************************
 *   @file    ad5592r_console_app.c
 *   @brief   AD5592R console application interfaces
 *   @details This file is specific to ad5592r and ad5593r console menu application handle.
 *            The functions defined in this file performs the action
 *            based on user selected console menu.
 *
 -----------------------------------------------------------------------------
 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 <stdio.h>
#include <string.h>
#include <stdbool.h>

#include "app_config.h"
#include "ad5592r_configs.h"

#include "platform_support.h"
#include "platform_drivers.h"
#include "spi_extra.h"
#include "i2c_extra.h"

#include "ad5592r-base.h"
#include "ad5592r.h"
#include "ad5593r.h"

#include "ad5592r_console_app.h"

/******************************************************************************/
/************************* Macros & Constant Definitions **********************/
/******************************************************************************/
// vref_voltage can be defined as EXTERNAL_VREF_VOLTAGE or INTERNAL_VREF_VOLTAGE
// Change EXTERNAL_VREF_VOLTAGE if using supply other than 2.5V
#define EXTERNAL_VREF_VOLTAGE			2.5
float vref_voltage = EXTERNAL_VREF_VOLTAGE;

#define	AD5592R_CHANNEL(N)			(N)
#define AD5592R_REG_ADC_SEQ_INCL(x)		BIT(x)
#define AD5592R_REG_PD_CHANNEL(x)		BIT(x)
#define AD5592R_GPIO(x)				BIT(x)

#define TEMP_SAMPLE_SIZE			5
#define CLEAR_CHANNEL_SELECTION			1000
#define MDELAY_TO_DISPLAY_INSTRUCTION		1000
#define TEMPERATURE_READBACK_CHANNEL		8

#define MAX_ADC_CODE				4095.0
#define ADC_GAIN_LOW_CONVERSION_VALUE		2.654
#define ADC_GAIN_HIGH_CONVERSION_VALUE		1.327

/* Private Variables */
static struct ad5592r_dev sAd5592r_dev;

static const char *mode_names[] = {
	"Unused",
	"ADC\t",
	"DAC\t",
	"ADC+DAC",
	"GPI\t",
	"GPO\t",
};
static const char *offstate_names[] = {
	"Pulldown",
	"Low\t",
	"High\t",
	"Tristate"
};
static bool active_channel_selections[NUM_CHANNELS] = {
	false,
	false,
	false,
	false,
	false,
	false,
	false,
	false
};

static uint16_t adc_channels_in_seq = AD5592R_REG_ADC_SEQ_TEMP_READBACK;

/******************************************************************************/
/***************************** Function Declarations **************************/
/******************************************************************************/
static int32_t do_software_reset(uint32_t id);
static int32_t do_read_die_temp(uint32_t id);
static float die_temp_calculation(uint16_t adc_temp_code, bool adc_gain);
static void do_set_channel_modes(void);
static int32_t do_toggle_channel_selection(uint32_t channel);
static int32_t do_mode_selection(uint32_t mode);
static int32_t do_reset_channel_modes(uint32_t id);
static int32_t do_offstate_selection(uint32_t mode);
static int32_t do_channel_7_adc_indicator(uint32_t id);
static int32_t do_general_settings_toggle(uint32_t reg_bit_id);
static int32_t do_write_dac_value(uint32_t id);
static int32_t do_dac_input_reg_to_output(uint32_t id);
static int32_t do_toggle_ldac_mode(uint32_t id);
static int32_t do_toggle_dac_powerdown(uint32_t id);
static int32_t do_toggle_incl_in_seq(uint32_t id);
static int32_t do_read_adc_sequence(uint32_t id);

extern console_menu power_down_pin_select_menu;
extern console_menu config_channels_menu;
extern console_menu general_settings_menu;
extern console_menu dac_menu;
extern console_menu gpio_menu;
extern console_menu adc_menu;

/******************************************************************************/
/***************************** Function Definitions ***************************/
/******************************************************************************/

/*!
 * @brief		Initialize AD5592/3R. ACTIVE_DEVICE defined in app_config.h
 *@details		The device initialization varies depending on what ACTIVE_DEVICE is defined.
 *			Device is reset and default register map values written.
 *			SPI or I2C initialization occurs.
 * @return		SUCCESS (0), FAILURE (negative)
 */
int32_t ad5592r_app_initalization(void)
{
	memcpy(&sAd5592r_dev, &ad5592r_dev_user, sizeof(ad5592r_dev_user));
	int32_t status;
	if (ACTIVE_DEVICE == ID_AD5593R) {
		sAd5592r_dev.i2c = &i2c_user_descr;
		i2c_init(&sAd5592r_dev.i2c, &i2c_user_params);
		status = ad5593r_init(&sAd5592r_dev, &ad5592r_user_param);
	} else {
		sAd5592r_dev.spi = &spi_user_descr;
		spi_init(&sAd5592r_dev.spi, &spi_user_params);
		status = ad5592r_init(&sAd5592r_dev, &ad5592r_user_param);
	}
	return status;
}

/*!
 * @brief		 Performs software reset
 * @details	Writes to the reset register. Resets sAd5592r_dev configuration using
 *			configuration from ad5592r_reset_config.c SPI, I2C and ops are stored
 *			and restored after the reset.
 * @return		MENU_CONTINUE
 */
static int32_t do_software_reset(uint32_t id)
{
	int32_t status;

	if ((status = ad5592r_software_reset(&sAd5592r_dev)) == SUCCESS) {
		// Save spi_desc field, i2c_desc and device ops settings as it is not reset
		spi_desc *spi_interface = sAd5592r_dev.spi;
		i2c_desc *i2c_interface = sAd5592r_dev.i2c;
		const struct ad5592r_rw_ops *dev_ops = sAd5592r_dev.ops;
		// Copy over the reset state of the device
		memcpy(&sAd5592r_dev, &ad5592r_dev_reset, sizeof(ad5592r_dev_reset));

		// Restore device ops
		sAd5592r_dev.ops = dev_ops;
		if (ACTIVE_DEVICE == ID_AD5592R) {
			// Restore the spi_desc pointer field
			sAd5592r_dev.spi = spi_interface;
			printf(EOL " --- AD5592R Software Reset Successful---" EOL);
		} else {
			// Restore the i2c_desc pointer field
			sAd5592r_dev.i2c = i2c_interface;
			printf(EOL " --- AD5593R Reset Request Successful---" EOL);
		}
	} else {
		printf(EOL " *** Software Reset Failure: %d ***" EOL, status);
		adi_press_any_key_to_continue();
	}

	adi_press_any_key_to_continue();
	return (MENU_CONTINUE);
}

/*!
 * @brief		Prints the temperature of the die
 * @details	Sets the devices to perform a temperature readback.
 *			Performs a number of samples based on TEMP_SAMPLE_SIZE
 * @return		MENU_CONTINUE
 */
static int32_t do_read_die_temp(uint32_t id)
{
	uint16_t readback_reg[1] = { 0 };
	int32_t status = 0, ch_state = 0;
	float result = 0;

	ch_state = sAd5592r_dev.channel_modes[7];
	sAd5592r_dev.channel_modes[7] = CH_MODE_ADC;
	do_set_channel_modes();

	do {

		for (int8_t i = 0; i < TEMP_SAMPLE_SIZE; i++) {
			do {
				status = sAd5592r_dev.ops->read_adc(&sAd5592r_dev,
								    AD5592R_CHANNEL(8),
								    readback_reg);
			} while (0);
			if (status != SUCCESS) {
				// Break out of for loop if not successful
				break;
			}
			result += die_temp_calculation(readback_reg[0],
						       (AD5592R_REG_CTRL_ADC_RANGE & sAd5592r_dev.cached_gp_ctrl));
		}

		result /= TEMP_SAMPLE_SIZE;

		if (status == SUCCESS) {
			// Print average of samples
			printf(EOL " --- Temperature: %.1f*C --- " EOL, result);
		} else {
			printf(EOL " *** Error reading die temperature: %d **" EOL, status);
			break;
		}
	} while (0);

	sAd5592r_dev.channel_modes[7] = ch_state;
	do_set_channel_modes();

	adi_press_any_key_to_continue();
	return (MENU_CONTINUE);
}

/*!
 * @brief		Calculates the die temperature
 * @details	Based on conversion equation, die temperature is estimated
 * @param	adc_temp_code - data read from ADC readback frame
 *			adc_gain - status of adc_gain
 * @return		result
 */
static float die_temp_calculation(uint16_t adc_temp_code, bool adc_gain)
{
	float result = 0;

	// use different equation depending on gain
	if(adc_gain) {
		result = 25 + ((AD5592R_REG_ADC_SEQ_CODE_MSK(adc_temp_code) -
				((0.5 / (2 * vref_voltage)) * MAX_ADC_CODE)) /
			       (ADC_GAIN_HIGH_CONVERSION_VALUE * (2.5 / vref_voltage)));
	} else {
		result = 25 + ((AD5592R_REG_ADC_SEQ_CODE_MSK(adc_temp_code) -
				((0.5 / vref_voltage) * MAX_ADC_CODE)) /
			       (ADC_GAIN_LOW_CONVERSION_VALUE * (2.5 / vref_voltage)));
	}
	return result;
}

/*!
 * @brief		Set channel modes
 *
 *@details		The channels modes are set by passing the altered device
 *			struct into the ad5592r_set_channel_modes function. There the channels are
 *			set to desired modes.
 */
static void do_set_channel_modes(void)
{
	int32_t status;
	if ((status =  ad5592r_set_channel_modes(&sAd5592r_dev)) != SUCCESS) {
		printf(EOL "Error configuring Channels (%d)" EOL, status);
		adi_press_any_key_to_continue();
	}
}

/*!
 * @brief		Toggle channels currently selected
 * @details	The channels the user has currently selected are set here.
 *			These are the channels that will be altered by mode or offstate selection
 * @param	channel - A channel that the user wants to add to the currently selected channels
 * @return		(MENU_CONTINUE)
 */
static int32_t do_toggle_channel_selection(uint32_t channel)
{
	if (channel == CLEAR_CHANNEL_SELECTION) {
		for (uint8_t i = 0; i < sAd5592r_dev.num_channels; i++) {

			active_channel_selections[i] = false;
		}
	} else {
		active_channel_selections[channel] = !active_channel_selections[channel];
	}

	return (MENU_CONTINUE);
}

/*!
 * @brief		Mode selection
 * @details	The mode the users wishes to apply to the currently selected channels
 *			are selected here. do_set_channel_modes is called which sets the channels
 *			on the device.
 * @param	mode -The mode that the user wishes to apply to the selected channels
 * @return		(MENU_CONTINUE)
 */
static int32_t do_mode_selection(uint32_t mode)
{
	for (uint8_t i = 0; i < sAd5592r_dev.num_channels; i++) {
		if (active_channel_selections[i] == true) {
			sAd5592r_dev.channel_modes[i]	= mode;
		}
	}
	do_set_channel_modes();
	do_toggle_channel_selection(CLEAR_CHANNEL_SELECTION);
	return (MENU_CONTINUE);
}

/*!
 * @brief		Offstate selection
 * @details	The offstate the users wishes to apply to the currently selected channels
 *		are selected here. do_set_channel_modes is called which sets the channels
 *		on the device.
 * @param	The offstate that the user wishes to apply to the selected channels
 * @return		(MENU_CONTINUE)
 */
static int32_t do_offstate_selection(uint32_t mode)
{
	for (uint8_t i = 0; i < sAd5592r_dev.num_channels; i++) {
		if (active_channel_selections[i] == true) {
			sAd5592r_dev.channel_offstate[i]	= mode;
		}
	}
	do_set_channel_modes();
	do_toggle_channel_selection(CLEAR_CHANNEL_SELECTION);
	return (MENU_CONTINUE);
}

/*!
 * @brief		Reset Channel Modes
 * @details	This resets all channel modes to unused.
 * @return		(MENU_CONTINUE)
 *
 */
static int32_t do_reset_channel_modes(uint32_t id)
{
	ad5592r_reset_channel_modes(&sAd5592r_dev);
	do_toggle_channel_selection(CLEAR_CHANNEL_SELECTION);
	return (MENU_CONTINUE);
}

/*!
 * @brief		Sets Channel 7 as ADC conversion indicator
 * @details	Channel 7 is set as a GPIO and the NOT BUSY bit is set in the GPIO
 *			write configuration register enabling channel 7 to be used as an indicator
 *			when ADC conversion are occurring. Channel 7 will go LOW when a conversion
 *			is occurring.
 *			***NOTE*** After selecting this Channel 7 will appear as GPO.
 *			***NOTE*** Ensure this is the last channel to be configured in order to
 *					     ensure preference will no be overwritten
 * @return		(MENU_CONTINUE)
 */
static int32_t do_channel_7_adc_indicator(uint32_t id)
{
	sAd5592r_dev.channel_modes[AD5592R_CHANNEL(7)] =
		((sAd5592r_dev.channel_modes[AD5592R_CHANNEL(7)] == CH_MODE_UNUSED)
		 ? CH_MODE_GPO : CH_MODE_UNUSED);
	do_set_channel_modes();
	do_general_settings_toggle(((AD5592R_REG_GPIO_OUT_EN << 12)
				    | AD5592R_REG_GPIO_OUT_EN_ADC_NOT_BUSY));
	do_toggle_channel_selection(CLEAR_CHANNEL_SELECTION);
	return (MENU_CONTINUE);
}

/*!
 * @brief		Toggle general setting
 * @details	Setting (reg_bit) in register (reg) is toggled
 * @param	reg_bit_id - Combined value containing register address and bit to toggle
 * @return		(MENU_CONTINUE)
 */
static int32_t do_general_settings_toggle(uint32_t reg_bit_id)
{
	uint8_t reg = (reg_bit_id >> 12);
	uint16_t reg_bit = (reg_bit_id & 0xFFF), readback_reg;
	int32_t status;

	if ((status = ad5592r_base_reg_read(&sAd5592r_dev, reg,
					    &readback_reg)) != SUCCESS) {
		printf(" *** Error Reading Setting Status (%x) *** " EOL, reg);
		adi_press_any_key_to_continue();
	} else if ((status = ad5592r_base_reg_write(&sAd5592r_dev, reg,
			     (reg_bit ^ readback_reg))) != SUCCESS) {
		printf(" *** Error  Toggling Setting (%x) *** " EOL, reg);
		adi_press_any_key_to_continue();
	}

	if (reg == AD5592R_REG_PD && reg_bit == AD5592R_REG_PD_EN_REF) {
		if ((AD5592R_REG_PD_EN_REF & (reg_bit ^ readback_reg))) {
			vref_voltage = INTERNAL_VREF_VOLTAGE;
		} else {
			vref_voltage = EXTERNAL_VREF_VOLTAGE;
		}
	}
	return (MENU_CONTINUE);
}

/*!
 * @brief      displays the general settings header
 */
static void display_general_setting_header(void)
{

	int32_t status = 0;
	uint16_t ctrl_reg_data = 0, pd_reg_data = 0;

	do {
		if ((status = ad5592r_base_reg_read(&sAd5592r_dev, AD5592R_REG_CTRL,
						    &ctrl_reg_data)) == SUCCESS) {
			sAd5592r_dev.cached_gp_ctrl = ctrl_reg_data;
		} else {
			printf(" *** Error reading register (%x) *** " EOL, AD5592R_REG_CTRL);
			adi_press_any_key_to_continue();
			break;
		}

		if ((status = ad5592r_base_reg_read(&sAd5592r_dev, AD5592R_REG_PD,
						    &pd_reg_data)) == SUCCESS) {
		} else {
			printf(" *** Error reading register (%x) *** " EOL, AD5592R_REG_PD);
			adi_press_any_key_to_continue();
			break;
		}
	} while(0);

	printf("\tSetting \tEnabled\t\tSetting \tEnabled"EOL);
	printf("\tEn Ref\t\t%s\t\tADC Gain\t%s"EOL,
	       (AD5592R_REG_PD_EN_REF & pd_reg_data)?"X":"\00",
	       (AD5592R_REG_CTRL_ADC_RANGE & ctrl_reg_data)?"X":"\00");
	printf("\tPC Buff\t\t%s\t\tPD All\t\t%s"EOL,
	       (AD5592R_REG_CTRL_ADC_PC_BUFF & ctrl_reg_data)?"X":"\00",
	       (AD5592R_REG_PD_PD_ALL & pd_reg_data) ? "X" : "\00");
	printf("\tBuff\t\t%s\t\tDAC Gain\t%s"EOL,
	       (AD5592R_REG_CTRL_ADC_BUFF_EN & ctrl_reg_data)?"X":"\00",
	       (AD5592R_REG_CTRL_DAC_RANGE & ctrl_reg_data)?"X":"\00");
	printf("\tLock Config\t%s\t\tWr All\t\t%s"EOL,
	       (AD5592R_REG_CTRL_CONFIG_LOCK & ctrl_reg_data)?"X":"\00",
	       (AD5592R_REG_CTRL_W_ALL_DACS & ctrl_reg_data)?"X":"\00");

}

/*!
 * @brief		DAC input register to DAC output
 * @details	Writes the data from the DAC input register to the DAC output.
 *			The LDAC mode is returned to write data to the input register only.
 * @return		(MENU_CONTINUE)
 */
static int32_t do_dac_input_reg_to_output(uint32_t id)
{
	int32_t status;
	if ((status = ad5592r_base_reg_write(&sAd5592r_dev, AD5592R_REG_LDAC,
					     AD5592R_REG_LDAC_INPUT_REG_OUT)) != SUCCESS) {
		printf("*** Error setting LDAC to write to output (%d) *** ", status);
		adi_press_any_key_to_continue();
	}
	sAd5592r_dev.ldac_mode = AD5592R_REG_LDAC_INPUT_REG_ONLY;
	return (MENU_CONTINUE);
}

/*!
 * @brief		User dac code
 * @details	Generate dac code that can be written to device from voltage provided by user
 * @param 	user_voltage - float value provided by user for voltage value to be set
 * @return		dac code value
 */
static uint16_t user_dac_code(float user_voltage)
{
	return (uint16_t) (((user_voltage) * MAX_ADC_CODE) / vref_voltage);
}

/*!
 * @brief		Code values to voltage
 * @details	Generate voltage based on code value
 * @param 	code - integer value used to generate voltage value
 * @return		float voltage value
 */
static float code_to_volts(int16_t code)
{
	return ((code / MAX_ADC_CODE) * vref_voltage);
}

/*!
 * @brief		Write DAC Values
 * @details	Write value specified by user to Channels selected by user in the DAC menu
 * @return		(MENU_CONTINUE)
 */
static int32_t do_write_dac_value(uint32_t id)
{
	int32_t status;
	uint16_t user_code = 0;
	float user_voltage = 2.5;

	printf(EOL "\tEnter voltage to write to selected DACs (0 - Vref) : " EOL);
	user_voltage = adi_get_decimal_float(5);
	user_code = user_dac_code(user_voltage);

	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		if (active_channel_selections[i]) {
			if ((status = sAd5592r_dev.ops->write_dac(&sAd5592r_dev, i,
					user_code)) != SUCCESS) {
				printf("*** Error writing DAC value to channel %d (%d) ***" EOL, i, status);
				adi_press_any_key_to_continue();
			}
			sAd5592r_dev.cached_dac[i] = user_code;
		}
	}
	return (MENU_CONTINUE);
}

/*!
 * @brief		Toggle LDAC Modes
 * @details	Toggles the LDAC mode variable between Immediate write to output
 *			and write values to input register
 * @return		(MENU_CONTINUE)
 */
static int32_t do_toggle_ldac_mode(uint32_t id)
{
	if (sAd5592r_dev.ldac_mode == AD5592R_REG_LDAC_INPUT_REG_ONLY) {
		sAd5592r_dev.ldac_mode = AD5592R_REG_LDAC_IMMEDIATE_OUT;
	} else {
		sAd5592r_dev.ldac_mode = AD5592R_REG_LDAC_INPUT_REG_ONLY;
	}
	return (MENU_CONTINUE);
}

/*!
 * @brief		Toggle DAC channels to power-down
 * @details	Toggles DAC channels that are powered down based on user selection
 * @return		(MENU_CONTINUE)
 */
static int32_t do_toggle_dac_powerdown(uint32_t id)
{
	int32_t status;
	uint16_t powerdown = 0;

	if ((status = ad5592r_base_reg_read(&sAd5592r_dev, AD5592R_REG_PD,
					    &powerdown)) != SUCCESS) {
		printf("*** Error Reading Power Down Config (%d)***" EOL, status);
		adi_press_any_key_to_continue();
	}

	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		if (active_channel_selections[i]) {
			powerdown ^= AD5592R_REG_PD_CHANNEL(i);
		}
	}

	if ((status = ad5592r_base_reg_write(&sAd5592r_dev, AD5592R_REG_PD,
					     powerdown)) != SUCCESS) {
		printf("*** Error writing Power Down Config (%d)***" EOL, status);
		adi_press_any_key_to_continue();
	}

	return (MENU_CONTINUE);
}

/*!
 * @brief		Toggle Channels to include in ADC Sequence
 * @details	Toggles channels that are included in the ADC conversion sequence
 * @return		MENU_CONTINUE
 */
static int32_t do_toggle_incl_in_seq(uint32_t id)
{
	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		if (active_channel_selections[i]) {
			adc_channels_in_seq ^= AD5592R_REG_ADC_SEQ_INCL(i);
		}
	}
	return (MENU_CONTINUE);
}

/*!
 * @brief		Read ADC Sequence
 * @details	The channels that are included in an ADC conversion sequence are read.
 *			For the number of channels in the sequence, the data is parsed, converted
 *			and printed.
 * @return		SUCCESS OR FAILURE
 */
int32_t do_read_adc_sequence(uint32_t id)
{
	int32_t status;
	uint16_t adc_seq_data[9] = {0,0,0,0,0,0,0,0,0}, adc_code;
	uint8_t chan;
	float temperature = 0, voltage = 0;
	size_t samples;

	samples = hweight8(adc_channels_in_seq);

	if ((status = sAd5592r_dev.ops->multi_read_adc(&sAd5592r_dev,
			adc_channels_in_seq, adc_seq_data)) != SUCCESS) {
		printf("*** Error reading adc_sequencer (%d)***" EOL, status);
		adi_press_any_key_to_continue();
		return FAILURE;
	}
	
	printf("\tCh \tCode \tVoltage \tTemp" EOL);
    
	for (uint8_t i = 0; i < samples; i++) {
		temperature = 0;

		adc_code = AD5592R_REG_ADC_SEQ_CODE_MSK(adc_seq_data[i]);
		chan = ((adc_seq_data[i] & 0xF000) >> 12);

		voltage = code_to_volts(adc_code);

		if (chan == TEMPERATURE_READBACK_CHANNEL) {
			temperature = die_temp_calculation(adc_code,
							   (AD5592R_REG_CTRL_ADC_RANGE &
							    sAd5592r_dev.cached_gp_ctrl));
			// Invalid data on temperature read back frame
			voltage = 0;
			adc_code = 0;
		}
		printf("\t%d \t%x \t%.2f \t\t%.1f" EOL,
		       chan,
		       adc_code,
		       voltage,
		       temperature
		      );
	}
	adi_press_any_key_to_continue();
	return SUCCESS;
}

/*!
 * @brief		Set GPI
 * @details	GPIO channels that are selected, with the selection being stored in
 *			active_channel_selections array are set to GPIO Inputs. The selection is then cleared.
 * @return		(MENU_CONTINUE)
 */
static int32_t do_set_gpio_input(uint32_t id)
{
	int32_t status;

	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		if (active_channel_selections[i] == true) {
			sAd5592r_dev.channel_modes[i] = CH_MODE_GPI;
			if ((status = ad5592r_gpio_direction_input
				      (&sAd5592r_dev, AD5592R_CHANNEL(i) )) != SUCCESS) {
				printf(" *** Error Setting GPIO Input on Channel %d (%d) ***" EOL, i, status);
				adi_press_any_key_to_continue();
			}
		}
	}
	do_toggle_channel_selection(CLEAR_CHANNEL_SELECTION);
	return (MENU_CONTINUE);
}

/*!
 * @brief		Set GPO
 * @details	GPIO channels that are selected, with the selection being stored in
 *			active_channel_selections array are set to GPIO Outputs with their
 *			output set LOW. The selection is then cleared.
 * @return		(MENU_CONTINUE)
 */
static int32_t do_set_gpio_output(uint32_t value)
{
	int32_t status;

	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		if (active_channel_selections[i] == true) {
			sAd5592r_dev.channel_modes[i] = CH_MODE_GPO;
			if ((status = ad5592r_gpio_direction_output
				      (&sAd5592r_dev, AD5592R_CHANNEL(i), GPIO_LOW)) != SUCCESS) {
				printf(" *** Error Setting GPIO Output on channel %d (%d) ***" EOL, i,status);
				adi_press_any_key_to_continue();
			}
		}
	}
	do_toggle_channel_selection(CLEAR_CHANNEL_SELECTION);
	return (MENU_CONTINUE);
}

/*!
 * @brief		Toggle GPO
 * @details	GPIO channels that are selected, with the selection being stored in
 *			active_channel_selections array are set to GPIO Outputs with their
 *			output set toggled HIGH and LOW. The selection is then cleared.
 * @return		(MENU_CONTINUE)
 */
static int32_t do_toggle_gpio_output(uint32_t id)
{
	int32_t status;

	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		if (active_channel_selections[i] == true) {
			if ((status = ad5592r_gpio_set(&sAd5592r_dev,
						       AD5592R_CHANNEL(i),
						       !ad5592r_gpio_get(&sAd5592r_dev, AD5592R_CHANNEL(i))) != SUCCESS)) {
				printf(" *** Error Toggling GPIO Output on Channel %d (%d) ***", i, status);
				adi_press_any_key_to_continue();
			}
		}
	}
	do_toggle_channel_selection(CLEAR_CHANNEL_SELECTION);
	return (MENU_CONTINUE);
}


/*!
 * @brief      calls the general configuration menu
 */
static int32_t menu_general_settings(uint32_t id)
{
	return adi_do_console_menu(&general_settings_menu);
}

/*!
 * @brief      calls the DAC configuration menu
 */
static int32_t menu_dac(uint32_t id)
{
	return adi_do_console_menu(&dac_menu);
}

/*!
 * @brief      calls the channel configuration menu
 */
static int32_t menu_config_channels(uint32_t id)
{
	return adi_do_console_menu(&config_channels_menu);
}

/*!
 * @brief      calls the ADC configuration menu
 */
static int32_t menu_adc(uint32_t id)
{
	return adi_do_console_menu(&adc_menu);
}

/*!
 * @brief      calls the menu to select GPIO pins to toggle
 */
static int32_t menu_gpio(uint32_t id)
{
	return adi_do_console_menu(&gpio_menu);
}

/*!
 * @brief      displays the channel configuration header
 */
static void display_channel_selection_header(void)
{
	printf(" Configuration Lock: %s" EOL,
	       (AD5592R_REG_CTRL_CONFIG_LOCK & sAd5592r_dev.cached_gp_ctrl)
	       ?"Enabled":"Disabled");

	printf("\tCh\tMode\t\tOffstate\tSelected" EOL);
	for (uint8_t i = 0; i < sAd5592r_dev.num_channels; i++) {
		printf("\t%d \t%s  \t%s \t\t%s" EOL,
		       i,
		       mode_names[sAd5592r_dev.channel_modes[i]],
		       offstate_names[sAd5592r_dev.channel_offstate[i]],
		       active_channel_selections[i]?"X":"\00" );
	}
}

/*!
 * @brief      displays the gpio menu header
 */
static void display_gpio_menu_header(void)
{
	printf("\tCh\tDir \tValue\tSelected" EOL);

	for (uint8_t i = 0; i < sAd5592r_dev.num_channels; i++) {

		printf("\t%d \t%s%s \t%s \t%s" EOL,
		       i,
		       (sAd5592r_dev.gpio_in &  AD5592R_GPIO(i)) ? "In " : "",
		       (sAd5592r_dev.gpio_out & AD5592R_GPIO(i)) ? "Out " : "",
		       ad5592r_gpio_get(&sAd5592r_dev, AD5592R_CHANNEL(i)) ? "High" : "Low",
		       active_channel_selections[i] ? "X" : "\00"
		      );
	}
}

/*!
 * @brief      displays the DAC menu header
 */
static void display_dac_menu_header(void)
{
	int32_t status;
	float voltage;
	uint16_t powerdown_read;
	char *dac_channel_state = "";

	printf("\tLDAC mode: %s" EOL EOL,
	       sAd5592r_dev.ldac_mode ? "Write to Input Register": "Immediate Output");

	printf("\tCH \tConfig \tCode \tVoltage \tSelected" EOL);

	if ((status = ad5592r_base_reg_read(&sAd5592r_dev,
					    AD5592R_REG_PD,
					    &powerdown_read))  != SUCCESS) {
		printf("*** Error checking Power Down status (%d) ***" EOL, status);
		adi_press_any_key_to_continue();
	}

	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		voltage = 0;
		switch (sAd5592r_dev.channel_modes[i]) {
		case CH_MODE_DAC:
		case CH_MODE_DAC_AND_ADC:
			if (powerdown_read &  AD5592R_REG_PD_CHANNEL(i)) {
				dac_channel_state = "PD";
			} else {
				dac_channel_state = "DAC";
				voltage = code_to_volts(sAd5592r_dev.cached_dac[i]);
			}
			break;
		default:
			dac_channel_state = "-";
			// Channel no longer set as DAC - Clear cached value
			sAd5592r_dev.cached_dac[i] = 0;
			break;
		}

		printf("\t%d \t%s \t%x  \t%.2fV \t\t%s" EOL,
		       i,
		       dac_channel_state,
		       sAd5592r_dev.cached_dac[i],
		       voltage,
		       active_channel_selections[i]?"X":"\00");
	}
}

/*!
 * @brief      displays the Main menu header
 */
static void display_main_menu_header(void)
{
	printf("\tCurrent Channel Configuration:" EOL);
	printf("\tCH \tMode " EOL);
	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		printf("\t%d \t%s" EOL,
		       i,
		       mode_names[sAd5592r_dev.channel_modes[i]]);
	}
}

/*!
 * @brief      displays the ADC menu header
 */
static void display_adc_menu_header(void)
{
	char *adc_channel_state = "";

	printf("\tCh \tMode \tIncl \tSelected" EOL);

	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
		switch (sAd5592r_dev.channel_modes[i]) {
		case CH_MODE_ADC:
		case CH_MODE_DAC_AND_ADC:
			adc_channel_state = "ADC";

			break;
		default:
			adc_channel_state = "-";
			break;
		}

		printf("\t%d \t%s \t%s \t%s" EOL,
		       i,
		       adc_channel_state,
		       (adc_channels_in_seq & AD5592R_REG_ADC_SEQ_INCL(i)) ?"X":"",
		       active_channel_selections[i]?"X":""
		      );
	}
}

/*
 * Definition of the menu of pins to include in adc sequence and menu itself
 */
console_menu_item gpio_menu_items[] = {
	{ "Select Channel", '\00', NULL },
	{ "Channel 0", 'A', do_toggle_channel_selection, AD5592R_CHANNEL(0) },
	{ "Channel 1", 'S', do_toggle_channel_selection, AD5592R_CHANNEL(1) },
	{ "Channel 2", 'D', do_toggle_channel_selection, AD5592R_CHANNEL(2) },
	{ "Channel 3", 'F', do_toggle_channel_selection, AD5592R_CHANNEL(3) },
	{ "Channel 4", 'G', do_toggle_channel_selection, AD5592R_CHANNEL(4) },
	{ "Channel 5", 'H', do_toggle_channel_selection, AD5592R_CHANNEL(5) },
	{ "Channel 6", 'J', do_toggle_channel_selection, AD5592R_CHANNEL(6) },
	{ "Channel 7", 'K', do_toggle_channel_selection, AD5592R_CHANNEL(7) },
	{ "", '\00', NULL },
	{ "Set as GPIO Input", 'Z', do_set_gpio_input },
	{ "Set as GPIO Output", 'X', do_set_gpio_output },
	{ "Toggle Output Value", 'C', do_toggle_gpio_output},
};

console_menu gpio_menu = {
	.title = "GPIO Menu" EOL,
	.items = gpio_menu_items,
	.itemCount = ARRAY_SIZE(gpio_menu_items),
	.headerItem = display_gpio_menu_header,
	.footerItem = NULL,
	.enableEscapeKey = true
};

/*
 * Definition of the ADC config menu and menu itself
 */
console_menu_item adc_menu_items[] = {
	{ "Select channels:" },
	{ "Channel 0", 'A', do_toggle_channel_selection, AD5592R_CHANNEL(0) },
	{ "Channel 1", 'S', do_toggle_channel_selection, AD5592R_CHANNEL(1) },
	{ "Channel 2", 'D', do_toggle_channel_selection, AD5592R_CHANNEL(2) },
	{ "Channel 3", 'F', do_toggle_channel_selection, AD5592R_CHANNEL(3) },
	{ "Channel 4", 'G', do_toggle_channel_selection, AD5592R_CHANNEL(4) },
	{ "Channel 5", 'H', do_toggle_channel_selection, AD5592R_CHANNEL(5) },
	{ "Channel 6", 'J', do_toggle_channel_selection, AD5592R_CHANNEL(6) },
	{ "Channel 7", 'K', do_toggle_channel_selection, AD5592R_CHANNEL(7) },
	{ "", '\00', NULL },
	{ "Toggle Channels in Sequence", 'Q', do_toggle_incl_in_seq },
	{ "Read ADC Sequence", 'W', do_read_adc_sequence},
};

console_menu adc_menu = {
	.title = "ADC Configuration Settings",
	.items = adc_menu_items,
	.itemCount = ARRAY_SIZE(adc_menu_items),
	.headerItem = display_adc_menu_header,
	.footerItem = NULL,
	.enableEscapeKey = true
};


/*
 * Definition of the DAC menu and menu itself
 */
console_menu_item dac_menu_items[] = {
	{ "Select Channels:"},
	{ "Channel 0", 'A', do_toggle_channel_selection, AD5592R_CHANNEL(0) },
	{ "Channel 1", 'S', do_toggle_channel_selection, AD5592R_CHANNEL(1) },
	{ "Channel 2", 'D', do_toggle_channel_selection, AD5592R_CHANNEL(2) },
	{ "Channel 3", 'F', do_toggle_channel_selection, AD5592R_CHANNEL(3) },
	{ "Channel 4", 'G', do_toggle_channel_selection, AD5592R_CHANNEL(4) },
	{ "Channel 5", 'H', do_toggle_channel_selection, AD5592R_CHANNEL(5) },
	{ "Channel 6", 'J', do_toggle_channel_selection, AD5592R_CHANNEL(6) },
	{ "Channel 7", 'K', do_toggle_channel_selection, AD5592R_CHANNEL(7) },
	{ "", '\00', NULL },
	{ "Write voltage to selected DAC channels", 'Q', do_write_dac_value },
	{ "Toggle Power Down selected DAC channels", 'W', do_toggle_dac_powerdown },
	{ "Write Input Reg to DAC output", 'E', do_dac_input_reg_to_output },
	{ "Toggle LDAC mode", 'R', do_toggle_ldac_mode },
};

console_menu dac_menu = {
	.title = "DAC Menu",
	.items = dac_menu_items,
	.itemCount = ARRAY_SIZE(dac_menu_items),
	.headerItem = display_dac_menu_header,
	.footerItem = NULL,
	.enableEscapeKey = true
};

/*
 * Definition of the General Settings menu and menu itself
 */
console_menu_item general_settings_menu_items[] = {
	{
		"Toggle Internal Voltage Ref (En Ref)",     'A', do_general_settings_toggle,
		((AD5592R_REG_PD << 12) | AD5592R_REG_PD_EN_REF)
	},
	{
		"Toggle ADC PreCharge Buffer  (PC Buff)",   'S', do_general_settings_toggle,
		((AD5592R_REG_CTRL << 12) | AD5592R_REG_CTRL_ADC_PC_BUFF)
	},
	{
		"Toggle ADC Buffer (Buff)",                 'D', do_general_settings_toggle,
		((AD5592R_REG_CTRL << 12) | AD5592R_REG_CTRL_ADC_BUFF_EN)
	},
	{
		"Toggle Lock Channel Config (Lock Config)", 'F', do_general_settings_toggle,
		((AD5592R_REG_CTRL << 12) | AD5592R_REG_CTRL_CONFIG_LOCK)
	},
	{
		"Toggle PD All DACs and Internal Ref",      'G', do_general_settings_toggle,
		((AD5592R_REG_PD << 12) | AD5592R_REG_PD_PD_ALL)
	},
	{
		"Toggle ADC Gain Range (ADC Gain)",         'H', do_general_settings_toggle,
		((AD5592R_REG_CTRL << 12) | AD5592R_REG_CTRL_ADC_RANGE)
	},
	{
		"Toggle DAC Gain Range (DAC Gain)",         'J', do_general_settings_toggle,
		((AD5592R_REG_CTRL << 12) | AD5592R_REG_CTRL_DAC_RANGE)
	},
	{
		"Toggle Write All DACS (Wr All)",           'K', do_general_settings_toggle,
		((AD5592R_REG_CTRL << 12) | AD5592R_REG_CTRL_W_ALL_DACS)
	},
};

console_menu general_settings_menu = {
	.title = "General Configuration Settings",
	.items = general_settings_menu_items,
	.itemCount = ARRAY_SIZE(general_settings_menu_items),
	.headerItem = display_general_setting_header,
	.footerItem = NULL,
	.enableEscapeKey = true
};

/*
 * Definition of the Channel  mode selection menu and menu itself
 */
console_menu_item config_channels_menu_items[] = {
	{ "Select Channels:"},
	{ "Channel 0", 'A', do_toggle_channel_selection, AD5592R_CHANNEL(0) },
	{ "Channel 1", 'S', do_toggle_channel_selection, AD5592R_CHANNEL(1) },
	{ "Channel 2", 'D', do_toggle_channel_selection, AD5592R_CHANNEL(2) },
	{ "Channel 3", 'F', do_toggle_channel_selection, AD5592R_CHANNEL(3) },
	{ "Channel 4", 'G', do_toggle_channel_selection, AD5592R_CHANNEL(4) },
	{ "Channel 5", 'H', do_toggle_channel_selection, AD5592R_CHANNEL(5) },
	{ "Channel 6", 'J', do_toggle_channel_selection, AD5592R_CHANNEL(6) },
	{ "Channel 7", 'K', do_toggle_channel_selection, AD5592R_CHANNEL(7) },
	{ "", '\00', NULL },
	{ "DAC",		'Q', do_mode_selection, CH_MODE_DAC },
	{ "ADC",		'W', do_mode_selection, CH_MODE_ADC },
	{ "ADC + DAC",	'E', do_mode_selection, CH_MODE_DAC_AND_ADC },
	{ "GPI",		'R', do_mode_selection, CH_MODE_GPI },
	{ "GPO",        'T', do_mode_selection, CH_MODE_GPO },
	{ "Unused",		'Y', do_mode_selection, CH_MODE_UNUSED },
	{ "Restore Default Modes",	'U', do_reset_channel_modes },
	{ "", '\00', NULL },
	{ "Pulldown",	'Z', do_offstate_selection, CH_OFFSTATE_PULLDOWN },
	{ "Output Low",	'X', do_offstate_selection, CH_OFFSTATE_OUT_LOW },
	{ "Output High",'C', do_offstate_selection, CH_OFFSTATE_OUT_HIGH },
	{ "Tristate",	'V', do_offstate_selection, CH_OFFSTATE_OUT_TRISTATE },
	{ "", '\00', NULL },
	{ "Channel 7 as ADC conversion indicator (AD5592R)", 'M', do_channel_7_adc_indicator },
};

console_menu config_channels_menu = {
	.title = "Configure IO Channels",
	.items = config_channels_menu_items,
	.itemCount = ARRAY_SIZE(config_channels_menu_items),
	.headerItem = display_channel_selection_header,
	.footerItem = NULL,
	.enableEscapeKey = true
};

/*
 * Definition of the Main Menu Items and menu itself
 */
console_menu_item main_menu_items[] = {
	{ "Software Reset", 'Q', do_software_reset },
	{ "Read ADC die temp", 'W', do_read_die_temp},
	{ "", '\00', NULL },
	{ "Configure Channels", 'A', menu_config_channels},
	{ "General Settings", 'S', menu_general_settings },
	{ "DAC Menu", 'D', menu_dac },
	{ "ADC Menu", 'F', menu_adc },
	{ "GPIO Menu", 'G', menu_gpio},
};

console_menu ad5592r_main_menu = {
	.title = "AD5592R/AD5593R Main Menu",
	.items = main_menu_items,
	.itemCount = ARRAY_SIZE(main_menu_items),
	.headerItem = display_main_menu_header,
	.footerItem = NULL,
	.enableEscapeKey = NULL
};