From 01f4e20346dfd7ca927eb110c4944162addaedf8 Mon Sep 17 00:00:00 2001 From: zhji Date: Sat, 9 Nov 2024 21:37:16 +0800 Subject: [PATCH] [feat] add printf component --- bootloader/CMakeLists.txt | 2 + bootloader/main.c | 19 +- component/CMakeLists.txt | 5 + component/libc/CMakeLists.txt | 9 + component/libc/printf.c | 88 ++ component/libc/vsnprintf.c | 1090 ++++++++++++++++++++ driver/CMakeLists.txt | 2 + driver/STM32H7xx_HAL_Driver/CMakeLists.txt | 4 +- driver/board/CMakeLists.txt | 7 + driver/board/uart_log.c | 73 ++ driver/board/uart_log.h | 44 + 11 files changed, 1335 insertions(+), 8 deletions(-) create mode 100644 component/CMakeLists.txt create mode 100644 component/libc/CMakeLists.txt create mode 100644 component/libc/printf.c create mode 100644 component/libc/vsnprintf.c create mode 100644 driver/board/CMakeLists.txt create mode 100644 driver/board/uart_log.c create mode 100644 driver/board/uart_log.h diff --git a/bootloader/CMakeLists.txt b/bootloader/CMakeLists.txt index 786fe92..6858beb 100644 --- a/bootloader/CMakeLists.txt +++ b/bootloader/CMakeLists.txt @@ -28,9 +28,11 @@ add_executable(${PROJ_NAME}.elf main.c) target_sources(${PROJ_NAME}.elf PUBLIC start.S) add_subdirectory(${SDK_PATH}/driver driver) +add_subdirectory(${SDK_PATH}/component component) add_subdirectory(${BOOTLOADER_PATH}/src src) target_link_libraries(${PROJ_NAME}.elf driver) +target_link_libraries(${PROJ_NAME}.elf component) target_link_libraries(${PROJ_NAME}.elf src) add_custom_command(TARGET ${PROJ_NAME}.elf POST_BUILD diff --git a/bootloader/main.c b/bootloader/main.c index a197e88..c672c35 100644 --- a/bootloader/main.c +++ b/bootloader/main.c @@ -1,24 +1,33 @@ #include "led.h" +#include "uart_log.h" +#include "stdio.h" void system_init(void) { led_init(LED_PIN); + uart_log_init(); } void led_blink(void) { - while (1) { - led_on(LED_PIN); - led_off(LED_PIN); - } + led_on(LED_PIN); + led_off(LED_PIN); } +extern UART_HandleTypeDef UartHandle; + int main(void) { system_init(); // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // __enable_irq(); - led_blink(); + printf("Run start ...\r\n"); + while (1) { + static uint32_t count = 0; + printf("Hello World, count = %ld\r\n", count++); + led_blink(); + } + return 0; } diff --git a/component/CMakeLists.txt b/component/CMakeLists.txt new file mode 100644 index 0000000..0bf64d2 --- /dev/null +++ b/component/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libc libc) + +add_library(component STATIC) +target_link_libraries(component libc) +target_link_libraries(component driver) diff --git a/component/libc/CMakeLists.txt b/component/libc/CMakeLists.txt new file mode 100644 index 0000000..ee8fc3b --- /dev/null +++ b/component/libc/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB FILELIST +printf.c +vsnprintf.c +) +add_library(libc STATIC ${FILELIST}) +target_include_directories(libc PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + +target_link_libraries(libc PUBLIC STM32H7xx_HAL_Driver) +target_link_libraries(libc PUBLIC board) diff --git a/component/libc/printf.c b/component/libc/printf.c new file mode 100644 index 0000000..d662c98 --- /dev/null +++ b/component/libc/printf.c @@ -0,0 +1,88 @@ +#include "uart_log.h" +#include "stdarg.h" +#include "string.h" + +UART_HandleTypeDef *console = NULL; +extern UART_HandleTypeDef UartHandle; + +int putchar(int c) +{ + if (console == NULL) { + return 0; + } + + HAL_UART_Transmit(console, (uint8_t*)&c, 0, HAL_TIMEOUT_VALUE); + + return c; +} + +int puts(const char *c) +{ + int len; + + len = strlen(c); + + if (console == NULL) { + return 0; + } + + HAL_UART_Transmit(console, (uint8_t*)c, len, HAL_TIMEOUT_VALUE); + + return len; +} + +int putstring(const char *c) +{ + int len; + + if (c == NULL) { + return 0; + } + + len = strlen(c); + + if (console == NULL) { + return 0; + } + + HAL_UART_Transmit(console, (uint8_t*)c, len, HAL_TIMEOUT_VALUE); + + return len; +} + +#if defined(CONFIG_VSNPRINTF_NANO) && CONFIG_VSNPRINTF_NANO +int printf(const char *fmt, ...) +{ + char print_buf[512]; + int len; + va_list ap; + + va_start(ap, fmt); + len = vsnprintf(print_buf, sizeof(print_buf), fmt, ap); + va_end(ap); + + len = (len > sizeof(print_buf)) ? sizeof(print_buf) : len; + + HAL_UART_Transmit(console, (uint8_t*)print_buf, len, HAL_TIMEOUT_VALUE); + + return len; +} +#else +extern int console_vsnprintf(const char *fmt, va_list args); +int printf(const char *fmt, ...) +{ + int len; + va_list ap; + + va_start(ap, fmt); + len = console_vsnprintf(fmt, ap); + va_end(ap); + + return len; +} +#endif + +void uart_log_set_console(void) +{ + console = &UartHandle; +} diff --git a/component/libc/vsnprintf.c b/component/libc/vsnprintf.c new file mode 100644 index 0000000..23ed71c --- /dev/null +++ b/component/libc/vsnprintf.c @@ -0,0 +1,1090 @@ +/** + * @author (c) Eyal Rozenberg + * 2021, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with + * a very limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "uart_log.h" + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +#ifndef PRINTF_INTEGER_BUFFER_SIZE +#define PRINTF_INTEGER_BUFFER_SIZE 32 +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32 +#endif + +// Support for the decimal notation floating point conversion specifiers (%f, %F) +#ifndef CONFIG_LIBC_FLOAT +#define CONFIG_LIBC_FLOAT 1 +#endif + +// Support for the exponential notatin floating point conversion specifiers (%e, %g, %E, %G) +#ifndef CONFIG_LIBC_FLOAT_EX +#define CONFIG_LIBC_FLOAT_EX 1 +#endif + +// Default precision for the floating point conversion specifiers (the C standard sets this at 6) +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6 +#endif + +// According to the C languages standard, printf() and related functions must be able to print any +// integral number in floating-point notation, regardless of length, when using the %f specifier - +// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, +// all values beyond this threshold are switched to exponential notation. +#ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL +#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 +#endif + +// Support for the long long integral types (with the ll, z and t length modifiers for specifiers +// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported. +#ifndef CONFIG_LIBC_LONG_LONG +#define CONFIG_LIBC_LONG_LONG 1 +#endif + +#ifndef PRINTF_SUPPORT_LENGTH_FILED +#define PRINTF_SUPPORT_LENGTH_FILED 1 +#endif + +#ifndef PRINTF_SUPPORT_CHAR +#define PRINTF_SUPPORT_CHAR 1 +#endif + +#ifndef PRINTF_SUPPORT_ADDR +#define PRINTF_SUPPORT_ADDR 1 +#endif + + +#if CONFIG_LIBC_LONG_LONG +typedef unsigned long long printf_unsigned_value_t; +typedef long long printf_signed_value_t; +#else +typedef unsigned long printf_unsigned_value_t; +typedef long printf_signed_value_t; +#endif + +#define PRINTF_PREFER_DECIMAL false +#define PRINTF_PREFER_EXPONENTIAL true + +/////////////////////////////////////////////////////////////////////////////// + +// The following will convert the number-of-digits into an exponential-notation literal +#define PRINTF_CONCATENATE(s1, s2) s1##s2 +#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) +#define PRINTF_FLOAT_NOTATION_THRESHOLD PRINTF_EXPAND_THEN_CONCATENATE(1e,PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL) + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) +#define FLAGS_POINTER (1U << 12U) +// Note: Similar, but not identical, effect as FLAGS_HASH + +#define BASE_BINARY 2 +#define BASE_OCTAL 8 +#define BASE_DECIMAL 10 +#define BASE_HEX 16 + +typedef uint8_t numeric_base_t; + +#if (CONFIG_LIBC_FLOAT || CONFIG_LIBC_FLOAT_EX) +#include +#if FLT_RADIX != 2 +#error "Non-binary-radix floating-point types are unsupported." +#endif + +#if DBL_MANT_DIG == 24 + +#define DOUBLE_SIZE_IN_BITS 32 +typedef uint32_t double_uint_t; +#define DOUBLE_EXPONENT_MASK 0xFFU +#define DOUBLE_BASE_EXPONENT 127 + +#elif DBL_MANT_DIG == 53 + +#define DOUBLE_SIZE_IN_BITS 64 +typedef uint64_t double_uint_t; +#define DOUBLE_EXPONENT_MASK 0x7FFU +#define DOUBLE_BASE_EXPONENT 1023 + +#else +#error "Unsupported double type configuration" +#endif +#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1) + +typedef union { + double_uint_t U; + double F; +} double_with_bit_access; + +// This is unnecessary in C99, since compound initializers can be used, +// but: 1. Some compilers are finicky about this; 2. Some people may want to convert this to C89; +// 3. If you try to use it as C++, only C++20 supports compound literals +static inline double_with_bit_access get_bit_access(double x) +{ + double_with_bit_access dwba; + dwba.F = x; + return dwba; +} + +static inline int get_sign(double x) +{ + // The sign is stored in the highest bit + return get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1); +} + +static inline int get_exp2(double_with_bit_access x) +{ + // The exponent in an IEEE-754 floating-point number occupies a contiguous + // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An + // unsigned offset from some negative value (with the extremal offset values reserved for + // special use). + return (int)((x.U >> DOUBLE_STORED_MANTISSA_BITS ) & DOUBLE_EXPONENT_MASK) - DOUBLE_BASE_EXPONENT; +} +#define PRINTF_ABS(_x) ( (_x) > 0 ? (_x) : -(_x) ) + +#endif // (CONFIG_LIBC_FLOAT || CONFIG_LIBC_FLOAT_EX) + +// Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid +// and well-defined, but if you're not careful you can easily trigger undefined +// behavior with -LONG_MIN or -LLONG_MIN +#define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t) ( (_x) > 0 ? (_x) : -((printf_signed_value_t)_x) )) + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_function_wrapper_type; + + +// internal buffer output +static inline void out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + +extern UART_HandleTypeDef *console; +static inline void out_console(char character, void* buffer, size_t idx, size_t maxlen) +{ + while (!(__HAL_UART_GET_FLAG(console, UART_FLAG_TXFNF))); + console->Instance->TDR = (uint8_t)character; +} + + +// internal null output +static inline void out_discard(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal secure strlen +// @return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int strnlen_s_(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// @return true if char is a digit +static inline bool is_digit_(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int atoi_(const char** str) +{ + unsigned int i = 0U; + while (is_digit_(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t out_rev_(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// Invoked by print_integer after the actual number has been printed, performing necessary +// work on the number's prefix (as the number is initially printed in reverse order) +static size_t print_integer_finalization(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, numeric_base_t base, unsigned int precision, unsigned int width, unsigned int flags) +{ + size_t unpadded_len = len; + + // pad with leading zeros + { + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + + if (base == BASE_OCTAL && (len > unpadded_len)) { + // Since we've written some zeros, we've satisfied the alternative format leading space requirement + flags &= ~FLAGS_HASH; + } + } + + // handle hash + if (flags & (FLAGS_HASH | FLAGS_POINTER)) { + if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { + // Let's take back some padding digits to fit in what will eventually + // be the format-specific prefix + if (unpadded_len < len) { + len--; + } + if (len && (base == BASE_HEX)) { + if (unpadded_len < len) { + len--; + } + } + } + if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return out_rev_(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// An internal itoa-like function +static size_t print_integer(out_fct_type out, char* buffer, size_t idx, size_t maxlen, printf_unsigned_value_t value, bool negative, numeric_base_t base, unsigned int precision, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_INTEGER_BUFFER_SIZE]; + size_t len = 0U; + + if (!value) { + if ( !(flags & FLAGS_PRECISION) ) { + buf[len++] = '0'; + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values, or (in the case of octal) we've already provided the special + // handling for this mode. + } + else if (base == BASE_HEX) { + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values + } + } + else { + do { + const char digit = (char)(value % base); + buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); + } + + return print_integer_finalization(out, buffer, idx, maxlen, buf, len, negative, base, precision, width, flags); +} + +#if (CONFIG_LIBC_FLOAT || CONFIG_LIBC_FLOAT_EX) + +struct double_components { + int_fast64_t integral; + int_fast64_t fractional; + bool is_negative; +}; + +#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T +static const double powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = { + 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, + 1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17 +}; + +#define PRINTF_MAX_SUPPORTED_PRECISION NUM_DECIMAL_DIGITS_IN_INT64_T - 1 + + +// Break up a double number - which is known to be a finite non-negative number - +// into its base-10 parts: integral - before the decimal point, and fractional - after it. +// Taken the precision into account, but does not change it even internally. +static struct double_components get_components(double number, unsigned int precision) +{ + struct double_components number_; + number_.is_negative = get_sign(number); + double abs_number = (number_.is_negative) ? -number : number; + number_.integral = (int_fast64_t)abs_number; + double remainder = (abs_number - number_.integral) * powers_of_10[precision]; + number_.fractional = (int_fast64_t)remainder; + + remainder -= (double) number_.fractional; + + if (remainder > 0.5) { + ++number_.fractional; + // handle rollover, e.g. case 0.99 with precision 1 is 1.0 + if ((double) number_.fractional >= powers_of_10[precision]) { + number_.fractional = 0; + ++number_.integral; + } + } + else if (remainder == 0.5) { + if ((number_.fractional == 0U) || (number_.fractional & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++number_.fractional; + } + } + + if (precision == 0U) { + remainder = abs_number - (double) number_.integral; + if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++number_.integral; + } + } + return number_; +} + +struct scaling_factor { + double raw_factor; + bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it +}; + +double apply_scaling(double num, struct scaling_factor normalization) +{ + return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; +} + +double unapply_scaling(double normalized, struct scaling_factor normalization) +{ + return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; +} + +struct scaling_factor update_normalization(struct scaling_factor sf, double extra_multiplicative_factor) +{ + struct scaling_factor result; + if (sf.multiply) { + result.multiply = true; + result.raw_factor = sf.raw_factor * extra_multiplicative_factor; + } + else { + int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); + int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); + + // Divide the larger-exponent raw raw_factor by the smaller + if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) { + result.multiply = false; + result.raw_factor = sf.raw_factor / extra_multiplicative_factor; + } + else { + result.multiply = true; + result.raw_factor = extra_multiplicative_factor / sf.raw_factor; + } + } + return result; +} + +#if CONFIG_LIBC_FLOAT_EX +static struct double_components get_normalized_components(bool negative, unsigned int precision, double non_normalized, struct scaling_factor normalization) +{ + struct double_components components; + components.is_negative = negative; + components.integral = (int_fast64_t) apply_scaling(non_normalized, normalization); + double remainder = non_normalized - unapply_scaling((double) components.integral, normalization); + double prec_power_of_10 = powers_of_10[precision]; + struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); + double scaled_remainder = apply_scaling(remainder, account_for_precision); + double rounding_threshold = 0.5; + + if (precision == 0U) { + components.fractional = 0; + components.integral += (scaled_remainder >= rounding_threshold); + if (scaled_remainder == rounding_threshold) { + // banker's rounding: Round towards the even number (making the mean error 0) + components.integral &= ~((int_fast64_t) 0x1); + } + } + else { + components.fractional = (int_fast64_t) scaled_remainder; + scaled_remainder -= components.fractional; + + components.fractional += (scaled_remainder >= rounding_threshold); + if (scaled_remainder == rounding_threshold) { + // banker's rounding: Round towards the even number (making the mean error 0) + components.fractional &= ~((int_fast64_t) 0x1); + } + // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), + // and must then be corrected into (1, 0). + if ((double) components.fractional >= prec_power_of_10) { + components.fractional = 0; + ++components.integral; + } + } + return components; +} +#endif + +static size_t print_broken_up_decimal( + struct double_components number_, out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned int precision, + unsigned int width, unsigned int flags, char *buf, size_t len) +{ + if (precision != 0U) { + // do fractional part, as an unsigned number + + unsigned int count = precision; + + if (flags & FLAGS_ADAPT_EXP && !(flags & FLAGS_HASH)) { + // %g/%G mandates we skip the trailing 0 digits... + if (number_.fractional > 0) { + while(true) { + int_fast64_t digit = number_.fractional % 10U; + if (digit != 0) { + break; + } + --count; + number_.fractional /= 10U; + } + + } + // ... and even the decimal point if there are no + // non-zero fractional part digits (see below) + } + + if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH) ) { + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)('0' + number_.fractional % 10U); + if (!(number_.fractional /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = '.'; + } + } + } + else { + if (flags & FLAGS_HASH) { + if (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = '.'; + } + } + } + + // Write the integer part of the number (it comes after the fractional + // since the character order is reversed) + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)('0' + (number_.integral % 10)); + if (!(number_.integral /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (number_.is_negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return out_rev_(out, buffer, idx, maxlen, buf, len, width, flags); +} + + // internal ftoa for fixed decimal floating point +static size_t print_decimal_number(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double number, unsigned int precision, unsigned int width, unsigned int flags, char* buf, size_t len) +{ + struct double_components value_ = get_components(number, precision); + return print_broken_up_decimal(value_, out, buffer, idx, maxlen, precision, width, flags, buf, len); +} + +#if CONFIG_LIBC_FLOAT_EX +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t print_exponential_number(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double number, unsigned int precision, unsigned int width, unsigned int flags, char* buf, size_t len) +{ + const bool negative = get_sign(number); + // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it + double abs_number = negative ? -number : number; + + int exp10; + bool abs_exp10_covered_by_powers_table; + struct scaling_factor normalization; + + + // Determine the decimal exponent + if (abs_number == 0.0) { + // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. + exp10 = 0; // ... and no need to set a normalization factor or check the powers table + } + else { + double_with_bit_access conv = get_bit_access(abs_number); + { + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + int exp2 = get_exp2(conv); + // drop the exponent, so conv.F comes into the range [1,2) + conv.U = (conv.U & (( (double_uint_t)(1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | ((double_uint_t) DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS); + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + exp10 = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^exp10 but we want to be sure it won't overflow + exp2 = (int)(exp10 * 3.321928094887362 + 0.5); + const double z = exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = ((double_uint_t)(exp2) + DOUBLE_BASE_EXPONENT) << DOUBLE_STORED_MANTISSA_BITS; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (abs_number < conv.F) { + exp10--; + conv.F /= 10; + } + } + abs_exp10_covered_by_powers_table = PRINTF_ABS(exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; + normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(exp10)] : conv.F; + } + + // We now begin accounting for the widths of the two parts of our printed field: + // the decimal part after decimal exponent extraction, and the base-10 exponent part. + // For both of these, the value of 0 has a special meaning, but not the same one: + // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width + // means "use as many characters as necessary". + + bool fall_back_to_decimal_only_mode = false; + if (flags & FLAGS_ADAPT_EXP) { + int required_significant_digits = (precision == 0) ? 1 : (int) precision; + // Should we want to fall-back to "%f" mode, and only print the decimal part? + fall_back_to_decimal_only_mode = (exp10 >= -4 && exp10 < required_significant_digits); + // Now, let's adjust the precision + // This also decided how we adjust the precision value - as in "%g" mode, + // "precision" is the number of _significant digits_, and this is when we "translate" + // the precision value to an actual number of decimal digits. + int precision_ = (fall_back_to_decimal_only_mode) ? + (int) precision - 1 - exp10 : + (int) precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point + precision = (precision_ > 0 ? (unsigned) precision_ : 0U); + flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above + } + + normalization.multiply = (exp10 < 0 && abs_exp10_covered_by_powers_table); + bool should_skip_normalization = (fall_back_to_decimal_only_mode || exp10 == 0); + struct double_components decimal_part_components = + should_skip_normalization ? + get_components(negative ? -abs_number : abs_number, precision) : + get_normalized_components(negative, precision, abs_number, normalization); + + // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects + // the exponent and may require additional tweaking of the parts + if (fall_back_to_decimal_only_mode) { + if ( (flags & FLAGS_ADAPT_EXP) && exp10 >= -1 && decimal_part_components.integral == powers_of_10[exp10 + 1]) { + exp10++; // Not strictly necessary, since exp10 is no longer really used + precision--; + // ... and it should already be the case that decimal_part_components.fractional == 0 + } + // TODO: What about rollover strictly within the fractional part? + } + else { + if (decimal_part_components.integral >= 10) { + exp10++; + decimal_part_components.integral = 1; + decimal_part_components.fractional = 0; + } + } + + // the exp10 format is "E%+03d" and largest possible exp10 value for a 64-bit double + // is "307" (for 2^1023), so we set aside 4-5 characters overall + unsigned int exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(exp10) < 100) ? 4U : 5U; + + unsigned int decimal_part_width = + ((flags & FLAGS_LEFT) && exp10_part_width) ? + // We're padding on the right, so the width constraint is the exponent part's + // problem, not the decimal part's, so we'll use as many characters as we need: + 0U : + // We're padding on the left; so the width constraint is the decimal part's + // problem. Well, can both the decimal part and the exponent part fit within our overall width? + ((width > exp10_part_width) ? + // Yes, so we limit our decimal part's width. + // (Note this is trivially valid even if we've fallen back to "%f" mode) + width - exp10_part_width : + // No; we just give up on any restriction on the decimal part and use as many + // characters as we need + 0U); + + const size_t start_idx = idx; + idx = print_broken_up_decimal(decimal_part_components, out, buffer, idx, maxlen, precision, decimal_part_width, flags, buf, len); + + if (! fall_back_to_decimal_only_mode) { + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + idx = print_integer(out, buffer, idx, maxlen, + ABS_FOR_PRINTING(exp10), + exp10 < 0, 10, 0, exp10_part_width - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + if (flags & FLAGS_LEFT) { + // We need to right-pad with spaces to meet the width requirement + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // CONFIG_LIBC_FLOAT_EX + + +static size_t print_floating_point(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int precision, unsigned int width, unsigned int flags, bool prefer_exponential) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + + // test for special values + if (value != value) + return out_rev_(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return out_rev_(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return out_rev_(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + if (!prefer_exponential && ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) { + // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean + // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated + // implementation. +#if CONFIG_LIBC_FLOAT_EX + return print_exponential_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len); +#else + return 0U; +#endif + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + precision = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // limit precision so that our integer holding the fractional part does not overflow + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { + buf[len++] = '0'; // This respects the precision in terms of result length only + precision--; + } + + return +#if CONFIG_LIBC_FLOAT_EX + prefer_exponential ? + print_exponential_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len) : +#endif + print_decimal_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len); +} + +#endif // (CONFIG_LIBC_FLOAT || CONFIG_LIBC_FLOAT_EX) + +// internal vsnprintf +static int __vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if ((!buffer) && (out != out_console)) { + // use null output function + out = out_discard; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (is_digit_(*format)) { + width = atoi_(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (is_digit_(*format)) { + precision = atoi_(&format); + } + else if (*format == '*') { + const int precision_ = (int)va_arg(va, int); + precision = precision_ > 0 ? (unsigned int)precision_ : 0U; + format++; + } + } +#if PRINTF_SUPPORT_LENGTH_FILED + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } +#endif + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + numeric_base_t base; + if (*format == 'x' || *format == 'X') { + base = BASE_HEX; + } + else if (*format == 'o') { + base = BASE_OCTAL; + } + else if (*format == 'b') { + base = BASE_BINARY; + } + else { + base = BASE_DECIMAL; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if CONFIG_LIBC_LONG_LONG + const long long value = va_arg(va, long long); + idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (signed char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if CONFIG_LIBC_LONG_LONG + idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if CONFIG_LIBC_FLOAT + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = print_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; +#endif +#if CONFIG_LIBC_FLOAT_EX + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = print_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + format++; + break; +#endif // CONFIG_LIBC_FLOAT_EX + +#if PRINTF_SUPPORT_CHAR + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } +#endif + case 's' : { + const char* p = va_arg(va, char*); + if (p == NULL) { + idx = out_rev_(out, buffer, idx, maxlen, ")llun(", 6, width, flags); + } + else { + unsigned int l = strnlen_s_(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + } + format++; + break; + } +#if PRINTF_SUPPORT_ADDR + case 'p' : { + width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix + flags |= FLAGS_ZEROPAD | FLAGS_POINTER; + uintptr_t value = (uintptr_t)va_arg(va, void*); + idx = (value == (uintptr_t) NULL) ? + out_rev_(out, buffer, idx, maxlen, ")lin(", 5, width, flags) : + print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); + format++; + break; + } +#endif + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + if(out != out_console){ + // termination + // console does not need to output NULL + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + } + + // return written chars without terminating \0 + return (int)idx; +} + +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + return __vsnprintf(out_buffer, buf, size, fmt, args); +} + +int console_vsnprintf(const char *fmt, va_list args) +{ + return __vsnprintf(out_console, NULL, 0, fmt, args); +} \ No newline at end of file diff --git a/driver/CMakeLists.txt b/driver/CMakeLists.txt index 1ccbc6e..61de533 100644 --- a/driver/CMakeLists.txt +++ b/driver/CMakeLists.txt @@ -1,6 +1,8 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/CMSIS CMSIS) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/STM32H7xx_HAL_Driver STM32H7xx_HAL_Driver) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/board board) add_library(driver STATIC) target_link_libraries(driver CMSIS) target_link_libraries(driver STM32H7xx_HAL_Driver) +target_link_libraries(driver board) diff --git a/driver/STM32H7xx_HAL_Driver/CMakeLists.txt b/driver/STM32H7xx_HAL_Driver/CMakeLists.txt index 82e6184..c432705 100644 --- a/driver/STM32H7xx_HAL_Driver/CMakeLists.txt +++ b/driver/STM32H7xx_HAL_Driver/CMakeLists.txt @@ -1,7 +1,5 @@ add_definitions(-DUSE_FULL_LL_DRIVER) -include_directories(Inc) - file(GLOB FILELIST Src/*.c ) @@ -12,7 +10,7 @@ target_include_directories(STM32H7xx_HAL_Driver PUBLIC ${CMAKE_CURRENT_LIST_DIR} set(CMSIS_PATH ${SDK_PATH}/driver/CMSIS) set(CMSIS_VENDOR ST) set(CMSIS_DEVICE stm32h7xx) -target_include_directories(STM32H7xx_HAL_Driver PRIVATE +target_include_directories(STM32H7xx_HAL_Driver PUBLIC ${CMSIS_PATH}/Core ${CMSIS_PATH}/Device/${CMSIS_VENDOR}/${CMSIS_DEVICE}/Include ) diff --git a/driver/board/CMakeLists.txt b/driver/board/CMakeLists.txt new file mode 100644 index 0000000..b837170 --- /dev/null +++ b/driver/board/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB FILELIST +*.c +) +add_library(board STATIC ${FILELIST}) +target_include_directories(board PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + +target_link_libraries(board STM32H7xx_HAL_Driver) diff --git a/driver/board/uart_log.c b/driver/board/uart_log.c new file mode 100644 index 0000000..f08c6cf --- /dev/null +++ b/driver/board/uart_log.c @@ -0,0 +1,73 @@ +#include "uart_log.h" + +UART_HandleTypeDef UartHandle; + +extern void uart_log_set_console(void); + +static void UART_LOG_Msp_Init(void) +{ + GPIO_InitTypeDef GPIO_InitStruct; + + RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit; + + /*##-1- Enable peripherals and GPIO Clocks #################################*/ + /* Enable GPIO TX/RX clock */ + USART_LOG_TX_GPIO_CLK_ENABLE(); + USART_LOG_RX_GPIO_CLK_ENABLE(); + + /* Select HSI as source of USARTx clocks */ + RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART_LOG; + RCC_PeriphClkInit.Usart16ClockSelection = RCC_USART_LOG_CLKSOURCE_HSI; + HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit); + + /* Enable USARTx clock */ + USART_LOG_CLK_ENABLE(); + + /*##-2- Configure peripheral GPIO ##########################################*/ + /* UART TX GPIO pin configuration */ + GPIO_InitStruct.Pin = USART_LOG_TX_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = USART_LOG_TX_AF; + + HAL_GPIO_Init(USART_LOG_TX_GPIO_PORT, &GPIO_InitStruct); + + /* UART RX GPIO pin configuration */ + GPIO_InitStruct.Pin = USART_LOG_RX_PIN; + GPIO_InitStruct.Alternate = USART_LOG_RX_AF; + + HAL_GPIO_Init(USART_LOG_RX_GPIO_PORT, &GPIO_InitStruct); + + /* NVIC for USART */ + HAL_NVIC_SetPriority(USART_LOG_IRQn, 0, 1); + HAL_NVIC_EnableIRQ(USART_LOG_IRQn); +} + +void uart_log_init(void) +{ + UartHandle.Instance = USART_LOG; + UartHandle.Init.BaudRate = UART_LOG_BAUDRATE; + UartHandle.Init.WordLength = UART_WORDLENGTH_8B; + UartHandle.Init.StopBits = UART_STOPBITS_1; + UartHandle.Init.Parity = UART_PARITY_NONE; + UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; + UartHandle.Init.Mode = UART_MODE_TX_RX; + UartHandle.Init.ClockPrescaler = UART_PRESCALER_DIV1; + UartHandle.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; + UartHandle.Init.OverSampling = UART_OVERSAMPLING_16; + + UART_LOG_Msp_Init(); + + if(HAL_UART_Init(&UartHandle) != HAL_OK) + { + while (1) { + } + } + /* Set the RXFIFO threshold */ + HAL_UARTEx_SetRxFifoThreshold(&UartHandle, UART_RXFIFO_THRESHOLD_1_2); + + /* Enable the FIFO mode */ + HAL_UARTEx_EnableFifoMode(&UartHandle); + uart_log_set_console(); +} diff --git a/driver/board/uart_log.h b/driver/board/uart_log.h new file mode 100644 index 0000000..61108a1 --- /dev/null +++ b/driver/board/uart_log.h @@ -0,0 +1,44 @@ +#ifndef __UART_LOG_H__ +#define __UART_LOG_H__ + +#include "stm32h7xx_hal.h" + +#define UART_LOG_BAUDRATE (2 * 1000 * 1000) + +#define HAL_TIMEOUT_VALUE 0xFFFFFFFF + +#define USART_LOG USART1 +#define USART_LOG_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE() +#define USART_LOG_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() +#define USART_LOG_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() + +#define __HAL_RCC_UART_LOG_CONFIG __HAL_RCC_USART1_CONFIG +#define RCC_UART_LOG_CLKSOURCE_HSI RCC_USART16CLKSOURCE_HSI + +#define RCC_PERIPHCLK_USART_LOG RCC_PERIPHCLK_USART16 +#define RCC_USART_LOG_CLKSOURCE_HSI RCC_USART16CLKSOURCE_HSI + +#define USART_LOG_FORCE_RESET() __HAL_RCC_USART1_FORCE_RESET() +#define USART_LOG_RELEASE_RESET() __HAL_RCC_USART1_RELEASE_RESET() + +#define USART_LOG_IRQn USART1_IRQn +#define USART_LOG_IRQHandler USART1_IRQHandler + +#define USART_LOG_TX_PIN GPIO_PIN_14 +#define USART_LOG_TX_GPIO_PORT GPIOB +#define USART_LOG_TX_AF GPIO_AF4_USART1 +#define USART_LOG_RX_PIN GPIO_PIN_15 +#define USART_LOG_RX_GPIO_PORT GPIOB +#define USART_LOG_RX_AF GPIO_AF4_USART1 + +#ifdef __cplusplus + extern "C" { +#endif + +void uart_log_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __UART_LOG_H__ */