Showing results for 
Search instead for 
Did you mean: 

ModusToolbox Blog


Using the retarget-io library to enable printf()

Last week I slid in a call to cy_retarget_io_init() in my application without explaining it. I felt a bit naughty about that and so promised to explain it to you. Here goes!

The Cypress retarget-io library implements low-level character I/O functions expected by the Arm and GNU C compilers. These functions are called from the printf() and scanf() functions that come from the STDIO C-language run-time library. The actual names of the functions depend on the compiler you are using, but the library is written so that the right implementations are enabled for you automatically. Here is some code to remind you how it works (this code works on all supported compilers).

Start by adding these two header files to tell the compiler about the low-level functions and printf(), respectively.

#include "cy_retarget_io.h"

#include <stdio.h>

In the main() function you need to just initialize the UART, check it worked correctly, and start printing with wild abandon.


    CY_ASSERT( result == CY_RSLT_SUCCESS );

    printf( "Program started...\r\n" );

Note that the CY_ASSERT() call just halts the application if the expression equates to false, which is probably a little simplistic in a real application but makes the point here. About the only reasons why this call would fail, though, are that you have a) passed in a pin that is not accessible from any of the PSoC SCB blocks, or b) requested a baud rate that cannot be achieved, or c) have already initialized the UART.

So, this is all fine and dandy but the arguments to cy_retarget_io_init() need a little explanation. The function just sets up the UART for the pins defined by the first two arguments - CYBSP_DEBUG_UART_TX and CYBSP_DEBUG_UART_RX. The prefix to those macros is a hint as to where they are defined - the BSP. Take a look in your project folder and jump down into the libs folder - this is where the Library Manager saves the content you select. There are folders with the prefix "TARGET", which are the kit BSPs. Looking in TARGET_CY8CKIT-062-WIFI-BT you will see a useful little file called cybsp_types.h. Here's a snippet from that file.

/** Pin: UART RX */

#define CYBSP_DEBUG_UART_RX         (P5_0)

/** Pin: UART TX */

#define CYBSP_DEBUG_UART_TX         (P5_1)

The author of the BSP has helpfully defined the pins that connect to the KitProg device. This means that, regardless of the kit you have, those macros will help you set up I/O across the KitProg bridge (to your computer). When you call cy_retarget_io_init() with those macros as the first two arguments, the library code figures out which SCB they are connected to and initializes it as a UART. If you wish to redirect the I/O then just change the arguments of the desired pins, such as P6_0 and P6_1.

The other argument in our function call is CY_RETARGET_IO_BAUDRATE, which is obviously the baud rate, but where is it defined. This is not a BSP macro. Rather, it is a retarget-io library define which helps all Cypress examples run UARTs at the same speed. Look in libs/retarget-io and you will see cy_retarget_io.h, the file that we included at the top of this blog. The following define is easy to find in that file.

/** UART baud rate */

#define CY_RETARGET_IO_BAUDRATE         (115200)

As you can see, if you want to run your UART at a different speed, just use a different number, such as 9600, like this:

    cy_retarget_io_init( P6_1, P6_0, 9600 );

Hopefully this explains what is going on with the library and explains how to make simple changes in your code to modify the printf() speed and hardware. I feel better for honoring my commitment from last week... next time I shall explain how the Library Manager works its magic!


Thanks for this. When trying to incorporate into a project, I found perhaps there is a little too much magic. 😉

The initialization will fail for a long list of reasons that are not very obvious, since it requires certain resources to be unallocated, and others to be compatible in the device configurator. 

So as you look at the device configurator, none of these resources will show, yet they need to be available. 

  • Pins cannot be used, but yet should be named.
  • SCB that matches those pins must be available.
  • An integer 16 but clock divider that can be set to the desired baud must be available. If your system clock requires a fractional divider to meet the desired baud, it will fail.

Personally,  I'd rather see them defined in the device configurator since any documentation produced by it is actually inaccurate if you use this important feature.


Thank you for the feedback. Definitely things for us to think about.

I think there are some checks in place to help the developer out though. First of all, if you want to completely control the UART set up , just use the configurator. Secondly, you do not need to name the pins. In the example I gave the P6_1 and P6_0 are defined for all devices, not by some magic software, so you can rely upon them to refer to the right pins. You are totally right about the SCB needing to be available but the init() function returns an error code to explain if that is the case. As for the fractional divider, I think that the HAL is targeting simple uses where there is a margin of error (we're enabling printf() here after all).

None of the above is meant to say that your opinion is wrong. I think there is definitely a window for confusion between configurator- and HAL-allocated resources. The HAL also brings a project documentation overhead for you. I'd really like to improve how we address that and so the more feedback we get the better.

Thanks again!


About the Author
Pioneer in RTLS and developer of wearable low power wireless sensors
Top labels