AD7124 Example Program
Dependencies: platform_drivers AD7124_no_OS
Diff: main.cpp
- Revision:
- 2:0897873979f3
- Parent:
- 0:83a0c7bbe493
--- a/main.cpp Wed Sep 04 22:14:13 2019 +0000 +++ b/main.cpp Thu Sep 05 20:50:43 2019 +0000 @@ -34,25 +34,1189 @@ 2019-01-10-7CBSD SLA + USING THE PROGRAM + * While using this program, the user has the option of selecting between two + * different configurations: Default and Custom. These configurations represent + * two unique register maps which can be altered by the user throughout using + * the program. There are sixteen unique channels and eight unique setups + * associated with the AD7124. Each of the channels is assigned a setup which + * includes fields such as gain and filter options associated with it. The + * following guide will detail how to get the most out of the program. + * + * After running the program. + * 1) Press menu item (11) "Print Device Register Map (User Friendly Mode)". + * This will show you each channel's information. There are only two channels + * enabled, one using setup "0" and the other using setup "1". + * + * 2) Press menu item (3) "Assign Channel Setup" + * You can now assign any channel any setup continuously until you elect to quit. + * Select channel (0) and assign it to setup (2), then quit (16). + * + * 3) Press menu item (11) "Print Device Register Map (User Friendly Mode)". + * You can now see that channel "0" uses setup "2". + * + * 4) Press menu item (7) "Select Filter/Filter Data Rate". + * Select setup (2) to change and then change the filter to Fast Settling (3) + * and select data rate (128). + * + * 5) Press menu item (11) "Print Device Register Map (User Friendly Mode)". + * As you can see, every channel with setup "2" has had their filters and filter + * data rate changed. + * ***NOTE*** + * When modifying a setup, it will affect every channel using that setup + * + * 6) Press menu item (12) "Print Device Register Map (Raw Data Mode)" + * Lets say you found a configuration that works for your application, you can + * now directly copy the current configuration's register map and paste it into + * the "ad7124_regs_custom.cpp" file to "save" that configuration for future use. + * This allows the user to further modify configurations when tweaking is needed + * so that the complete flexibilty of the AD7124 is at the hands of the particular + * application. + * + * Additional Information: + * There are many other features of this part that are not in this program. + * The way that the structs are set up, it is easy to add additional + * functionality for features such as "Analog positive/negative input" and + * "Clock source". Feel free to modify this program to suit your needs even + * further. */ +/***Libraries***/ +#include <stdio.h> +#include <string.h> + #include "mbed.h" +#include "platform_drivers.h" -Serial pc(USBTX, USBRX); // tx, rx -DigitalOut awakeSignal(LED4); -DigitalOut led1(LED1); +#include "ad7124.h" +#include "ad7124_regs.h" +#include "ad7124_regs_configs.h" + +/***Defines for SPI Protocol***/ +#define SPI_PLATFORM MBED +#define SPI_TYPE GENERIC_SPI +#define SPI_ID 0 +#define SPI_MAX_FREQUENCY 1500000 +#define SPI_MODE SPI_MODE_3 +#define SPI_POLL_COUNT 10000 + +/***Defines for UART Protocol***/ +#define BAUD_RATE 115200 + +/***Defines for Code to Voltage***/ +#define VREF 2.5 +#define NUM_BITS 24 + +/***Defines for Active Channels Array***/ +#define NUM_OF_CHANNELS 16 +#define NUM_OF_SETUPS 8 + +/***Defines for Operating Modes***/ +#define CONTINOUS_CONV_MODE 0x0 +#define SINGLE_CONV_MODE 0x1 +#define STANDBY_MODE 0x2 +#define POWER_DOWN_MODE 0x3 +#define IDLE_MODE 0x4 +#define INTERNAL_ZERO_SCALE 0x5 +#define INTERNAL_FULL_SCALE 0x6 +#define SYSTEM_ZERO_SCALE 0x7 +#define SYSTEM_FULL_SCALE 0x8 + +/***Defines for Power Modes***/ +#define LOW_POWER_MODE 0x0 +#define MED_POWER_MODE 0x1 +#define HIGH_POWER_MODE 0x2 + +/***Defines for Filters***/ +#define SINC4 0x0 +#define SINC3 0x2 +#define FAST_SETTLING 0x4 +#define FAST_SETTLING_SINC3 0x5 +#define POST 0x7 + +/***Defines for Gains***/ +#define GAIN_1 0x0 +#define GAIN_2 0x1 +#define GAIN_4 0x2 +#define GAIN_8 0x3 +#define GAIN_16 0x4 +#define GAIN_32 0x5 +#define GAIN_64 0x6 +#define GAIN_128 0x7 + +/***Defines for Register Reads***/ +#define POWER_MODE_READ(x) (((x) >> 6) & 0x3) +#define GAIN_READ(x) (((x) >> 0) & 0x7) +#define FILTER_READ(x) (((x) >> 21) & 0x7) +#define DATA_RATE_READ(x) (((x) >> 0) & 0x7FF) +#define SETUP_READ(x) (((x) >> 12) & 0x7) + +/***Configuration Defines***/ +#define DEF_CONFIG 0 +#define CUSTOM_CONFIG 1 + +/*Connecting Hardware Pin Names + to Software Variables*/ +DigitalOut SS(SDP_SPI_CS_A); +mbed::SPI spi(SDP_SPI_MOSI, SDP_SPI_MISO, SDP_SPI_SCK); +mbed::I2C i2c(SDP_I2C_SDA, SDP_I2C_SCL); + +/*Configure and instantiate UART protocol + and baud rate*/ +Serial port(USBTX, USBRX, BAUD_RATE); + +/***Global Variables***/ +uint8_t adc_conf = DEF_CONFIG; -#define SLEEP_TIME 500 // (msec) +const char* conf_strings[] = +{"Default Configuration","Custom Configuration"}; +const char* power_strings[] = +{"Low Power Mode","Medium Power Mode","High Power Mode"}; +const char* filter_strings[] = +{"Sinc4","","Sinc3","","Fast Settling","Fast Settling + Sinc3","","Post"}; + +/***Menu Function Declarations***/ +static int8_t menu_config_select(struct ad7124_dev ** dev, int8_t sel_conf); + +static int8_t menu_assign_setup(struct ad7124_dev * dev); + +static int8_t menu_enable_disable_channels(struct ad7124_dev * dev); + +static int8_t menu_sample_channels(struct ad7124_dev * dev); + +static int8_t menu_select_gain(struct ad7124_dev * dev); + +static int8_t menu_select_filter_options(struct ad7124_dev * dev); + +static int8_t menu_select_calibration(struct ad7124_dev * dev); + +static int8_t menu_select_power_mode(struct ad7124_dev * dev); + +static int8_t menu_print_ID(struct ad7124_dev * dev); + +static int8_t menu_print_channel_register_map_view_mode(struct ad7124_dev * dev); + +static int8_t menu_print_channel_register_map_raw_mode(struct ad7124_dev * dev); + +static int8_t menu_print_setups(struct ad7124_dev * dev); + +/***Support Functions***/ +static void print_title(void); + +static void print_prompt(void); + +static int8_t fill_structs(struct ad7124_dev * dev); + +static int8_t write_device(struct ad7124_dev * dev); + +static int8_t update_write_reg(struct ad7124_dev * dev, uint8_t reg, uint32_t mask, uint32_t val); + +static void code_to_voltage(uint32_t code, uint8_t channel); -// main() runs in its own thread in the OS +static int8_t err_detect(int8_t ret); + +static int8_t setup_select(uint8_t *setup); + +static int8_t channel_select(uint8_t *channel); + +/*SPI Initialization Parameters*/ +spi_init_param spi_params = { + SPI_PLATFORM, + SPI_TYPE, + SPI_ID, + SPI_MAX_FREQUENCY, + SPI_MODE, + SDP_SPI_CS_A +}; + +/*Struct with individual channel information*/ +struct channel_setup { + uint8_t setup_id; + bool bipolar; + uint8_t gain; + uint8_t filter; + uint16_t data_rate; +}; + +/*Struct with individual setup information*/ +struct channel_info { + uint8_t power_mode; + bool enable; + float sample; + struct channel_setup *setup; +}; + +/*Declaring array of 16 channels and 8 setups*/ +struct channel_info channels[NUM_OF_CHANNELS]; +struct channel_setup setups[NUM_OF_SETUPS]; + +struct ad7124_dev * dev = NULL; /*Device Handler (AD7124)*/ + +/* Main function + * + * Parameters: None + * Return Value: SUCCESS(0), FAILURE (Negative) + */ int main() { - awakeSignal = 1; - pc.printf("Hello World!"); - while (true) { - // Blink LED and wait 0.5 seconds - led1 = !led1; - wait_ms(SLEEP_TIME); + uint8_t user_command; + + /*Setup device handler and write register map to device from + the defailt configuration*/ + int8_t connected = menu_config_select(&dev, DEF_CONFIG); + + print_title(); + + while(err_detect(connected) != FAILURE) { + + print_prompt(); + port.scanf("%d", (int *) &user_command); + + switch (user_command) { + case 1: + menu_config_select(&dev, DEF_CONFIG); + break; + + case 2: + menu_config_select(&dev, CUSTOM_CONFIG); + break; + + case 3: + menu_assign_setup(dev); + break; + + case 4: + menu_enable_disable_channels(dev); + break; + + case 5: + menu_sample_channels(dev); + break; + + case 6: + menu_select_gain(dev); + break; + + case 7: + menu_select_filter_options(dev); + break; + + case 8: + menu_select_calibration(dev); + break; + + case 9: + menu_select_power_mode(dev); + break; + + case 10: + menu_print_ID(dev); + break; + + case 11: + menu_print_channel_register_map_view_mode(dev); + break; + + case 12: + menu_print_channel_register_map_raw_mode(dev); + break; + + case 13: + menu_print_setups(dev); + break; + + default: + port.printf("\t***Illegal Entry***\n\n"); + break; + } + } + return FAILURE; +} + +/***Function Definitions***/ + +/* Print title of the program + * + * Parameters: None + * Return Value: None + */ +static void print_title() +{ + port.printf("\n*****************************************************************\n"); + port.printf("* EVAL-AD7124 Demonstration Program *\n"); + port.printf("* *\n"); + port.printf("* This program demonstrates how to interface and configure the *\n"); + port.printf("* AD7124 High-Precision Sigma-Delta ADC *\n"); + port.printf("* *\n"); + port.printf("* *\n"); + port.printf("* Set the baud rate to 115200 and select the newline terminator.*\n"); + port.printf("* *\n"); + port.printf("*******************************************************************\n"); +} + +/*Print command summary + * + *Parameters: None + *Return Value: None + */ +static void print_prompt() +{ + port.printf("\n\n\tMain Menu Summary\n"); + port.printf("\tConfiguration Selected: %s\n", conf_strings[adc_conf]); + port.printf("\t=======================================\n"); + port.printf("\t 1 - Select Configuration (A) - Default\n"); + port.printf("\t 2 - Select Configuration (C) - Custom\n\n"); + port.printf("\t 3 - Assign Channel Setup\n"); + port.printf("\t 4 - Enable/Disable Channel\n\n"); + port.printf("\t 5 - Sample Channel\n"); + port.printf("\t 6 - Select Gain\n"); + port.printf("\t 7 - Select Filter/Filter Data Rate\n"); + port.printf("\t 8 - Select Calibration\n"); + port.printf("\t 9 - Select Power Mode\n\n"); + port.printf("\t 10 - Print Device ID\n"); + port.printf("\t 11 - Print Device Register Map (User Friendly Mode)\n"); + port.printf("\t 12 - Print Device Register Map (Raw Data Mode)\n"); + port.printf("\t 13 - Print Setup Information\n\n"); +} + +/* Switch between default and custom configurations + * + * Parameters: Double pointer to device handler, configuration selection variable + * Return Value: SUCCESS (0) , FAILURE (Negative) + */ +static int8_t menu_config_select(struct ad7124_dev ** dev, int8_t sel_conf) +{ + int8_t ret; + + /*Free device handler*/ + ret = ad7124_remove(*dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Use user selected register map (default, custom)*/ + struct ad7124_init_param ad7124_params = { + spi_params, + (sel_conf == DEF_CONFIG) ? ad7124_regs_default : ad7124_regs_custom, + SPI_POLL_COUNT + }; + + /*Setup device handler with corresponding register map*/ + ret = ad7124_setup(dev, ad7124_params); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Fill up channel and setup structs with register map information*/ + ret = fill_structs(*dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Switch global configuration variable*/ + adc_conf = (sel_conf == DEF_CONFIG) ? DEF_CONFIG : CUSTOM_CONFIG; + + return SUCCESS; +} + +/* Assign any channel to a given setup. This channel will then use that setup's + * features when it is sampled. This function continues to probe the user for + * more channels to change the setup of until the proper "main menu" command + * is input. + * + * Paremeters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_assign_setup(struct ad7124_dev * dev) +{ + uint8_t channel, setup; + int8_t ret; + + /*Fill all structs with register map information from device*/ + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Continue until user wants to quit*/ + while (1) { + /*Obtain Channel*/ + port.printf("\n\tSelect Channel to Assign Setup\n"); + ret = channel_select(&channel); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Obtain Setup*/ + port.printf("\n\tSelect Setup to Assign to Channel %d\n", channel); + ret = setup_select(&setup); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Write back to device*/ + channels[channel].setup = &setups[setup]; + channels[channel].setup->setup_id = setup; + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + } +} + +/* Offers the feature of enabling and disabling any channels that the user + * selects. It also continuously probes the user for more channels to + * enable/disable until the "main menu" command is input. + * + * Parameters: Device handler + * Return Value: SUCCESS(0), FAILURE (Negative) + */ +static int8_t menu_enable_disable_channels(struct ad7124_dev * dev) +{ + uint8_t channel, enable; + int8_t ret; + + /*Fill all structs with register map information from device*/ + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + while (1) { + /*Obtain channel*/ + port.printf("\n\tSelect Channel to Enable/Disable\n"); + ret = channel_select(&channel); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Obtain enable/disable command*/ + port.printf("\n\tChoose to Enable or Disable Channel %d\n", channel); + port.printf("\t===============================\n"); + port.printf("\t 1 - Disable\n"); + port.printf("\t 2 - Enable\n\n"); + port.printf("\t 3 - Main Menu\n\n"); + + port.scanf("%d", (int *) &enable); + if (enable > 2) + return FAILURE; + + /*Write back to device*/ + channels[channel].enable = enable - 1; + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + } +} + +/* Sample all active channels and print out the corresponding voltages. The first + * option is single conversion mode where all the channels are sampled one after + * the other. The device is put into standby mode after. The next option is + * continuous conversion mode where all channels are sampled continuously. The user + * can press any key to stop the continuous sampling. + * + * Parameters: Device handler + * Return Value: SUCCESS (0) , FAILURE (Negative) + */ +static int8_t menu_sample_channels(struct ad7124_dev * dev) +{ + struct ad7124_st_reg *regs; + uint32_t bitfield_mask, temp_val, ones_mask = 0xFFFFFFFF; + uint8_t conversion_mode, channel, previous_channel; + int8_t ret; + + regs = dev->regs; + + /*Fill up channel and setup structs with register map information*/ + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("\n\tSelect Conversion Mode\n"); + port.printf("\t=======================================\n"); + port.printf("\t 1 - Single Conversion Mode\n"); + port.printf("\t 2 - Continuous Conversion Mode\n"); + port.printf("\t 3 - Main Menu\n\n"); + + /*Obtain user input and check validity*/ + port.scanf("%d", (int *) &conversion_mode); + if ((conversion_mode < 1) || (conversion_mode > 2)) + return FAILURE; + + /*Update ADC operating mode to single/continous conversion mode*/ + bitfield_mask = AD7124_ADC_CTRL_REG_MODE(ones_mask); + temp_val = (conversion_mode == SINGLE_CONV_MODE) ? AD7124_ADC_CTRL_REG_MODE(SINGLE_CONV_MODE): + AD7124_ADC_CTRL_REG_MODE(CONTINOUS_CONV_MODE); + ret = update_write_reg(dev, AD7124_ADC_Control, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Continue to sample*/ + while (1) { + + /*Wait for conversion complete, then obtain sample*/ + ad7124_wait_for_conv_ready(dev, dev->spi_rdy_poll_cnt); + ret = ad7124_read_register(dev, ®s[AD7124_Data]); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Save previous channel*/ + previous_channel = channel; + + /*Read status register to find out which channel was sampled*/ + ret = ad7124_read_register(dev, ®s[AD7124_Status]); + if (err_detect(ret) == FAILURE) + return FAILURE; + + channel = AD7124_STATUS_REG_CH_ACTIVE(regs[AD7124_Status].value); + + /*Print out samples if a new channel was sampled*/ + if (channel != previous_channel) { + code_to_voltage(regs[AD7124_Data].value, channel); + port.printf("Channel %d: %f\n", channel, channels[channel].sample); + } + + /*In all modes, print out conversions until user says stop*/ + if (!port.readable()) + return SUCCESS; } } +/* Updates the gain of the setup selected. + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_select_gain(struct ad7124_dev * dev) +{ + uint8_t setup, gain; + int8_t ret; + + /*Fill all structs with register map information from device*/ + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("Select Setup to Change the Gain of\n"); + ret = setup_select(&setup); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("Select the Gain to Assign to Setup %d\n", setup); + port.printf("\t=======================================\n"); + port.printf("\t 1 - Gain 1\n"); + port.printf("\t 2 - Gain 2\n"); + port.printf("\t 3 - Gain 4\n"); + port.printf("\t 4 - Gain 8\n"); + port.printf("\t 5 - Gain 16\n"); + port.printf("\t 6 - Gain 32\n"); + port.printf("\t 7 - Gain 64\n"); + port.printf("\t 8 - Gain 128\n"); + port.printf("\t 9 - Main Menu\n\n"); + + port.scanf("%d", (int *) &gain); + if (gain > 8) + return FAILURE; + + /*Write to device*/ + setups[setup].gain = gain-1; + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + return SUCCESS; +} + +/* Updates the filter options of the ADC for a given setup. The user selects + * the filter and the data rate of the filter. Also updates the data rate of the + * filter selected. + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_select_filter_options(struct ad7124_dev * dev) +{ + uint8_t setup, filter; + uint16_t data_rate; + int8_t ret; + + /*Fill all structs with register map information from device*/ + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("Select Setup to Change the Filter of\n"); + ret = setup_select(&setup); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("\n\tSelect Filter\n"); + port.printf("\t=======================================\n"); + port.printf("\t 1 - Sinc4\n"); + port.printf("\t 2 - Sinc3\n"); + port.printf("\t 3 - Fast Settling\n"); + port.printf("\t 4 - Fast Settling + Sinc3\n"); + port.printf("\t 5 - Post\n"); + port.printf("\t 6 - Main Menu\n\n"); + + port.scanf("%d", (int *) &filter); + if ((filter < 1) || (filter > 5)) + return FAILURE; + + switch(filter) { + case 1: + setups[setup].filter = SINC4; + break; + + case 2: + setups[setup].filter = SINC3; + break; + + case 3: + setups[setup].filter = FAST_SETTLING; + break; + + case 4: + setups[setup].filter = FAST_SETTLING_SINC3; + break; + + case 5: + setups[setup].filter = POST; + break; + + default: + port.printf("\t***Illegal Entry***\n\n"); + return FAILURE; + } + + /*Write back to device*/ + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("Select Data Rate for the Filter\n"); + port.printf("\t=======================================\n"); + port.printf("\t (1-2047)\n"); + port.printf("\t 2048 - Main Menu\n"); + + port.scanf("%d", (int *) &data_rate); + if ((data_rate < 1) || (data_rate > 2047)) + return FAILURE; + + /*Write back to device*/ + setups[setup].data_rate = data_rate; + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + return SUCCESS; +} + +/* Select one of the four claibration modes of the ADC. There are specific + * things that the user must do before selecting any of the calibrations. + * These are outlined on page 53 of the data sheet. + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_select_calibration(struct ad7124_dev * dev) +{ + uint32_t bitfield_mask, temp_val, ones_mask = 0xFFFFFFFF; + uint8_t channel, calibration; + bool active_channels[NUM_OF_CHANNELS]; + int8_t ret; + + ret = channel_select(&channel); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("\n\tSelect Calibration Mode\n"); + port.printf("\t=======================================\n"); + port.printf("\t 1 - Internal Zero Scale\n"); + port.printf("\t 2 - Internal Full Scale\n"); + port.printf("\t 3 - System Zero Scale\n"); + port.printf("\t 4 - System Full Scale\n"); + port.printf("\t 5 - Main Menu\n\n"); + + port.scanf("%d", (int *) &calibration); + if ((calibration < 1) || (calibration > 4)) + return FAILURE; + + /*Disable all channels except for one being calibrated*/ + for (int channel_index = 0; channel_index < NUM_OF_CHANNELS; channel_index++) { + if (channel != channel_index) + if (channels[channel_index].enable == true) + active_channels[channel_index] = true; + channels[channel_index].enable = false; + } + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Put into low power mode*/ + bitfield_mask = AD7124_ADC_CTRL_REG_POWER_MODE(ones_mask); + temp_val = AD7124_ADC_CTRL_REG_POWER_MODE(LOW_POWER_MODE); + ret = update_write_reg(dev, AD7124_ADC_Control, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Put into standy mode*/ + bitfield_mask = AD7124_ADC_CTRL_REG_MODE(ones_mask); + temp_val = AD7124_ADC_CTRL_REG_MODE(LOW_POWER_MODE); + ret = update_write_reg(dev, AD7124_ADC_Control, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Switch based off calibration mode selected*/ + bitfield_mask = AD7124_ADC_CTRL_REG_MODE(ones_mask); + switch(calibration) { + case 1: + temp_val = AD7124_ADC_CTRL_REG_MODE(INTERNAL_ZERO_SCALE); + break; + + case 2: + update_write_reg(dev, channels[channel].setup->setup_id + AD7124_Gain_0, ones_mask, 0x800000); + temp_val = AD7124_ADC_CTRL_REG_MODE(INTERNAL_FULL_SCALE); + break; + + case 3: + temp_val = AD7124_ADC_CTRL_REG_MODE(SYSTEM_ZERO_SCALE); + break; + + case 4: + temp_val = AD7124_ADC_CTRL_REG_MODE(SYSTEM_FULL_SCALE); + break; + + default: + port.printf("\t***Illegal Entry***\n\n"); + return FAILURE; + } + ret = update_write_reg(dev, AD7124_ADC_Control, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + ad7124_wait_for_conv_ready(dev, SPI_POLL_COUNT); + port.printf("\tSuccessfully Calibrated\n\n"); + wait(1); + + /*Re-activate disabled channels*/ + for (int channel_index = 0; channel_index < NUM_OF_CHANNELS; channel_index++) { + if (active_channels[channel_index] == true) + channels[channel_index].enable = true; + } + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("\tSuccess\n"); + return SUCCESS; +} + +/* Choose the power mode of the device + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_select_power_mode(struct ad7124_dev * dev) +{ + uint8_t power_mode; + int8_t ret; + + /*Fill all structs with register map information from device*/ + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("\n\tSelect Filter\n"); + port.printf("\t=======================================\n"); + port.printf("\t 1 - Low Power Mode\n"); + port.printf("\t 2 - Medium Power Mode\n"); + port.printf("\t 3 - High Power Mode\n"); + port.printf("\t 4 - Main Menu\n"); + + port.scanf("%d", (int *) &power_mode); + if ((power_mode < 1) || (power_mode > 3)) + return FAILURE; + + /*Write to device*/ + channels[0].power_mode = power_mode - 1; + ret = write_device(dev); + if (err_detect(ret) == FAILURE) + return FAILURE; + + return SUCCESS; +} + +/* Print out the device ID. This is helpful for debugging SPI serial + * communication with the ADC + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_print_ID(struct ad7124_dev * dev) +{ + struct ad7124_st_reg *regs; + int8_t ret; + + regs = dev->regs; + + ret = ad7124_read_register(dev, ®s[AD7124_ID]); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("\tSuccessfully Read Device ID\n"); + port.printf("\tID: %#x\n", regs[AD7124_ID].value); + + return SUCCESS; +} + +/* Updates a specific bitfield for a particular register. This function allows + * for a register to only be altered in the area that is required + * + * Example to change power mode from low to medium power: + * + * (Mask of all ones) + * ones_mask = 0xFFFFFFFF; + * + * (Isolate power mode area) + * bitfield_mask = AD7124_ADC_CTRL_REG_POWER_MODE(ones_mask); + * + * (Provide new value) + * temp_val = AD7124_ADC_CTRL_REG_POWER_MODE(MED_POWER_MODE); + * + * (Update Value) + * update_write_reg(dev, AD7124_ADC_Control, bitfield_mask, temp_val; + * + * Parameters: Device handler, register, bitfield area mask (ones), value to be written + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t update_write_reg(struct ad7124_dev * dev, + uint8_t reg, uint32_t mask, uint32_t val) +{ + struct ad7124_st_reg *regs; + int8_t ret; + + regs = dev->regs; + + ret = ad7124_read_register(dev, ®s[reg]); + if (err_detect(ret) == FAILURE) + return FAILURE; + + regs[reg].value = (regs[reg].value & ~mask) | (val & mask); + ret = ad7124_write_register(dev, regs[reg]); + if (err_detect(ret) == FAILURE) + return FAILURE; + + return SUCCESS; +} + +/* Converts a single channel's sample from the ADC into voltage based off + * neccessary information about the setup associated with that channel + * + * Parameters: code from ADC, channel being sampled + * Return Value: None + */ +static void code_to_voltage(uint32_t code, uint8_t channel) +{ + uint8_t gain = 1 << (channels[channel].setup->gain); + uint8_t bipolar = channels[channel].setup->bipolar; + + if (bipolar) + channels[channel].sample = (((float)(code)/(1 << (NUM_BITS-1)))-1)*(2.5*gain); + else if (!bipolar) + channels[channel].sample = (float)code*VREF/((1 << NUM_BITS)*gain); +} + +/* Prints out all channel struct information in a user friendly format after + * reading the register map from the ADC. It only prints out channel member + * information, but can be modified to keep track of anything else. + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_print_channel_register_map_view_mode(struct ad7124_dev * dev) +{ + uint8_t channel = 0; + int8_t ret; + + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE ) + return FAILURE; + + port.printf("\tPower Mode: %s\n", power_strings[channels[channel].power_mode]); + port.printf("\t(Channel #, Enable, Setup, Gain, Filter, Filter Data Rate)\n"); + port.printf("\t=============================================================\n"); + for (channel = 0; channel < 16; channel++) { + port.printf("\tChannel %02d: %d, %d, %03d, %s, %d\n", + channel, channels[channel].enable, channels[channel].setup->setup_id, + 1 << channels[channel].setup->gain, filter_strings[channels[channel].setup->filter], + channels[channel].setup->data_rate); + } + return SUCCESS; +} + +/* Prints out the user friendly register map mode as well as the raw register map + * values. After selecting this function, the user can immediately copy over the + * current register map and paste it in the custom configuration register map file. + * This function waits for a key to be pressed to go back to the main menu to allow + * the user to easily copy the raw data from the serial monitor. + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_print_channel_register_map_raw_mode(struct ad7124_dev * dev) +{ + struct ad7124_st_reg *regs; + uint8_t reg_nr; + int8_t ret; + + regs = dev->regs; + + port.printf("=====================================================\n"); + port.printf(" COPY BELOW HERE \n"); + port.printf("=====================================================\n\n"); + + port.printf("/*\n\n"); + menu_print_channel_register_map_view_mode(dev); + port.printf("\n*/\n"); + + port.printf("#include \"ad7124_regs_configs.h\"\n\n"); + port.printf("struct ad7124_st_reg ad7124_regs_custom[AD7124_REG_NO] = {\n"); + for (reg_nr = AD7124_Status; reg_nr < AD7124_REG_NO; reg_nr++) { + ret = ad7124_read_register(dev, ®s[reg_nr]); + if (err_detect(ret) == FAILURE) + return FAILURE; + + port.printf("\t\t{0x%02x, 0x%06x, %d, %d},\n", + regs[reg_nr].addr, regs[reg_nr].value, + regs[reg_nr].size, regs[reg_nr].rw); + } + port.printf("};\n"); + /*Wait until user enters a command to allow time to copy information*/ + while(1) { + if (!port.readable()) + return SUCCESS; + } +} + +/* Print all of the setup's information in a user friendly format. + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t menu_print_setups(struct ad7124_dev * dev) +{ + uint8_t setup; + int8_t ret; + + ret = fill_structs(dev); + if (err_detect(ret) == FAILURE ) + return FAILURE; + + port.printf("\t(Setup #: Gain, Filter, Filter Data Rate)\n"); + port.printf("\t=============================================================\n"); + for (setup = 0; setup < NUM_OF_SETUPS; setup++) { + port.printf("\tSetup %d: %03d, %s, %d\n", + setup, 1 << setups[setup].gain, filter_strings[setups[setup].filter], + setups[setup].data_rate); + } + return SUCCESS; +} + +/* Function to fill up structs based off what has been written to the ADC's + * register map. Using this function along with "write_device", the user + * can at all times have an up to date register map that can be written to thea + * ADC's register map or updated with the most current values of the ADC. + * + * Parameters: Device handler + * Return Value: SUCCESS (0) , FAILURE (Negative) + */ +static int8_t fill_structs(struct ad7124_dev * dev) +{ + struct ad7124_st_reg *regs; + uint8_t reg_nr, index = 0; + int8_t ret; + + regs = dev->regs; + + for (reg_nr = AD7124_Status ; reg_nr < AD7124_Offset_0; reg_nr++) { + /*Read register information from device*/ + ret = ad7124_read_register(dev, ®s[reg_nr]); + if (err_detect(ret) == FAILURE) + return FAILURE; + + if (reg_nr == AD7124_ADC_Control) { + /*Update power mode (0th channel only)*/ + channels[index].power_mode = POWER_MODE_READ(regs[reg_nr].value); + + } else if ((reg_nr > AD7124_Mclk_Count) && (reg_nr < AD7124_Config_0)) { + /*Updating enable bit*/ + channels[index].enable = (regs[reg_nr].value & AD7124_CH_MAP_REG_CH_ENABLE); + + /*Updating setup*/ + channels[index].setup = &setups[SETUP_READ(regs[reg_nr].value)]; + + /*Updating setup id*/ + channels[index++].setup->setup_id = SETUP_READ(regs[reg_nr].value); + + /*Reset index*/ + if (reg_nr == AD7124_Channel_15) + index = 0; + + } else if ((reg_nr > AD7124_Channel_15) && (reg_nr < AD7124_Filter_0)) { + /*Updating bipolar bit*/ + setups[index].bipolar = (regs[reg_nr].value & AD7124_CFG_REG_BIPOLAR); + + /*Updating gain*/ + setups[index++].gain = GAIN_READ(regs[reg_nr].value); + + /*Reset index*/ + if (reg_nr == AD7124_Config_7) + index = 0; + + } else if ((reg_nr > AD7124_Config_7) && (reg_nr < AD7124_Offset_0)) { + /*Updating filter*/ + setups[index].filter = FILTER_READ(regs[reg_nr].value); + + /*Updating filter data rate*/ + setups[index++].data_rate = DATA_RATE_READ(regs[reg_nr].value); + } + } + return SUCCESS; +} + +/* Write back struct channel and setup struct values to the device. This + * function only writes back the members of those structs. This function can + * be modified in conjunction with the "fill_structs" function to add other + * fields that you would like to modify and keep track of. Ex. AINP, AINM values + * + * Parameters: Device handler + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t write_device(struct ad7124_dev * dev) +{ + uint32_t ones_mask = 0xFFFFFFFF; + uint32_t bitfield_mask, temp_val; + uint8_t reg_nr, index = 0; + int8_t ret; + + for (reg_nr = AD7124_Status; reg_nr < AD7124_Offset_0; reg_nr++) { + + if (reg_nr == AD7124_ADC_Control) { + + /*Write power mode*/ + bitfield_mask = AD7124_ADC_CTRL_REG_POWER_MODE(ones_mask); + temp_val = AD7124_ADC_CTRL_REG_POWER_MODE(channels[0].power_mode); + ret = update_write_reg(dev, AD7124_ADC_Control, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + } else if ((reg_nr > AD7124_Mclk_Count) && (reg_nr < AD7124_Config_0)) { + /*Set enable bits*/ + bitfield_mask = AD7124_CH_MAP_REG_CH_ENABLE; + temp_val = (channels[index].enable == 1) ? AD7124_CH_MAP_REG_CH_ENABLE: + !AD7124_CH_MAP_REG_CH_ENABLE; + /*Write to device*/ + ret = update_write_reg(dev, reg_nr, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Set setup bits*/ + bitfield_mask = AD7124_CH_MAP_REG_SETUP(ones_mask); + temp_val = AD7124_CH_MAP_REG_SETUP(channels[index].setup->setup_id); + ret = update_write_reg(dev, reg_nr, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + index++; + /*Reset index*/ + if (reg_nr == AD7124_Channel_15) + index = 0; + + } else if ((reg_nr > AD7124_Channel_15) && (reg_nr < AD7124_Filter_0)) { + /*Set gain bits*/ + bitfield_mask = AD7124_CFG_REG_PGA(ones_mask); + temp_val = AD7124_CFG_REG_PGA(setups[index++].gain); + + /*Write to device*/ + ret = update_write_reg(dev, reg_nr, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Reset index*/ + if (reg_nr == AD7124_Config_7) + index = 0; + + } else if ((reg_nr > AD7124_Config_7) && (reg_nr < AD7124_Offset_0)) { + /*Set filter bits*/ + bitfield_mask = AD7124_FILT_REG_FILTER(ones_mask); + temp_val = AD7124_FILT_REG_FILTER(setups[index].filter); + ret = update_write_reg(dev, reg_nr, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + + /*Set data rate bits*/ + bitfield_mask = AD7124_FILT_REG_FS(ones_mask); + temp_val = AD7124_FILT_REG_FS(setups[index++].data_rate); + ret = update_write_reg(dev, reg_nr, bitfield_mask, temp_val); + if (err_detect(ret) == FAILURE) + return FAILURE; + } + } + return SUCCESS; +} + +/* Prints out the error message corresponding to the No-OS drivers functions + * + * Parameters: Return value from some function + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t err_detect(int8_t ret) +{ + switch(ret) { + case -1: + port.printf("\tInvalid Argument...\n"); + wait(1); + return FAILURE; + + case -2: + port.printf("\tCommunication Error on Receive...\n"); + wait(1); + return FAILURE; + + case -3: + port.printf("\tA Timeout has Occured...\n"); + wait(1); + return FAILURE; + + default: + return SUCCESS; + } +} + +/* Obtains the setup that the user would like to select + * + * Parameters: None + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t setup_select(uint8_t *setup) +{ + port.printf("\t===============================\n"); + port.printf("\t Setup (0 - 7)\n"); + port.printf("\t 8 - Main Menu\n\n"); + + port.scanf("%d", (int *) setup); + if (*setup > 7) + return FAILURE; + + return SUCCESS; +} + +/* Obtains the channel that the user would like to select + * + * Parameters: None + * Return Value: SUCCESS (0), FAILURE (Negative) + */ +static int8_t channel_select(uint8_t *channel) +{ + port.printf("\t===============================\n"); + port.printf("\t Channel (0 - 15)\n"); + port.printf("\t 16 - Main Menu\n\n"); + + port.scanf("%d", (int *) channel); + if (*channel > 15) + return FAILURE; + + return SUCCESS; +} \ No newline at end of file