Retargeting printf

Note: you can also direct printf output to a debugger window while running the program under debug control by using a feature called semihosting (see above).

 

printf is a well-known function in the stdio.h part of the C library. It allows almost unlimited formatting of arguments to the output stream. A web search will show the full capability of the function.

 

On a traditional computer system, an output stream usually refers to a console command prompt or a Unix shell. To get printf to work with an MCU environment, for example, to output to a serial port, you need to implement the following two functions:

 

int putchar(int ch)

    {

    char xch = ch;

    if (ch == '\n')

        putchar('\r');

    HAL_UART_Transmit(&huart1, &xch, 1, -1);

    return ch;

    }

 

int _write(int fd, unsigned char *p, int len)

    {

    while (len--)

        putchar(*p++);

    }

 

_write is the low-level function that printf calls to output a sequence of characters. In this example, it calls putchar to output one byte at a time. (In a system that supports a full file system, the first argument “fd” may specify the file descriptor to which the output should go. In the embedded system case, we can just ignore it.)

 

putchar outputs a single character. Under Windows, a “newline” (in C, the escape character ‘\n’) actually consists of two characters: Carriage Return (CR) followed by Linefeed (also confusingly called Newline (NL)). Therefore, the first thing the function does is to check whether the character to be output is the escape character ’\n’, and if so, it recursively calls itself to output the character ‘\r’ first before writing out the actual ‘\n’ character.

 

The actual outputting of a character is device-and-library specific. In this example, it uses ST’s HAL library function to transmit a single byte through UART1. In the lowest level, the code for HAL_UART_Transmit boils down to something similar to:

 

while ((USART1->SR & UART_TXNE) != 0)

            ;

USART1->DR = ch;

 

The names may be different depending on the MCU and the library include file, but the gist of the behavior is to wait until the UART transmit register is empty, then copy the character to the UART data register.

 

If you need to retarget printf to other devices such as the LCD, similar code can be used. You can also direct printf output to the ADT debugger semihosting window by enabling the semihosting feature (see above).