diff --git a/lib/busvoodoo_uart.c b/lib/busvoodoo_uart.c index 9b34afa..50c9213 100644 --- a/lib/busvoodoo_uart.c +++ b/lib/busvoodoo_uart.c @@ -35,6 +35,7 @@ #include "usart_enhanced.h" // utilities for USART enhancements #include "busvoodoo_global.h" // BusVoodoo definitions #include "busvoodoo_oled.h" // OLED utilities +#include "busvoodoo_uart_generic.h" // generic UART mode #include "busvoodoo_uart.h" // own definitions /** @defgroup busvoodoo_uart USART peripheral used for UART communication @@ -43,31 +44,30 @@ #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_HWFLOWCTL, - BUSVOODOO_UART_SETTING_DRIVE, - BUSVOODOO_UART_SETTING_DONE, -} busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; /**< current mode setup stage */ -/** UART baud rate (in bps) */ -static uint32_t busvoodoo_uart_baudrate = 115200; -/** UART data bits */ -static uint8_t busvoodoo_uart_databits = 8; -/** UART parity setting */ -static enum usart_enhanced_parity_t busvoodoo_uart_parity = USART_ENHANCED_PARITY_NONE; -/** UART stop bits setting */ -static uint32_t busvoodoo_uart_stopbits = USART_STOPBITS_1; -/** UART hardware flow control setting (true = with hardware flow control, false = without hardware flow control */ -static bool busvoodoo_uart_hwflowctl = false; -/** pin drive mode (true = push-pull, false = open-drain) */ -static bool busvoodoo_uart_drive = true; -/** if embedded pull-up resistors are used */ -static bool busvoodoo_uart_pullup = false; +/** UART specific methods that will be called by the generic methods */ +static const struct busvoodoo_uart_generic_specific_t busvoodoo_uart_generic_uart = { + .usart = USART(BUSVOODOO_USART_ID), + .usart_rcc = RCC_USART(BUSVOODOO_USART_ID), + .usart_rst = RST_USART(BUSVOODOO_USART_ID), + .multidrive = true, + .tx_port = USART_TX_PORT(BUSVOODOO_USART_ID), + .tx_pin = USART_TX_PIN(BUSVOODOO_USART_ID), + .tx_rcc = RCC_USART_PORT(BUSVOODOO_USART_ID), + .tx_pre = NULL, + .tx_post = NULL, + .rx_port = USART_RX_PORT(BUSVOODOO_USART_ID), + .rx_pin = USART_RX_PIN(BUSVOODOO_USART_ID), + .rx_rcc = RCC_USART_PORT(BUSVOODOO_USART_ID), + .rx_pre = NULL, + .rx_post = NULL, + .hwflowctl = true, + .rts_port = USART_RTS_PORT(BUSVOODOO_USART_ID), + .rts_pin = USART_RTS_PIN(BUSVOODOO_USART_ID), + .rts_rcc = RCC_USART_PORT(BUSVOODOO_USART_ID), + .cts_port = USART_CTS_PORT(BUSVOODOO_USART_ID), + .cts_pin = USART_CTS_PIN(BUSVOODOO_USART_ID), + .cts_rcc = RCC_USART_PORT(BUSVOODOO_USART_ID), +}; /** setup UART mode * @param[out] prefix terminal prompt prefix @@ -78,183 +78,25 @@ static bool busvoodoo_uart_setup(char** prefix, const char* line) { bool complete = false; // is the setup complete if (NULL==line) { // first call - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // re-start configuration + busvoodoo_uart_generic_configure(&busvoodoo_uart_generic_uart); // provide the UART specific information } - switch (busvoodoo_uart_setting) { - case BUSVOODOO_UART_SETTING_NONE: - snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "baud rate in bps (1200-2000000) [%u]", busvoodoo_uart_baudrate); - *prefix = busvoodoo_global_string; // 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 - } + complete = busvoodoo_uart_generic_setup(prefix, line); // configure underlying generic UART + if (complete) { // generic configuration finished + busvoodoo_led_blue_off(); // disable blue LED because there is no activity + *prefix = "UART"; // display mode + busvoodoo_oled_text_left(*prefix); // set mode title on OLED display + const char* pinout_io[10] = {"GND", "5V", "3V3", "LV", "Rx", "Tx", "RTS", "CTS", NULL, NULL}; // UART mode pinout + for (uint8_t i=0; i=5 && databits<=8) { // 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 - printf("1) none\n"); - printf("2) even\n"); - printf("3) odd\n"); - printf("4) mark\n"); - printf("5) space\n"); - snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "parity (1,2,3,4,5) [%u]", busvoodoo_uart_parity+1); // prepare next setting - *prefix = busvoodoo_global_string; // 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 if (1==strlen(line)) { // setting provided - uint8_t parity = atoi(line); // parse setting - if (parity>0 && parity<6) { // check settin - busvoodoo_uart_parity = parity-1; - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting - } - } - if (BUSVOODOO_UART_SETTING_STOPBITS==busvoodoo_uart_setting) { // if next setting - printf("1) 0.5\n"); - printf("2) 1\n"); - printf("3) 1.5\n"); - printf("4) 2\n"); - snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "stop bits (1,2,3,4) [%c]", USART_STOPBITS_0_5==busvoodoo_uart_stopbits ? '1' : (USART_STOPBITS_1==busvoodoo_uart_stopbits ? '2' : (USART_STOPBITS_1_5==busvoodoo_uart_stopbits ? '3' : '4'))); // prepare next setting - *prefix = busvoodoo_global_string; // display next setting - } - break; - case BUSVOODOO_UART_SETTING_STOPBITS: - if (NULL==line || 0==strlen(line)) { // use default setting - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting - } else if (1==strlen(line)) { // setting provided - if ('1'==line[0]) { // 0.5 stop bits - busvoodoo_uart_stopbits = USART_STOPBITS_0_5; // remember setting - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting - } else if ('2'==line[0]) { // 1 stop bits - busvoodoo_uart_stopbits = USART_STOPBITS_1; // remember setting - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting - } else if ('3'==line[0]) { // 1.5 stop bits - busvoodoo_uart_stopbits = USART_STOPBITS_1_5; // remember setting - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting - } else if ('4'==line[0]) { // 2 stop bits - busvoodoo_uart_stopbits = USART_STOPBITS_2; // remember setting - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting - } - } - if (BUSVOODOO_UART_SETTING_HWFLOWCTL==busvoodoo_uart_setting) { // if next setting - printf("1) no flow control\n"); - printf("2) RTS/CTS hardware flow control\n"); - snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "flow control (1,2) [%c]", busvoodoo_uart_hwflowctl ? '2' : '1'); // prepare next setting - *prefix = busvoodoo_global_string; // display next setting - } - break; - case BUSVOODOO_UART_SETTING_HWFLOWCTL: - if (NULL==line || 0==strlen(line)) { // use default setting - busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting - } else if (1==strlen(line)) { // setting provided - if ('1'==line[0] || '2'==line[0]) { // setting provided - busvoodoo_uart_hwflowctl = ('2'==line[0]); // 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("1) push-pull (3.3V)\n"); - printf("2) open-drain, with embedded pull-up resistors (2kO)\n"); - printf("3) open-drain, with external pull-up resistors\n"); - snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "drive mode (1,2,3) [%c]", busvoodoo_uart_drive ? '1' : (busvoodoo_uart_pullup ? '2' : '3')); // show drive mode - *prefix = busvoodoo_global_string; // 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 if (1==strlen(line)) { // setting provided - uint8_t drive = atoi(line); // parse setting - if (1==drive || 2==drive || 3==drive) { // check setting - busvoodoo_uart_drive = (1==drive); // remember setting - busvoodoo_uart_pullup = (2==drive); // 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(RCC_AFIO); // enable clock for USART alternate function - rcc_periph_clock_enable(RCC_USART(BUSVOODOO_USART_ID)); // enable clock for USART peripheral - rcc_periph_reset_pulse(RST_USART(BUSVOODOO_USART_ID)); // reset USART peripheral - usart_set_baudrate(USART(BUSVOODOO_USART_ID), busvoodoo_uart_baudrate); // set baud rate - usart_enhanced_config(USART(BUSVOODOO_USART_ID), busvoodoo_uart_databits, busvoodoo_uart_parity); // use enhanced USART to configure the USART peripherals, supporting more data-bits and parity configurations - usart_set_stopbits(USART(BUSVOODOO_USART_ID), busvoodoo_uart_stopbits); // set stop bits - if (busvoodoo_uart_hwflowctl) { - usart_set_flow_control(USART(BUSVOODOO_USART_ID), USART_FLOWCONTROL_RTS_CTS); // set RTS/CTS flow control - } else { - usart_set_flow_control(USART(BUSVOODOO_USART_ID), USART_FLOWCONTROL_NONE); // set no flow control - } - usart_set_mode(USART(BUSVOODOO_USART_ID), USART_MODE_TX_RX); // full-duplex communication - rcc_periph_clock_enable(RCC_USART_PORT(BUSVOODOO_USART_ID)); // enable clock for USART GPIO peripheral - if (busvoodoo_uart_drive) { // use push-pull drive mode - gpio_set_mode(USART_TX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_TX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit - gpio_set(USART_RX_PORT(BUSVOODOO_USART_ID), USART_RX_PIN(BUSVOODOO_USART_ID)); // pull up to avoid noise when not connected - gpio_set_mode(USART_RX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_RX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive - if (busvoodoo_uart_hwflowctl) { // use open drain drive mode - gpio_set_mode(USART_RTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, USART_RTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit - gpio_set(USART_CTS_PORT(BUSVOODOO_USART_ID), USART_CTS_PIN(BUSVOODOO_USART_ID)); // pull up to block transmission unless requested - gpio_set_mode(USART_CTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_CTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive - } - } else { - gpio_set_mode(USART_TX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_TX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit - gpio_set_mode(USART_RX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_RX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive - if (busvoodoo_uart_hwflowctl) { - gpio_set_mode(USART_RTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, USART_RTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit - gpio_set_mode(USART_CTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_CTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive - } - } - if (!busvoodoo_uart_drive && busvoodoo_uart_pullup) { // enable embedded pull-ups if used - busvoodoo_embedded_pullup(true); // set embedded pull-ups - printf("use LV to set pull-up voltage\n"); - } - usart_enable(USART(BUSVOODOO_USART_ID)); // enable USART - busvoodoo_led_blue_off(); // disable blue 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(*prefix); // set mode title on OLED display - char* pinout_io[10] = {"GND", "5V", "3V3", "LV", "Rx", "Tx", NULL, NULL, NULL, NULL}; // UART mode pinout - if (busvoodoo_uart_hwflowctl) { // hardware flow control is used - pinout_io[6] = "RTS"; // update pin name - pinout_io[7] = "CTS"; // update pin name - } - for (uint8_t i=0; i='0' && action[1]<='9') { // send decimal - return busvoodoo_uart_action(action+1, repetition, perform); // just retry without leading 0 - } else { // malformed action - return false; - } - } else if ('x'==action[0] && length>1) { // send hexadecimal value - for (uint32_t i=1; i='0' && action[i]<='9') || (action[i]>='a' && action[i]<='f') || (action[i]>='A' && action[i]<='F'))) { // check for hexadecimal character - return false; // not an hexadecimal string - } - } - if (!perform) { - return true; - } - uint32_t value = strtol(&action[1], NULL, 16); // get hex value - for (uint32_t i=0; i1) { // send binary value - for (uint32_t i=1; i'1') { // check for binary character - return false; // not a binary string - } - } - if (!perform) { - return true; - } - uint32_t value = strtol(&action[1], NULL, 2); // get binary value - for (uint32_t i=0; i='1' && action[0]<='9') { // send decimal value - for (uint32_t i=1; i'9') { // check for decimal character - return false; // not a decimal string - } - } - if (!perform) { - return true; - } - uint32_t value = strtol(&action[0], NULL, 10); // get decimal value - for (uint32_t i=0; i=2 && ('"'==action[0] || '\''==action[0]) && (action[length-1]==action[0])) { // send ASCII character - if (!perform) { - return true; - } - for (uint32_t r=0; r0) { - if (0==strcmp(argument, "h") || 0==strcmp(argument, "hex")) { // user wants hexadecimal display - display_hex = true; // remember to display in hexadecimal - } else if (0==strcmp(argument, "b") || 0==strcmp(argument, "bin")) { // user wants binary display - display_bin = true; // remember to display in binary - } else { - printf("malformed argument\n"); - return; - } - } - 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 - uint8_t input = usart_enhanced_recv(USART(BUSVOODOO_USART_ID)); // receive character - busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception - if (display_hex) { // display data in hex - printf("%02x ", input); - } else if (display_bin) { // display data in binary - printf("%08b ", input); - } else { // display in ASCII - printf("%c", input); // 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 (true) { // 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 - if (esc_count<5) { // check for escape sequence - while ((0==(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty - if (USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) { // we can send a character - usart_enhanced_send(USART(BUSVOODOO_USART_ID), c); // send user character - busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show transmission - } - } else { // user wants to exit - break; // exit infinite loop - } - } - if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received - char input = usart_enhanced_recv(USART(BUSVOODOO_USART_ID)); // receive character - busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception - printf("%c", input); // print received character - } - } - printf("\n"); // get to next line -} - -/** command to verify incoming transmission for error - * @param[in] argument argument not required - */ -static void busvoodoo_uart_command_error(void* argument) -{ - (void)argument; // argument not used - printf("press any key to exit\n"); - while (!user_input_available) { // wait until user interrupt - busvoodoo_uart_read(); // read incoming data (this also checks for errors - } - user_input_get(); // discard user input -} - -/** UART menu commands */ -static const struct menu_command_t busvoodoo_uart_commands[] = { - { - .shortcut = 'a', - .name = "action", - .command_description = "perform protocol actions", - .argument = MENU_ARGUMENT_STRING, - .argument_description = "[actions]", - .command_handler = &busvoodoo_uart_command_actions, - }, - { - .shortcut = 'r', - .name = "receive", - .command_description = "show incoming data [in hexadecimal or binary]", - .argument = MENU_ARGUMENT_STRING, - .argument_description = "[hex|bin]", - .command_handler = &busvoodoo_uart_command_receive, - }, - { - .shortcut = 't', - .name = "transmit", - .command_description = "transmit ASCII text (empty for CR+LF)", - .argument = MENU_ARGUMENT_STRING, - .argument_description = "[text]", - .command_handler = &busvoodoo_uart_command_transmit, - }, - { - .shortcut = 'x', - .name = "transceive", - .command_description = "transmit and receive data", - .argument = MENU_ARGUMENT_NONE, - .argument_description = NULL, - .command_handler = &busvoodoo_uart_command_transceive, - }, - { - .shortcut = 'e', - .name = "error", - .command_description = "verify incoming transmission for errors", - .argument = MENU_ARGUMENT_NONE, - .argument_description = NULL, - .command_handler = &busvoodoo_uart_command_error, - }, -}; - const struct busvoodoo_mode_t busvoodoo_uart_mode = { .name = "uart", .description = "Universal Asynchronous Receiver-Transmitter", .full_only = false, .setup = &busvoodoo_uart_setup, - .commands = busvoodoo_uart_commands, - .commands_nb = LENGTH(busvoodoo_uart_commands), + .commands = busvoodoo_uart_generic_commands, + .commands_nb = busvoodoo_uart_generic_commands_nb, .exit = &busvoodoo_uart_exit, };