There is one way to use function printf with our microcontroller even though if we do not have a screen to display information, well we do. we can use our debugger and OpenOCD to display information.

imagine we have the following code:

#include <stdio.h>

extern void initialise_monitor_handles(void);

int main ( void )
{
    int i;

    initialise_monitor_handles();

    printf("Hola semihosting\n\r");
    for(;;)
    {
        /* bucle */
        for(i=0;i<10;i++)
        {
            printf("la variable i = %d\n\r", i);
        }
        printf("fin del bucle\n\n\r");
    }
}

The magic is done with the function initialise_monitor_handles, once this function is called we can use printf wherever we want. btw it is mandatory to call each printf with an \r ant the end of the string. One more thing, it is necessary to uncomment the following line on file .gdbinit

mon arm semihosting enable

So now just make and make open, to open a connection in OpenOCD. Open a second terminal and init a debug sesssion with make debug, hit continue and you will see the information display in the OpenOCD window

xPSR: 0xf1000000 pc: 0x0800028c msp: 0x20024000, semihosting
Info : halted: PC: 0x080001c0
Hola semihosting
la variable i = 0
la variable i = 1
la variable i = 2
la variable i = 3
la variable i = 4
la variable i = 5
la variable i = 6
la variable i = 7
la variable i = 8
la variable i = 9

Just keep in mind this functionality only works when you have a debug session running, but if you only want to flash the program using make flash you need to comment all the calls to printf and initialise_monitor_handles otherwise your program wont run.

Just take into account printf are not for free, actually consume several hundreds of bytes of memory RAM and FLASH plus it takes time to send those character trough the debugger, around 20ms for just then characters!!. Well this depends on your debugger speed