4 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:

  • Configure the USART peripheral the same as for the first use case
  • Create a used defined stream
  • Replace the standard output stream with the user-defined stream

Usually, 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 USART1_printChar function. This function is a wrapper of the USART1_sendChar function but has a slightly different signature to match what FDEV_SETUP_STREAM expects as a parameter.

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

static int USART1_printChar(char c, FILE *stream)
{ 
    USART1_sendChar(c);
    return 0; 
}

static FILE USART_stream = FDEV_SETUP_STREAM(USART1_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 specifiers to mark where to insert variables in the string template. Some of the available specifiers are in the table below:
Table 4-1. printf Specifiers
SpecifierDescription
%dInsert a signed integer
%sInsert a sequence of characters
%cInsert a character
%xInsert integer unsigned in hex format

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

An MPLAB MCC generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here: