Debugging with Real Time Terminal

In SDK 12.0.0 or later, logging/debugging over RTT is built in to the logger module NRF_LOG. To enable logging over RTT, "check" the NRF_LOG_BACKEND_SERIAL_USES_RTT option in sdk_config.h in the configuration wizard.

This tutorial is therefore mostly relevant for SDK 11 or older.

Normally when creating software there is a need to debug and monitor the execution of the code. There are several options when it comes to debugging, for example printing information on the serial port, or using breakpoints and stepping through the code in Keil. However, these methods don't work very well when debugging real-time critical applications like bluetooth applications. A good alternative to standard logging on the serial port is to use the Real Time Terminal from SEGGER. This tutorial will show how this debug feature can be added to any existing project.


Adding RTT files to project

As with the previous tutorials, this tutorial will be based on the ble_app_uart project, but any other project can be used.

  • Open the ble_app_uart example as explained in tutorial 1

  • Download the zip file with the required RTT files as linked above, and unzip it

  • Copy the RTT and Syscalls folder from the zip file into C:\Keil_v5\ARM\Pack\NordicSemiconductor

  • In Keil, go to Project -> Options for target

  • In the C/C++ tab, add C:\Keil_v5\ARM\Pack\NordicSemiconductor\RTT to the include path

  • Include SEGGER_RTT.h to the project by writing #include "SEGGER_RTT.h" near the top of the main.c file in the project

Now we have the header file for the RTT functions but we also have to add the .c files to the project.

  • In Keil, right-click on the project-folder and click Add Group. Name it RTT

  • Right click on the new group and select Add existing files to group 'RTT'

  • Navigate to C:\Keil_v5\ARM\Pack\NordicSemiconductor\RTT and add SEGGER_RTT.c

You should now be able to send simple strings through the RTT interface. In the main function in the main file, add the following line right before the main loop:

SEGGER_RTT_WriteString(0, "Hello World!\n");

The first argument is which channel to write the text to. You can read more about channels on the SEGGER webapge, but the default channel is 0. Compile the code to make sure everything went ok.

Opening a Real Time Terminal

Now that the code is sending output to our RTT, we have to be able to read it. There are several ways this can be done, as summarized on the segger webpage. The easiest way is to use the J-Link RTT Viewer that comes with the J-link software package.

  • Open J-Link RTT Viewer. You will then see the image below. If you have more than one device connected, check "Serial no." and enter the serial number of the device you wish to connect to.

image description

  • Click Ok. The screen below will then appear.

image description

Now, load the code you compiled above onto your device, and you should see the text "Hello World!" appear. Notice if you open Termite you will also see the text "Start.." which is still printed on the serial port as before. You can now use the ble_app_uart project as intended, but also send text to RTT in order to debug. Keep in mind that SEGGER_RTT_WriteString() is much faster than printf, so you can safely call this function without it affecting the real time properties of your application.

Sending text to target

You can also use RTT to send text to your device, as an alternative to UART. Modify the main loop in the ble_app_uart project so it looks like below. Remember to also include nrf_delay.h to use the delay function.

char c = 0;
for (;;)
	c = SEGGER_RTT_WaitKey(); // will block until data is available
	if(c == 'r'){
		SEGGER_RTT_WriteString(0, "Resetting..\n");
  • Compile and flash the code onto the device

  • In RTT Viewer, go to Input -> Sending and click Send on Enter. (Otherwise it sends on every key you press, which can be annoying)

  • Write an 'r' in the textfield and hit enter. You should now see the board resetting.

image description

image description

More advanced printing

So far we have only looked at unformatted printing, using SEGGER_RTT_WriteString(). A more powerful function is the SEGGER_RTT_printf() function. To be able to use it, we must modify the project a bit more.

  • Add the file SEGGER_RTT_printf.c to the project, just like you added SEGGER_RTT.c above

  • In the same way, add RTT_Syscalls_KEIL.c, from C:\Keil_v5\ARM\Pack\NordicSemiconductor\Syscalls

  • Right-click nRF_Libraries and click Options

image description

  • In the list of Software Components, click retarget, and then the remove-button

image description

  • Now go to Project -> Options for target, and uncheck "Use MicroLIB"

image description

  • Edit the main loop so it looks like below:

    char c = 0; for (;;) { c = SEGGER_RTT_WaitKey(); // will block until data is available if(c == 'r'){ SEGGER_RTT_printf(0, "%sResetting in %d second..%s\n", RTT_CTRL_BG_BRIGHT_RED, 1, RTT_CTRL_RESET); nrf_delay_ms(1000); sd_nvic_SystemReset(); } //power_manage(); }

Compile and run the project. Notice that the output can be color-coded to allow for a visually appealing debug output. In RTT-viewer, go to Terminal 0 to see the colored output.

image description

Notice that the printf function also directs its output to RTT now. The ble_app_uart project will still work as intended though, because it uses app_uart_put which outputs to the serial port.

An example on how to use the printf function:

SEGGER_RTT_printf(0, "variable value: %d\n", variable);

where the first parameter specifies that the output should be printed to Terminal 0 in the J-Link RTT Viewer, second parameter is the string to print, and the value of "variable" is inserted for "%d"

For more information on RTT and the various functions, you can read more in chapter 10 in the J-Link manual.