AD7124 Example Program
Dependencies: platform_drivers AD7124_no_OS
main.cpp
- Committer:
- MitchAD
- Date:
- 2019-09-05
- Revision:
- 2:0897873979f3
- Parent:
- 0:83a0c7bbe493
File content as of revision 2:0897873979f3:
/* Copyright (c) 2019 Analog Devices, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Modified versions of the software must be conspicuously marked as such. - This software is licensed solely and exclusively for use with processors/products manufactured by or for Analog Devices, Inc. - This software may not be combined or merged with other code in any manner that would cause the software to become subject to terms and conditions which differ from those listed here. - Neither the name of Analog Devices, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - The use of this software may or may not infringe the patent rights of one or more patent holders. This license does not release you from the requirement that you obtain separate licenses from these patent holders to use this software. THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, TITLE, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANALOG DEVICES, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, DAMAGES ARISING OUT OF CLAIMS OF INTELLECTUAL PROPERTY RIGHTS INFRINGEMENT; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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" #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; 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); 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() { 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; }