/* 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-2018 */ /* standard libraries */ #include // standard integer types #include // string utilities /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // general purpose input output library #include // real-time control clock library #include // debug utilities /* own libraries */ #include "global.h" // board definitions #include "print.h" // printing utilities #include "uart.h" // USART utilities #include "usb_cdcacm.h" // USB CDC ACM utilities #include "terminal.h" // handle the terminal interface #include "menu.h" // menu utilities #include "busvoodoo_global.h" // BusVoodoo definitions #include "busvoodoo_oled.h" // OLED utilities #include "busvoodoo_hiz.h" // BusVoodoo HiZ mode #include "busvoodoo_uart.h" // BusVoodoo UART mode #include "busvoodoo_i2c.h" // BusVoodoo I2C mode #include "busvoodoo_spi.h" // BusVoodoo SPI mode #include "busvoodoo_onewire.h" // BusVoodoo 1-wire mode #include "busvoodoo_rs232.h" // BusVoodoo RS-232 mode #include "busvoodoo_rs485.h" // BusVoodoo RS-485/422 mode #include "busvoodoo_sdio.h" // BusVoodoo SDIO mode /** all supported BusVoodoo modes */ static const struct busvoodoo_mode_t* busvoodoo_modes[] = { &busvoodoo_hiz_mode, &busvoodoo_uart_mode, &busvoodoo_i2c_mode, &busvoodoo_spi_mode, &busvoodoo_onewire_mode, &busvoodoo_rs232_mode, &busvoodoo_rs485_mode, &busvoodoo_sdio_mode, }; /** current BusVoodoo mode */ static struct busvoodoo_mode_t const * busvoodoo_mode = NULL; /** is mode setup complete */ static bool busvoodoo_mode_complete = false; size_t putc(char c) { size_t length = 0; // number of characters printed static char last_c = 0; // to remember on which character we last sent if ('\n' == c) { // send carriage return (CR) + line feed (LF) newline for each LF if ('\r' != last_c) { // CR has not already been sent uart_putchar_nonblocking('\r'); // send CR over USART usb_cdcacm_putchar('\r'); // send CR over USB length++; // remember we printed 1 character } } uart_putchar_nonblocking(c); // send byte over USART usb_cdcacm_putchar(c); // send byte over USB length++; // remember we printed 1 character last_c = c; // remember last character return length; // return number of characters printed } /** switch BusVoddoo mode * @param[in] mode mode to switch to */ static void switch_mode(const struct busvoodoo_mode_t* mode) { if (busvoodoo_mode) { (*busvoodoo_mode->exit)(); // exit current mode } busvoodoo_leds_off(); // switch off LEDs busvoodoo_safe_state(); // return to safe state // reset pinout for (uint8_t i=0; isetup)(&terminal_prefix, NULL); // start setup terminal_send(0); // update the terminal prompt } /** command to show help * @param[in] argument no argument required */ static void command_help(void* argument); /** command to select mode * @param[in] argument mode to select */ static void command_mode(void* argument); /** command to quit current BusVoodoo mode * @param[in] argument no argument required */ static void command_quit(void* argument); /** command to reset board * @param[in] argument no argument required */ static void command_reset(void* argument); /** command to reboot into bootloader * @param[in] argument no argument required */ /** list of all supported commands */ static const struct menu_command_t menu_commands[] = { { .shortcut = 'm', .name = "mode", .command_description = "select mode", .argument = MENU_ARGUMENT_STRING, .argument_description = "[mode]", .command_handler = &command_mode, }, { .shortcut = 'q', .name = "quit", .command_description = "quit current mode", .argument = MENU_ARGUMENT_NONE, .argument_description = NULL, .command_handler = &command_quit, }, { .shortcut = 'R', .name = "reset", .command_description = "reset board", .argument = MENU_ARGUMENT_NONE, .argument_description = NULL, .command_handler = &command_reset, }, { .shortcut = 'h', .name = "help", .command_description = "display help", .argument = MENU_ARGUMENT_NONE, .argument_description = NULL, .command_handler = &command_help, }, }; static void command_help(void* argument) { (void)argument; // we won't use the argument printf("available commands:\n"); menu_print_commands(menu_commands, LENGTH(menu_commands)); // print global commands menu_print_commands(busvoodoo_global_commands, busvoodoo_global_commands_nb); // print BusVoodoo global commands if (busvoodoo_full) { menu_print_commands(busvoodoo_global_full_commands, busvoodoo_global_full_commands_nb); // print BusVoodoo global commands } if (!busvoodoo_mode->full_only || busvoodoo_full) { menu_print_commands(busvoodoo_mode->commands, busvoodoo_mode->commands_nb); // print BusVoodoo mode commands } } static void command_mode(void* argument) { if (NULL==argument || 0==strlen(argument)) { // no mode provided: list all modes printf("available modes:\n"); for (uint8_t i=0; ifull_only || busvoodoo_full) { printf("%s\t%s\n", busvoodoo_modes[i]->name, busvoodoo_modes[i]->description); // display mode information } } } else { // mode provided bool mode_found = false; // to know if we found the matching mode for (uint8_t i=0; iname)) { // check for corresponding mode if (!busvoodoo_mode->full_only || busvoodoo_full) { switch_mode(busvoodoo_modes[i]); // switch to mode } else { printf("mode only available for BusVoodoo full\n"); } mode_found = true; // remember we found the mode break; // stop searching for mode } } if (!mode_found) { printf("unknown mode: %s\n", argument); } } } static void command_quit(void* argument) { (void)argument; // we won't use the argument switch_mode(NULL); // switch do default mode } static void command_reset(void* argument) { (void)argument; // we won't use the argument scb_reset_system(); // reset device while (true); // wait for the reset to happen } /** process user command * @param[in] str user command string (\0 ended) */ static void process_command(char* str) { // ensure actions are available if (NULL==menu_commands || 0==LENGTH(menu_commands)) { return; } // handle user input if (NULL==busvoodoo_mode) { // no mode set switch_mode(NULL); // set default mode } if (!busvoodoo_mode_complete) { // mode setup is not complete busvoodoo_mode_complete = (*busvoodoo_mode->setup)(&terminal_prefix, str); // continue setup terminal_send(0); // update the terminal prompt } else { // mode setup is complete // don't handle empty lines if (!str || 0==strlen(str)) { return; } bool command_handled = false; if (!busvoodoo_mode->full_only || busvoodoo_full) { command_handled = menu_handle_command(str, busvoodoo_mode->commands, busvoodoo_mode->commands_nb); // try if the mode can handle this command } if (!command_handled && busvoodoo_full) { command_handled = menu_handle_command(str, busvoodoo_global_full_commands, busvoodoo_global_full_commands_nb); // try if full BusVoodoo can handle this command } if (!command_handled) { command_handled = menu_handle_command(str, busvoodoo_global_commands, busvoodoo_global_commands_nb); // try if the base BusVoodoo can handle this command } if (!command_handled) { command_handled = menu_handle_command(str, menu_commands, LENGTH(menu_commands)); // try if this is not a global command } if (!command_handled) { printf("command not recognized. enter help to list commands\n"); } } } /** program entry point * this is the firmware function started by the micro-controller */ void main(void); void main(void) { rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock #if defined(DEBUG) && DEBUG // enable functionalities for easier debug DBGMCU_CR |= DBGMCU_CR_STANDBY; // allow debug also in standby mode (keep digital part and clock powered) DBGMCU_CR |= DBGMCU_CR_STOP; // allow debug also in stop mode (keep clock powered) DBGMCU_CR |= DBGMCU_CR_SLEEP; // allow debug also in sleep mode (keep clock powered) #endif // setup board board_setup(); // setup board uart_setup(); // setup USART (for printing) busvoodoo_setup(); // setup BusVoodoo board usb_cdcacm_setup(); // setup USB CDC ACM (for printing) #if DEBUG printf("\nreset cause:"); if (RCC_CSR & RCC_CSR_LPWRRSTF) { printf(" low-power"); } if (RCC_CSR & RCC_CSR_WWDGRSTF) { printf(" window-watchdog"); } if (RCC_CSR & RCC_CSR_IWDGRSTF) { printf(" independent-watchdog"); } if (RCC_CSR & RCC_CSR_SFTRSTF) { printf(" software"); } if (RCC_CSR & RCC_CSR_PORRSTF) { printf(" POR/PDR"); } if (RCC_CSR & RCC_CSR_PINRSTF) { printf(" reset-pin"); } printf("\n"); RCC_CSR |= RCC_CSR_RMVF; // clear reset flags #endif printf("\nwelcome to \x1b[32mBus\x1b[35mVoodoo\x1b[0m (%s)\n", busvoodoo_full ? "full" : "light"); // print welcome message // setup terminal terminal_prefix = "BV"; // set default prefix terminal_process = &process_command; // set central function to process commands terminal_setup(); // start terminal // setup OLED display sleep_ms(10); // wait a bit until the display is ready busvoodoo_oled_setup(); // setup OLED display // display version busvoodoo_oled_clear(); busvoodoo_oled_text_left("BusVoodoo"); char str[20]; snprintf(str, sizeof(str), "fl: %s", busvoodoo_full ? "full" : "light"); busvoodoo_oled_text_pos(1, 16+(fonts[FONT_KING10].height+2)*1, FONT_KING10, str); snprintf(str, sizeof(str), "hw: %c", busvoodoo_version); busvoodoo_oled_text_pos(1, 16+(fonts[FONT_KING10].height+2)*2, FONT_KING10, str); snprintf(str, sizeof(str), "fw: %04u-%02u-%02u\n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY); busvoodoo_oled_text_pos(1, 16+(fonts[FONT_KING10].height+2)*3, FONT_KING10, str); snprintf(str, sizeof(str), "bus.cuvoodoo.info"); busvoodoo_oled_text_pos((127-((fonts[FONT_KING8].width+1)*strlen(str)))/2, 63, FONT_KING8, str); busvoodoo_oled_update(); sleep_ms(1000); busvoodoo_oled_clear(); // setup default mode switch_mode(NULL); // main loop bool action = false; // if an action has been performed don't go to sleep button_flag = false; // reset button flag while (true) { // infinite loop while (user_input_available) { // user input received action = true; // action has been performed char c = user_input_get(); // get user input if (0x04==c) { // CTRL+D is used to quit the mode printf("quit\n"); // acknowledge quitting command_quit(NULL); // quit current mode } else { terminal_send(c); // send received character to terminal } } if (action) { // go to sleep if nothing had to be done, else recheck for activity action = false; } else { __WFI(); // go to sleep } } // main loop }