diff --git a/application.c b/application.c index 28071bf..be84e23 100644 --- a/application.c +++ b/application.c @@ -39,7 +39,7 @@ /* own libraries */ #include "global.h" // board definitions #include "print.h" // printing utilities -#include "usart.h" // USART utilities +#include "uart.h" // USART utilities #include "usb_cdcacm.h" // USB CDC ACM utilities #define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */ @@ -61,9 +61,9 @@ size_t putc(char c) length = 0; // don't print string termination character } else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r) - usart_putchar_nonblocking('\r'); // send CR over USART + uart_putchar_nonblocking('\r'); // send CR over USART usb_cdcacm_putchar('\r'); // send CR over USB - usart_putchar_nonblocking('\n'); // send LF over USART + uart_putchar_nonblocking('\n'); // send LF over USART usb_cdcacm_putchar('\n'); // send LF over USB length += 2; // remember we printed 2 characters newline = c; // remember on which character we sent the newline @@ -71,7 +71,7 @@ size_t putc(char c) length = 0; // the \r or \n of \n\r or \r\n has already been printed } } else { - usart_putchar_nonblocking(c); // send byte over USART + uart_putchar_nonblocking(c); // send byte over USART usb_cdcacm_putchar(c); // send byte over USB newline = 0; // clear new line length++; // remember we printed 1 character @@ -189,7 +189,7 @@ void main(void) #endif board_setup(); // setup board - usart_setup(); // setup USART (for printing) + uart_setup(); // setup USART (for printing) usb_cdcacm_setup(); // setup USB CDC ACM (for printing) printf("welcome to the CuVoodoo STM32F1 example application\n"); // print welcome message @@ -224,10 +224,10 @@ void main(void) bool char_flag = false; // a new character has been received while (true) { // infinite loop iwdg_reset(); // kick the dog - while (usart_received) { // data received over UART + while (uart_received) { // data received over UART action = true; // action has been performed led_toggle(); // toggle LED - c = usart_getchar(); // store receive character + c = uart_getchar(); // store receive character char_flag = true; // notify character has been received } while (usb_cdcacm_received) { // data received over USB @@ -239,7 +239,8 @@ void main(void) while (char_flag) { // user data received char_flag = false; // reset flag action = true; // action has been performed - printf("%c",c); // echo receive character + //printf("%c",c); // echo receive character + printf("%02x\n",c); if (c=='\r' || c=='\n') { // end of command received if (command_i>0) { // there is a command to process command[command_i] = 0; // end string diff --git a/bootloader.c b/bootloader.c index 33edc31..f036d62 100644 --- a/bootloader.c +++ b/bootloader.c @@ -51,12 +51,13 @@ void main(void) rcc_periph_clock_enable(RCC_GPIO(DFU_FORCE_PORT)); // enable clock for GPIO domain gpio_set_mode(GPIO(DFU_FORCE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO(DFU_FORCE_PIN)); // set GPIO to input // pull on the opposite of the expected value - if (DFU_FORCE_VALUE) { - gpio_clear(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN)); // pull down to be able to detect when tied to high - } else { - gpio_set(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN)); // pull up to be able to detect when tied to low - } - if ((!DFU_FORCE_VALUE && 0==gpio_get(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN))) || (DFU_FORCE_VALUE && 0!=gpio_get(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN)))) { // check if output is set to the value to force DFU mode +#if (DFU_FORCE_VALUE==1) + gpio_clear(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN)); // pull down to be able to detect when tied to high + if (gpio_get(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN))) { // check if output is set to the value to force DFU mode +#else + gpio_set(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN)); // pull up to be able to detect when tied to low + if (0==gpio_get(GPIO(DFU_FORCE_PORT), GPIO(DFU_FORCE_PIN))) { // check if output is set to the value to force DFU mode +#endif dfu_force = true; // DFU mode forced } } diff --git a/global.c b/global.c index 73ddf04..5cf28b6 100644 --- a/global.c +++ b/global.c @@ -61,21 +61,23 @@ char* b2s(uint64_t binary, uint8_t rjust) /** switch on board LED */ void led_on(void) { -#if defined(SYSTEM_BOARD) || defined(BLUE_PILL) || defined(CORE_BOARD) - gpio_clear(GPIO(LED_PORT), GPIO(LED_PIN)); -#elif defined(MAPLE_MINI) +#if defined(LED_ON) && LED_ON gpio_set(GPIO(LED_PORT), GPIO(LED_PIN)); +#else + gpio_clear(GPIO(LED_PORT), GPIO(LED_PIN)); #endif } + /** switch off board LED */ void led_off(void) { -#if defined(SYSTEM_BOARD) || defined(BLUE_PILL) || defined(CORE_BOARD) - gpio_set(GPIO(LED_PORT), GPIO(LED_PIN)); -#elif defined(MAPLE_MINI) +#if defined(LED_ON) && LED_ON gpio_clear(GPIO(LED_PORT), GPIO(LED_PIN)); +#else + gpio_set(GPIO(LED_PORT), GPIO(LED_PIN)); #endif } + /** toggle board LED */ void led_toggle(void) { @@ -133,10 +135,10 @@ void board_setup(void) gpio_set_mode(GPIO(BUTTON_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO(BUTTON_PIN)); // set button pin to input rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt exti_select_source(EXTI(BUTTON_PIN), GPIO(BUTTON_PORT)); // mask external interrupt of this pin only for this port -#if defined(MAPLE_MINI) +#if defined(BUTTON_PRESSED) && BUTTON_PRESSED gpio_clear(GPIO(BUTTON_PORT), GPIO(BUTTON_PIN)); // pull down to be able to detect button push (go high) exti_set_trigger(EXTI(BUTTON_PIN), EXTI_TRIGGER_RISING); // trigger when button is pressed -#elif defined(CORE_BOARD) +#else gpio_set(GPIO(BUTTON_PORT), GPIO(BUTTON_PIN)); // pull up to be able to detect button push (go low) exti_set_trigger(EXTI(BUTTON_PIN), EXTI_TRIGGER_FALLING); // trigger when button is pressed #endif diff --git a/global.h b/global.h index 1559ed0..52c38d0 100644 --- a/global.h +++ b/global.h @@ -309,14 +309,17 @@ /* on system and core board LED is on pin 11/PA1 */ #define LED_PORT A /**< GPIO port (port A) */ #define LED_PIN 1 /**< GPIO pin (pin PA1) */ + #define LED_ON 0 /**< LED is on when pin is low */ #elif defined(BLUE_PILL) /* on minimum system LED is on pin 2/PC13 */ #define LED_PORT C /**< GPIO port (port C on blue pill) */ #define LED_PIN 13 /**< GPIO pin (pin PC13 on system board) */ + #define LED_ON 0 /**< LED is on when pin is low */ #elif defined (MAPLE_MINI) /* on maple mini LED is on pin 19/PB1 */ #define LED_PORT B /**< GPIO port (port B on maple mini) */ #define LED_PIN 1 /**< GPIO pin (pin PB1 on maple mini) */ + #define LED_ON 1 /**< LED is on when pin is high */ #endif /** @} */ @@ -327,10 +330,12 @@ /* on maple mini user button is on 32/PB8 */ #define BUTTON_PORT B /**< GPIO port (port B on maple mini) */ #define BUTTON_PIN 8 /**< GPIO pin (pin PB8 on maple mini) */ + #define BUTTON_PRESSED 1 /**< pin is high when button is pressed */ #elif defined(CORE_BOARD) /* on core board user button is on PA8 */ #define BUTTON_PORT A /**< GPIO port (port A) */ #define BUTTON_PIN 8 /**< GPIO pin (pin PA8) */ + #define BUTTON_PRESSED 0 /**< pin is low when button is pressed */ #endif /** @} */ @@ -341,17 +346,17 @@ /* use button */ #define DFU_FORCE_PORT BUTTON_PORT /**< button port */ #define DFU_FORCE_PIN BUTTON_PIN /**< button pin */ - #define DFU_FORCE_VALUE true /**< button is pulled low unpressed, high pressed to force DFU mode */ + #define DFU_FORCE_VALUE 1 /**< button is pulled low unpressed, high pressed to force DFU mode */ #elif defined(CORE_BOARD) /* use button */ #define DFU_FORCE_PORT BUTTON_PORT /**< button port */ #define DFU_FORCE_PIN BUTTON_PIN /**< button pin */ - #define DFU_FORCE_VALUE false /**< button floating unpressed, connected to ground pressed to force DFU mode */ + #define DFU_FORCE_VALUE 0 /**< button floating unpressed, connected to ground pressed to force DFU mode */ #else - /* use the JNTRST pin as JPIO (this will disable the SWJ function, but we are not using it) */ + /* use the JNTRST pin as GPIO (this will disable the SWJ function, but we are not using it) */ #define DFU_FORCE_PORT B /**< JNTRST port (needs to be remapped to become PB4) */ #define DFU_FORCE_PIN 4 /**< JNTRST pin (needs to be remapped to become PB4) */ - #define DFU_FORCE_VALUE false /**< must be high to force DFU mode, since it's low after reset */ + #define DFU_FORCE_VALUE 1 /**< must be high to force DFU mode, since it's low after reset */ #endif /** @} */ diff --git a/lib/print.c b/lib/print.c index e32d0c3..c250f33 100644 --- a/lib/print.c +++ b/lib/print.c @@ -22,15 +22,29 @@ #include // standard definitions #include // boolean types #include // variadic utilities +#include // mathematics utilities to handle floating points /* own libraries */ #include "print.h" // printing utilities -/** @defgroup print_crlf output \r\n (Carriage Return + Line Feed) for each \r, \n, \r\n, or \n\r for better terminal compatibility - * @{ - **/ -#define CRLF true /**< if CR+LN new line should be enforced */ -/** @} */ +uint8_t print_error; + +/** add printed length to total printed length, and sets error if maximum size is exceeded + * @param[in,out] length total printed length + * @param[in] printed printed length + */ +static void print_printed(size_t* length, size_t printed) +{ + if (NULL==length) { // check if total is provided + return; + } + if (*length>SIZE_MAX-printed) { // prevent integer overflow + *length = SIZE_MAX; // set to maximum + print_error |= PRINT_ERROR_MAX; // set error + } else { + *length += printed; // save printed length + } +} /** print character * @param[out] str string to print character on (use NULL to print on user output) @@ -45,13 +59,12 @@ static size_t print_char(char** str, size_t* size, char c) length = 0; // remember we didn't print anything } else if (NULL==str || NULL==*str || NULL==size) { // character should not be saved on string length = putc(c); // print on user define output - } else if (*size>1) { // // there is enough space in the string to store the character + } else if (*size>1) { // there is enough space in the string to store the character **str = c; // add provided character to string *str += 1; // go to next character on string *size -= 1; // remember we used one character on string - } else if (1==*size) { // string is reaching it's end - **str = '\0'; // add termination character to string (don't go to next character) - *size -= 1; // remember we used one character on string + } else { // string is reached its end + print_error |= PRINT_ERROR_TRUNCATED; // indicate we did not save the character } return length; } @@ -66,20 +79,20 @@ static size_t print_string(char** str, size_t* size, const char* s) { size_t length = 0; // number of characters printed while (*s) { // stop at end of string - length += print_char(str, size, *(s++)); // print character + print_printed(&length, print_char(str, size, *(s++))); // print character } return length; } -/** print unsigned number - * @param[out] str string to print unsigned number on (use NULL to print on user output) +/** print unsigned integer + * @param[out] str string to print unsigned integer on (use NULL to print on user output) * @param[in,out] size size of string - * @param[in] u unsigned number to be printed + * @param[in] u unsigned integer to be printed * @param[in] padding number of 0's to pad * @param[in] sign if sign should be printed * @return number of characters printed **/ -static size_t print_unsigned(char** str, size_t* size, uint64_t u, uint8_t padding, bool sign) { +static size_t print_unsigned(char** str, size_t* size, uint64_t u, uint32_t padding, bool sign) { char number[20] = {0}; // construct the number in reverse order (20 chars are required to store UINT64_MAX) uint8_t digits = 0; // to count the number of digits size_t length = 0; // number of characters printed @@ -87,36 +100,112 @@ static size_t print_unsigned(char** str, size_t* size, uint64_t u, uint8_t paddi number[digits++] = '0'+(u%10); // store digit u /= 10; // go to next digit } while (u>0); - if (digits>sizeof(number)) { // prevent buffer underflow - return 0; - } if (sign) { // print sign - length += print_char(str, size, '+'); // we only have positive numbers + print_printed(&length, print_char(str, size, '+')); // we only have positive numbers } - for (uint8_t zeros = digits; zerospow(10.0, exponent)) { // find the positive exponent, base 10 + exponent += 3; // increment in kilo + } + if (padding) { // respect padding wish + exponent -= padding; + } else { + exponent -= 3; + } + } + // print integer part + f_abs /= pow(10.0, exponent); // convert to scientific format + print_printed(&length, print_unsigned(str, size, f_abs, padding, false)); // print integer part as scientific number + // print fractional part + if (fractional) { + print_printed(&length, print_char(str, size, '.')); // print decimal point + f_abs -= (uint64_t)f_abs; // remove integer part + for (uint32_t frac=0; frac0x00ffffff) { + if (hex>0xffffffffffffffUL) { + digits = 16; + } else if (hex>0xffffffffffffUL) { + digits = 14; + } else if (hex>0xffffffffffUL) { + digits = 12; + } else if (hex>0xffffffffUL) { + digits = 10; + } else if (hex>0xffffffUL) { digits = 8; - } else if (hex>0x0000ffff) { + } else if (hex>0xffffUL) { digits = 6; - } else if (hex>0x000000ff) { + } else if (hex>0xffUL) { digits = 4; } else { digits = 2; } - for (uint8_t zeros = digits; zeros>((digits-digit-1)*4), upcase); // print nibble (in reverse order) + print_printed(&length, print_nibble(str, size, hex>>((digits-digit-1)*4), upcase)); // print nibble (in reverse order) } return length; // return number of characters printed } @@ -184,8 +281,8 @@ static size_t print_hex(char** str, size_t* size, uint32_t hex, uint8_t padding, * @param[in] prefix if 0b prefix should be printed * @return number of characters printed **/ -static size_t print_bits(char** str, size_t* size, uint32_t u, uint8_t padding, bool prefix) { - char bits[32] = {0}; // construct the bit string in reverse order +static size_t print_bits(char** str, size_t* size, uint64_t u, uint32_t padding, bool prefix) { + char bits[64] = {0}; // construct the bit string in reverse order uint8_t digits = 0; // to count the number of digits size_t length = 0; // number of characters printed do { @@ -196,14 +293,14 @@ static size_t print_bits(char** str, size_t* size, uint32_t u, uint8_t padding, return 0; } if (prefix) { // print prefix - length += print_char(str, size, '0'); - length += print_char(str, size, 'b'); + print_printed(&length, print_char(str, size, '0')); + print_printed(&length, print_char(str, size, 'b')); } - for (uint8_t zeros = digits; zeros='0' && *format<='9') { - padding = *format-'0'; - format++; // go to format specifier - if (0==*format) { // end of string detected + while (*format>='0' && *format<='9') { + if (padding>UINT32_MAX/10) { // check for overflow + print_error |= PRINT_ERROR_UNSUPPORTED; // set error goto end; } + padding *= 10; // go to next magnitude + if (padding>UINT32_MAX-(*format-'0')) { // check for overflow + print_error |= PRINT_ERROR_UNSUPPORTED; // set error + goto end; + } + padding += *format-'0'; // save digit + format++; // go to next character } + if (0==*format) { // end of string detected + print_error |= PRINT_ERROR_MALFORMED; // set error + goto end; + } + } + // check fractional + if ('.'==*format) { // fractional required + format++; // go to fractional number + while (*format>='0' && *format<='9') { + if (fractional>UINT32_MAX/10) { // check for overflow + print_error |= PRINT_ERROR_UNSUPPORTED; // set error + goto end; + } + fractional *= 10; // go to next magnitude + if (fractional>UINT32_MAX-(*format-'0')) { // check for overflow + print_error |= PRINT_ERROR_UNSUPPORTED; // set error + goto end; + } + fractional += *format-'0'; // save digit + format++; // go to next character + } + if (0==*format) { // end of string detected + print_error |= PRINT_ERROR_MALFORMED; // set error + goto end; + } + } else { + fractional = 2; // default fractional precision } // check format specifier switch (*format) { - case 'u': // for uint8_t, uint16_t, uint32_t, unsigned int, unsigned long - length += print_unsigned(str, size, va_arg(va,uint32_t), padding, sign); - break; - case 'U': // for uint64_t, unsigned long long - length += print_unsigned(str, size, va_arg(va,uint64_t), padding, sign); - break; - case 'd': // for int8_t, int16_t, int32_t, int, long - length += print_signed(str, size, va_arg(va,int32_t), padding, sign); - break; - case 'D': // for int64_t, long long - length += print_signed(str, size, va_arg(va,int64_t), padding, sign); - break; case 'c': // for char, unsigned char - length += print_char(str, size, (char)(va_arg(va,int))); // needs casting because the returned value is promoted - break; - case 'x': // for downcase hexadecimal - length += print_hex(str, size, va_arg(va,uint32_t), padding, sign, false); - break; - case 'X': // for upcase hexadecimal - length += print_hex(str, size, va_arg(va,uint32_t), padding, sign, true); - break; - case 'b': // for bits - length += print_bits(str, size, va_arg(va,uint32_t), padding, sign); + print_printed(&length, print_char(str, size, (char)(va_arg(va,int)))); // needs casting because the returned value is promoted break; case 's': // for strings - length += print_string(str, size, va_arg(va,char*)); + print_printed(&length, print_string(str, size, va_arg(va,char*))); + break; + case 'u': // for uint8_t, uint16_t, uint32_t, unsigned int, unsigned long + print_printed(&length, print_unsigned(str, size, va_arg(va,uint32_t), padding, sign)); + break; + case 'U': // for uint64_t, unsigned long long + print_printed(&length, print_unsigned(str, size, va_arg(va,uint64_t), padding, sign)); + break; + case 'd': // for int8_t, int16_t, int32_t, int, long + print_printed(&length, print_signed(str, size, va_arg(va,int32_t), padding, sign)); + break; + case 'D': // for int64_t, long long + print_printed(&length, print_signed(str, size, va_arg(va,int64_t), padding, sign)); + break; + case 'f': + print_printed(&length, print_float(str, size, va_arg(va,double), padding, fractional, sign)); + break; + case 'x': // for uint8_t, uint16_t, uint32_t downcase hexadecimal + print_printed(&length, print_hex(str, size, va_arg(va,uint32_t), padding, sign, false)); + break; + case 'X': // for uint64_t downcase hexadecimal + print_printed(&length, print_hex(str, size, va_arg(va,uint64_t), padding, sign, false)); + break; + case 'h': // for uint8_t, uint16_t, uint32_t upcase hexadecimal + print_printed(&length, print_hex(str, size, va_arg(va,uint32_t), padding, sign, true)); + break; + case 'H': // for uint64_t upcase hexadecimal + print_printed(&length, print_hex(str, size, va_arg(va,uint64_t), padding, sign, true)); + break; + case 'b': // for uint8_t, uint16_t, uint32_t bits + print_printed(&length, print_bits(str, size, va_arg(va,uint32_t), padding, sign)); + break; + case 'B': // for uint64_t bits + print_printed(&length, print_bits(str, size, va_arg(va,uint64_t), padding, sign)); break; default: - length += print_char(str, size, *format); // print character (unknown format specifier) + print_error |= PRINT_ERROR_UNSUPPORTED; // set error + print_printed(&length, print_char(str, size, *format)); // print character (unknown format specifier) } format++; // go to next character } @@ -290,26 +433,31 @@ static size_t vsnprintf(char** str, size_t* size, const char *format, va_list va end: if (NULL!=str && NULL!=*str && NULL!=size) { // when working on a string **str='\0'; // enforce null termination + if (*size>0) { + *size -= 1; // remember we used memory + } else { + print_error |= PRINT_ERROR_TRUNCATED; // indicate we truncated the string + } } - return length; // return number of characters it should have written + return length; // return number of characters it should have written (not including the '\0' null termination character) } size_t printf(const char *format, ...) { - size_t length = 0; + print_error = PRINT_ERROR_NONE; // clear error va_list arglist; va_start(arglist, format); - length = vsnprintf(NULL, NULL, format, arglist); + size_t length = vsnprintf(NULL, NULL, format, arglist); va_end(arglist); return length; } size_t snprintf(char* str, size_t size, const char* format, ...) { - size_t length = 0; + print_error = PRINT_ERROR_NONE; // clear error va_list arglist; va_start(arglist, format); - length = vsnprintf(&str, &size, format, arglist); + size_t length = vsnprintf(&str, &size, format, arglist); va_end(arglist); return length; } diff --git a/lib/print.h b/lib/print.h index 440909e..85e811b 100644 --- a/lib/print.h +++ b/lib/print.h @@ -13,14 +13,35 @@ * */ /** printing utilities to replace the large printf from the standard library (API) - * @note use % as format specifier prefix, followed by + to enforce sign of prefix, 0 and 0-9 for padding, and format specifier - * format specifier supported are: c for far, s for string, u for uint32_t, d for int32_t, U for uint64_t, D for int64_t, x for lower case hex up to uint32_t, X for upper case hex up to uint32_t, b for bits up to uint32_t + * @note use % as format specifier prefix, followed by + to enforce sign or 0x prefix, 0 followed by n for padding or forcing integer part of floating point number, . followed by n for number for fractional precision of floating point numbers, and format specifier + * format specifier supported are: + * - c for character + * - s for string + * - u for up to uint32_t unsigned integer + * - U for uint64_t unsigned integer + * - d for up to int32_t signed integer + * - D for int64_t signed integer + * - f for float and double floating point numbers + * - x for up to uint32_t lower case hexadecimal + * - X for uint64_t lower case hexadecimal + * - h for up to uint32_t upper case hexadecimal + * - H for uint64_t upper case hexadecimal + * - b for up to uint32_t bits + * - B for uint64_t bits * @file print.h * @author King Kévin * @date 2017 */ #pragma once +extern uint8_t print_error; /**< flags to indicate which error(s) occurred within printf or snprintf */ + +#define PRINT_ERROR_NONE 0 /**< no error occurred */ +#define PRINT_ERROR_MALFORMED 0x1 /**< input format string is malformed */ +#define PRINT_ERROR_UNSUPPORTED 0x2 /**< input format string is not supported */ +#define PRINT_ERROR_MAX 0x04 /**< maximum returned printed length reached but more has been printed */ +#define PRINT_ERROR_TRUNCATED 0x08 /**< output string size is not large enough to include complete printed string */ + /** print a single character on user output * @warning this must be implemented by the user (using the desired output interface) * @param[in] c character to be printed diff --git a/lib/uart.c b/lib/uart.c new file mode 100644 index 0000000..dfbc7ea --- /dev/null +++ b/lib/uart.c @@ -0,0 +1,148 @@ +/* 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 . + * + */ +/** library for UART communication (code) + * @file uart.c + * @author King Kévin + * @date 2016 + * @note peripherals used: USART @ref uart + */ + +/* standard libraries */ +#include // standard integer types +#include // standard I/O facilities +#include // general utilities + +/* STM32 (including CM3) libraries */ +#include // real-time control clock library +#include // general purpose input output library +#include // universal synchronous asynchronous receiver transmitter library +#include // interrupt handler +#include // Cortex M3 utilities + +#include "uart.h" // UART header and definitions +#include "global.h" // common methods + +/** @defgroup uart UART peripheral used for UART communication + * @{ + */ +#define UART_ID 1 /**< UART peripheral */ +/** @} */ + +#define UART_BAUDRATE 921600 /**< serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */ + +/* input and output ring buffer, indexes, and available memory */ +static uint8_t rx_buffer[UART_BUFFER] = {0}; /**< ring buffer for received data */ +static volatile uint8_t rx_i = 0; /**< current position of read received data */ +static volatile uint8_t rx_used = 0; /**< how much data has been received and not red */ +static uint8_t tx_buffer[UART_BUFFER] = {0}; /**< ring buffer for data to transmit */ +static volatile uint8_t tx_i = 0; /**< current position of transmitted data */ +static volatile uint8_t tx_used = 0; /**< how much data needs to be transmitted */ + +volatile bool uart_received = false; + +void uart_setup(void) +{ + /* enable UART I/O peripheral */ + rcc_periph_clock_enable(USART_PORT_RCC(UART_ID)); // enable clock for UART port peripheral + rcc_periph_clock_enable(USART_RCC(UART_ID)); // enable clock for UART peripheral + rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (UART) + gpio_set_mode(USART_PORT(UART_ID), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(UART_ID)); // setup GPIO pin UART transmit + gpio_set_mode(USART_PORT(UART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(UART_ID)); // setup GPIO pin UART receive + gpio_set(USART_PORT(UART_ID), USART_PIN_RX(UART_ID)); // pull up to avoid noise when not connected + + /* setup UART parameters */ + usart_set_baudrate(USART(UART_ID), UART_BAUDRATE); + usart_set_databits(USART(UART_ID), 8); + usart_set_stopbits(USART(UART_ID), USART_STOPBITS_1); + usart_set_mode(USART(UART_ID), USART_MODE_TX_RX); + usart_set_parity(USART(UART_ID), USART_PARITY_NONE); + usart_set_flow_control(USART(UART_ID), USART_FLOWCONTROL_NONE); + + nvic_enable_irq(USART_IRQ(UART_ID)); // enable the UART interrupt + usart_enable_rx_interrupt(USART(UART_ID)); // enable receive interrupt + usart_enable(USART(UART_ID)); // enable UART + + /* reset buffer states */ + tx_i = 0; + tx_used = 0; + rx_i = 0; + rx_used = 0; + uart_received = false; +} + +void uart_putchar_blocking(char c) +{ + uart_flush(); // empty buffer first + usart_send_blocking(USART(UART_ID), c); // send character +} + +void uart_flush(void) +{ + while (tx_used) { // idle until buffer is empty + __WFI(); // sleep until interrupt + } + usart_wait_send_ready(USART(UART_ID)); // wait until transmit register is empty (transmission might not be complete) +} + +char uart_getchar(void) +{ + while (!rx_used) { // idle until data is available + __WFI(); // sleep until interrupt + } + char to_return = rx_buffer[rx_i]; // get the next available character + usart_disable_rx_interrupt(USART(UART_ID)); // disable receive interrupt to prevent index corruption + rx_i = (rx_i+1)%LENGTH(rx_buffer); // update used buffer + rx_used--; // update used buffer + uart_received = (rx_used!=0); // update available data + usart_enable_rx_interrupt(USART(UART_ID)); // enable receive interrupt + return to_return; +} + +void uart_putchar_nonblocking(char c) +{ + while (tx_used>=LENGTH(tx_buffer)) { // idle until buffer has some space + usart_enable_tx_interrupt(USART(UART_ID)); // enable transmit interrupt + __WFI(); // sleep until something happened + } + usart_disable_tx_interrupt(USART(UART_ID)); // disable transmit interrupt to prevent index corruption + tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // put character in buffer + tx_used++; // update used buffer + usart_enable_tx_interrupt(USART(UART_ID)); // enable transmit interrupt +} + +/** UART interrupt service routine called when data has been transmitted or received */ +void USART_ISR(UART_ID)(void) +{ + if (usart_get_flag(USART(UART_ID), USART_SR_TXE)) { // data has been transmitted + if (!tx_used) { // no data in the buffer to transmit + usart_disable_tx_interrupt(USART(UART_ID)); // disable transmit interrupt + } else { + usart_send(USART(UART_ID),tx_buffer[tx_i]); // put data in transmit register + tx_i = (tx_i+1)%LENGTH(rx_buffer); // update location on buffer + tx_used--; // update used size + } + } + while (usart_get_flag(USART(UART_ID), USART_SR_RXNE)) { // data has been received (repeat while receiving) + char c = usart_recv(USART(UART_ID)); // save character and free UART buffer + // only save data if there is space in the buffer + if (rx_used>=LENGTH(rx_buffer)) { // if buffer is full + rx_i = (rx_i+1)%LENGTH(rx_buffer); // drop oldest data + rx_used--; // update used buffer information + } + rx_buffer[(rx_i+rx_used)%LENGTH(rx_buffer)] = c; // put character in buffer + rx_used++; // update used buffer + uart_received = true; // update available data + } +} diff --git a/lib/uart.h b/lib/uart.h new file mode 100644 index 0000000..8aa5501 --- /dev/null +++ b/lib/uart.h @@ -0,0 +1,47 @@ +/* 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 . + * + */ +/** library for UART communication (API) + * @file uart.h + * @author King Kévin + * @date 2016 + * @note peripherals used: USART @ref uart + */ +#pragma once + +/** transmit and receive buffer sizes */ +#define UART_BUFFER 128 +/** how many bytes available in the received buffer since last read */ +extern volatile bool uart_received; + +/** setup USART peripheral */ +void uart_setup(void); +/** send character over USART (blocking) + * @param[in] c character to send + * @note blocks until character transmission started */ +void uart_putchar_blocking(char c); +/** ensure all data has been transmitted (blocking) + * @note block until all data has been transmitted + */ +void uart_flush(void); +/** get character received over USART (blocking) + * @return character received over USART + * @note blocks until character is received over USART when received buffer is empty + */ +char uart_getchar(void); +/** send character over USART (non-blocking) + * @param[in] c character to send + * @note blocks if transmit buffer is full, else puts in buffer and returns + */ +void uart_putchar_nonblocking(char c); diff --git a/lib/usart.c b/lib/usart.c index 5344a2f..17794fd 100644 --- a/lib/usart.c +++ b/lib/usart.c @@ -40,7 +40,7 @@ #define USART_ID 1 /**< USART peripheral */ /** @} */ -#define USART_BAUDRATE 1500000 /**< serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */ +#define USART_BAUDRATE 921600 /**< serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */ /* input and output ring buffer, indexes, and available memory */ static uint8_t rx_buffer[USART_BUFFER] = {0}; /**< ring buffer for received data */ @@ -134,14 +134,15 @@ void USART_ISR(USART_ID)(void) tx_used--; // update used size } } - if (usart_get_flag(USART(USART_ID), USART_SR_RXNE)) { // data has been received + while (usart_get_flag(USART(USART_ID), USART_SR_RXNE)) { // data has been received (repeat while receiving) + char c = usart_recv(USART(USART_ID)); // save character and free USART buffer // only save data if there is space in the buffer - while (rx_used>=LENGTH(rx_buffer)) { // if buffer is full + if (rx_used>=LENGTH(rx_buffer)) { // if buffer is full rx_i = (rx_i+1)%LENGTH(rx_buffer); // drop oldest data rx_used--; // update used buffer information } - rx_buffer[(rx_i+rx_used)%LENGTH(rx_buffer)] = usart_recv(USART(USART_ID)); // put character in buffer + rx_buffer[(rx_i+rx_used)%LENGTH(rx_buffer)] = c; // put character in buffer rx_used++; // update used buffer - usart_received = (rx_used!=0); // update available data + usart_received = true; // update available data } }