Sean Smith
/
WIP
Initial Commit
main.cpp
- Committer:
- ssmith73
- Date:
- 2019-05-28
- Revision:
- 1:384a1737b0df
File content as of revision 1:384a1737b0df:
/*************************************************************************//** * @file main.cpp * @brief Main application code for AD5686 firmware example program * @author ssmith (sean.smith@analog.com) ****************************************************************************** * 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. * * 20180927-7CBSD SLA *****************************************************************************/ #include <stdio.h> #include <mbed.h> #include "app_config.h" #define NOT_USED 0 #define WAIT_MENU_TIME 1 static void print_title(void); static float get_voltage_float(void); static int getMenuSelect(uint8_t *menuSelect); static void print_prompt(int16_t selected_dac, float ref_voltage); static uint16_t voltage_to_code(float voltage, float vRef); static uint8_t menu_1_select_dac(uint16_t *selected_dac); static void menu_2_write_to_input_register(int16_t selected_dac, float vref); static void menu_3_update_dac(int16_t selected_dac); static void menu_4_write_and_update_dac(int16_t selected_dac, float vref); static int8_t menu_5_power_down_mode(int16_t selected_dac); static int8_t menu_6_select_ref_voltage(float *vref); static void menu_7_read_back_registers(); static int8_t menu_8_set_ldac_mask(); static void menu_9_assert_ldac(); static int8_t menu_10_set_gain(); static void menu_11_assert_soft_reset(); static void menu_12_assert_hard_reset(); static uint16_t get_voltage_code(float vRef); static uint8_t selectChannel(uint16_t selectedDac); static void set_default_pins(void); /******************************************************************************/ /************************** Variables Declarations ****************************/ /******************************************************************************/ extern ad5686_chip_info chip_info[]; /** The following definitions are a requirement for the platform_driver Pin are changed if required in the app_config.h file ***/ DigitalOut SS(SPI_CS); mbed::SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK); mbed::I2C i2c(I2C_SDA, I2C_SCL); i2c_init_param i2c_params = { // i2c type GENERIC_I2C, // i2c id NOT_USED, // i2c max speed (hz) 100000, // i2c slave address (AD5693) //0x98 // i2c slave address (AD5696) 0x18 }; spi_init_param spi_params = { MBED, //spi_type GENERIC_SPI, //id NOT_USED, //frequency 2000000, // SPI mode SPI_MODE_2, //CS mapping SPI_CS, }; gpio_desc gpio_gain = { GENERIC_GPIO, NOT_USED, NOT_USED, GAIN_PIN }; gpio_desc gpio_ldac = { GENERIC_GPIO, NOT_USED, NOT_USED, LDAC_PIN }; gpio_desc gpio_reset = { GENERIC_GPIO, //TYPE NOT_USED, //ID NOT_USED, //NUMBER RESET_PIN }; ad5686_init_param init_params = { // I2C parameters i2c_params, // SPI parameters spi_params, //GPIO's gpio_reset, gpio_ldac, gpio_gain, //Set this in app_config.h ACTIVE_DEVICE, }; ad5686_dev *device; int32_t connected = -1; uint8_t gpioGainVal = 0; //! Configure and instantiate the UART Serial pc(USBTX, USBRX, 115200); int main() { uint8_t menuSelect = 0; uint16_t selected_dac = 1; float ref_voltage = 2.5; print_title(); connected = ad5686_init(&device, init_params); set_default_pins(); while (connected == SUCCESS) { // Switch menu based on user's input print_prompt(selected_dac, ref_voltage); if (getMenuSelect(&menuSelect) == FAILURE) print_prompt(selected_dac, ref_voltage); else switch (menuSelect) { case 1: menu_1_select_dac(&selected_dac); break; case 2: menu_2_write_to_input_register(selected_dac, ref_voltage); break; case 3: menu_3_update_dac(selected_dac); break; case 4: menu_4_write_and_update_dac(selected_dac, ref_voltage); break; case 5: menu_5_power_down_mode(selected_dac); break; case 6: menu_6_select_ref_voltage(&ref_voltage); break; case 7: menu_7_read_back_registers(); break; case 8: menu_8_set_ldac_mask(); break; case 9: menu_9_assert_ldac(); break; case 10: menu_10_set_gain(); break; case 11: menu_11_assert_soft_reset(); break; case 12: menu_12_assert_hard_reset(); break; default : pc.printf("Incorrect Option\n"); break; } //Allow some time before menu switches wait(1); } //Should never be reached return 0; } static void print_title() { pc.printf("*****************************************************************\n"); pc.printf("* EVAL-nanoDAC+ Demonstration Program for mbed *\n"); pc.printf("* *\n"); pc.printf("* This program demonstrates communication with the nanoDAC+ *\n"); pc.printf("* family of Rail-to-Rail DACs with SPI/I2C Interface. *\n"); pc.printf("* *\n"); pc.printf("* Set the baud rate to 115200 *\n"); pc.printf("* For Compile requirements see the user-guide *\n"); pc.printf("*****************************************************************\n"); } /*************************************************************************//** * @brief - Prints the "main menu" prompt to the console * * @param selected_dac - only 1,2,4,8 (DAC A/B/C/D) are valid * @param ref_voltage - For displaying in the UI * * @return None. *****************************************************************************/ static void print_prompt(int16_t selected_dac, float ref_voltage) { pc.printf("\n\nCommand Summary:\n\n"); if (chip_info[device->act_device].register_map == AD5686_REG_MAP) { pc.printf(" 1 -Select DAC\n"); pc.printf(" 2 -Write to input register (no update)\n"); pc.printf(" 3 -Update DAC from input\n"); pc.printf(" 4 -Write and update DAC\n"); pc.printf(" 5 -Power down mode\n"); pc.printf(" 6 -Select reference voltage\n"); pc.printf(" 7 -Read back all registers\n"); pc.printf(" 8 -Set LDAC# mask\n"); pc.printf(" 9 -Assert LDAC#\n"); pc.printf(" 10-Set gain\n"); pc.printf(" 11-Assert Software Reset\n"); pc.printf(" 12-Assert Hardware Reset\n"); pc.printf("\n"); pc.printf("\n Reference Voltage: %f\n", ref_voltage); switch (selected_dac) { case 1:{ pc.printf(" DAC A is currently selected\n"); break; } case 2:{ pc.printf(" DAC B is currently selected\n"); break; } case 4:{ pc.printf(" DAC C is currently selected\n"); break; } case 8:{ pc.printf(" DAC D is currently selected\n"); break; } default: { pc.printf(" Please select a DAC (Option 1):\n"); selected_dac = 0; } } } else { /* * This code runs for products in the nanoDAC+ family that have the * reduced register map */ pc.printf(" 1 -NOP: Do nothing\n"); pc.printf(" 2 -Write Input register\n"); pc.printf(" 3 -Software LDAC\n"); pc.printf(" 4 -Write to DAC and Input registers\n"); pc.printf(" 5 -Write to control registers\n"); pc.printf("\n"); pc.printf("\n Reference Voltage: %f\n", ref_voltage); } } static int getMenuSelect(uint8_t *menuSelect) { int inp = pc.scanf("%2d", (int *) menuSelect); if (inp == 0 || *menuSelect > 12) { pc.printf("\n\n***** Invalid entry: ignoring *****\n"); wait(WAIT_MENU_TIME); return FAILURE; } else return SUCCESS; } /*************************************************************************//** * @brief - Select active DAC - only one channel can be active * * @param selected_dac - only 1,2,4,8 (DAC A/B/C/D) are valid * * @return None. *****************************************************************************/ static uint8_t menu_1_select_dac(uint16_t *selected_dac) { char string[5] = "0001"; char *str = string; int8_t cnt = 3; uint16_t dac = 0; pc.printf(" \nDAC selections are represented in binary with\n"); pc.printf(" bits corresponding to: DCBA\n"); pc.printf(" (See datasheet for details and explanation)\n"); pc.printf(" For example:\n\n"); pc.printf(" 0001 - DAC A\n"); pc.printf(" 0010 - DAC B\n"); pc.printf(" 0100 - DAC C\n"); pc.printf(" 1000 - DAC D\n"); pc.printf(" i.e. Enter 4 binary digits. All other values will be ignored\n"); pc.printf(" Enter DAC to write to: "); pc.scanf("%4s", string); while (cnt >= 0) { if ((*str != '1' && *str != '0') || *str == '\0') { pc.printf("\nInvalid entry - exactly 4 binary characters are required\n"); dac = 0; break; } else if (*str++ == '1') dac |= (1 << cnt); cnt--; } switch (dac) { case 1:{ pc.printf("You selected DAC A\n"); *selected_dac = dac; break; } case 2:{ pc.printf("You selected DAC B\n"); *selected_dac = dac; break; } case 4:{ pc.printf("You selected DAC C\n"); *selected_dac = dac; break; } case 8:{ pc.printf("You selected DAC D\n"); *selected_dac = dac; break; } default: { pc.printf("\n Please select DAC in the format described above:\n"); pc.printf("\n No changes made:\n"); wait(2); } } return SUCCESS; // Always returns success, consider adding a fail code later. } /*************************************************************************//** * @brief Write to input-register - do not update DAC * * @param selected_dac - only 1,2,4,8 (DAC A/B/C/D) are valid * * @return None. *****************************************************************************/ static void menu_2_write_to_input_register(int16_t selected_dac, float vref) { unsigned short vdata = get_voltage_code(vref); enum ad5686_dac_channels chan = (ad5686_dac_channels)selectChannel(selected_dac); ad5686_write_register(device, chan, vdata); } /*************************************************************************//** * @brief - Updates DAC outputs from its input registers * * @param selected_dac - Selected DAC (1,2,4,8 -> DAC A,B,C,D) *****************************************************************************/ static void menu_3_update_dac(int16_t selected_dac) { enum ad5686_dac_channels chan = (ad5686_dac_channels)selectChannel(selected_dac); ad5686_update_register(device, chan); pc.printf(" Updated DAC(s)\n"); wait(3); } /*************************************************************************//** * @brief - Write a value to the select_dac * * @param selected_dac - Selected DAC (1,2,4,8 -> DAC A,B,C,D) * @param vRef - Reference voltage *****************************************************************************/ static void menu_4_write_and_update_dac(int16_t selected_dac, float vref) { unsigned short vdata = get_voltage_code(vref); pc.printf("Entered code: %x ", vdata); //!Convert from user-selected 2**n dac-select to the enum //!used in the NoOS driver enum ad5686_dac_channels chan = (ad5686_dac_channels)selectChannel(selected_dac); ad5686_write_update_register(device, chan, vdata); } /*************************************************************************//** * @brief - Write a value to the select_dac * * @param selected_dac - Selected DAC (1,2,4,8 -> DAC A,B,C,D) * @param vRef - Reference voltage * * @return - currently active channel *****************************************************************************/ static uint8_t selectChannel(uint16_t selectedDac) { enum ad5686_dac_channels chan; switch (selectedDac) { case 1 : {chan = AD5686_CH_0; break; } case 2 : {chan = AD5686_CH_1; break; } case 4 : {chan = AD5686_CH_2; break; } case 8 : {chan = AD5686_CH_3; break; } case 16 : {chan = AD5686_CH_4; break; } case 32 : {chan = AD5686_CH_5; break; } case 64 : {chan = AD5686_CH_6; break; } case 128: {chan = AD5686_CH_7; break; } default: { break; } } return (uint8_t)chan; } /*************************************************************************//** * @brief - Configure the power-down mode * * @param selected_dac - Selected DAC (1,2,4,8 -> DAC A,B,C,D) * @param vRef - Reference voltage * * @return - PASS/FAIL flag - currently unused *****************************************************************************/ static int8_t menu_5_power_down_mode(int16_t selected_dac) { // Cancel if no DAC selected if(selected_dac == 0) { pc.printf(" No DAC selected, no changes made"); return FAILURE; } // Prompt for power mode printf("\n Power Modes:\n"); printf(" 1-Normal Operation\n"); printf(" 2-1kOhm to GND Power-Down\n"); printf(" 3-100kOhm to GND Power-Down\n"); printf(" 4-Three-State Power-Down\n"); printf(" Select a power mode: "); // Get input int mode_input; int inp = pc.scanf("%1d", &mode_input); if (inp == 0) { pc .printf("***** Invalid entry: ignoring *****\n"); wait(3); } uint8_t selected_mode = 0; // Basic input validation if(mode_input > 4 || mode_input < 1) { printf("Invalid selection - no changes made\n"); wait(2); return SUCCESS; } else // Show validated input on console pc.printf("\nSelected DAC: %d", mode_input); // Select proper power mode switch(mode_input) { case 1: selected_mode = AD5686_PWRM_NORMAL; break; case 2: selected_mode = AD5686_PWRM_1K; break; case 3: selected_mode = AD5686_PWRM_100K; break; case 4: selected_mode = AD5686_PWRM_THREESTATE; break; default: break; } // Check bit-wise which DACs are selected uint8_t dac1 = (selected_dac >> 0) & 1; uint8_t dac2 = (selected_dac >> 1) & 1; uint8_t dac3 = (selected_dac >> 2) & 1; uint8_t dac4 = (selected_dac >> 3) & 1; // Apply power to selected DACS if(dac1) { pc.printf(" Applying power mode to DAC A..."); ad5686_power_mode(device, AD5686_CH_0, selected_mode); } if (dac2) { pc.printf(" Applying power mode to DAC B..."); ad5686_power_mode(device, AD5686_CH_1, selected_mode); } if (dac3) { pc.printf(" Applying power mode to DAC C..."); ad5686_power_mode(device, AD5686_CH_2, selected_mode); } if (dac4) { pc.printf(" Applying power mode to DAC D..."); ad5686_power_mode(device, AD5686_CH_3, selected_mode); } pc.printf(" Done!"); return SUCCESS; } /*************************************************************************//** * @brief - Select internal or external reference * * @param vRef - Reference voltage * * @return - PASS/FAIL flag - currently unused *****************************************************************************/ static int8_t menu_6_select_ref_voltage(float *vref) { // Prompt for internal or external pc.printf(" 1-Internal (2.5v)\n"); pc.printf(" 2-External\n"); pc.printf(" Select a reference voltage source:"); int vref_source; int inp = pc.scanf("%1d", &vref_source); if (inp == 0) { pc .printf("***** Invalid entry: ignoring *****\n"); wait(3); } if (vref_source == INTERNAL) pc.printf("\nInternal selected\n"); else if (vref_source == EXTERNAL) pc.printf(" External selected\n"); else { pc.printf("\nInvalid Entry: No changes made\n"); wait(2); return FAILURE; } float fvref = 0; // Custom vref switch(vref_source) { case 1: *vref = 2.5; break; case 2: // If external, prompt for exact vref pc.printf(" Enter selected external reference voltage:"); inp = pc.scanf("%f", &fvref); if (inp == 0) { pc .printf("***** Invalid entry: ignoring *****\n"); wait(3); } pc.printf("%fV selected\n", fvref); *vref = fvref; wait(2); break; default: pc.printf(" Incorrect entry\n"); wait(2); break; } return SUCCESS; } /*************************************************************************//** * @brief - Reads back all DAC registers *****************************************************************************/ static void menu_7_read_back_registers() { uint32_t reg1 = ad5686_read_back_register(device, AD5686_CH_0); uint32_t reg2 = ad5686_read_back_register(device, AD5686_CH_1); uint32_t reg3 = ad5686_read_back_register(device, AD5686_CH_2); uint32_t reg4 = ad5686_read_back_register(device, AD5686_CH_3); pc.printf("\n All DAC register values:\n"); pc.printf(" DAC A - %x\n", (unsigned int) reg1); pc.printf(" DAC B - %x\n", (unsigned int) reg2); pc.printf(" DAC C - %x\n", (unsigned int) reg3); pc.printf(" DAC D - %x\n", (unsigned int) reg4); } /*************************************************************************//** * @brief - Set the LDAC mask bits * * @return - PASS/FAIL flag - currently unused *****************************************************************************/ static int8_t menu_8_set_ldac_mask() { int enable; int inp; enum ad5686_dac_channels channel; pc.printf("\n Enter channel to update as a decimal number (0-n)\n"); pc.printf(" If masked, selected channel will not be updated when LDAC is asserted\n"); pc.printf(" Channel: "); inp = pc.scanf("%2d", (int *)&channel); if (inp == 0) { pc .printf("***** Invalid entry: ignoring *****\n"); wait(3); } pc.printf("\n Enter LDAC mask value (1 set, 0 clear) as a decimal number\n"); pc.printf(" Mask: "); inp = pc.scanf("%1d", &enable); if (inp == 0) { pc .printf("***** Invalid entry: ignoring *****\n"); wait(3); } switch (enable) { case 0: pc.printf("\n\nClearing LDAC mask\n"); break; case 1: pc.printf("\n\nSettting LDAC mask\n"); break; default: pc.printf("\n\nInvalid entry: No changes made\n"); return FAILURE; } if (enable) pc.printf("\n\nSet LDAC mask for channel %d\n", channel); else pc.printf("Cleared LDAC mask for channel %d\n", channel); ad5686_ldac_mask(device, channel, enable); return SUCCESS; } /*************************************************************************//** * @brief - Toggle the LDAC pin and update the DAC(s) *****************************************************************************/ static void menu_9_assert_ldac() { gpio_direction_output(device->gpio_ldac, GPIO_LOW); // Wait to allow for controller latency wait(0.1); gpio_direction_output(device->gpio_ldac, GPIO_HIGH); pc.printf(" Asserted LDAC\n"); } /*************************************************************************//** * @brief - Set the GAIN pin * * @return - PASS/FAIL flag - currently unused *****************************************************************************/ static int8_t menu_10_set_gain() { pc.printf(" GAIN options: \n"); pc.printf(" 1-Low gain (1x)\n"); pc.printf(" 2-High gain (2x)\n"); pc.printf(" Make selection: \n"); int selected_gain; int inp = pc.scanf("%d", &selected_gain); if (inp == 0) { pc .printf("***** Invalid entry: ignoring *****\n"); wait(3); } if (selected_gain > 2 || selected_gain < 1) { pc.printf("Invalid selection - no changes made\n"); wait(2); return FAILURE; } else pc.printf("Selected gain: %d\n", selected_gain); switch (selected_gain) { case 1: //AD5686_GAIN_LOW; gpio_gain.id = NOT_USED; gpio_set_value(&gpio_gain, GPIO_LOW); pc.printf(" Setting gain low"); gpioGainVal = 1; break; case 2: gpio_set_value(device->gpio_gain, GPIO_HIGH); pc.printf(" Setting gain high\n"); gpioGainVal = 0; break; default: break; } return SUCCESS; } /*************************************************************************//** * @brief - Do a soft (software) reset *****************************************************************************/ static void menu_11_assert_soft_reset() { pc.printf(" Performing software reset\n"); ad5686_software_reset(device); } /*************************************************************************//** * @brief - Do a hardware (gpio) reset *****************************************************************************/ static void menu_12_assert_hard_reset() { // Pull reset low then high pc.printf(" Performing hardware reset\n"); gpio_direction_output(device->gpio_reset, GPIO_LOW); // Wait to allow for controller latency wait(0.1); gpio_set_value(device->gpio_reset, GPIO_HIGH); } /*************************************************************************//** * @brief - Configure the GPIO pins to default values *****************************************************************************/ static void set_default_pins() { gpio_set_value(device->gpio_reset, GPIO_HIGH); gpio_set_value(device->gpio_gain, GPIO_LOW); gpio_set_value(device->gpio_ldac, GPIO_LOW); } /*************************************************************************//** * @brief Gets a voltage from the user and converts * it to the code the DAC understands * * @param vRef - voltage value to convert - 0 to VREF * * @return - corrosponding code value *****************************************************************************/ static uint16_t get_voltage_code(float vRef) { return voltage_to_code(get_voltage_float(), vRef); } /*************************************************************************//** * @brief - Convert voltage float to code the DAC understands * * @param voltage - voltage value to convert - 0 to VREF * @param vRef - Reference voltage * * @return - corrosponding code value *****************************************************************************/ static uint16_t voltage_to_code(float voltage, float vRef) { pc.printf("GAIN PIN STATE: %d\n", gpioGainVal); if (gpioGainVal == GPIO_HIGH) vRef *= 2; uint32_t max_code = ((uint32_t)1 << 16) - 1; return (unsigned short)(voltage * (float)max_code / vRef); } /*************************************************************************//** * @brief - Gets a voltage from the user * * @return - voltage as a float *****************************************************************************/ static float get_voltage_float() { float dac_voltage; pc.printf(" Enter Desired DAC output voltage: "); wait_ms(1); int inp = pc.scanf("%f", &dac_voltage); if (inp == 0) { pc .printf("***** Invalid entry: ignoring *****\n"); wait(3); } pc.printf("%f V\n", dac_voltage); return dac_voltage; }