BlocNotes

Notepad of a tinker, maker, hacker or whatever you call it :)

STM32 - Log and printf

I am currently using a library which heavily use LOG() functions, which requires to output text on printf() or one of its derivative.
As I am also evaluating ST's HAL, I tried to use it instead of low level putc() function.

First, we need to be able to use printf() in combination with HAL_UART_Transmit().
For that, a small trick called variadic functions is required, it allows passing multiple arguments to our function. The other trick is vprintf() which is a printf() with variadic args input and char* output.
Two functions are implemented to be able to match the library Log prototype: void logE(const char* fmt, ...)

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#define PRINT_BUFFER_SIZE 100

/** Custom printf function in order to use HAL_UART_Transmit()
 * @param *fmt String to print
 * @param argp Parameters list
 */
void HAL_printf_valist(const char *fmt, va_list argp) {  
  char string[PRINT_BUFFER_SIZE];

  if (vsprintf(string, fmt, argp) > 0) {
    HAL_UART_Transmit(&HAL_PRINT_UART, (uint8_t*)string, strlen(string), HAL_MAX_DELAY); // send message via UART
  } else {
    HAL_UART_Transmit(&HAL_PRINT_UART, (uint8_t*)"E - Print\n", 14, HAL_MAX_DELAY);
  }
}

/** Custom printf function, only translate to va_list arg HAL_UART.
 * @param *fmt String to print
 * @param ... Data
 */
void HAL_printf(const char *fmt, ...) {  
  va_list argp;

  va_start(argp, fmt);
  HAL_printf_valist(fmt, argp);
  va_end(argp);
}

Now serial printf() can be used with a simple HAL_printf("Test: %d", value); call.

And now the implementation of logI() and logE() is obvious:

/** Generic LOG procedure
 * @param Log level
 * @param *fmt String to print
 * @param argp Parameters list
 */
static void log(uint8_t level, const char *fmt, va_list argp) {  
    HAL_printf("%c - ", level);
    HAL_printf_valist(fmt, argp);
}

/** LOG procedure - Info
 * @param *fmt String to print
 * @param ... Parameters list
 */
void logI(const char* fmt, ...) {  
  va_list argp;

    va_start(argp, fmt);
    log('I', fmt, argp);
    va_end(argp);
}

/** LOG procedure - Error
 * @param *fmt String to print
 * @param .. Parameters list
 */
void logE(const char* fmt, ...) {  
  va_list argp;

  va_start(argp, fmt);
  log('E', fmt, argp);
  va_end(argp);
}

Notes:

  • If you want to display floats with %f and you use GNU ARM provided by OpenSTM32, the flag -u _printf_float must be added to linker options otherwise the data will be only whitespaces.
  • The main drawback of this method is the size of the char* buffer defined by PRINT_BUFFER_SIZE, you have to be sure that it is big enough for all your strings.