Send Formatted Strings/Send String Templates Using printf

It is a common use case for an application to send a string with variable fields, for example when the application reports its state or a counter value. Using formatted strings is a very flexible approach and reduces the number of code lines. This can be accomplished by changing the output stream of the ‘printf’ function.

This use case follows these steps:

Normally, when using ‘printf’, the characters are sent to a stream of data, called standard output stream. On a PC, the standard output stream is handled by the function to display characters on the screen. But streams can be created so that another function handles their data.

The following code creates a user-defined stream that will be handled by the USART0_printChar function. This function is a wrapper of the USART_sendChar function, but has a slightly different signature to match what FDEV_SETUP_STREAM expects as parameter.

static void USART0_sendChar(char c)
{
    while (!(USART0.STATUS & USART_DREIF_bm))
    {
        ;
    }
    USART0.TXDATAL = c;
}

static int USART0_printChar(char c, FILE *stream)
{ 
    USART0_sendChar(c);
    return 0; 
}

static FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);

Then replace the standard output stream with the user defined stream, handled by the USART send function.

stdout = &USART_stream;

The application can now use ‘printf’ instead of writing to USART registers directly.

uint8_t count = 0;
while (1)
{
    printf("Counter value is: %d\r\n", count++);
    _delay_ms(500);
}
Note: The ‘printf’ function uses placeholders to mark where to insert variables in the string template. Some of the available placeholders are in the table below:
Placeholder Description
%d Insert a signed integer
%s Insert a sequence of characters
%c Insert a character
%x Insert integer unsigned in hex format

Other settings do not change, therefore are skipped in the code snippets above. See full code example on GitHub.