/* 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 UART mode (code) * @file busvoodoo_uart.c * @author King Kévin * @date 2018 * @note peripherals used: USART @ref busvoodoo_uart */ /* standard libraries */ #include // standard integer types #include // standard utilities #include // string utilities /* STM32 (including CM3) libraries */ #include // general purpose input output library #include // real-time control clock library #include // USART 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_oled.h" // OLED utilities #include "busvoodoo_uart.h" // own definitions /** @defgroup busvoodoo_uart USART peripheral used for UART communication * @{ */ #define BUSVOODOO_USART_ID 3 /**< USART peripheral */ /** @} */ /** mode setup stage */ static enum busvoodoo_uart_setting_t { BUSVOODOO_UART_SETTING_NONE, BUSVOODOO_UART_SETTING_BAUDRATE, BUSVOODOO_UART_SETTING_DATABITS, BUSVOODOO_UART_SETTING_PARITY, BUSVOODOO_UART_SETTING_STOPBITS, BUSVOODOO_UART_SETTING_DRIVE, BUSVOODOO_UART_SETTING_DONE, } busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; /** UART baud rate (in bps) */ uint32_t busvoodoo_uart_baudrate = 115200; /** UART data bits */ uint8_t busvoodoo_uart_databits = 8; /** UART parity setting */ char busvoodoo_uart_parity= 'n'; /** UART stop bits (multiplied by 2) */ uint8_t busvoodoo_uart_stopbits = 2; /** pin drive mode */ char busvoodoo_uart_drive = 'p'; /** setup UART mode * @param[out] prefix terminal prompt prefix * @param[in] line terminal prompt line to configure mode * @return if setup is complete */ static bool busvoodoo_uart_setup(char** prefix, const char* line) { bool complete = false; // is the setup complete static char config[39] = {0}; // configuration string used as prefix if (NULL==line) { // first call busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // re-start configuration } switch (busvoodoo_uart_setting) { case BUSVOODOO_UART_SETTING_NONE: snprintf(config, LENGTH(config), "baud rate in bps (1-2000000) [%u]", busvoodoo_uart_baudrate); *prefix = config; // ask for baud rate busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_BAUDRATE; break; case BUSVOODOO_UART_SETTING_BAUDRATE: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting } else { // setting provided uint32_t baudrate = atoi(line); // parse setting if (baudrate>0 && baudrate<2000000) { // check setting busvoodoo_uart_baudrate = baudrate; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting } } if (BUSVOODOO_UART_SETTING_DATABITS==busvoodoo_uart_setting) { // if next setting snprintf(config, LENGTH(config), "data bits (8-9) [%u]", busvoodoo_uart_databits); // prepare next setting *prefix = config; // display next setting } break; case BUSVOODOO_UART_SETTING_DATABITS: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting } else { // setting provided uint8_t databits = atoi(line); // parse setting if (8==databits || 9==databits) { // check setting busvoodoo_uart_databits = databits; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting } } if (BUSVOODOO_UART_SETTING_PARITY==busvoodoo_uart_setting) { // if next setting snprintf(config, LENGTH(config), "parity (n,e,o) [%c]", busvoodoo_uart_parity); // prepare next setting *prefix = config; // display next setting } break; case BUSVOODOO_UART_SETTING_PARITY: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting } else { // setting provided if (0==strcmp("n", line) || 0==strcmp("e", line) || 0==strcmp("o", line)) { // check setting busvoodoo_uart_parity = line[0]; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting } } if (BUSVOODOO_UART_SETTING_STOPBITS==busvoodoo_uart_setting) { // if next setting snprintf(config, LENGTH(config), "stop bits (0.5-2.0) [%.1f]", busvoodoo_uart_stopbits/2.0); // prepare next setting *prefix = config; // display next setting } break; case BUSVOODOO_UART_SETTING_STOPBITS: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting } else { // setting provided if (0==strcmp("0.5", line) || 0==strcmp(".5", line)) { // check setting busvoodoo_uart_stopbits = 1; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting } else if (0==strcmp("1", line) || 0==strcmp("1.0", line)) { // check setting busvoodoo_uart_stopbits = 2; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting } else if (0==strcmp("1.5", line)) { // check setting busvoodoo_uart_stopbits = 3; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting } else if (0==strcmp("2", line) || 0==strcmp("2.0", line)) { // check setting busvoodoo_uart_stopbits = 4; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting } } if (BUSVOODOO_UART_SETTING_DRIVE==busvoodoo_uart_setting) { // if next setting printf("p) push-pull (3.3V)\n"); printf("o) open-drain (set lV do desired voltage), with embedded pull-up (2kO)\n"); snprintf(config, LENGTH(config), "drive mode (p,o) [%c]", busvoodoo_uart_drive); // show drive mode *prefix = config; // display next setting } break; case BUSVOODOO_UART_SETTING_DRIVE: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting } else { // setting provided if (0==strcmp("p", line) || 0==strcmp("o", line)) { // check setting busvoodoo_uart_drive = line[0]; // remember setting busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting } } if (BUSVOODOO_UART_SETTING_DONE==busvoodoo_uart_setting) { // we have all settings, configure UART rcc_periph_clock_enable(USART_RCC(BUSVOODOO_USART_ID)); // enable clock for USART peripheral usart_set_baudrate(USART(BUSVOODOO_USART_ID), busvoodoo_uart_baudrate); // set baud rate usart_set_databits(USART(BUSVOODOO_USART_ID), busvoodoo_uart_databits); // set data bits switch (busvoodoo_uart_parity) { // set parity case 'n': usart_set_parity(USART(BUSVOODOO_USART_ID), USART_PARITY_NONE); break; case 'e': usart_set_parity(USART(BUSVOODOO_USART_ID), USART_PARITY_EVEN); break; case 'o': usart_set_parity(USART(BUSVOODOO_USART_ID), USART_PARITY_ODD); break; default: usart_set_parity(USART(BUSVOODOO_USART_ID), USART_PARITY_NONE); break; } switch (busvoodoo_uart_stopbits) { // set stop bits case 1: usart_set_stopbits(USART(BUSVOODOO_USART_ID), USART_STOPBITS_0_5); break; case 2: usart_set_stopbits(USART(BUSVOODOO_USART_ID), USART_STOPBITS_1); break; case 3: usart_set_stopbits(USART(BUSVOODOO_USART_ID), USART_STOPBITS_1_5); break; case 4: usart_set_stopbits(USART(BUSVOODOO_USART_ID), USART_STOPBITS_2); break; default: usart_set_stopbits(USART(BUSVOODOO_USART_ID), USART_STOPBITS_1); break; } usart_set_flow_control(USART(BUSVOODOO_USART_ID), USART_FLOWCONTROL_NONE); // set hardware flow control usart_set_mode(USART(BUSVOODOO_USART_ID), USART_MODE_TX_RX); // full-duplex communication switch (busvoodoo_uart_drive) { // set drive mode case 'p': busvoodoo_embedded_pullup(false); // disable embedded pull-ups gpio_set_mode(USART_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit gpio_set_mode(USART_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive gpio_set(USART_PORT(BUSVOODOO_USART_ID), USART_PIN_RX(BUSVOODOO_USART_ID)); // pull up to avoid noise when not connected break; case 'o': busvoodoo_embedded_pullup(true); // enable embedded pull-ups gpio_set_mode(USART_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, USART_PIN_TX(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit gpio_set_mode(USART_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_PIN_RX(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive break; } usart_enable(USART(BUSVOODOO_USART_ID)); // enable USART printf("I/O pinout:\n"); printf("+----- -------+\n"); printf("|xx xx RX 3V3 GND|\n"); printf("|xx xx TX lV 5V |\n"); printf("+----------------+\n"); led_off(); // disable LED because there is no activity busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time *prefix = "UART"; // display mode busvoodoo_oled_text_left("UART"); // set mode title on OLED display const char* pins[10] = {NULL, NULL, "Rx", "5V", "GND", NULL, NULL, "Tx", "lV", "3V3"}; // UART without flow control mode pinout busvoodoo_oled_text_pinout(pins, true); // set pinout on display busvoodoo_oled_update(); // update display to show text and pinout complete = true; // configuration is complete } break; default: // unknown case busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time break; } return complete; } /** exit UART mode */ static void busvoodoo_uart_exit(void) { usart_disable(USART(BUSVOODOO_USART_ID)); // disable USART rcc_periph_clock_enable(USART_RCC(BUSVOODOO_USART_ID)); // disable domain clock gpio_set_mode(USART_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_PIN_TX(BUSVOODOO_USART_ID)); // set pin back to floating input gpio_set_mode(USART_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_PIN_RX(BUSVOODOO_USART_ID)); // set pin back to floating input busvoodoo_embedded_pullup(false); // disable embedded pull-ups } // command handlers /** command to transmit a word * @param[in] argument word to transmit */ static void busvoodoo_uart_command_transmit(void* argument) { if (NULL==argument || 0==strlen(argument)) { // nothing to transmit return; } uint16_t i=0; while (((char*)(argument))[i]) { // go through word to send usart_send_blocking(USART(BUSVOODOO_USART_ID), ((char*)(argument))[i++]); // send character busvoodoo_led_red(100); // enable red LED to show transmission } } /** command to receive data * @param[in] argument no argument required */ static void busvoodoo_uart_command_receive(void* argument) { (void)argument; // we won't use the argument printf("press any key to exit\n"); while (!user_input_available) { // check for user input to exit if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received char c = usart_recv(USART(BUSVOODOO_USART_ID)); // receive character busvoodoo_led_blue(100); // enable blue LED to show reception printf("%c", c); // print received character } } user_input_get(); // discard user input printf("\n"); // get to next line } /** command to transmit and receive data * @param[in] argument no argument required */ static void busvoodoo_uart_command_transceive(void* argument) { (void)argument; // we won't use the argument printf("press 5 times escape to exit\n"); char last_c = 0; // last user character received uint8_t esc_count = 0; // number of times escape has press received while (esc_count<5) { // check for escape sequence if (user_input_available) { // check if user wants to transmit something char c = user_input_get(); // get user input if (0x1b==c) { // user pressed escape if (0x1b!=last_c) { // this is the first escape press esc_count = 0; } esc_count++; // increment escape count } last_c = c; // remember key press usart_send_blocking(USART(BUSVOODOO_USART_ID), c); // send user character busvoodoo_led_red(100); // enable red LED to show transmission } if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received char c = usart_recv(USART(BUSVOODOO_USART_ID)); // receive character busvoodoo_led_blue(100); // enable blue LED to show reception printf("%c", c); // print received character } } printf("\n"); // get to next line } static const struct menu_command_t busvoodoo_uart_commands[] = { { 'r', "receive", "show incoming data", MENU_ARGUMENT_NONE, NULL, &busvoodoo_uart_command_receive, }, { 't', "transmit", "transmit word", MENU_ARGUMENT_STRING, "word", &busvoodoo_uart_command_transmit, }, { 'x', "transceive", "transmit and receive data", MENU_ARGUMENT_NONE, NULL, &busvoodoo_uart_command_transceive, }, }; struct busvoodoo_mode_t busvoodoo_uart_mode = { "uart", "Universal Asynchronous Receiver-Transmitter", &busvoodoo_uart_setup, busvoodoo_uart_commands, LENGTH(busvoodoo_uart_commands), &busvoodoo_uart_exit, };