/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** STM32F1 BusVoodoo application * @file application.c * @author King Kévin * @date 2016-2017 */ /* standard libraries */ #include // standard integer types #include // standard utilities #include // string utilities #include // math utilities /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // vector table definition #include // interrupt utilities #include // general purpose input output library #include // real-time control clock library #include // external interrupt utilities #include // independent watchdog utilities #include // debug utilities #include // flash utilities #include // design utilities #include // ADC utilities #include // DAC utilities /* own libraries */ #include "global.h" // board definitions #include "print.h" // printing utilities #include "usart.h" // USART utilities #include "usb_cdcacm.h" // USB CDC ACM utilities //#include "rs.h" // RS-232/485 utilities #include "i2c_master.h" #define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */ /** @defgroup busvoodoo_peripherals peripheral pin definitions * @{ */ #define BUSVOODOO_5VPULLUP_PORT A /**< 5V pull-up enable pin (active low) */ #define BUSVOODOO_5VPULLUP_PIN 15 /**< 5V pull-up enable pin (active low) */ #define BUSVOODOO_XVPULLUP_PORT B /**< xV pull-up enable pin (active low) */ #define BUSVOODOO_XVPULLUP_PIN 3 /**< xV pull-up enable pin (active low) */ #define BUSVOODOO_XVEN_PORT A /**< xV enable pin (active high) */ #define BUSVOODOO_XVEN_PIN 6 /**< xV enable pin (active high) */ #define BUSVOODOO_12VEN_PORT A /**< 12V enable pin (active high) */ #define BUSVOODOO_12VEN_PIN 7 /**< 12V enable pin (active high) */ #define BUSVOODOO_RS232EN_PORT C /**< RS-232 enable pin (active low) */ #define BUSVOODOO_RS232EN_PIN 13 /**< RS-232 enable pin (active low) */ #define BUSVOODOO_VOUTEN_PORT B /**< voltage output (5V and 3.3V) enable pin (active low) */ #define BUSVOODOO_VOUTEN_PIN 4 /**< voltage output (5V and 3.3V) enable pin (active low) */ /** @} */ /** @defgroup busvoodoo_adc inputs to measure voltages * @{ */ #define BUSVOODOO_3V3_CHANNEL 12 /**< ADC channel to measure 5V rail */ #define BUSVOODOO_5V_CHANNEL 13 /**< ADC channel to measure 3.3V rail */ #define BUSVOODOO_XV_CHANNEL 11 /**< ADC channel to measure xV rail */ #define BUSVOODOO_12V_CHANNEL 10 /**< ADC channel to measure 12V rail */ /** @} */ #define BUSVOOFOO_XVCTL_CHANNEL CHANNEL_1 /**< DAC channel to control xV output voltage */ #define BUSVOODOO_XV_DEFAULT (0.8*(1+33.0/10.0)) /**< default (when not driven) xV voltage regulator output voltage based on R1 and R2 */ #define BUSVOODOO_XV_TEST 2.5 /**< target xV output voltage to test if we can set control the xV voltage regulator */ #define BUSVOODOO_XV_SET(x) ((0.8*(1+33.0/10.0)-x)*(10.0/33.0)+0.8) /**< voltage to output for the DAC to set the desired xV output voltage (based on resistor values on the xV adjust pins and xV voltage reference) */ #define BUSVOOFOO_12VCTL_CHANNEL CHANNEL_2 /**< DAC channel to control 12V output voltage */ #define BUSVOODOO_12V_DEFAULT (1.25*(1+100.0/10.0)) /**< default (when not driven) 12V voltage regulator output voltage based on R1 and R2 */ #define BUSVOODOO_12V_TEST 12.0 /**< target 12V output voltage to test if we can set control the 12V voltage regulator */ #define BUSVOODOO_12V_SET(x) ((1.25*(1+100.0/10.0)-x)*(10.0/100.0)+1.25) /**< voltage to output for the DAC to set the desired 12V output voltage (based on resistor values on the 12V adjust pins and 12V voltage reference) */ /** @defgroup busvoodoo_io I/O pin definitions * @{ */ static const char* busvoodoo_io_names[13] = {"I2C_SMBA/SPI_NSS/I2S_WS", "SDIO_CMD", "USART_CTS/SPI_SCK/I2S_CK", "SDIO_D3/UART_RX", "I2C_SDA/USART_RX", "SDIO_D0", "SPI_MOSI/I2S_SD", "SDIO_CK/USART_CK", "I2C_SCL/USART_TX", "SDIO_D1", "I2S_MCK", "USART_RTS/SPI_MISO", "SDIO_D2/UART_TX"}; /**< I/O individual signal names */ static const uint32_t busvoodoo_io_ports[13] = {GPIOB, GPIOD, GPIOB, GPIOC, GPIOB, GPIOC, GPIOB, GPIOC, GPIOB, GPIOC, GPIOC, GPIOB, GPIOC}; /**< port of individual signals */ static const uint32_t busvoodoo_io_pins[13] = {GPIO12, GPIO2, GPIO13, GPIO11, GPIO11, GPIO8, GPIO15, GPIO12, GPIO10, GPIO9, GPIO6, GPIO14, GPIO10}; /**< pin of individual signals */ // ideal pinout //static const uint8_t busvoodoo_io_groups[13] = {1, 1, 3, 3, 5, 5, 2, 2, 4, 4, 6, 6, 6}; /**< which I/O (group) does the signal belong to */ // schematic/layout v0.001 pinout (SDIO_D2/UART_TX landed in group 6 instead of 3) static const uint8_t busvoodoo_io_groups[13] = {1, 1, 3, 3, 5, 5, 2, 2, 4, 4, 6, 6, 3}; /**< which I/O (group) does the signal belong to */ /** @} */ size_t putc(char c) { size_t length = 0; // number of characters printed static char newline = 0; // to remember on which character we sent the newline if (0==c) { length = 0; // don't print string termination character } else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r) usart_putchar_nonblocking('\r'); // send CR over USART usb_cdcacm_putchar('\r'); // send CR over USB usart_putchar_nonblocking('\n'); // send LF over USART usb_cdcacm_putchar('\n'); // send LF over USB length += 2; // remember we printed 2 characters newline = c; // remember on which character we sent the newline } else { length = 0; // the \r or \n of \n\r or \r\n has already been printed } } else { usart_putchar_nonblocking(c); // send byte over USART usb_cdcacm_putchar(c); // send byte over USB newline = 0; // clear new line length++; // remember we printed 1 character } return length; // return number of characters printed } static bool wait_space(void) { // disable watchdog when waiting for user input printf("press space to continue, or any other key to abort\n"); while (!usart_received && !usb_cdcacm_received) { // wait for user input __WFI(); // go to sleep } char c = 0; if (usart_received) { c = usart_getchar(); // read user input from UART } else if (usb_cdcacm_received) { c = usb_cdcacm_getchar(); // read user input from USB } else { return false; // this should not happen } if (' '==c) { // space entered return true; } else { // something else entered return false; } } /** set safe state by disabling all outputs */ static void safe_state(void) { // disable voltage outputs gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable 5V and 3.3V output on connector gpio_set_mode(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(BUSVOODOO_VOUTEN_PIN)); // set pin as output (open-drain pulled high to disable the pMOS) gpio_clear(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // disable xV voltage regulator gpio_set_mode(GPIO(BUSVOODOO_XVEN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_XVEN_PIN)); // set pin as output (push-pull, pulled low for safety) gpio_clear(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // disable 12V voltage regulator gpio_set_mode(GPIO(BUSVOODOO_12VEN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_12VEN_PIN)); // set pin as output (push-pull, pulled low for safety) // disable embedded pull-ups gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); // disable JTAG (but keep SWD) so to use the underlying GPIOs (PA15, PB3, PB4) gpio_set(GPIO(BUSVOODOO_5VPULLUP_PORT), GPIO(BUSVOODOO_5VPULLUP_PIN)); // set pin high to disable 5V embedded pull-up gpio_set_mode(GPIO(BUSVOODOO_5VPULLUP_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(BUSVOODOO_5VPULLUP_PIN)); // set pin as output (open-drain pulled high to disable the pMOS) gpio_set(GPIO(BUSVOODOO_XVPULLUP_PORT), GPIO(BUSVOODOO_XVPULLUP_PIN)); // set pin high to disable xV embedded pull-up gpio_set_mode(GPIO(BUSVOODOO_XVPULLUP_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(BUSVOODOO_XVPULLUP_PIN)); // set pin as output (open-drain pulled high to disable the pMOS) // disable all signal I/O outputs for (uint8_t pin=0; pin5.5) { printf("5V power rail voltage is too high: %d.%02uV, check USB port\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } // check 3.3V power rail voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get 3.3V power rail voltage if (voltage<3.0) { printf("3.3V power rail voltage is too low: %d.%02uV, check OLED connector and voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } else if (voltage>3.6) { printf("3.3V power rail voltage is too high: %d.%02uV, check OLED connector and voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } // check xV voltage regulator gpio_set(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // enable xV voltage regulator sleep_ms(1); // let the voltage regulator start and voltage settle voltage = rail_voltage(BUSVOODOO_XV_CHANNEL); // get xV voltage // without being driven it should be around the default voltage if (voltageBUSVOODOO_XV_DEFAULT+0.2) { printf("xV voltage is too high: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } // check if we can control xV voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get reference voltage if (isnan(voltage)) { printf("can get 3V3 rail voltage"); goto error; } uint16_t dac_set = BUSVOODOO_XV_SET(BUSVOODOO_XV_TEST)/voltage*4095; // DAC value corresponding to the voltage dac_load_data_buffer_single(dac_set, RIGHT12, BUSVOOFOO_XVCTL_CHANNEL); // set output so the voltage regulator is set to 2.5V dac_software_trigger(BUSVOOFOO_XVCTL_CHANNEL); // transfer the value to the DAC dac_enable(BUSVOOFOO_XVCTL_CHANNEL); // enable DAC sleep_ms(5); // let voltage settle voltage = rail_voltage(BUSVOODOO_XV_CHANNEL); // get xV voltage // check if it matched desired voltage if (voltage<-BUSVOODOO_XV_TEST-0.2) { printf("xV voltage is too low: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } else if (voltage>BUSVOODOO_XV_TEST+0.2) { printf("xV voltage is too high: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } dac_disable(BUSVOOFOO_XVCTL_CHANNEL); // disable xV control gpio_clear(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // disable xV voltage regulator sleep_ms(1); // let voltage settle // check 12V voltage regulator gpio_set(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // enable 12V voltage regulator sleep_ms(1); // let the voltage regulator start and voltage settle voltage = rail_voltage(BUSVOODOO_12V_CHANNEL); // get 12V voltage // without being driven it should be around the default voltage if (voltageBUSVOODOO_12V_DEFAULT+0.3) { printf("12V voltage is too high: %d.%02uV, check 12V voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } // check if we can control 12V voltage regulator voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get reference voltage if (isnan(voltage)) { printf("can get 3V3 rail voltage"); goto error; } dac_set = BUSVOODOO_12V_SET(BUSVOODOO_12V_TEST)/voltage*4095; // DAC value corresponding to the voltage dac_load_data_buffer_single(dac_set, RIGHT12, BUSVOOFOO_12VCTL_CHANNEL); // set output so the voltage regulator is set to desired output voltage dac_software_trigger(BUSVOOFOO_12VCTL_CHANNEL); // transfer the value to the DAC dac_enable(BUSVOOFOO_12VCTL_CHANNEL); // enable DAC sleep_ms(5); // let voltage settle voltage = rail_voltage(BUSVOODOO_12V_CHANNEL); // get 12V voltage if (voltage<-BUSVOODOO_12V_TEST-0.3) { printf("12V voltage is too low: %d.%02uV, check 12V voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } else if (voltage>BUSVOODOO_12V_TEST+0.3) { printf("12V voltage is too high: %d.%02uV, check 12V voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } dac_disable(BUSVOOFOO_12VCTL_CHANNEL); // disable 12V control gpio_clear(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // disable 12V voltage regulator sleep_ms(1); // let voltage settle // pull all pins down and ensure they are low for (uint8_t pin=0; pin5.5) { printf("5V power rail voltage is too high: %d.%02uV, check USB port\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } for (uint8_t pin=0; pinBUSVOODOO_XV_DEFAULT+0.2) { printf("xV voltage is too high: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } for (uint8_t pin=0; pin5.5) { printf("5V power rail voltage is too high: %d.%02uV, check pin 2 on I/O connector\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get 3.3V power rail voltage if (voltage<3.0) { printf("3.3V power rail voltage is too low: %d.%02uV, check pin 3 on I/O connector\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } else if (voltage>3.6) { printf("3.3V power rail voltage is too high: %d.%02uV, check pin 3 on I/O connector\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100)); #if DEBUG while (true); #else goto error; #endif } gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout to_return = true; // all tests are successful error: safe_state(); // set back to safe state if (!to_return) { printf("the test procedure has been interrupted for safety reasons\n"); } return to_return; } /** perform tests using external user */ static void test_external(void) { safe_state(); // start from safe state with all outputs switched off // test 5V output on pin 2 printf("check pin 2 on I/O connector to verify 5V output, it should be switched off\n"); if (!wait_space()) { goto end; } gpio_clear(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // enable Vout printf("check pin 2 on I/O connector to verify 5V output, it should be switched on\n"); if (!wait_space()) { goto end; } gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout // test 3.3V output on pin 3 printf("check pin 3 on I/O connector to verify 3.3V output, it should be switched off\n"); if (!wait_space()) { goto end; } gpio_clear(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // enable Vout printf("check pin 3 on I/O connector to verify 3.3V output, it should be switched on\n"); if (!wait_space()) { goto end; } gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout // test xV output on pin 4 printf("check pin 4 on I/O connector to verify xV output, it should be switched off\n"); if (!wait_space()) { goto end; } gpio_set(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // enable xV voltage regulator printf("check pin 4 on I/O connector to verify xV output, it should be switched at 3.4V\n"); if (!wait_space()) { goto end; } gpio_clear(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // disable xV voltage regulator // test I/O pins for (uint8_t io=1; io<=6; io++) { // test each I/O pin for (uint8_t pin=0; pin0) { // there is a command to process command[command_i] = 0; // end string command_i = 0; // prepare for next command process_command(command); // process user command } } else { // user command input command[command_i] = c; // save command input if (command_i