/* 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 // 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_hiz.h" // BusVoodoo HiZ mode utilities 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 } bool wait_space(void) { printf("press space to continue, or any other key to abort\n"); while (!uart_received && !usb_cdcacm_received) { // wait for user input __WFI(); // go to sleep } char c = 0; if (uart_received) { c = uart_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; } } /** command to show help * @param[in] argument no argument required */ static void command_help(void* argument); /** command to switch LED * @param[in] argument on, off, toggle to switch LED, or NULL to display LED status */ static void command_led(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 */ static void command_bootloader(void* argument); /** list of all supported commands */ static const struct menu_command_t menu_commands[] = { { 'h', "help", "display help", MENU_ARGUMENT_NONE, NULL, &command_help, }, { 'l', "led", "switch LED", MENU_ARGUMENT_STRING, "[on|off|toggle]", &command_led, }, { 'r', "reset", "reset board", MENU_ARGUMENT_NONE, NULL, &command_reset, }, { 'b', "bootloader", "reboot into DFU bootloader", MENU_ARGUMENT_NONE, NULL, &command_bootloader, }, }; 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, LENGTH(busvoodoo_global_commands)); // print BusVoodoo global commands menu_print_commands(busvoodoo_hiz_commands, LENGTH(busvoodoo_hiz_commands)); // print BusVoodoo HiZ commands } static void command_led(void* argument) { if (NULL==argument || 0==strlen(argument)) { printf("LED is "); if (gpio_get(GPIO(LED_PORT), GPIO(LED_PIN))) { printf("on\n"); } else { printf("off\n"); } } else if (0==strcmp(argument, "on")) { led_on(); // switch LED on printf("LED switched on\n"); // notify user } else if (0==strcmp(argument, "off")) { led_off(); // switch LED off printf("LED switched off\n"); // notify user } else if (0==strcmp(argument, "toggle")) { led_toggle(); // toggle LED printf("LED toggled\n"); // notify user } else { printf("option malformed: %s\n", argument); } } 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 } static void command_bootloader(void* argument) { (void)argument; // we won't use the argument RCC_CSR |= RCC_CSR_RMVF; // clear reset flags scb_reset_core(); // reset core (the bootloader will interpret it as starting into DFU) 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) { // don't handle empty lines if (!str || 0==strlen(str)) { return; } // ensure actions are available if (NULL==menu_commands || 0==LENGTH(menu_commands)) { return; } // handle user input if (!menu_handle_command(str, busvoodoo_hiz_commands, LENGTH(busvoodoo_hiz_commands))) { if (!menu_handle_command(str, busvoodoo_global_commands, LENGTH(busvoodoo_global_commands))) { if (!menu_handle_command(str, menu_commands, LENGTH(menu_commands))) { 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 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 board_setup(); // setup board uart_setup(); // setup USART (for printing) usb_cdcacm_setup(); // setup USB CDC ACM (for printing) led_blink(0, 1); // switch blue LED on to show firmware is working busvoodoo_setup(); // setup BusVoodoo board printf("\nwelcome to BusVoodoo ("); // print welcome message if (busvoodoo_full) { printf("full"); } else { printf("light"); } printf(" version)\n"); // main loop terminal_prefix = "BV: "; // set terminal prefix terminal_process = &process_command; terminal_setup(); // start terminal bool action = false; // if an action has been performed don't go to sleep button_flag = false; // reset button flag char c = '\0'; // to store received character bool char_flag = false; // a new character has been received while (true) { // infinite loop while (uart_received) { // data received over UART action = true; // action has been performed led_toggle(); // toggle LED c = uart_getchar(); // store receive character char_flag = true; // notify character has been received } while (usb_cdcacm_received) { // data received over USB action = true; // action has been performed led_toggle(); // toggle LED c = usb_cdcacm_getchar(); // store receive character char_flag = true; // notify character has been received } while (char_flag) { // user data received char_flag = false; // reset flag action = true; // action has been performed // printf("%02x\n", c); terminal_send(c); // send received character to terminal } while (button_flag) { // user pressed button action = true; // action has been performed printf("button pressed\n"); led_toggle(); // toggle LED for (uint32_t i=0; i<1000000; i++) { // wait a bit to remove noise and double trigger __asm__("nop"); } button_flag = false; // reset flag } if (action) { // go to sleep if nothing had to be done, else recheck for activity action = false; } else { __WFI(); // go to sleep } } // main loop }