From 4fd19a0d821696f00f3cfddf0f7a2de70d8dc390 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Sun, 15 Oct 2017 19:26:01 -0700 Subject: [PATCH] Support for floating points (disabled by default) Use mbed_app.json to enable floating points. Automatically ignore flags and width specifiers. --- features/minimal-printf/README.md | 161 ++++++++++++++++++-------- features/minimal-printf/mbed_lib.json | 13 +++ features/minimal-printf/mbed_printf.c | 155 ++++++++++++++++++++----- 3 files changed, 249 insertions(+), 80 deletions(-) mode change 100644 => 100755 features/minimal-printf/README.md create mode 100755 features/minimal-printf/mbed_lib.json diff --git a/features/minimal-printf/README.md b/features/minimal-printf/README.md old mode 100644 new mode 100755 index 7440b50ceb..b21d3bd483 --- a/features/minimal-printf/README.md +++ b/features/minimal-printf/README.md @@ -1,14 +1,37 @@ -# Minimal printf +# Minimal printf and snprintf -Prints directly to stdio/UART without using malloc. -Doesn't support any modifiers. +Library supports both printf and snprintf in 960 bytes of flash. + +Prints directly to stdio/UART without using malloc. All flags, length, and precision modifiers are ignored. Floating point is disabled by default. + +Supports: +* %d: signed long int. +* %i: signed long int. +* %u: unsigned long int. +* %x: unsigned char printed as hexadecimal number (e.g., FF). +* %X: unsigned char printed as hexadecimal number (e.g., FF). +* %f: floating point (disabled by default). +* %F: floating point (disabled by default). +* %g: floating point (disabled by default). +* %G: floating point (disabled by default). +* %c: character. +* %s: string. +* %p: pointer (e.g. 0x00123456). + +## Enabling floating point and setting baud rate + +In mbed_app.json: + +``` + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 115200, + "minimal-printf.floating-point": true, + "minimal-printf.floating-point-max-decimals": 10 + } + } +``` -Supports: -* %d: signed long int -* %u: unsigned long int -* %p: void* (e.g. 0x00123456) -* %s: string -* %X: unsigned char printed as hexadecimal number (e.g., FF) ## Size comparison @@ -33,39 +56,107 @@ mbed-os/events/* #include "mbed_printf.h" #include -int main() +int main() { char buffer[1000]; int result; + double pi = 3.14159265359; + #if 0 - result = printf("hello world %d %u %X %p %s %% %\r\n", LONG_MAX, ULONG_MAX, UCHAR_MAX, buffer, "muh"); + result = printf("hello world %d %u %X %p %s %2.5f %% %\r\n", LONG_MAX, ULONG_MAX, UCHAR_MAX, buffer, "muh", pi); printf("results: %d\r\n", result); - result = snprintf(buffer, 1000, "hello world %d %u %X %p %s %% %\r\n", LONG_MIN, 0, 0, buffer, "muh"); + result = snprintf(buffer, 1000, "hello world %d %u %X %p %s %2.5f %% %\r\n", LONG_MIN, 0, 0, buffer, "muh", -1*pi); printf("%s\r\n", buffer); printf("results: %d\r\n", result); #else - result = mbed_printf("hello world %d %u %X %p %s %% %\r\n", LONG_MAX, ULONG_MAX, UCHAR_MAX, buffer, "muh"); + result = mbed_printf("hello world %ld %llu %02X %p %s %2.5f %% %\r\n", LONG_MAX, ULONG_MAX, UCHAR_MAX, buffer, "muh", pi); mbed_printf("results: %d\r\n", result); - result = mbed_snprintf(buffer, 1000, "hello world %d %u %X %p %s %% %\r\n", LONG_MIN, 0, 0, buffer, "muh"); + result = mbed_snprintf(buffer, 1000, "hello world %d %u %X %p %s %2.5f %% %\r\n", LONG_MIN, 0, 0, buffer, "muh", -1*pi); mbed_printf("%s\r\n", buffer); - + mbed_printf("results: %d\r\n", result); #endif } ``` -### K64F standard printf + +### K64F GCC minimal printf without floating point ``` +------------------------------------------+-------+-------+------+ | Module | .text | .data | .bss | +------------------------------------------+-------+-------+------+ -| [fill] | 52 | 4 | 2071 | +| [fill] | 16 | 3 | 2102 | +| [lib]/c.a/lib_a-init.o | 80 | 0 | 0 | +| [lib]/c.a/lib_a-memcpy.o | 308 | 0 | 0 | +| [lib]/c.a/lib_a-memset.o | 156 | 0 | 0 | +| [lib]/misc/ | 248 | 4 | 28 | +| main.o | 256 | 0 | 0 | +| mbed-os/hal/mbed_gpio.o | 96 | 0 | 0 | +| mbed-os/hal/mbed_pinmap_common.o | 212 | 0 | 0 | +| mbed-os/hal/mbed_ticker_api.o | 214 | 0 | 0 | +| mbed-os/hal/mbed_us_ticker_api.o | 44 | 0 | 24 | +| mbed-os/platform/mbed_board.o | 90 | 0 | 0 | +| mbed-os/platform/mbed_critical.o | 56 | 0 | 5 | +| mbed-os/platform/mbed_error.o | 32 | 0 | 1 | +| mbed-os/platform/mbed_retarget.o | 20 | 0 | 0 | +| mbed-os/platform/mbed_sdk_boot.o | 74 | 0 | 0 | +| mbed-os/platform/mbed_wait_api_no_rtos.o | 32 | 0 | 0 | +| mbed-os/targets/TARGET_Freescale | 4758 | 12 | 384 | +| mbed-printf/mbed_printf.o | 960 | 1 | 188 | +| Subtotals | 7652 | 20 | 2732 | ++------------------------------------------+-------+-------+------+ +Total Static RAM memory (data + bss): 2752 bytes +Total Flash memory (text + data): 7672 bytes +``` + + +### K64F GCC minimal printf with floating point + +``` ++------------------------------------------+-------+-------+------+ +| Module | .text | .data | .bss | ++------------------------------------------+-------+-------+------+ +| [fill] | 24 | 3 | 2102 | +| [lib]/c.a/lib_a-init.o | 80 | 0 | 0 | +| [lib]/c.a/lib_a-memcpy.o | 308 | 0 | 0 | +| [lib]/c.a/lib_a-memset.o | 156 | 0 | 0 | +| [lib]/gcc.a/_arm_addsubdf3.o | 880 | 0 | 0 | +| [lib]/gcc.a/_arm_fixdfsi.o | 80 | 0 | 0 | +| [lib]/gcc.a/_arm_muldivdf3.o | 1060 | 0 | 0 | +| [lib]/misc/ | 248 | 4 | 28 | +| main.o | 256 | 0 | 0 | +| mbed-os/hal/mbed_gpio.o | 96 | 0 | 0 | +| mbed-os/hal/mbed_pinmap_common.o | 212 | 0 | 0 | +| mbed-os/hal/mbed_ticker_api.o | 214 | 0 | 0 | +| mbed-os/hal/mbed_us_ticker_api.o | 44 | 0 | 24 | +| mbed-os/platform/mbed_board.o | 90 | 0 | 0 | +| mbed-os/platform/mbed_critical.o | 56 | 0 | 5 | +| mbed-os/platform/mbed_error.o | 32 | 0 | 1 | +| mbed-os/platform/mbed_retarget.o | 20 | 0 | 0 | +| mbed-os/platform/mbed_sdk_boot.o | 74 | 0 | 0 | +| mbed-os/platform/mbed_wait_api_no_rtos.o | 32 | 0 | 0 | +| mbed-os/targets/TARGET_Freescale | 4758 | 12 | 384 | +| mbed-printf/mbed_printf.o | 1160 | 1 | 188 | +| Subtotals | 9880 | 20 | 2732 | ++------------------------------------------+-------+-------+------+ +Total Static RAM memory (data + bss): 2752 bytes +Total Flash memory (text + data): 9900 bytes +``` + + +### K64F GCC standard printf + +``` ++------------------------------------------+-------+-------+------+ +| Module | .text | .data | .bss | ++------------------------------------------+-------+-------+------+ +| [fill] | 62 | 4 | 2071 | | [lib]/c.a/lib_a-callocr.o | 96 | 0 | 0 | | [lib]/c.a/lib_a-closer.o | 36 | 0 | 0 | | [lib]/c.a/lib_a-ctype_.o | 257 | 0 | 0 | @@ -120,7 +211,7 @@ int main() | [lib]/gcc.a/_dvmd_tls.o | 4 | 0 | 0 | | [lib]/gcc.a/_udivmoddi4.o | 732 | 0 | 0 | | [lib]/misc/ | 248 | 4 | 28 | -| main.o | 169 | 0 | 0 | +| main.o | 215 | 0 | 0 | | mbed-os/hal/mbed_gpio.o | 96 | 0 | 0 | | mbed-os/hal/mbed_pinmap_common.o | 212 | 0 | 0 | | mbed-os/hal/mbed_ticker_api.o | 214 | 0 | 0 | @@ -133,38 +224,8 @@ int main() | mbed-os/platform/mbed_sdk_boot.o | 74 | 0 | 0 | | mbed-os/platform/mbed_wait_api_no_rtos.o | 32 | 0 | 0 | | mbed-os/targets/TARGET_Freescale | 4834 | 12 | 384 | -| Subtotals | 34882 | 2496 | 2868 | +| Subtotals | 34938 | 2496 | 2868 | +------------------------------------------+-------+-------+------+ Total Static RAM memory (data + bss): 5364 bytes -Total Flash memory (text + data): 37378 bytes -``` - -### K64F minimal printf - -``` -+------------------------------------------+-------+-------+------+ -| Module | .text | .data | .bss | -+------------------------------------------+-------+-------+------+ -| [fill] | 15 | 3 | 2102 | -| [lib]/c.a/lib_a-init.o | 80 | 0 | 0 | -| [lib]/c.a/lib_a-memcpy.o | 308 | 0 | 0 | -| [lib]/c.a/lib_a-memset.o | 156 | 0 | 0 | -| [lib]/misc/ | 248 | 4 | 28 | -| main.o | 169 | 0 | 0 | -| mbed-os/hal/mbed_gpio.o | 96 | 0 | 0 | -| mbed-os/hal/mbed_pinmap_common.o | 212 | 0 | 0 | -| mbed-os/hal/mbed_ticker_api.o | 214 | 0 | 0 | -| mbed-os/hal/mbed_us_ticker_api.o | 44 | 0 | 24 | -| mbed-os/platform/mbed_board.o | 90 | 0 | 0 | -| mbed-os/platform/mbed_critical.o | 56 | 0 | 5 | -| mbed-os/platform/mbed_error.o | 32 | 0 | 1 | -| mbed-os/platform/mbed_retarget.o | 20 | 0 | 0 | -| mbed-os/platform/mbed_sdk_boot.o | 74 | 0 | 0 | -| mbed-os/platform/mbed_wait_api_no_rtos.o | 32 | 0 | 0 | -| mbed-os/targets/TARGET_Freescale | 4758 | 12 | 384 | -| mbed-printf/mbed_printf.o | 820 | 1 | 188 | -| Subtotals | 7424 | 20 | 2732 | -+------------------------------------------+-------+-------+------+ -Total Static RAM memory (data + bss): 2752 bytes -Total Flash memory (text + data): 7444 bytes +Total Flash memory (text + data): 37434 bytes ``` diff --git a/features/minimal-printf/mbed_lib.json b/features/minimal-printf/mbed_lib.json new file mode 100755 index 0000000000..5b536f7b76 --- /dev/null +++ b/features/minimal-printf/mbed_lib.json @@ -0,0 +1,13 @@ +{ + "name": "minimal-printf", + "config": { + "floating-point": { + "help": "Enable floating point printing", + "value": false + }, + "floating-point-max-decimals": { + "help": "Maximum number of decimals to be printed", + "value": 3 + } + } +} diff --git a/features/minimal-printf/mbed_printf.c b/features/minimal-printf/mbed_printf.c index 7fa9cd7969..e8db354c45 100755 --- a/features/minimal-printf/mbed_printf.c +++ b/features/minimal-printf/mbed_printf.c @@ -38,7 +38,7 @@ static serial_t stdio_uart = { 0 }; //#endif #endif -static void init_serial() +static void init_serial() { if (not_initialized) { @@ -60,6 +60,8 @@ static void init_serial() /* Linux */ /***************************/ #else +#define MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT 1 +#define MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT_MAX_DECIMALS 10 #define MBED_INITIALIZE_PRINT(x) { ; } #define MBED_PRINT_CHARACTER(x) { printf("%c", x); } #endif @@ -71,10 +73,18 @@ static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t leng static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, int character); static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, char* string); +#ifndef MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT +#define MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT 0 +#endif + +#ifndef MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT_MAX_DECIMALS +#define MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT_MAX_DECIMALS 0 +#endif + static void mbed_minimal_formatted_string_long_int(char* buffer, size_t length, int* result, long int value) { /* only continue if buffer can fit at least 1 characters */ - if ((*result + 1) <= (int) length) + if ((*result + 1) <= (long int) length) { unsigned long int new_value = 0; @@ -109,7 +119,7 @@ static void mbed_minimal_formatted_string_long_int(char* buffer, size_t length, static void mbed_minimal_formatted_string_unsigned_long_int(char* buffer, size_t length, int* result, unsigned long int value) { /* only continue if buffer can fit at least 1 characters */ - if ((*result + 1) <= (int) length) + if ((*result + 1) <= (long int) length) { /* treat 0 as a corner case */ if (value == 0) @@ -127,6 +137,7 @@ static void mbed_minimal_formatted_string_unsigned_long_int(char* buffer, size_t } else { + /* allocate 3 digits per byte */ char scratch[sizeof(unsigned long int) * 3] = { 0 }; size_t index = 0; @@ -142,7 +153,7 @@ static void mbed_minimal_formatted_string_unsigned_long_int(char* buffer, size_t } /* write scratch pad to buffer or output */ - for ( ; (*result < (int) length) && (index > 0); index--) + for ( ; (*result < (long int) length) && (index > 0); index--) { if (buffer) { @@ -162,12 +173,12 @@ static void mbed_minimal_formatted_string_unsigned_long_int(char* buffer, size_t static void mbed_minimal_formatted_string_unsigned_char(char* buffer, size_t length, int* result, int value) { /* only continue if buffer can fit at least 2 characters */ - if ((*result + 2) <= (int) length) + if ((*result + 2) <= (long int) length) { unsigned int nibble_one = (value & 0xFF) >> 4; unsigned int nibble_two = (value & 0x0F); - const char int2hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + const char int2hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; if (buffer) @@ -212,10 +223,47 @@ static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t leng } } +#if MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT +static void mbed_minimal_formatted_string_double(char* buffer, size_t length, int* result, double value) +{ + /* only continue if buffer can fit at least 1 characters */ + if ((*result + 1) <= (long int) length) + { + /* get integer part */ + long int integer = value; + + /* write integer part */ + mbed_minimal_formatted_string_long_int(buffer, length, result, integer); + + /* write decimal point */ + mbed_minimal_formatted_string_character(buffer, length, result, '.'); + + /* get decimal part */ + double precision = 1.0; + + for (size_t index = 0; index < MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT_MAX_DECIMALS; index++) + { + precision *= 10; + } + + long int decimal = (value - integer) * precision; + + /* take absolute value */ + if (decimal < 0) + { + decimal = -1 * decimal; + } + + /* write decimal part */ + mbed_minimal_formatted_string_long_int(buffer, length, result, decimal); + } +} +#endif + static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, int character) { /* only continue if the buffer can fit 1 character */ - if ((*result + 1) <= (int) length) + if ((*result + 1) <= (long int) length) { if (buffer) { @@ -233,7 +281,7 @@ static void mbed_minimal_formatted_string_character(char* buffer, size_t length, static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, char* string) { /* only continue if the buffer can fit at least 1 character */ - if ((*result + 1) <= (int) length) + if ((*result + 1) <= (long int) length) { /* count characters in string */ size_t remaining = length - *result; @@ -251,7 +299,7 @@ static void mbed_minimal_formatted_string_string(char* buffer, size_t length, in for (size_t index = 0; index < string_length; index++) { buffer[*result + index] = string[index]; - } + } } /* print string */ else @@ -282,63 +330,110 @@ static int mbed_minimal_formatted_string(char* buffer, size_t length, const char { /* format specifier begin */ if (format[index] == '%') - { - char next = format[index + 1]; + { + size_t next_index = index + 1; - if (next == 'd') + /* while there is room in buffer and format string is not empty */ + while ((next_index < length) && (format[next_index] != '\0') && + /* skip all size modifiers */ + (((format[next_index] >= '0') && (format[next_index] <= '9')) || + (format[next_index] == 'h') || + (format[next_index] == 'l') || + (format[next_index] == 'j') || + (format[next_index] == 'z') || + (format[next_index] == 't') || + (format[next_index] == 'L') || + (format[next_index] == '.'))) + { + /* skip to next character */ + next_index++; + } + + /* read out character - this a supported format character, '\0', or a not suported character */ + char next = format[next_index]; + + /* signed integer */ + if ((next == 'd') || (next == 'i')) { long int value = va_arg(arguments, long int); - index++; + index = next_index; mbed_minimal_formatted_string_long_int(buffer, length, &result, value); } + /* unsigned integer */ else if (next == 'u') { unsigned long int value = va_arg(arguments, unsigned long int); - index++; + index = next_index; mbed_minimal_formatted_string_unsigned_long_int(buffer, length, &result, value); } - else if (next == 'X') + /* treat both hexadecimeal modifiers the same */ + else if ((next == 'x') || (next == 'X')) { int value = va_arg(arguments, int); - index++; + index = next_index; mbed_minimal_formatted_string_unsigned_char(buffer, length, &result, value); } - else if (next == 'p') +#if MBED_CONF_MINIMAL_PRINTF_FLOATING_POINT + /* treat all floating points the same */ + else if ((next == 'f') || (next == 'F') || (next == 'g') || (next == 'G')) { - void* value = va_arg(arguments, void*); - index++; + double value = va_arg(arguments, double); + index = next_index; - mbed_minimal_formatted_string_void_pointer(buffer, length, &result, value); + mbed_minimal_formatted_string_double(buffer, length, &result, value); } +#endif + /* character */ else if (next == 'c') { int value = va_arg(arguments, int); - index++; + index = next_index; mbed_minimal_formatted_string_character(buffer, length, &result, value); } + /* string */ else if (next == 's') { char* value = va_arg(arguments, char*); - index++; + index = next_index; mbed_minimal_formatted_string_string(buffer, length, &result, value); } - else if (next == '\0') + /* pointer */ + else if (next == 'p') { - mbed_minimal_formatted_string_character(buffer, length, &result, '%'); + void* value = va_arg(arguments, void*); + index = next_index; + + mbed_minimal_formatted_string_void_pointer(buffer, length, &result, value); } - else + else { - mbed_minimal_formatted_string_character(buffer, length, &result, '%'); + /* write all characters between format beginning and unrecognied modifier */ + while (index < next_index) + { + mbed_minimal_formatted_string_character(buffer, length, &result, format[index]); + index++; + } + + /* if this is not the end of the string, write unrecognized modifier */ + if (next != '\0') + { + mbed_minimal_formatted_string_character(buffer, length, &result, format[index]); + } + else + { + /* break out of for loop */ + break; + } } } else { - mbed_minimal_formatted_string_character(buffer, length, &result, format[index]); + mbed_minimal_formatted_string_character(buffer, length, &result, format[index]); } } @@ -357,10 +452,10 @@ int mbed_printf(const char *format, ...) va_list arguments; va_start(arguments, format); - int result = mbed_minimal_formatted_string(NULL, ULONG_MAX, format, arguments); + int result = mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments); va_end(arguments); - return result; + return result; } int mbed_snprintf(char* buffer, size_t length, const char* format, ...) @@ -372,5 +467,5 @@ int mbed_snprintf(char* buffer, size_t length, const char* format, ...) int result = mbed_minimal_formatted_string(buffer, length, format, arguments); va_end(arguments); - return result; + return result; }