AD7124 Example Program

Dependencies:   platform_drivers AD7124_no_OS

Revision:
2:0897873979f3
Parent:
0:83a0c7bbe493
diff -r cbad662d8c2c -r 0897873979f3 main.cpp
--- 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, &regs[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, &regs[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, &regs[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, &regs[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, &regs[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, &regs[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