/* 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 . * */ /** BusVoodoo high impedance (HiZ) default mode (code) * @file busvoodoo_hiz.c * @author King Kévin * @date 2018 */ /* standard libraries */ #include // standard integer types #include // math utilities /* STM32 (including CM3) libraries */ #include // general purpose input output library #include // real-time control clock library #include // debug utilities #include // design utilities #include // DAC utilities /* own libraries */ #include "global.h" // board definitions #include "print.h" // printing utilities #include "menu.h" // menu definitions #include "busvoodoo_global.h" // BusVoodoo definitions #include "busvoodoo_hiz.h" // own definitions #define BUSVOODOO_XV_DEFAULT (0.8*(1+30.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_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 */ /** setup HiZ mode * @param[out] prefix terminal prompt prefix * @param[in] line terminal prompt line to configure mode * @return if setup is complete */ static bool busvoodoo_hiz_setup(char** prefix, const char* line) { (void)line; // no configuration is required *prefix = "HiZ"; // set command line prefix led_blink(0, 1); // switch blue LED on to show we are ready return true; } /** exit HiZ mode */ static void busvoodoo_hiz_exit(void) { // there is nothing to do } /** perform self tests * @return if self tests passed */ static bool busvoodoo_hiz_test_self(void) { bool to_return = false; // success of the self-test busvoodoo_safe_state(); // start from a safe state // get device information // get device identifier (DEV_ID) // 0x412: low-density, 16-32 kB flash // 0x410: medium-density, 64-128 kB flash // 0x414: high-density, 256-512 kB flash // 0x430: XL-density, 768-1024 kB flash // 0x418: connectivity if (0==(DBGMCU_IDCODE&DBGMCU_IDCODE_DEV_ID_MASK)) { printf("device identifier not set: this is probably a defective micro-controller\n"); } else if (0x414!=(DBGMCU_IDCODE&DBGMCU_IDCODE_DEV_ID_MASK)) { printf("this (DEV_ID=%03x) is not a high-density device: a wrong micro-controller might have been used\n", (DBGMCU_IDCODE&DBGMCU_IDCODE_DEV_ID_MASK)); } // ensure flash size is ok if (0xffff==DESIG_FLASH_SIZE) { printf("unknown flash size: this is probably a defective micro-controller\n"); } // check 5V power rail float voltage = busvoodoo_vreg_get(BUSVOODOO_5V_CHANNEL); // get 5V power rail voltage if (voltage<4.0) { printf("5V power rail voltage is too low: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } else if (voltage>5.5) { printf("5V power rail voltage is too high: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } // check 3.3V power rail voltage = busvoodoo_vreg_get(BUSVOODOO_3V3_CHANNEL); // get 3.3V power rail voltage if (voltage<3.0) { printf("3.3V power rail voltage is too low: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } else if (voltage>3.6) { printf("3.3V power rail voltage is too high: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } // test 5V and 3.3V outputs busvoodoo_vout_switch(true); // enable Vout voltage = busvoodoo_vreg_get(BUSVOODOO_5V_CHANNEL); // get 5V power rail voltage if (voltage<4.0) { printf("5V power rail voltage is too low when 5V output is enabled: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } else if (voltage>5.5) { printf("5V power rail voltage is too high when 5V output is enabled: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } voltage = busvoodoo_vreg_get(BUSVOODOO_3V3_CHANNEL); // get 3.3V power rail voltage if (voltage<3.0) { printf("3.3V power rail voltage is too low when 3V3 output is enabled: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } else if (voltage>3.6) { printf("3.3V power rail voltage is too high when 3V3 is enabled: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } busvoodoo_vout_switch(false); // disable Vout // check xV voltage regulator voltage = busvoodoo_vreg_set(BUSVOODOO_XV_CHANNEL, 0); // disable xV voltage regulator if (voltage>0.2) { // ensure the output is at 0V when the regulator is not enabled printf("xV voltage is %.2fV instead of 0V when the regulator is disabled\n", voltage); #if DEBUG while (true); #else goto error; #endif } gpio_set(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // enable xV voltage regulator sleep_ms(5); // let the voltage regulator start and voltage settle voltage = busvoodoo_vreg_get(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 higher (%.2fV) than expected (%.2fV) when the regulator is enabled\n", voltage, BUSVOODOO_XV_DEFAULT); #if DEBUG while (true); #else goto error; #endif } // check if we can control xV voltage = busvoodoo_vreg_set(BUSVOODOO_XV_CHANNEL, BUSVOODOO_XV_TEST); // get xV voltage if (voltageBUSVOODOO_XV_TEST+0.2) { printf("xV voltage is highed (%.2fV) than set (%.2fV)\n", voltage, BUSVOODOO_XV_TEST); #if DEBUG while (true); #else goto error; #endif } busvoodoo_vreg_set(BUSVOODOO_XV_CHANNEL, 0); // disable xV voltage regulator // check 12V voltage regulator if (busvoodoo_full) { voltage = busvoodoo_vreg_set(BUSVOODOO_12V_CHANNEL, 0); // disable 12V voltage regulator if (voltage>0.2) { // ensure the output is at 0V when the regulator is not enabled printf("12V voltage is %.2fV instead of 0V when the regulator is disabled\n", voltage); #if DEBUG while (true); #else goto error; #endif } gpio_clear(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // enable 12V voltage regulator sleep_ms(10); // let the voltage regulator start and voltage settle voltage = busvoodoo_vreg_get(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 higher (%.2fV) than expected (%.2fV) when regulator is enabled\n", voltage, BUSVOODOO_12V_DEFAULT); #if DEBUG while (true); #else goto error; #endif } // check if we can control 12V voltage regulator voltage = busvoodoo_vreg_set(BUSVOODOO_12V_CHANNEL, BUSVOODOO_12V_TEST); // set voltage on 12V voltage regulator if (voltageBUSVOODOO_12V_TEST+0.3) { printf("12V voltage is higher (%.2fV) than set (%.2fV)\n", voltage, BUSVOODOO_12V_TEST); #if DEBUG while (true); #else goto error; #endif } voltage = busvoodoo_vreg_set(BUSVOODOO_12V_CHANNEL, 0); // disable 12V voltage regulator } // pull all pins down and ensure they are low for (uint8_t pin=0; pin5.5) { printf("5V power rail voltage is too high when used to pull up: %.2fV\n", voltage); #if DEBUG while (true); #else goto error; #endif } for (uint8_t pin=0; pinBUSVOODOO_XV_DEFAULT+0.2) { printf("xV voltage is higher (%.2fV) than expected (%.2fV) when used to pull up\n", voltage, BUSVOODOO_XV_DEFAULT); #if DEBUG while (true); #else goto error; #endif } for (uint8_t pin=0; pin0.2) { // wait until pin is shorted to ground sleep_ms(200); // wait for user to make connection } gpio_clear(GPIO(BUSVOODOO_XVCTL_PORT), GPIO(BUSVOODOO_XVCTL_PIN)); // set pin low gpio_set_mode(GPIO(BUSVOODOO_XVCTL_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_XVCTL_PIN)); // set xV control pin as output led_toggle(); // notify user test is almost almost sleep_ms(200); // wait for voltage to settle an debounce if (busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)>0.2) { printf(xv_high); #if DEBUG while (true); #else goto error; #endif } gpio_set_mode(GPIO(BUSVOODOO_XVCTL_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, GPIO(BUSVOODOO_XVCTL_PIN)); // set xV control pin back to analog input for DAC led_toggle(); // notify user test is complete // test 5V output on pin 2 gpio_clear(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // enable Vout printf("%sI/O pin 2\n", xv_to); while (busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)<0.2) { // wait until pin is connected sleep_ms(200); // wait for user to make connection } gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout led_toggle(); // notify user test is almost complete sleep_ms(200); // wait for voltage to settle and debounce if (busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)>0.2) { printf(xv_high); #if DEBUG while (true); #else goto error; #endif } led_toggle(); // notify user test is complete // test 3.3V output on pin 3 gpio_clear(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // enable Vout printf("%sI/O pin 3\n", xv_to); while (busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)<0.2 || busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)>3.5) { // wait until pin is connected sleep_ms(200); // wait for user to make connection } gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout led_toggle(); // notify user test is almost complete sleep_ms(200); // wait for voltage to settle and debounce if (busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)>0.2) { printf(xv_high); #if DEBUG while (true); #else goto error; #endif } led_toggle(); // notify user test is complete // test I/O pins for (uint8_t io=1; io<=6; io++) { // test each I/O pin for (uint8_t pin=0; pin0.2) { printf(xv_high); #if DEBUG while (true); #else goto error; #endif } gpio_set_mode(busvoodoo_io_ports[pin], GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_io_pins[pin]); // set pin back to input led_toggle(); // notify user test is complete break; // stop looking for pin } } } if (busvoodoo_full) { // test 12V output on RS/CAN pin 1 double voltage = busvoodoo_vreg_get(BUSVOODOO_3V3_CHANNEL); // get reference voltage uint16_t dac_set = BUSVOODOO_12V_SET(5.0)/voltage*4095; // DAC value corresponding to the voltage dac_load_data_buffer_single(dac_set, RIGHT12, BUSVOODOO_12VCTL_CHANNEL); // set output so the voltage regulator is set to desired output voltage dac_software_trigger(BUSVOODOO_12VCTL_CHANNEL); // transfer the value to the DAC dac_enable(BUSVOODOO_12VCTL_CHANNEL); // enable DAC gpio_clear(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // enable 12V voltage regulator printf("%sRS/CAN pin 1\n", xv_to); while (busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)<0.2) { // wait until pin is connected sleep_ms(200); // wait for user to make connection } gpio_set(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // disable 12V voltage regulator dac_disable(BUSVOODOO_12VCTL_CHANNEL); // disable 12V control led_toggle(); // notify user test is almost complete sleep_ms(200); // wait for voltage to settle (and debounce) if (busvoodoo_vreg_get(BUSVOODOO_XV_CHANNEL)>0.2) { printf(xv_high); #if DEBUG while (true); #else goto error; #endif } led_toggle(); // notify user test is complete // test RS-232 port (with itself) rcc_periph_clock_enable(RCC_GPIO(BUSVOODOO_RS232_EN_PORT)); // enable clock for GPIO domain gpio_clear(GPIO(BUSVOODOO_RS232_EN_PORT), GPIO(BUSVOODOO_RS232_EN_PIN)); // set low to enable receiver gpio_set_mode(GPIO(BUSVOODOO_RS232_EN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(BUSVOODOO_RS232_EN_PIN)); // set pin as output (open-drain pulled high to disable receiver) rcc_periph_clock_enable(RCC_GPIO(BUSVOODOO_RS232_SHDN_PORT)); // enable clock for GPIO domain gpio_set(GPIO(BUSVOODOO_RS232_SHDN_PORT), GPIO(BUSVOODOO_RS232_SHDN_PIN)); // set high to enable transmitter gpio_set_mode(GPIO(BUSVOODOO_RS232_SHDN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_RS232_SHDN_PIN)); // set pin as output (push-pull pulled low to disable transmitter) rcc_periph_clock_enable(RCC_GPIO(BUSVOODOO_RS232_TX_PORT)); // enable clock for GPIO gpio_set_mode(GPIO(BUSVOODOO_RS232_TX_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_RS232_TX_PIN)); // set pin as output (push-pull) rcc_periph_clock_enable(RCC_GPIO(BUSVOODOO_RS232_RX_PORT)); // enable clock for GPIO gpio_set_mode(GPIO(BUSVOODOO_RS232_RX_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO(BUSVOODOO_RS232_RX_PIN)); // set pin as input (with pull resistors) // start by setting low since unconnected (pulled to ground by 3 kO) is considered as high gpio_clear(GPIO(BUSVOODOO_RS232_TX_PORT), GPIO(BUSVOODOO_RS232_TX_PIN)); // set low gpio_set(GPIO(BUSVOODOO_RS232_RX_PORT), GPIO(BUSVOODOO_RS232_RX_PIN)); // pull high to avoid false negative sleep_ms(5); printf("connect RS/CAN pin 2 to RS/CAN pin 3\n"); while (gpio_get(GPIO(BUSVOODOO_RS232_RX_PORT), GPIO(BUSVOODOO_RS232_RX_PIN))) { // wait until pin is connected sleep_ms(200); // wait for user to make connection } gpio_set(GPIO(BUSVOODOO_RS232_TX_PORT), GPIO(BUSVOODOO_RS232_TX_PIN)); // set high gpio_clear(GPIO(BUSVOODOO_RS232_RX_PORT), GPIO(BUSVOODOO_RS232_RX_PIN)); // pull low to avoid false negative led_toggle(); // notify user test is almost complete sleep_ms(200); // wait for voltage to settle and debounce if (!gpio_get(GPIO(BUSVOODOO_RS232_RX_PORT), GPIO(BUSVOODOO_RS232_RX_PIN))) { // check if RX is set low by TX printf("CAN/RS pin 2 is high while it should be set low by pin 3\n"); #if DEBUG while (true); #else goto error; #endif } gpio_set_mode(GPIO(BUSVOODOO_RS232_TX_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUSVOODOO_RS232_TX_PIN)); // free pin gpio_set_mode(GPIO(BUSVOODOO_RS232_RX_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUSVOODOO_RS232_RX_PIN)); // free pin led_toggle(); // notify user test is complete rcc_periph_clock_enable(RCC_GPIO(BUSVOODOO_RS232_RTS_PORT)); // enable clock for GPIO gpio_set_mode(GPIO(BUSVOODOO_RS232_RTS_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_RS232_RTS_PIN)); // set pin as output (push-pull) rcc_periph_clock_enable(RCC_GPIO(BUSVOODOO_RS232_CTS_PORT)); // enable clock for GPIO gpio_set_mode(GPIO(BUSVOODOO_RS232_CTS_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO(BUSVOODOO_RS232_CTS_PIN)); // set pin as input (with pull resistors) // start by setting low since unconnected (pulled to ground by 3 kO) is considered as high gpio_clear(GPIO(BUSVOODOO_RS232_RTS_PORT), GPIO(BUSVOODOO_RS232_RTS_PIN)); // set low gpio_set(GPIO(BUSVOODOO_RS232_CTS_PORT), GPIO(BUSVOODOO_RS232_CTS_PIN)); // pull high to avoid false negative printf("connect RS/CAN pin 4 to RS/CAN pin 5\n"); while (gpio_get(GPIO(BUSVOODOO_RS232_CTS_PORT), GPIO(BUSVOODOO_RS232_CTS_PIN))) { // wait until pin is connected sleep_ms(200); // wait for user to make connection } gpio_set(GPIO(BUSVOODOO_RS232_RTS_PORT), GPIO(BUSVOODOO_RS232_RTS_PIN)); // set high gpio_clear(GPIO(BUSVOODOO_RS232_CTS_PORT), GPIO(BUSVOODOO_RS232_CTS_PIN)); // pull low to avoid false negative led_toggle(); // notify user test is almost complete sleep_ms(200); // wait for voltage to settle an debounce if (!gpio_get(GPIO(BUSVOODOO_RS232_CTS_PORT), GPIO(BUSVOODOO_RS232_CTS_PIN))) { // check if CTS is set high by RTS printf("CAN/RS pin 5 is high while it should be set low by pin 4\n"); #if DEBUG while (true); #else goto error; #endif } gpio_set_mode(GPIO(BUSVOODOO_RS232_RTS_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUSVOODOO_RS232_RTS_PIN)); // free pin gpio_set_mode(GPIO(BUSVOODOO_RS232_CTS_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUSVOODOO_RS232_CTS_PIN)); // free pin led_toggle(); // notify user test is complete gpio_set(GPIO(BUSVOODOO_RS232_EN_PORT), GPIO(BUSVOODOO_RS232_EN_PIN)); // set high to disable receiver gpio_clear(GPIO(BUSVOODOO_RS232_SHDN_PORT), GPIO(BUSVOODOO_RS232_SHDN_PIN)); // set low to disable transmitter } to_return = true; // all tests passed #if DEBUG #else error: #endif busvoodoo_safe_state(); // go back to safe state if (!to_return) { printf("the test procedure has been aborted for safety reasons\n"); } return to_return; } // command handlers /** command to perform board self-test * @param[in] argument no argument required */ static void busvoodoo_hiz_command_test_self(void* argument) { (void)argument; // we won't use the argument printf("performing self-test\n"); printf("remove all cables from connectors and press space to start (or any other key to abort)\n"); if (wait_space()) { if (busvoodoo_hiz_test_self()) { // perform self-test led_blink(0, 1.0); // show blue OK LED printf("self-test succeeded\n"); // notify user } else { led_blink(0.5, 0.5); // show error on LEDs printf("self-test failed\n"); // notify user } } else { printf("self-test aborted\n"); } } /** command to perform pins test * @param[in] argument no argument required */ static void busvoodoo_hiz_command_test_pins(void* argument) { (void)argument; // we won't use the argument printf("performing pins test\n"); printf("remove all cables from connectors and press space to start (or any other key to abort)\n"); if (wait_space()) { if (busvoodoo_hiz_test_pins()) { // perform pin test led_blink(0, 1.0); // show blue OK LED printf("pins test succeeded\n"); // notify user } else { led_blink(0.5, 0.5); // show error on LEDs printf("pins test failed\n"); // notify user } } else { printf("pins test aborted\n"); } } static const struct menu_command_t busvoodoo_hiz_commands[] = { { 's', "self-test", "perform board self-test", MENU_ARGUMENT_NONE, NULL, &busvoodoo_hiz_command_test_self, }, { 't', "pins-test", "perform connector pins test", MENU_ARGUMENT_NONE, NULL, &busvoodoo_hiz_command_test_pins, }, }; struct busvoodoo_mode_t busvoodoo_hiz_mode = { "hiz", "High Impedance (Z)", &busvoodoo_hiz_setup, busvoodoo_hiz_commands, LENGTH(busvoodoo_hiz_commands), &busvoodoo_hiz_exit, };