diff --git a/examples/stm32/f4/nucleo-f411re/usart-stdio/Makefile b/examples/stm32/f4/nucleo-f411re/usart-stdio/Makefile new file mode 100644 index 0000000..31a3e29 --- /dev/null +++ b/examples/stm32/f4/nucleo-f411re/usart-stdio/Makefile @@ -0,0 +1,25 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2015 Chuck McManis +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library 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 Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +BINARY = usart-stdio + +LDSCRIPT = ../nucleo-f411re.ld + +include ../../Makefile.include + diff --git a/examples/stm32/f4/nucleo-f411re/usart-stdio/README.md b/examples/stm32/f4/nucleo-f411re/usart-stdio/README.md new file mode 100644 index 0000000..f823bcf --- /dev/null +++ b/examples/stm32/f4/nucleo-f411re/usart-stdio/README.md @@ -0,0 +1,21 @@ +# README + +This program demonstrates using the standard I/O and library +functions built into newlib on the Nucleo F411RE eval board. + +Normally the functions would seem to be extravagant for an +embedded application but the F4 series has a lot of flash +(512K in the case of the STM32F411RE) and so you can use this +to make it easier to work with. + +This example asks you to enter a number, 1 or greater, and +it uses that number as the delay constant when blinking the LED +on the Nucleo board. It blinks the LED 1000 times so if you +choose a large number it will take a while to finish. + +100000 is reasonably fast, 500000 is quite slow. + +There is also a simple buffered input routine in the code so +you can edit the number as you are entering it. The ^H or DEL +keys will delete a character, ^U will erase the line, and +^W will delete the last word (defined by space characters). diff --git a/examples/stm32/f4/nucleo-f411re/usart-stdio/usart-stdio.c b/examples/stm32/f4/nucleo-f411re/usart-stdio/usart-stdio.c new file mode 100644 index 0000000..4934e9c --- /dev/null +++ b/examples/stm32/f4/nucleo-f411re/usart-stdio/usart-stdio.c @@ -0,0 +1,234 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Chuck McManis + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * To implement the STDIO functions you need to create + * the _read and _write functions and hook them to the + * USART you are using. This example also has a buffered + * read function for basic line editing. + */ +int _write(int fd, char *ptr, int len); +int _read(int fd, char *ptr, int len); +void get_buffered_line(void); + +/* + * This is a pretty classic ring buffer for characters + */ +#define BUFLEN 127 + +static uint16_t start_ndx; +static uint16_t end_ndx; +static char buf[BUFLEN+1]; +#define buf_len ((end_ndx - start_ndx) % BUFLEN) +static inline int inc_ndx(int n) { return ((n + 1) % BUFLEN); } +static inline int dec_ndx(int n) { return (((n + BUFLEN) - 1) % BUFLEN); } + + +static void clock_setup(void) +{ + /* Enable GPIOD clock for LED & USARTs. */ + rcc_periph_clock_enable(RCC_GPIOA); + + /* Enable clocks for USART2. */ + rcc_periph_clock_enable(RCC_USART2); +} + +static void usart_setup(void) +{ + /* Setup USART2 parameters. */ + usart_set_baudrate(USART2, 115200); + usart_set_databits(USART2, 8); + usart_set_stopbits(USART2, USART_STOPBITS_1); + usart_set_mode(USART2, USART_MODE_TX_RX); + usart_set_parity(USART2, USART_PARITY_NONE); + usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); + + /* Finally enable the USART. */ + usart_enable(USART2); +} + +static void gpio_setup(void) +{ + /* Setup GPIO pin GPIO5 on GPIO port A for LED. */ + gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5); + + /* Setup GPIO pins for USART2 transmit and receive. */ + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3); + + /* Setup USART2 TX pin as alternate function. */ + gpio_set_af(GPIOA, GPIO_AF7, GPIO2 | GPIO3); +} + +int main(void) +{ + int i, j; + + clock_setup(); + gpio_setup(); + usart_setup(); + printf("\nStandard I/O Example.\n"); + + /* Blink the LED (PD12) on the board with every transmitted byte. */ + while (1) { + int delay = 0; + char local_buf[32]; + + gpio_toggle(GPIOA, GPIO5); /* LED on/off */ + do { + printf("Enter the delay constant for blink : "); + fflush(stdout); + fgets(local_buf, 32, stdin); + delay = atoi(local_buf); + if (delay <= 0) { + printf("Error: expected a delay > 0\n"); + } + } while (delay <= 0); + + printf("Blinking with a delay of %d\n", delay); + for (j = 0; j < 1000; j++) { + gpio_toggle(GPIOA, GPIO5); + for (i = 0; i < delay; i++) { /* Wait a bit. */ + __asm__("NOP"); + } + } + } + return 0; +} + +/* back up the cursor one space */ +static inline void back_up(void) +{ + end_ndx = dec_ndx(end_ndx); + usart_send_blocking(USART2, '\010'); + usart_send_blocking(USART2, ' '); + usart_send_blocking(USART2, '\010'); +} + +/* + * A buffered line editing function. + */ +void +get_buffered_line(void) { + char c; + + if (start_ndx != end_ndx) { + return; + } + while (1) { + c = usart_recv_blocking(USART2); + if (c == '\r') { + buf[end_ndx] = '\n'; + end_ndx = inc_ndx(end_ndx); + buf[end_ndx] = '\0'; + usart_send_blocking(USART2, '\r'); + usart_send_blocking(USART2, '\n'); + return; + } + /* ^H or DEL erase a character */ + if ((c == '\010') || (c == '\177')) { + if (buf_len == 0) { + usart_send_blocking(USART2, '\a'); + } else { + back_up(); + } + /* ^W erases a word */ + } else if (c == 0x17) { + while ((buf_len > 0) && + (!(isspace((int) buf[end_ndx])))) { + back_up(); + } + /* ^U erases the line */ + } else if (c == 0x15) { + while (buf_len > 0) { + back_up(); + } + /* Non-editing character so insert it */ + } else { + if (buf_len == (BUFLEN - 1)) { + usart_send_blocking(USART2, '\a'); + } else { + buf[end_ndx] = c; + end_ndx = inc_ndx(end_ndx); + usart_send_blocking(USART2, c); + } + } + } +} + + +/* + * Called by libc stdio fwrite functions + */ +int +_write(int fd, char *ptr, int len) +{ + int i = 0; + + /* + * Write "len" of char from "ptr" to file id "fd" + * Return number of char written. + * + * Only work for STDOUT, STDIN, and STDERR + */ + if (fd > 2) { + return -1; + } + while (*ptr && (i < len)) { + usart_send_blocking(USART2, *ptr); + if (*ptr == '\n') { + usart_send_blocking(USART2, '\r'); + } + i++; + ptr++; + } + return i; +} + +/* + * Called by the libc stdio fread fucntions + * + * Implements a buffered read with line editing. + */ +int +_read(int fd, char *ptr, int len) +{ + int my_len; + + if (fd > 2) { + return -1; + } + + get_buffered_line(); + my_len = 0; + while ((buf_len > 0) && (len > 0)) { + *ptr++ = buf[start_ndx]; + start_ndx = inc_ndx(start_ndx); + my_len++; + len--; + } + return my_len; /* return the length we got */ +}