Support for floating points (disabled by default)

Use mbed_app.json to enable floating points.
Automatically ignore flags and width specifiers.
pull/11051/head
Marcus Chang 2017-10-15 19:26:01 -07:00
parent a70a5332a8
commit 4fd19a0d82
3 changed files with 249 additions and 80 deletions

155
features/minimal-printf/README.md Normal file → Executable file
View File

@ -1,14 +1,37 @@
# Minimal printf # Minimal printf and snprintf
Prints directly to stdio/UART without using malloc. Library supports both printf and snprintf in 960 bytes of flash.
Doesn't support any modifiers.
Prints directly to stdio/UART without using malloc. All flags, length, and precision modifiers are ignored. Floating point is disabled by default.
Supports: Supports:
* %d: signed long int * %d: signed long int.
* %u: unsigned long int * %i: signed long int.
* %p: void* (e.g. 0x00123456) * %u: unsigned long int.
* %s: string * %x: unsigned char printed as hexadecimal number (e.g., FF).
* %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
}
}
```
## Size comparison ## Size comparison
@ -38,20 +61,22 @@ int main()
char buffer[1000]; char buffer[1000];
int result; int result;
double pi = 3.14159265359;
#if 0 #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); 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("%s\r\n", buffer);
printf("results: %d\r\n", result); printf("results: %d\r\n", result);
#else #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); 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("%s\r\n", buffer);
mbed_printf("results: %d\r\n", result); mbed_printf("results: %d\r\n", result);
@ -59,13 +84,79 @@ int main()
} }
``` ```
### K64F standard printf
### K64F GCC minimal printf without floating point
``` ```
+------------------------------------------+-------+-------+------+ +------------------------------------------+-------+-------+------+
| Module | .text | .data | .bss | | 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-callocr.o | 96 | 0 | 0 |
| [lib]/c.a/lib_a-closer.o | 36 | 0 | 0 | | [lib]/c.a/lib_a-closer.o | 36 | 0 | 0 |
| [lib]/c.a/lib_a-ctype_.o | 257 | 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/_dvmd_tls.o | 4 | 0 | 0 |
| [lib]/gcc.a/_udivmoddi4.o | 732 | 0 | 0 | | [lib]/gcc.a/_udivmoddi4.o | 732 | 0 | 0 |
| [lib]/misc/ | 248 | 4 | 28 | | [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_gpio.o | 96 | 0 | 0 |
| mbed-os/hal/mbed_pinmap_common.o | 212 | 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_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_sdk_boot.o | 74 | 0 | 0 |
| mbed-os/platform/mbed_wait_api_no_rtos.o | 32 | 0 | 0 | | mbed-os/platform/mbed_wait_api_no_rtos.o | 32 | 0 | 0 |
| mbed-os/targets/TARGET_Freescale | 4834 | 12 | 384 | | 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 Static RAM memory (data + bss): 5364 bytes
Total Flash memory (text + data): 37378 bytes Total Flash memory (text + data): 37434 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
``` ```

View File

@ -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
}
}
}

View File

@ -60,6 +60,8 @@ static void init_serial()
/* Linux */ /* Linux */
/***************************/ /***************************/
#else #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_INITIALIZE_PRINT(x) { ; }
#define MBED_PRINT_CHARACTER(x) { printf("%c", x); } #define MBED_PRINT_CHARACTER(x) { printf("%c", x); }
#endif #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_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); 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) 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 */ /* 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; 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) 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 */ /* 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 */ /* treat 0 as a corner case */
if (value == 0) if (value == 0)
@ -127,6 +137,7 @@ static void mbed_minimal_formatted_string_unsigned_long_int(char* buffer, size_t
} }
else else
{ {
/* allocate 3 digits per byte */
char scratch[sizeof(unsigned long int) * 3] = { 0 }; char scratch[sizeof(unsigned long int) * 3] = { 0 };
size_t index = 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 */ /* write scratch pad to buffer or output */
for ( ; (*result < (int) length) && (index > 0); index--) for ( ; (*result < (long int) length) && (index > 0); index--)
{ {
if (buffer) if (buffer)
{ {
@ -162,7 +173,7 @@ 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) 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 */ /* 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_one = (value & 0xFF) >> 4;
unsigned int nibble_two = (value & 0x0F); unsigned int nibble_two = (value & 0x0F);
@ -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) 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 */ /* only continue if the buffer can fit 1 character */
if ((*result + 1) <= (int) length) if ((*result + 1) <= (long int) length)
{ {
if (buffer) 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) 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 */ /* 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 */ /* count characters in string */
size_t remaining = length - *result; size_t remaining = length - *result;
@ -283,57 +331,104 @@ static int mbed_minimal_formatted_string(char* buffer, size_t length, const char
/* format specifier begin */ /* format specifier begin */
if (format[index] == '%') 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); long int value = va_arg(arguments, long int);
index++; index = next_index;
mbed_minimal_formatted_string_long_int(buffer, length, &result, value); mbed_minimal_formatted_string_long_int(buffer, length, &result, value);
} }
/* unsigned integer */
else if (next == 'u') else if (next == 'u')
{ {
unsigned long int value = va_arg(arguments, unsigned long int); 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); 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); int value = va_arg(arguments, int);
index++; index = next_index;
mbed_minimal_formatted_string_unsigned_char(buffer, length, &result, value); 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*); double value = va_arg(arguments, double);
index++; 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') else if (next == 'c')
{ {
int value = va_arg(arguments, int); int value = va_arg(arguments, int);
index++; index = next_index;
mbed_minimal_formatted_string_character(buffer, length, &result, value); mbed_minimal_formatted_string_character(buffer, length, &result, value);
} }
/* string */
else if (next == 's') else if (next == 's')
{ {
char* value = va_arg(arguments, char*); char* value = va_arg(arguments, char*);
index++; index = next_index;
mbed_minimal_formatted_string_string(buffer, length, &result, value); 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 else
@ -357,7 +452,7 @@ int mbed_printf(const char *format, ...)
va_list arguments; va_list arguments;
va_start(arguments, format); 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); va_end(arguments);
return result; return result;