EVAL-AD7124 Mbed Example Program.
Dependencies: adi_console_menu platform_drivers
Diff: app/ad7124_console_app.c
- Revision:
- 3:779bb1e55f1a
- Child:
- 5:ca3854efb1e9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/ad7124_console_app.c Mon Mar 02 17:04:20 2020 +0000 @@ -0,0 +1,2067 @@ +/*! + ***************************************************************************** + @file: ad7124_console_app.c + + @brief: Implementation for the menu functions that handle the AD7124 + + @details: This file is specific to ad7124 console menu application handle. + The functions defined in this file performs the action + based on user selected console menu. + ----------------------------------------------------------------------------- + Copyright (c) 2019, 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. +*****************************************************************************/ + +/* includes */ +#include <stdio.h> +#include <string.h> +#include <stdbool.h> + +#include "platform_support.h" +#include "platform_drivers.h" +#include "spi_extra.h" + +#include "ad7124.h" +#include "ad7124_regs.h" +#include "ad7124_support.h" +#include "ad7124_regs_configs.h" + +#include "ad7124_console_app.h" +#include "app_config.h" + + +/* defines */ +#define AD7124_CHANNEL_COUNT 16 + +#define SHOW_ALL_CHANNELS false +#define SHOW_ENABLED_CHANNELS true + +#define DISPLAY_DATA_TABULAR 0 +#define DISPLAY_DATA_STREAM 1 + +#define AD7124_MAX_SETUPS 8 +#define AD7124_MAX_CHANNELS 16 +#define NUM_OF_FILTERS 5 +#define MAX_FILTER_DATA_RATE_FS 2047 +#define MIN_FILTER_DATA_RATE 1 +#define MAX_GAIN_BITS_VALUE 7 +#define MIN_PROGRAMMABLE_GAIN 1 +#define MAX_PROGRAMMABLE_GAIN 128 +#define MAX_ANALOG_INPUTS 32 + + +/* Private Variables */ +/* + * This is the 'live' AD7124 register map that is used by the driver + * the other 'default' configs are used to populate this at init time + */ +static struct ad7124_st_reg ad7124_register_map[AD7124_REG_NO]; + +// Pointer to the struct representing the AD7124 device +static struct ad7124_dev * pAd7124_dev = NULL; + +// GPIO descriptor and init parameters for the activity LED pin +static gpio_desc *activity_led_desc = NULL; +static gpio_init_param activity_led_init_param = { LED_GREEN, NULL }; + +// Last Sampled values for All ADC channels +static uint32_t channel_samples[AD7124_CHANNEL_COUNT] = {0}; +// How many times a given channel is sampled in total for one sample run +static uint32_t channel_samples_count[AD7124_CHANNEL_COUNT] = {0}; + +static ad7124_setup_config setup[AD7124_MAX_SETUPS]; // AD7124 setup + +static float filter_data_rate_raw; // filter data rate value +static uint32_t gain_raw; // Gain value +static uint8_t power_mode; // power mode of ADC + +typedef enum { + CHN_DISABLE, + CHN_ENABLE +} Chn_EnbleDisable_action; + +/* Filter names */ +static char *filter_name[] = { + "SINC4", + "Reserved", + "SINC3", + "Reserved", + "FS SINC4", + "FS SINC3", + "Reserved", + "POST SINC3" +}; + +static char *reference_name[] = { + "REFIN1", + "REFIN2", + "INTERNAL", + "AVDD" +}; + +static char *enable_disable_status[] = { + "DISABLED", + "ENABLED" +}; + +static char *polarity_status[] = { + "UNIPOLAR", + "BIPOLAR" +}; + +static char *power_modes_str[] = { + "Low Power", + "Medium Power", + "Full Power", + "Full Power" +}; + +/* Programmable gain values */ +static uint8_t p_gain[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + +/* Temperature sensor data structure */ +typedef struct { + int16_t temp; // Temperature + uint32_t adc_sample; // ADC conversion sample +} temp_loopup; + + +/* Function protoypes */ +static int32_t menu_continuous_conversion_tabular(void); +static int32_t menu_continuous_conversion_stream(void); +static int32_t menu_single_conversion(void); +static int32_t menu_read_status(void); +static int32_t menu_read_id(void); +static int32_t menu_reset(void); +static int32_t menu_reset_to_configuration(uint32_t config_type); +static int32_t menu_read_temperature(void); +static int32_t menu_select_power_mode(void); +static int32_t menu_power_modes_selection(uint32_t power_mode); +static int32_t menu_rw_ad7124_register(uint32_t rw_id); +static int32_t menu_sample_channels(void); +static int32_t menu_enable_disable_channels(void); +static int32_t menu_config_and_assign_setup(void); +static int32_t menu_connect_input_to_channel(void); +static int32_t menu_calibrate_adc(void); + +static bool was_escape_key_pressed(void); +static void toggle_activity_led(void); +static void read_status_register(void); +static void dislay_channel_samples(bool showOnlyEnabledChannels, + uint8_t console_mode); +static void clear_channel_samples(void); +static int32_t do_continuous_conversion(uint8_t display_mode); +static int16_t scan_temperature(uint32_t value); +static void init_with_configuration(uint8_t configID); +static uint8_t get_setup_selection(void); +static uint8_t get_channel_selection(void); +static void config_analog_inputs(ad7124_setup_config *psetup); +static float calculate_data_rate(filter_type filter, uint8_t power_mode, + float data_rate); +static void config_filter_parameters(ad7124_setup_config *psetup); +static void assign_setup_to_channel(uint8_t setup); + + +/* The temperature lookup table is formed using below formula: + * temp = ((adc_conv_reading - 8388608) / 13584) - 272.5; + * + * Note: Refer datasheet page 71 (TEMPERATURE SENSOR) for using internal + * temperature sensor of AD7124, which monitors the die temperature. + * This method of temperature sensing is not accurate and might have deviations + * of +/-1C due to non-floating calculations. In order to get more precise result, + * use floating point calculations (using above formula). + **/ + +/* Temperature Range: -20C to +50C */ +static temp_loopup temperature_lookup[] = { + { -20, 11818568 }, + { -19, 11832152 }, + { -18, 11845736 }, + { -17, 11859320 }, + { -16, 11872904 }, + { -15, 11886488 }, + { -14, 11900072 }, + { -13, 11913656 }, + { -12, 11927240 }, + { -11, 11940824 }, + { -10, 11954408 }, + { -9, 11967992 }, + { -8, 11981576 }, + { -7, 11995160 }, + { -6, 12008744 }, + { -5, 12022328 }, + { -4, 12035912 }, + { -3, 12049496 }, + { -2, 12063080 }, + { -1, 12076664 }, + { 0, 12090248 }, + { 1, 12103832 }, + { 2, 12117416 }, + { 3, 12131000 }, + { 4, 12144584 }, + { 5, 12158168 }, + { 6, 12171752 }, + { 7, 12185336 }, + { 8, 12198920 }, + { 9, 12212504 }, + { 10, 12226088 }, + { 11, 12239672 }, + { 12, 12253256 }, + { 13, 12266840 }, + { 14, 12280424 }, + { 15, 12294008 }, + { 16, 12307592 }, + { 17, 12321176 }, + { 18, 12334760 }, + { 19, 12348344 }, + { 20, 12361928 }, + { 21, 12375512 }, + { 22, 12389096 }, + { 23, 12402680 }, + { 24, 12416264 }, + { 25, 12429848 }, + { 26, 12443432 }, + { 27, 12457016 }, + { 28, 12470600 }, + { 29, 12484184 }, + { 30, 12497768 }, + { 31, 12511352 }, + { 32, 12524936 }, + { 33, 12538520 }, + { 34, 12552104 }, + { 35, 12565688 }, + { 36, 12579272 }, + { 37, 12592856 }, + { 38, 12606440 }, + { 39, 12620024 }, + { 40, 12633608 }, + { 41, 12647192 }, + { 42, 12660776 }, + { 43, 12674360 }, + { 44, 12687944 }, + { 45, 12701528 }, + { 46, 12715112 }, + { 47, 12728696 }, + { 48, 12742280 }, + { 49, 12755864 }, + { 50, 12769448 } +}; + + +// Public Functions + +/*! + * @brief Initialize the AD7124 device and the SPI port as required + * + * @details This resets and then writes the default register map value to + * the device. A call to init the SPI port is made, but may not + * actually do very much, depending on the platform + */ +int32_t ad7124_app_initialize(uint8_t configID) +{ + // Init SPI extra parameters structure + mbed_spi_init_param spi_init_extra_params = { + .spi_clk_pin = SPI_SCK, + .spi_miso_pin = SPI_MISO, + .spi_mosi_pin = SPI_MOSI + }; + + // Create a new descriptor for activity led + if (gpio_get(&activity_led_desc, &activity_led_init_param) == FAILURE) { + return FAILURE; + } + + // Set the direction of activity LED + if ((gpio_direction_output(activity_led_desc, GPIO_HIGH)) == FAILURE) { + return FAILURE; + } + + /* + * Copy one of the default/user configs to the live register memory map + * Requirement, not checked here, is that all the configs are the same size + */ + switch (configID) { + case AD7124_CONFIG_A: { + memcpy(ad7124_register_map, ad7124_regs_config_a, sizeof(ad7124_register_map)); + break; + } + case AD7124_CONFIG_B: { + memcpy(ad7124_register_map, ad7124_regs_config_b, sizeof(ad7124_register_map)); + break; + } + default: + // Not a defined configID + return(FAILURE); + } + + // Used to create the ad7124 device + struct ad7124_init_param sAd7124_init = { + // spi_init_param type + { + 2500000, // Max SPI Speed + SPI_SS_A, // Chip Select pin + SPI_MODE_3, // CPOL = 1, CPHA =1 + &spi_init_extra_params, // SPI extra configurations + }, + ad7124_register_map, + + 10000 // Retry count for polling + }; + + return (ad7124_setup(&pAd7124_dev, sAd7124_init)); +} + +// Private Functions + +/*! + * @brief determines if the Escape key was pressed + * + * @return bool- key press status + */ +static bool was_escape_key_pressed(void) +{ + char rxChar; + bool wasPressed = false; + + // Check for Escape key pressed + if ((rxChar = getchar_noblock()) > 0) { + if (rxChar == ESCAPE_KEY_CODE) { + wasPressed = true; + } + } + return (wasPressed); +} + + +/** + * @brief toggles an LED to show something has happened + * @param None + * @return None + */ +static void toggle_activity_led(void) +{ + static int led_state = GPIO_HIGH; + + /* Toggle the LED state */ + if (led_state == GPIO_LOW) { + led_state = GPIO_HIGH; + } else { + led_state = GPIO_LOW; + } + + gpio_set_value(activity_led_desc, led_state); +} + + +/*! + * @brief reads and displays the status register on the AD7124 + * + * @details + */ +static void read_status_register(void) +{ + if (ad7124_read_register(pAd7124_dev, + &ad7124_register_map[AD7124_Status]) < 0) { + printf("\r\nError Encountered reading Status register\r\n"); + } else { + uint32_t status_value = (uint32_t)ad7124_register_map[AD7124_Status].value; + printf("\r\nRead Status Register = 0x%02lx\r\n", status_value); + } +} + + +/*! + * @brief displays the current sample value for a ADC channels + * + * @details + * + * @param showOnlyEnabledChannels only channels that are enabled are displayed + * + */ +static void dislay_channel_samples(bool showOnlyEnabledChannels, + uint8_t console_mode) +{ + switch (console_mode) { + case DISPLAY_DATA_TABULAR: { + printf("\tCh\tValue\t\tCount\t\tVoltage\r\n"); + for (uint8_t i = 0; i < AD7124_CHANNEL_COUNT; i++) { + // if showing all channels, or channel is enabled + if ((showOnlyEnabledChannels == false) + || (ad7124_register_map[AD7124_Channel_0 + i].value & + AD7124_CH_MAP_REG_CH_ENABLE)) { + printf("\t%-2d\t%-10ld\t%ld\t\t% .6f\r\n", \ + i, + channel_samples[i], + channel_samples_count[i], + ad7124_convert_sample_to_voltage(pAd7124_dev, i, channel_samples[i])); + } + } + break; + } + case DISPLAY_DATA_STREAM: { + // Output a CSV list of the sampled channels as voltages on a single line + bool channel_printed = false; + + for (uint8_t i = 0; i < AD7124_CHANNEL_COUNT; i++) { + // if showing all channels, or channel is enabled + if ((showOnlyEnabledChannels == false) + || (ad7124_register_map[AD7124_Channel_0 + i].value & + AD7124_CH_MAP_REG_CH_ENABLE)) { + /* + * add the comma before we output the next channel but + * only if at least one channel has been printed + */ + if (channel_printed) { + printf(", "); + } + printf("%.6f", \ + ad7124_convert_sample_to_voltage(pAd7124_dev, i, channel_samples[i])); + channel_printed = true; + } + } + printf("\r\n"); + break; + } + default: { + // ASSERT(false); + } + } +} + + +/*! + * @brief resets the channelSampleCounts to zero + * + * @details + */ +static void clear_channel_samples(void) +{ + for (uint8_t i = 0; i < AD7124_CHANNEL_COUNT; i++) { + channel_samples[i] = 0; + channel_samples_count[i] = 0; + } +} + + +/*! + * @brief Continuously acquires samples in Continuous Conversion mode + * + * @details The ADC is run in continuous mode, and all samples are acquired + * and assigned to the channel they come from. Escape key an be used + * to exit the loop + */ +static int32_t do_continuous_conversion(uint8_t display_mode) +{ + int32_t error_code; + int32_t sample_data; + + // Clear the ADC CTRL MODE bits, has the effect of selecting continuous mode + ad7124_register_map[AD7124_ADC_Control].value &= ~(AD7124_ADC_CTRL_REG_MODE( + 0xf)); + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_ADC_Control])) < 0) { + printf("Error (%ld) setting AD7124 Continuous conversion mode.\r\n", + error_code); + adi_press_any_key_to_continue(); + return (MENU_CONTINUE); + } + + clear_channel_samples(); + + /* + * If displaying data in stream form, want to output a channel header + */ + if (display_mode == DISPLAY_DATA_STREAM) { + bool channel_printed = false; + + for (uint8_t i = 0; i < AD7124_CHANNEL_COUNT; i++) { + // if showing all channels, or channel is enabled + if(ad7124_register_map[AD7124_Channel_0 + i].value & + AD7124_CH_MAP_REG_CH_ENABLE) { + /* + * add the comma before we output the next channel but + * only if at least one channel has been printed + */ + if (channel_printed) { + printf(", "); + } + printf("%d", i); + } + channel_printed = true; + } + printf("\r\n"); + } + + // Continuously read the channels, and store sample values + while(was_escape_key_pressed() != true) { + toggle_activity_led(); + + if (display_mode == DISPLAY_DATA_TABULAR) { + adi_clear_console(); + printf("Running continuous conversion mode...\r\nPress Escape to stop\r\n\r\n"); + } + + /* + * this polls the status register READY/ bit to determine when conversion is done + * this also ensures the STATUS register value is up to date and contains the + * channel that was sampled as well. + * Generally, no need to read STATUS separately, but for faster sampling + * enabling the DATA_STATUS bit means that status is appended to ADC data read + * so the channel being sampled is read back (and updated) as part of the same frame + */ + + if ((error_code = ad7124_wait_for_conv_ready(pAd7124_dev, 10000)) < 0) { + printf("Error/Timeout waiting for conversion ready %ld\r\n", error_code); + continue; + } + + if ((error_code = ad7124_read_data(pAd7124_dev, &sample_data)) < 0) { + printf("Error reading ADC Data (%ld).\r\n", error_code); + continue; + } + + /* + * No error, need to process the sample, what channel has been read? update that channelSample + */ + uint8_t channel_read = ad7124_register_map[AD7124_Status].value & 0x0000000F; + + if (channel_read < AD7124_CHANNEL_COUNT) { + channel_samples[channel_read] = sample_data; + channel_samples_count[channel_read]++; + } else { + printf("Channel Read was %d, which is not < AD7124_CHANNEL_COUNT\r\n", + channel_read); + } + + dislay_channel_samples(SHOW_ENABLED_CHANNELS, display_mode); + } + + // All done, ADC put into standby mode + ad7124_register_map[AD7124_ADC_Control].value &= ~(AD7124_ADC_CTRL_REG_MODE( + 0xf)); + // 2 = sleep/standby mode + ad7124_register_map[AD7124_ADC_Control].value |= AD7124_ADC_CTRL_REG_MODE(2); + + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_ADC_Control])) < 0) { + printf("Error (%ld) setting AD7124 ADC into standby mode.\r\n", error_code); + adi_press_any_key_to_continue(); + } + + return (MENU_CONTINUE); +} + + +/*! + * @brief Samples all enabled channels and displays in tabular form + * + * @details + */ +static int32_t menu_continuous_conversion_tabular(void) +{ + do_continuous_conversion(DISPLAY_DATA_TABULAR); + + adi_clear_console(); + printf("Continuous Conversion completed...\r\n\r\n"); + dislay_channel_samples(SHOW_ALL_CHANNELS, DISPLAY_DATA_TABULAR); + adi_press_any_key_to_continue(); + + return (MENU_CONTINUE); +} + + +/*! + * @brief Samples all enabled channels and displays on the console + * + * @details + */ +static int32_t menu_continuous_conversion_stream(void) +{ + do_continuous_conversion(DISPLAY_DATA_STREAM); + printf("Continuous Conversion completed...\r\n\r\n"); + adi_press_any_key_to_continue(); + return (MENU_CONTINUE); +} + + +/*! + * @brief Samples all enabled channels once in Single Conversion mode + * + * @details This stores all channels that are enabled in a bitmask, and then + * runs the ADC in single conversion mode, which acquires one channel + * of data at a time. After capture, that channel is disabled, and + * single conversion run again, until no channels are enabled. + * The original enable state of each channel is then restored. + */ +static int32_t menu_single_conversion(void) +{ + int32_t error_code; + uint16_t channel_enable_mask = 0; + uint8_t channel_count = 0; + int32_t sample_data; + + // Need to store which channels are enabled in this config so it can be restored + for (uint8_t i = 0 ; i < AD7124_CHANNEL_COUNT ; i++) { + if (ad7124_register_map[AD7124_Channel_0 + i].value & + AD7124_CH_MAP_REG_CH_ENABLE) { + channel_enable_mask |= (1 << i); + channel_count++; + } + } + + clear_channel_samples(); + adi_clear_console(); + printf("Running Single conversion mode...\r\nPress Escape to stop\r\n\r\n"); + + // Clear the ADC CTRL MODE bits, selecting continuous mode + ad7124_register_map[AD7124_ADC_Control].value &= ~(AD7124_ADC_CTRL_REG_MODE( + 0xf)); + + // read the channels, and store sample values + for (uint8_t loopCount = 0 ; ((was_escape_key_pressed() != true) + && (loopCount < channel_count)) ; loopCount++) { + toggle_activity_led(); + + // 1 = single conversion mode + ad7124_register_map[AD7124_ADC_Control].value |= AD7124_ADC_CTRL_REG_MODE(1); + + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_ADC_Control])) < 0) { + printf("Error (%ld) setting AD7124 Single conversion mode.\r\n", error_code); + adi_press_any_key_to_continue(); + continue; + } + + /* + * this polls the status register READY/ bit to determine when conversion is done + * this also ensures the STATUS register value is up to date and contains the + * channel that was sampled as well. No need to read STATUS separately + */ + if ((error_code = ad7124_wait_for_conv_ready(pAd7124_dev, 10000)) < 0) { + printf("Error/Timeout waiting for conversion ready %ld\r\n", error_code); + continue; + } + + if ((error_code = ad7124_read_data(pAd7124_dev, &sample_data)) < 0) { + printf("Error reading ADC Data (%ld).\r\n", error_code); + continue; + } + /* + * No error, need to process the sample, what channel has been read? update that channelSample + */ + uint8_t channelRead = ad7124_register_map[AD7124_Status].value & 0x0000000F; + + if (channelRead < AD7124_CHANNEL_COUNT) { + channel_samples[channelRead] = sample_data; + channel_samples_count[channelRead]++; + + /* also need to clear the channel enable bit so the next single conversion cycle will sample the next channel */ + ad7124_register_map[AD7124_Channel_0 + channelRead].value &= + ~AD7124_CH_MAP_REG_CH_ENABLE; + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + channelRead])) < 0) { + printf("Error (%ld) Clearing channel %d Enable bit.\r\n", error_code, + channelRead); + adi_press_any_key_to_continue(); + continue; + } + } else { + printf("Channel Read was %d, which is not < AD7124_CHANNEL_COUNT\r\n", + channelRead); + } + } + + // All done, ADC put into standby mode + ad7124_register_map[AD7124_ADC_Control].value &= ~(AD7124_ADC_CTRL_REG_MODE( + 0xf)); + // 2 = sleep/standby mode + ad7124_register_map[AD7124_ADC_Control].value |= AD7124_ADC_CTRL_REG_MODE(2); + + // Need to restore the channels that were disabled during acquisition + for (uint8_t i = 0 ; i < AD7124_CHANNEL_COUNT ; i++) { + if (channel_enable_mask & (1 << i)) { + ad7124_register_map[AD7124_Channel_0 + i].value |= AD7124_CH_MAP_REG_CH_ENABLE; + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + i])) < 0) { + printf("Error (%ld) Setting channel %d Enable bit.\r\r\n", error_code, i); + adi_press_any_key_to_continue(); + return (MENU_CONTINUE); + } + } + } + + printf("Single Conversion completed...\r\n\r\n"); + dislay_channel_samples(SHOW_ENABLED_CHANNELS, DISPLAY_DATA_TABULAR); + + adi_press_any_key_to_continue(); + return (MENU_CONTINUE); +} + + +/*! + * @brief menu item that reads the status register the AD7124 + * + * @details + */ +static int32_t menu_read_status(void) +{ + read_status_register(); + adi_press_any_key_to_continue(); + return (MENU_CONTINUE); +} + + +/*! + * @brief reads the ID register on the AD7124 + * + * @details + */ +static int32_t menu_read_id(void) +{ + if (ad7124_read_register(pAd7124_dev, &ad7124_register_map[AD7124_ID]) < 0) { + printf("\r\nError Encountered reading ID register\r\n"); + } else { + printf("\r\nRead ID Register = 0x%02lx\r\n", + (uint32_t)ad7124_register_map[AD7124_ID].value); + } + adi_press_any_key_to_continue(); + return (MENU_CONTINUE); +} + + +/*! + * @brief Initialize the part with a specific configuration + * + * @details + */ +static void init_with_configuration(uint8_t configID) +{ + int32_t status = 0; + + // Free the device resources + (void)gpio_remove(activity_led_desc); + (void)ad7124_remove(pAd7124_dev); + + status = ad7124_app_initialize(configID); + if (status < 0) { + printf("\r\n\r\n Error setting Configuration %c \r\n\r\n", + (char)(configID + 'A')); + } else { + printf("\r\n\r\n Configuration %c Set\r\n\r\n", (char)(configID + 'A')); + } + adi_press_any_key_to_continue(); +} + + +/* + * @brief Sends a reset command on the SPI to reset the AD7124 + * + * @details + */ +static int32_t menu_reset(void) +{ + if (ad7124_reset(pAd7124_dev) < 0) { + printf("\r\n\r\n Error performing Reset\r\n\r\n"); + } else { + // Need to set the live register map to defaults as well + memcpy(ad7124_register_map, ad7124_regs, sizeof(ad7124_register_map)); + printf("\r\n\r\n Reset Complete\r\n\r\n"); + } + adi_press_any_key_to_continue(); + return (MENU_CONTINUE); +} + + +/*! + * @brief Reset and set the ad7124 with configuration A or B + * + * @details + */ +static int32_t menu_reset_to_configuration(uint32_t config_type) +{ + if (AD7124_CONFIG_A == config_type) { + init_with_configuration(AD7124_CONFIG_A); + } else { + init_with_configuration(AD7124_CONFIG_B); + } + + return (MENU_CONTINUE); +} + + +/* @brief Scan the temparture value from the lookup table using binry search + * + * @param ADC conversion sample + * @return Temperature + **/ +static int16_t scan_temperature(uint32_t value) +{ + uint16_t key=0, start, end; + bool found = false; + + start = 0; + end = sizeof(temperature_lookup) / sizeof(temperature_lookup[0]) - 1; + + while ((start < end) && !found) { + key = (start + end) >> 1; + + if (temperature_lookup[key].adc_sample == value) { + found = true; + } else if (value > temperature_lookup[key].adc_sample) { + start = key + 1; + } else if (value < temperature_lookup[key].adc_sample) { + end = key - 1; + } else { + break; + } + } + + /* Return the scanned temperature value */ + return temperature_lookup[key].temp; +} + + +/* @brief Console menu to read and display device temperature + * + * @return int32_t- menu status constant + **/ +static int32_t menu_read_temperature(void) +{ + int32_t error_code; // adc read/write error + uint8_t samples; // sample count + int32_t temp_readings; // temperature equivalent adc count + int16_t temperature = 0; // temperature read + uint8_t chn_cnt; // channel counter + uint16_t chn_mask = 0; // channels enable mask + + /* Save the previous values of below registers in order to not disturb setup + * configured by the user (Note: Channel 0 is used for the temperature sensing) */ + int32_t prev_adc_reg_values[] = { + ad7124_register_map[AD7124_Channel_0].value, // Channel_0 Register previous value + ad7124_register_map[AD7124_Config_0].value, // Config_0 Register previous value + ad7124_register_map[AD7124_ADC_Control].value // Control Register previous value + }; + + /* Disable the other enabled channels, to read temperature from only 0th channel */ + for (chn_cnt = 1; chn_cnt < AD7124_MAX_CHANNELS; chn_cnt++) { + if ((error_code = ad7124_read_register(pAd7124_dev, + &ad7124_register_map[AD7124_Channel_0 + chn_cnt]) < 0)) { + printf("\r\n\tError reading temperature!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + + if (ad7124_register_map[AD7124_Channel_0 + chn_cnt].value & + AD7124_CH_MAP_REG_CH_ENABLE) { + // Save enabled channel + chn_mask |= (1 << chn_cnt); + + /* Disable the curent channel */ + ad7124_register_map[AD7124_Channel_0 + chn_cnt].value &= + (~AD7124_CH_MAP_REG_CH_ENABLE); + + /* Write to ADC channel register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + chn_cnt]) < 0)) { + printf("\r\n\tError reading temperature!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + } + } + + /* Channel 0 Selections: AINP= Temp (16), AINM= AVSS (17), Setup= 0, CHN Enabled= True */ + ad7124_register_map[AD7124_Channel_0].value = (AD7124_CH_MAP_REG_AINP( + 16) | AD7124_CH_MAP_REG_AINM(17) | + AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_CH_ENABLE); + + /* Write to ADC channel 0 register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0]) < 0)) { + printf("\r\n\tError reading temperature!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + + /* Setup 0 selections: Bipolar= 1, AINP/M Buffer= Enabled, Ref= EXT1(2.5v), Gain= 1 */ + ad7124_register_map[AD7124_Config_0].value = (AD7124_CFG_REG_BIPOLAR | + AD7124_CFG_REG_AIN_BUFP | + AD7124_CFG_REG_AINN_BUFM | AD7124_CFG_REG_REF_SEL(0) | + AD7124_CFG_REG_PGA(0)); + + /* Write to ADC config 0 register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Config_0]) < 0)) { + printf("\r\n\tError reading temperature!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + + /* ADC operating mode: Single Conversion (masking off bits 5:2) */ + ad7124_register_map[AD7124_ADC_Control].value = (( + ad7124_register_map[AD7124_ADC_Control].value & + ~AD7124_ADC_CTRL_REG_MSK) | + AD7124_ADC_CTRL_REG_MODE(1)); + + /* Write to ADC control register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_ADC_Control]) < 0)) { + printf("\r\n\tError reading temperature!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + + printf("\r\n\r\n\tReading temperature...\r\n"); + + for (samples = 0; samples < 2; samples++) { + /* Wait for conversion to complete, then obtain sample */ + ad7124_wait_for_conv_ready(pAd7124_dev, pAd7124_dev->spi_rdy_poll_cnt); + + if ((error_code = ad7124_read_data(pAd7124_dev, &temp_readings) < 0)) { + printf("\r\n\tError reading temperature!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + + //temp += ((temp_readings - 8388608) / 13584) - 272.5; // Use this for more precision + temperature += scan_temperature(temp_readings); + } + + /* Get the averaged temperature value */ + temperature >>= 1; + + /* Validate temperaure range as specified in look-up table */ + if (temperature >= -20 || temperature <= 50) { + printf("\r\n\tTemperature: %d Celcius\r\n", temperature); + } else { + printf("\r\n\tError reading temperature!!\r\n"); + } + + /* Restore the ADC registers with previous values (i.e before modifying them for temperature sensing) + * Note: This needs to be done to not disturb the setup configured by user through console menus */ + ad7124_register_map[AD7124_Channel_0].value = prev_adc_reg_values[0]; + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_Channel_0]); + + ad7124_register_map[AD7124_Config_0].value = prev_adc_reg_values[1]; + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_Config_0]); + + ad7124_register_map[AD7124_ADC_Control].value = prev_adc_reg_values[2]; + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_ADC_Control]); + + /* Enable the previously disabled channels */ + for (chn_cnt = 1; chn_cnt < AD7124_MAX_CHANNELS; chn_cnt++) { + if ((chn_mask >> chn_cnt) & 0x01) { + ad7124_register_map[AD7124_Channel_0 + chn_cnt].value |= + AD7124_CH_MAP_REG_CH_ENABLE; + + /* Write to ADC channel regiter */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + chn_cnt]) < 0)) { + printf("\r\n\tError reading temperature!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + } + } + + adi_press_any_key_to_continue(); + adi_clear_console(); + + return (MENU_CONTINUE); +} + + +/* @brief Console menu to select the power modes of adc + * + * @return int32_t- menu status constant + **/ +static int32_t menu_power_modes_selection(uint32_t mode) +{ + int32_t error_code; + + ad7124_register_map[AD7124_ADC_Control].value = + ((ad7124_register_map[AD7124_ADC_Control].value & + ~AD7124_ADC_CTRL_REG_POWER_MODE_MSK) | + AD7124_ADC_CTRL_REG_POWER_MODE(mode)); + + /* Write to ADC channel regiter */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_ADC_Control])) < 0) { + printf("\r\n\tError setting %s mode!!\r\n", power_modes_str[mode]); + } else { + power_mode = mode; + printf("\r\n\t%s mode selected...\r\n", power_modes_str[mode]); + } + + adi_press_any_key_to_continue(); + adi_clear_console(); + + return MENU_CONTINUE; +} + + +/* @brief Console menu to read the adc register + * + * @return int32_t- menu status constant + **/ +static int32_t menu_rw_ad7124_register(uint32_t rw_id) +{ + uint32_t reg_address; + uint32_t reg_data; + int32_t error_code; + + printf("\r\n\tEnter the register address (in hex): "); + reg_address = adi_get_hex_integer(sizeof(reg_address)); + + if ((uint32_t)DEVICE_REG_READ_ID == rw_id) { + /* Read from ADC channel register */ + if ((reg_address >= AD7124_REG_NO) || + ((error_code = ad7124_read_register(pAd7124_dev, + &ad7124_register_map[reg_address])) < 0)) { + printf("\r\n\tError in reading adc register!!\r\n"); + } else { + reg_data = ad7124_register_map[reg_address].value; + printf("\r\n\tRead Value: 0x%x", reg_data); + } + } else { + printf("\r\n\tEnter the register data (in hex): "); + reg_data = adi_get_hex_integer(sizeof(reg_data)); + + ad7124_register_map[reg_address].value = reg_data; + + /* Write to ADC channel register */ + if ((reg_address >= AD7124_REG_NO) || + ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[reg_address])) < 0)) { + printf("\r\n\tError in writing adc register!!\r\n"); + } else { + printf("\r\n\tWrite Successful...\r\n"); + } + } + + adi_press_any_key_to_continue(); + adi_clear_console(); + + return MENU_CONTINUE; +} + + +/* @brief Enable or disable adc channels + * + * @param uint32_t action- channel ENABLE/DISABLE action + **/ +static void menu_channels_enable_disable(uint32_t action) +{ + char rx_char; // received character from the serial port + int32_t error_code; // data read/write error code + uint8_t current_channel; // channel to be enabled + + do { + /* Get the channel selection */ + current_channel = get_channel_selection(); + + if (CHN_ENABLE == action) { + /* Enable the selected channel */ + ad7124_register_map[AD7124_Channel_0 + current_channel].value |= + AD7124_CH_MAP_REG_CH_ENABLE; + printf("\tChannel %d is Enabled ", current_channel); + } else { + /* Disable the selected channel */ + ad7124_register_map[AD7124_Channel_0 + current_channel].value &= + (~AD7124_CH_MAP_REG_CH_ENABLE); + printf("\tChannel %d is Disabled ", current_channel); + } + + /* Write to ADC channel register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + current_channel]) < 0)) { + printf("\tError in channel Enable/Disable!!\r\n"); + break; + } + + printf("\r\n\r\n\tDo you want to continue (y/n)?: "); + rx_char = toupper(getchar()); + + if (rx_char != 'N' && rx_char != 'Y') { + printf("Invalid entry!!\r\n"); + } else { + /* Print the entered character back on console window (serial port) */ + printf("%c\r\n", rx_char); + } + + } while (rx_char != 'N'); +} + + +/* @brief Assign setup to adc channel + * + * @param uint8_t setup- setup to be assigned + **/ +static void assign_setup_to_channel(uint8_t setup) +{ + uint8_t current_channel; // channel to be assigned with setup + int32_t error_code; // data read/write error code + + adi_clear_console(); + + /* Get the channel selection */ + current_channel = get_channel_selection(); + + /* Load the setup value */ + ad7124_register_map[AD7124_Channel_0 + current_channel].value = + ((ad7124_register_map[AD7124_Channel_0 + current_channel].value & + ~AD7124_CH_MAP_REG_SETUP_MSK) | + AD7124_CH_MAP_REG_SETUP(setup)); + + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + current_channel])) < 0) { + printf("\r\n\tError in setup assignment!!\r\n"); + } else { + printf("\r\n\tSetup %d is assigned to channel %d successfully...\r\n", setup, + current_channel); + } + + adi_press_any_key_to_continue(); +} + + +/* @brief Select adc channel to be assigned to setup + * + * @param uint8_t current_setup- setup + **/ +static void select_chn_assignment(uint8_t current_setup) +{ + bool current_selection_done = false; + char rx_char; + + do { + printf("\r\n\r\n\tDo you want to assign setup to a channel (y/n)?: "); + rx_char = toupper(getchar()); + + if (rx_char == 'Y') { + assign_setup_to_channel(current_setup); + current_selection_done = true; + } else if (rx_char == 'N') { + current_selection_done = true; + } else { + printf("\r\n\tInvalid entry!!"); + } + } while (current_selection_done == false); +} + + +/* @brief Configure the setup and check if want to assign to channel + * + * @return int32_t- menu status constant + **/ +static int32_t menu_config_and_assign_setup(void) +{ + uint8_t current_setup; // current setup to be configured + int32_t error_code; // data read/write error + + adi_clear_console(); + + /* Get the current setup selection */ + current_setup = get_setup_selection(); + + /* Select the filter parameters */ + config_filter_parameters(&setup[current_setup]); + + /* Select the analog input parameters */ + config_analog_inputs(&setup[current_setup]); + + /* Select device gain */ + ad7124_register_map[AD7124_Config_0 + current_setup].value = + ((ad7124_register_map[AD7124_Config_0 + current_setup].value & + ~AD7124_CFG_REG_PGA_MSK) | + AD7124_CFG_REG_PGA(setup[current_setup].programmable_gain_bits)); + + /* Select the polarity (bit 11) */ + if (setup[current_setup].polarity) { + // Bipolar (1) + ad7124_register_map[AD7124_Config_0 + current_setup].value |= + AD7124_CFG_REG_BIPOLAR; + } else { + // Unipolar (0) + ad7124_register_map[AD7124_Config_0 + current_setup].value &= + (~AD7124_CFG_REG_BIPOLAR); + } + + /* Enable/Disable analog inputs AINP & AINM buffers */ + if (setup[current_setup].input_buffers) { + // Buffers enabled (1) + ad7124_register_map[AD7124_Config_0 + current_setup].value |= + (AD7124_CFG_REG_AIN_BUFP | + AD7124_CFG_REG_AINN_BUFM); + } else { + // Buffers disabled (0) + ad7124_register_map[AD7124_Config_0 + current_setup].value &= + (~AD7124_CFG_REG_AIN_BUFP & + ~AD7124_CFG_REG_AINN_BUFM); + } + + /* Enable/Disable reference buffer */ + if (setup[current_setup].reference_buffers) { + // Buffers enabled (1) + ad7124_register_map[AD7124_Config_0 + current_setup].value |= + (AD7124_CFG_REG_REF_BUFP | + AD7124_CFG_REG_REF_BUFM); + } else { + // Buffers disabled (0) + ad7124_register_map[AD7124_Config_0 + current_setup].value &= + (~AD7124_CFG_REG_REF_BUFP & + ~AD7124_CFG_REG_REF_BUFM); + } + + /* Select the reference source */ + ad7124_register_map[AD7124_Config_0 + current_setup].value = + ((ad7124_register_map[AD7124_Config_0 + current_setup].value & + ~AD7124_CFG_REG_REF_SEL_MSK) | + AD7124_CFG_REG_REF_SEL(setup[current_setup].reference)); + + + /* Write to ADC config register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Config_0 + current_setup])) < 0) { + printf("\r\n\tError in configuring device setup!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + + /* Select filter type */ + ad7124_register_map[AD7124_Filter_0 + current_setup].value = + ((ad7124_register_map[AD7124_Filter_0 + current_setup].value & + ~AD7124_FILT_REG_FILTER_MSK) | + AD7124_FILT_REG_FILTER(setup[current_setup].filter)); + + /* Set the data rate FS value */ + ad7124_register_map[AD7124_Filter_0 + current_setup].value = + ((ad7124_register_map[AD7124_Filter_0 + current_setup].value & + ~AD7124_FILT_REG_FS_MSK) | + AD7124_FILT_REG_FS(setup[current_setup].data_rate_fs_val)); + + /* Write to ADC filter register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Filter_0 + current_setup])) < 0) { + printf("\r\n\tError in configuring device setup!!\r\n"); + adi_press_any_key_to_continue(); + return MENU_CONTINUE; + } + + /* Print selections */ + printf("\r\n\r\n\tSetup %d is configured successfully =>\r\n", current_setup); + printf("\r\n\tFilter Type: %s", filter_name[setup[current_setup].filter]); + printf("\r\n\tData Rate: %f", filter_data_rate_raw); + printf("\r\n\tGain: %u", gain_raw); + printf("\r\n\tReference: %s", reference_name[setup[current_setup].reference]); + printf("\r\n"); + + select_chn_assignment(current_setup); + + return MENU_CONTINUE; +} + + +/* @brief Connect analog inputs (AINP & AINM) to a channel + * + * @return int32_t- menu status constant + **/ +static int32_t menu_connect_input_to_channel(void) +{ + uint32_t pos_analog_input; // positive analog input + uint32_t neg_analog_input; // negative analog input + uint32_t analog_input; // user entered analog input + uint8_t current_channel; // current channel + bool current_selection_done = + false; // flag checking if current menu selection is done + int32_t error_code; // data read/write error + + adi_clear_console(); + + /* Get the channel selection */ + current_channel = get_channel_selection(); + + /* Select analog inputs (positive and negative) */ + for (uint8_t index = 0; index < 2; index++) { + current_selection_done = false; + + do { + if (index == 0) { + printf("\r\n\tEnter positive analog input- AINP <0-31>: "); + } else { + printf("\r\n\tEnter negative analog input- AINM <0-31>: "); + } + + analog_input = adi_get_decimal_int(sizeof(analog_input)); + + /* Validate channel selection */ + if (analog_input < MAX_ANALOG_INPUTS) { + /* Break the loop by setting below flag */ + current_selection_done = true; + + if (index == 0) { + pos_analog_input = analog_input; + } else { + neg_analog_input = analog_input; + } + } else { + printf("\r\n\tInvalid analog input!!\r\n"); + } + + } while (current_selection_done == false); + } + + /* Select positive analog input */ + ad7124_register_map[AD7124_Channel_0 + current_channel].value = + ((ad7124_register_map[AD7124_Channel_0 + current_channel].value & + ~AD7124_CH_MAP_REG_AINP_MSK) | + AD7124_CH_MAP_REG_AINP(pos_analog_input)); + + /* Select negative analog input */ + ad7124_register_map[AD7124_Channel_0 + current_channel].value = + ((ad7124_register_map[AD7124_Channel_0 + current_channel].value & + ~AD7124_CH_MAP_REG_AINM_MSK) | + AD7124_CH_MAP_REG_AINM(neg_analog_input)); + + /* Write to ADC channel register */ + if ((error_code = ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + current_channel])) < 0) { + printf("\r\n\tError in analog input connection!!\r\n"); + } else { + printf("\r\n\tAIN%d is connected to AINP and AIN%d is connectd to AINM for channel %d\r\n\r\n", + pos_analog_input, + neg_analog_input, + current_channel); + } + + adi_press_any_key_to_continue(); + + return MENU_CONTINUE; +} + + +/* @brief display and handle console menu for calibrating adc + * + * @return int32_t- menu status constant + **/ +static int32_t menu_calibrate_adc(void) +{ + int32_t adc_control_reg_val; + int32_t cal_error; + int32_t error_code; + uint8_t chn_cnt; + uint8_t channel_enable_mask = 0; + + adi_clear_console(); + + // Save the ADC control register + ad7124_read_register(pAd7124_dev, &ad7124_register_map[AD7124_ADC_Control]); + adc_control_reg_val = ad7124_register_map[AD7124_ADC_Control].value; + + // Enable calibration error monitoring + ad7124_register_map[AD7124_Error_En].value |= AD7124_ERREN_REG_ADC_CAL_ERR_EN; + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_Error_En]); + + // Need to store which channels are enabled in this config so it can be restored + for(chn_cnt = 0 ; chn_cnt < AD7124_MAX_CHANNELS ; chn_cnt++) { + ad7124_read_register(pAd7124_dev, + &ad7124_register_map[AD7124_Channel_0 + chn_cnt]); + if (ad7124_register_map[AD7124_Channel_0 + chn_cnt].value & + AD7124_CH_MAP_REG_CH_ENABLE) { + channel_enable_mask |= (1 << chn_cnt); + + /* Disable the curent channel */ + ad7124_register_map[AD7124_Channel_0 + chn_cnt].value &= + (~AD7124_CH_MAP_REG_CH_ENABLE); + ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + chn_cnt]); + } + } + + // Calibrate all the channels + for (chn_cnt = 0 ; chn_cnt < AD7124_MAX_CHANNELS ; chn_cnt++) { + printf("\r\n\tCalibrating Channel %d => \r\n", chn_cnt); + + // Enable current channel + ad7124_register_map[AD7124_Channel_0 + chn_cnt].value |= + AD7124_CH_MAP_REG_CH_ENABLE; + ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + chn_cnt]); + + // Write 0x800000 to offset register before full-scale calibration + ad7124_register_map[AD7124_Offset_0 + chn_cnt].value = 0x800000; + ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Offset_0 + chn_cnt]); + + // Start full scale internal calibration (mode: 6) + printf("\tRunning full-scale internal calibration...\r\n"); + ad7124_register_map[AD7124_ADC_Control].value = + ((ad7124_register_map[AD7124_ADC_Control].value & ~AD7124_ADC_CTRL_REG_MSK) | \ + AD7124_ADC_CTRL_REG_MODE(6)); + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_ADC_Control]); + + /* Wait for calibration to over */ + if ((error_code = ad7124_wait_for_conv_ready(pAd7124_dev, + pAd7124_dev->spi_rdy_poll_cnt)) < 0) { + printf("\tError in calibration...\r\n"); + } else { + // Start zero scale internal calibration (mode: 5) + printf("\tRunning zero-scale internal calibration...\r\n"); + ad7124_register_map[AD7124_ADC_Control].value = + ((ad7124_register_map[AD7124_ADC_Control].value & ~AD7124_ADC_CTRL_REG_MSK) | \ + AD7124_ADC_CTRL_REG_MODE(5)); + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_ADC_Control]); + + // Wait for calibration to over + if((error_code = ad7124_wait_for_conv_ready(pAd7124_dev, + pAd7124_dev->spi_rdy_poll_cnt)) < 0) { + printf("\tError in calibration...\r\n"); + } else { + // Check for any calibration error (bit 18 of AD7124_ERROR register) + ad7124_read_register(pAd7124_dev, &ad7124_register_map[AD7124_Error]); + cal_error = ((ad7124_register_map[AD7124_Error].value >> 18) & 0x01); + + if (!cal_error) { + printf("\tCalibration Successful...\r\n"); + } else { + printf("\tError in calibration...\r\n"); + } + } + } + + // Disable current channel + ad7124_register_map[AD7124_Channel_0 + chn_cnt].value &= + (~AD7124_CH_MAP_REG_CH_ENABLE); + ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + chn_cnt]); + } + + // Need to restore the channels that were disabled during calibration + for (chn_cnt = 0 ; chn_cnt < AD7124_MAX_CHANNELS ; chn_cnt++) { + if (channel_enable_mask & (1 << chn_cnt)) { + ad7124_register_map[AD7124_Channel_0 + chn_cnt].value |= + AD7124_CH_MAP_REG_CH_ENABLE; + ad7124_write_register(pAd7124_dev, + ad7124_register_map[AD7124_Channel_0 + chn_cnt]); + } + } + + // Write back previous value of control register + ad7124_register_map[AD7124_ADC_Control].value = adc_control_reg_val; + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_ADC_Control]); + + // Disable calibration error monitoring + ad7124_register_map[AD7124_Error_En].value &= + (~AD7124_ERREN_REG_ADC_CAL_ERR_EN); + ad7124_write_register(pAd7124_dev, ad7124_register_map[AD7124_Error_En]); + + adi_press_any_key_to_continue(); + adi_clear_console(); + + return MENU_CONTINUE; +} + + +/* @brief Display the setup + * + * @return int32_t- menu status constant + **/ +static uint8_t menu_display_setup(void) +{ + ad7124_setup_config device_setup; // setup to be displayed + float filter_data_rate; // Filter data rate in SPS + uint8_t setup_cnt; // setup counter + uint8_t chn_cnt; // channel counter + int32_t error_code; // data read/write error + uint8_t power_mode_index; // Index pointing to power mode string + + printf("\r\n\t---------------------------------------\r\n"); + printf("\r\n"); + + /* Extract and print the power mode */ + (void)ad7124_read_register(pAd7124_dev, + &ad7124_register_map[AD7124_ADC_Control]); + power_mode_index = + AD7124_ADC_CTRL_REG_POWER_MODE_RD( + ad7124_register_map[AD7124_ADC_Control].value); + printf("\tPower Mode: %s\r\n", power_modes_str[power_mode_index]); + + printf("\r\n"); + printf("\t---------------------------------------\r\n"); + printf("\tChannel# | Status | Setup | AINP | AINM\r\n"); + printf("\t---------------------------------------\r\n"); + + for (chn_cnt = 0; chn_cnt < AD7124_MAX_CHANNELS; chn_cnt++) { + /* Read the channel register */ + if ((error_code = ad7124_read_register(pAd7124_dev, + &ad7124_register_map[AD7124_Channel_0 + chn_cnt])) < 0) { + printf("\r\nError reading setup!!\r\n"); + break; + } + + /* Extract the channel parameters from device register read value */ + + device_setup.channel_enabled = + AD7124_CH_MAP_REG_CH_ENABLE_RD(ad7124_register_map[AD7124_Channel_0 + + chn_cnt].value); + + device_setup.setup_assigned = + AD7124_CH_MAP_REG_SETUP_RD(ad7124_register_map[AD7124_Channel_0 + + chn_cnt].value); + + device_setup.pos_analog_input = + AD7124_CH_MAP_REG_AINP_RD(ad7124_register_map[AD7124_Channel_0 + + chn_cnt].value); + + device_setup.neg_analog_input = + AD7124_CH_MAP_REG_AINM_RD(ad7124_register_map[AD7124_Channel_0 + + chn_cnt].value); + + // Channel# | Status | Setup | AINP | AINM + printf("\t%4d %13s %4d %7d %6d\r\n", + chn_cnt, + enable_disable_status[device_setup.channel_enabled], + device_setup.setup_assigned, + device_setup.pos_analog_input, + device_setup.neg_analog_input); + } + + printf("\r\n"); + printf("\t-----------------------------------------------------------------------------------------------------------\r\n"); + printf("\tSetup# | Filter Type | Data Rate | AIN_BUFP | AIN_BUFM | REF_BUFP | REF_BUFM | Polarity | Gain | REF SOURCE\r\n"); + printf("\t-----------------------------------------------------------------------------------------------------------\r\n"); + + for (setup_cnt = 0; setup_cnt < AD7124_MAX_SETUPS; setup_cnt++) { + /* Read the filter register */ + if ((error_code = ad7124_read_register(pAd7124_dev, + &ad7124_register_map[AD7124_Filter_0 + setup_cnt])) < 0) { + printf("\r\nError reading setup!!\r\n"); + break; + } + + /* Read the config register */ + if ((error_code = ad7124_read_register(pAd7124_dev, + &ad7124_register_map[AD7124_Config_0 + setup_cnt])) < 0) { + printf("\r\nError reading setup!!\r\n"); + break; + } + + /* Extract the setup parameters from device register read value */ + + device_setup.filter = + AD7124_FILT_REG_FILTER_RD(ad7124_register_map[AD7124_Filter_0 + + setup_cnt].value); + + device_setup.data_rate_fs_val = + AD7124_FILT_REG_FS_RD(ad7124_register_map[AD7124_Filter_0 + setup_cnt].value); + + device_setup.input_buffers = + (AD7124_CFG_REG_AIN_BUFP_RD(ad7124_register_map[AD7124_Config_0 + + setup_cnt].value) << 1) | + (AD7124_CFG_REG_AINM_BUFP_RD(ad7124_register_map[AD7124_Config_0 + + setup_cnt].value)); + + device_setup.reference_buffers = + (AD7124_CFG_REG_REF_BUFP_RD(ad7124_register_map[AD7124_Config_0 + + setup_cnt].value) << 1) | + (AD7124_CFG_REG_REF_BUFM_RD(ad7124_register_map[AD7124_Config_0 + + setup_cnt].value)); + + device_setup.polarity = + AD7124_CFG_REG_BIPOLAR_RD(ad7124_register_map[AD7124_Config_0 + + setup_cnt].value); + + device_setup.programmable_gain_bits = + AD7124_CFG_REG_PGA_RD(ad7124_register_map[AD7124_Config_0 + setup_cnt].value); + + device_setup.reference = + AD7124_CFG_REG_REF_SEL_RD(ad7124_register_map[AD7124_Config_0 + + setup_cnt].value); + + filter_data_rate = calculate_data_rate(device_setup.filter, power_mode, + (float)device_setup.data_rate_fs_val); + + // Setup# | Filter Type | Data Rate | AIN_BUFP | AIN_BUFM | REF_BUFP | REF_BUFM | Polarity | Gain | REF + printf("\t%4d %15s %10.2f %12s %10s %10s %10s %10s %5d %12s\r\n", + setup_cnt, + filter_name[device_setup.filter], + filter_data_rate, + enable_disable_status[(device_setup.input_buffers >> 1) & 0x01], + enable_disable_status[(device_setup.input_buffers & 0x01)], + enable_disable_status[(device_setup.reference_buffers >> 1) & 0x01], + enable_disable_status[device_setup.reference_buffers & 0x01], + polarity_status[device_setup.polarity], + p_gain[device_setup.programmable_gain_bits], + reference_name[device_setup.reference]); + } + + adi_press_any_key_to_continue(); + + return MENU_CONTINUE; +} + + +/* @brief Configure the analog inputs for polarity and input buffers + * + * @param ad7124_setup_config *psetup- pointer to setup to be assigned + **/ +static void config_analog_inputs(ad7124_setup_config *psetup) +{ + bool current_selection_done = + false; // flag checking if current menu selection is done + uint32_t polarity; // Polarity of ADC channel + uint32_t input_buffers; // Analog input buffers enable/disable status + uint32_t ref_buffers; // Reference buffers enable/disable status + uint32_t reference_sel; // Reference source selection + + /* Polarity selection */ + do { + printf("\r\n\tSelect the polarity <0: Unipolar, 1: Bipolar>: "); + polarity = adi_get_decimal_int(sizeof(polarity)); + + /* Validate the range for polarity values */ + if (polarity <= 1) { + psetup->polarity = (uint8_t)polarity; + current_selection_done = true; + } else { + printf("\r\n\tPolarity out of range!!\r\n"); + } + + } while (current_selection_done == false) ; + + + /* Input buffer enable/disable selection */ + current_selection_done = false; + + do { + printf("\r\n\tEnable the AINP/M Buffers <0: Disable, 1: Enable>: "); + input_buffers = adi_get_decimal_int(sizeof(input_buffers)); + + /* Validate the range for input buffer values */ + if (input_buffers <= 1) { + psetup->input_buffers = (uint8_t)input_buffers; + current_selection_done = true; + } else { + printf("\r\n\tInvalid selection !!\r\n"); + } + + } while (current_selection_done == false); + + + /* Reference buffer enable/disable selection */ + current_selection_done = false; + + do { + printf("\r\n\tEnable the Reference Buffers <0: Disable, 1: Enable>: "); + ref_buffers = adi_get_decimal_int(sizeof(ref_buffers)); + + /* Validate the range for reference buffer values */ + if (ref_buffers <= 1) { + psetup->reference_buffers = (uint8_t)ref_buffers; + current_selection_done = true; + } else { + printf("\r\n\tInvalid selection !!\r\n"); + } + + } while (current_selection_done == false); + + + /* Input buffer enable/disable selection */ + current_selection_done = false; + + do { + printf("\r\n\tEnter the reference source: "); + printf("\r\n\t[0] %s \r\n\t[1] %s \r\n\t[2] %s \r\n\t[3] %s \r\n\t", + reference_name[0], + reference_name[1], + reference_name[2], + reference_name[3]); + + reference_sel = adi_get_decimal_int(sizeof(reference_sel)); + + /* Validate the range for reference sources (0:3) */ + if (reference_sel <= 3) { + psetup->reference = (uint8_t)reference_sel; + current_selection_done = true; + } else { + printf("\r\n\tInvalid selection !!\r\n"); + } + + } while (current_selection_done == false); +} + + +/* @brief Calculate the data rate based on data rate FS value and vice a versa + * + * @param filter_type filter- Filter Type + * @param uint8_t power_mode- Power mode of device + * @param float data_rate- Actual Data Rate + **/ +static float calculate_data_rate(filter_type filter, uint8_t power_mode, + float data_rate) +{ + float calc_data_rate = 120.0; // default data rate + + // The data rate selection depends upon the power mode and device frequency. + // fadc = Output data rate, fclk = master clock frequency + // fclk = 614.4Khz (full power), 153.6Khz (mid power), 76.8Khz (low power) + // fadc = 9.38SPS to 19200SPS (full power), 2.35SPS to 4800SPS (mid power), + // 1.17SPS to 2400SPS for low power + + // Calculate FS value for SINC4 and SINC3 filter + // FS[10:0] = fclk / (32 * fadc) OR fadc = fclk / (32 * FS[10:0]) + if (filter == SINC4_FILTER || filter == SINC3_FILTER) { + if (power_mode == FULL_POWER_MODE) { + calc_data_rate = (FUL_POWER_MODE_FREQUENCY / (data_rate * 32)); + } else if (power_mode == MED_POWER_MODE) { + calc_data_rate = (MED_POWER_MODE_FREQUENCY / (data_rate * 32)); + } else { + // Low power mode (default) + calc_data_rate = (LOW_POWER_MODE_FREQUENCY / (data_rate * 32)); + } + } + + // Calculate FS value for fast settling SINC4 filter + // FS[10:0] = fclk / ((4+Avg-1) * 32 * fadc) Or fadc = fclk / ((4+Avg-1) * 32 * FS[10:0]) + // Avg = 16 for Full and Med power mode, 8 for low power mode + if (filter == FAST_SETTLING_SINC4_FILTER) { + if (power_mode == FULL_POWER_MODE) { + calc_data_rate = (FUL_POWER_MODE_FREQUENCY / (19 * (data_rate * 32))); + } else if (power_mode == MED_POWER_MODE) { + calc_data_rate = (MED_POWER_MODE_FREQUENCY / (19 * (data_rate * 32))); + } else { + // Low power mode (default) + calc_data_rate = (LOW_POWER_MODE_FREQUENCY / (11 * (data_rate * 32))); + } + } + + // Calculate FS value for fast settling SINC3 filter + // FS[10:0] = fclk / ((3+Avg-1) * 32 * fadc) Or fadc = fclk / ((3+Avg-1) * 32 * FS[10:0]) + // Avg = 16 for Full and Med power mode, 8 for low power mode + if (filter == FAST_SETTLING_SINC3_FILTER) { + if (power_mode == FULL_POWER_MODE) { + calc_data_rate = (FUL_POWER_MODE_FREQUENCY / (18 * (data_rate * 32))); + } else if (power_mode == MED_POWER_MODE) { + calc_data_rate = (MED_POWER_MODE_FREQUENCY / (18 * (data_rate * 32))); + } else { + // Low power mode (default) + calc_data_rate = (LOW_POWER_MODE_FREQUENCY / (10 * (data_rate * 32))); + } + } + + return calc_data_rate; +} + + +/* @brief Configure the filter parameters + * + * @param ad7124_setup_config *psetup- pointer to setup to be assigned + **/ +static void config_filter_parameters(ad7124_setup_config *psetup) +{ + bool current_selection_done = + false; // flag checking if current menu selection is done + uint32_t filter_type = NUM_OF_FILTERS; // filter type selection + uint16_t data_rate_fs = MAX_FILTER_DATA_RATE_FS + + 1; // filter data rate FS value + uint8_t gain_bits_value = 0; // gain bits value + + do { + printf("\r\n\tEnter the filter type selection: "); + printf("\r\n\t[0] %s \r\n\t[1] %s \r\n\t[2] %s \r\n\t[3] %s \r\n\t", + filter_name[SINC4_FILTER], + filter_name[SINC3_FILTER], + filter_name[FAST_SETTLING_SINC4_FILTER], + filter_name[FAST_SETTLING_SINC3_FILTER]); + + filter_type = adi_get_decimal_int(sizeof(filter_type)); + + /* Check for valid menu item selection (menu keys 0:3) */ + if (filter_type <= 3) { + switch (filter_type) { + case 0: + psetup->filter = SINC4_FILTER; + break; + + case 1: + psetup->filter = SINC3_FILTER; + break; + + case 2: + psetup->filter = FAST_SETTLING_SINC4_FILTER; + break; + + case 3: + psetup->filter = FAST_SETTLING_SINC3_FILTER; + break; + + default: + psetup->filter = SINC4_FILTER; + break; + } + + current_selection_done = true; + } else { + printf("\r\n\tInvalid filter type selection!!\r\n"); + } + + } while (current_selection_done == false) ; + + + /* Data rate selection */ + current_selection_done = false; + + /* Get the data rate for the selected filter except SINC3 Post filter, which + * has fixed data rates selectable from bits 19:17 of filter register */ + while (current_selection_done == false) { + printf("\r\n\tEnter the filter Data Rate (in SPS): "); + filter_data_rate_raw = adi_get_decimal_float(sizeof(filter_data_rate_raw) * 2); + + // Get the value and round off to nearest integer + data_rate_fs = (uint16_t)(calculate_data_rate(psetup->filter, power_mode, + filter_data_rate_raw) + 0.5); + + /* Validate entered filter data range */ + if (data_rate_fs >= MIN_FILTER_DATA_RATE + && data_rate_fs <= MAX_FILTER_DATA_RATE_FS) { + psetup->data_rate_fs_val = data_rate_fs; + current_selection_done = true; + } else { + printf("\r\n\tData rate out of range!!\r\n"); + } + } + + + /* Select the gain factor for the filter */ + current_selection_done = false; + + do { + printf("\r\n\tSelect the programmable gain <1-128>: "); + gain_raw = adi_get_decimal_int(sizeof(gain_raw)); + + // Get the gain bits value + for(gain_bits_value = 0 ; gain_bits_value <= MAX_GAIN_BITS_VALUE ; + gain_bits_value++) { + if (gain_raw == p_gain[gain_bits_value]) { + psetup->programmable_gain_bits = gain_bits_value; + current_selection_done = true; + break; + } + } + + /* Validate the range for gain values */ + if (!current_selection_done) { + printf("\r\n\tGain out of range!!\r\n"); + } + + } while (current_selection_done == false); +} + + +/* @brief Get the channel selection + * + * @return uint8- Selected channel + **/ +static uint8_t get_channel_selection(void) +{ + uint32_t current_channel = AD7124_MAX_CHANNELS; + bool current_selection_done = false; + + do { + printf("\r\n\tEnter Channel Value <0-15>: "); + current_channel = adi_get_decimal_int(sizeof(current_channel)); + + /* Validate channel selection */ + if (current_channel < AD7124_MAX_CHANNELS) { + /* Break the loop by setting below flag */ + current_selection_done = true; + } else { + printf("\r\n\tInvalid channel selection!!\r\n"); + } + + } while (current_selection_done == false); + + return current_channel; +} + + +/* @brief Get the setup selection + * + * @return uint8- Selected setup + **/ +static uint8_t get_setup_selection(void) +{ + uint32_t current_setup = AD7124_MAX_SETUPS; + bool current_selection_done = false; + + /* Setup selection */ + do { + printf("\r\n\tEnter Setup Selection <0-7>: "); + current_setup = adi_get_decimal_int(sizeof(current_setup)); + + if (current_setup < AD7124_MAX_SETUPS) { + current_selection_done = true; + } else { + printf("\r\n\tInvalid setup selection!!\r\n"); + } + + } while (current_selection_done == false); + + return current_setup; +} + + +/* + * Definition of the power mode Menu Items and menu itself + */ +static console_menu_item power_mode_menu_items[] = { + { "Low Power Mode", 'L', menu_power_modes_selection, (uint32_t)LOW_POWER_MODE }, + { "Med Power Mode", 'M', menu_power_modes_selection, (uint32_t)MED_POWER_MODE }, + { "Full Power Mode", 'F', menu_power_modes_selection, (uint32_t)FULL_POWER_MODE }, +}; + +static console_menu power_mode_menu = { + .title = "Power Mode Selection Menu", + .items = power_mode_menu_items, + .itemCount = ARRAY_SIZE(power_mode_menu_items), + .headerItem = NULL, + .footerItem = NULL, + .enableEscapeKey = true +}; + +/* + * Definition of the Sampling Menu Items and menu itself + */ +static console_menu_item acquisition_menu_items[] = { + { "Single Conversion Mode", 'S', menu_single_conversion }, + { "Continuous Conversion Mode - Table View", 'T', menu_continuous_conversion_tabular }, + { "Continuous Conversion Mode - Stream Data", 'C', menu_continuous_conversion_stream }, +}; + +static console_menu acquisition_menu = { + .title = "Data Acquisition Menu", + .items = acquisition_menu_items, + .itemCount = ARRAY_SIZE(acquisition_menu_items), + .headerItem = NULL, + .footerItem = NULL, + .enableEscapeKey = true +}; + +/* + * Definition of the channel enable/disable Menu Items and menu itself + */ +static console_menu_item chn_enable_disable_items[] = { + { "Enable Channels", 'E', menu_channels_enable_disable, (uint32_t)CHN_ENABLE }, + { "Disable Channels", 'D', menu_channels_enable_disable, (uint32_t)CHN_DISABLE }, +}; + +static console_menu chn_enable_disable_menu = { + .title = "Channel Enable/Disable Menu", + .items = chn_enable_disable_items, + .itemCount = ARRAY_SIZE(chn_enable_disable_items), + .headerItem = NULL, + .footerItem = NULL, + .enableEscapeKey = true +}; + +/* + * Definition of the adc register read/write Menu Items and menu itself + */ +static console_menu_item reg_read_write_items[] = { + { "Read Device Register", 'R', menu_rw_ad7124_register, (uint32_t)DEVICE_REG_READ_ID }, + { "Write Device Register", 'W', menu_rw_ad7124_register, (uint32_t)DEVICE_REG_WRITE_ID }, +}; + +static console_menu reg_read_write_items_menu = { + .title = "Register Read/Write Menu", + .items = reg_read_write_items, + .itemCount = ARRAY_SIZE(reg_read_write_items), + .headerItem = NULL, + .footerItem = NULL, + .enableEscapeKey = true +}; + + +/*! + * @brief displays and handles the Sample Channel menu + * + * @return int32_t- menu status constant + */ +static int32_t menu_sample_channels(void) +{ + return (adi_do_console_menu(&acquisition_menu)); +} + + +/* @brief display and handle console menu for enabling/disabling adc channels + * + * @return int32_t- menu status constant + **/ +static int32_t menu_enable_disable_channels(void) +{ + return (adi_do_console_menu(&chn_enable_disable_menu)); +} + + +/* @brief display and handle console menu for reading/writing adc registers + * + * @return int32_t- menu status constant + **/ +static int32_t menu_read_write_device_regs(void) +{ + return (adi_do_console_menu(®_read_write_items_menu)); +} + +/*! + * @brief displays and handles the power mode select menu + * + * @return int32_t- menu status constant + */ +static int32_t menu_select_power_mode(void) +{ + return (adi_do_console_menu(&power_mode_menu)); +} + + +/* + * Definition of the Main Menu Items and menu itself + */ +static console_menu_item main_menu_items[] = { + { "Reset to Default Configuration", 'A', menu_reset }, + { "Reset to Configuration A", 'B', menu_reset_to_configuration, (uint32_t)AD7124_CONFIG_A }, + { "Reset to Configuration B", 'C', menu_reset_to_configuration, (uint32_t)AD7124_CONFIG_B }, + { "", '\00', NULL }, + { "Read ID Register", 'D', menu_read_id }, + { "Read Status Register", 'E', menu_read_status }, + { "", '\00', NULL }, + { "Sample Channels", 'F', menu_sample_channels }, + { "", '\00', NULL }, + { "Select Power Mode", 'G', menu_select_power_mode }, + { "", '\00', NULL }, + { "Enable/Disable Channels", 'H', menu_enable_disable_channels }, + { "", '\00', NULL }, + { "Connect Analog Inputs to Channel", 'I', menu_connect_input_to_channel }, + { "", '\00', NULL }, + { "Configure and Assign Setup", 'J', menu_config_and_assign_setup }, + { "", '\00', NULL }, + { "Display setup", 'K', menu_display_setup }, + { "", '\00', NULL }, + { "Read Temperature", 'L', menu_read_temperature }, + { "", '\00', NULL }, + { "Calibrate ADC (Internal)", 'M', menu_calibrate_adc }, + { "", '\00', NULL }, + { "Read/Write Device Registers", 'N', menu_read_write_device_regs }, +}; + +console_menu ad7124_main_menu = { + .title = "AD7124 Main Menu", + .items = main_menu_items, + .itemCount = ARRAY_SIZE(main_menu_items), + .headerItem = NULL, + .footerItem = NULL, + .enableEscapeKey = false +};