В SDK 12.0.0 или более новой версии встроена функция вывода отладочных сообщений в терминал реального времени (logging/debugging over Real Time Terminal, сокращенно RTT) соответствующий код находится в logger module NRF_LOG. Чтобы разрешить вывод в лог через RTT, разрешите опции NRF_LOG_ENABLED и NRF_LOG_BACKEND_SERIAL_USES_RTT в sdk_config.h (опции файла sdk_config.h можно редактировать как напрямую, так и через утилиту CMSIS Configuration Wizard [2]). Таким образом, это руководство (перевод [1]) больше подходит для SDK 11 или его более ранней версии.
При создании программы для MCU обычно возникает необходимость отладки и мониторинга поведения кода в реальном времени. Есть несколько способов организации такого мониторинга - управление уровнем ножки порта GPIO, подключение отладчика, вставка в код точек останова (breakpoints), выполнение операторов по шагам. Однако наиболее информативный способ отладки приложений, критичных ко времени выполнения (таких, как приложения Bluetooth) - вывод информационных сообщений через последовательный порт в реальном времени, без остановки программы. Хороший вариант организации такого вывода - текстовый лог с использованием Real Time Terminal (RTT) от SEGGER. В этом руководстве показано, как эту функцию отладки добавить в любой существующий проект.
Что потребуется:
• J-Link software pack v4.98 (или более свежей версии) [3]. • Файлы RTT [4]. • Keil uVision 5.14 (или более свежей версии).
Как добавить файлы RTT в любой проект nRF5x (на примере проекта ble_app_uart):
• Загрузите zip-архив [4] и распакуйте его. • Скопируйте папки RTT и Syscalls, распакованные из zip-архива, в каталог C:\Keil_v5\ARM\Pack\NordicSemiconductor. • Зайдите в меню Keil Project -> Options for target. • На закладке C/C++ добавьте C:\Keil_v5\ARM\Pack\NordicSemiconductor\RTT в пути поиска заголовков (include path). • Подключите SEGGER_RTT.h к проекту, добавив строчку #include "SEGGER_RTT.h" в начало модуля main.c проекта.
Мы добавили файл заголовка для функций RTT, но еще нужно добавить в проект модули *.c.
• В Keil сделайте правый клик на папку проекта, и кликните Add Group, введите RTT для имени группы. • Сделайте правый клик на созданной новой группе, и выберите Add existing files to group 'RTT'. • В диалоге выбора файлов перейдите в C:\Keil_v5\ARM\Pack\NordicSemiconductor\RTT, и добавьте SEGGER_RTT.c.
Теперь у Вас должна работать отправка простых строк через интерфейс RTT. Добавьте в функцию main сразу перед входом в бесконечный цикл следующую строку:
SEGGER_RTT_WriteString(0, "Hello World!\n");
Первый аргумент задает, в какой канал записывать текст. Канал по умолчанию 0, более подробно про каналы можно прочитать на сайте SEGGER.
[Использование Real Time Terminal]
Наш код посылает строку сообщения в RTT, но у нас также должна быть возможность её прочитать. Есть несколько способов, описанных на страничке SEGGER [5]. Самый простой из них - использовать J-Link RTT Viewer, который входит в пакет программ для J-Link (J-link software package).
• Откройте J-Link RTT Viewer, появится окно, как на скриншоте ниже. Если подключено больше одного адаптера J-Link, то поставьте галочку "Serial no.", и введите серийный номер устройства, к которому нужно подключиться.
• Кликните Ok. Появится окно J-Link RTT Viewer:
Загрузите скомпилированный код, и после его запуска Вы увидите текст "Hello World!". Обратите внимание, что если открыть окно терминала с последовательным соединением UART (Termite, putty, HyperTerminal и т. п.), то Вы также увидите в нем текст "Start..", который все еще печатается через последовательный порт, как и раньше. Теперь Вы можете использовать проект ble_app_uart, как это предполагалось изначально, однако также можете посылать текст через RTT, и использовать эту возможность для отладки. Имейте в виду, что SEGGER_RTT_WriteString() работает намного быстрее, чем printf, так что можно безопасно вызывать эту функцию без влияния на параметры поведения в реальном времени своего приложения.
[Передача текста в приложение]
Вы также можете посылать текст через RTT своему MCU, как альтернативу использованию UART. Измените главный цикл функции main в проекте ble_app_uart так, как показано ниже. Для использования функции задержки необходимо также подключить заголовок nrf_delay.h.
char c =0;
for (;;)
{
c = SEGGER_RTT_WaitKey(); // блокировка, пока не поступят данныеif(c =='r'){
SEGGER_RTT_WriteString(0, "Resetting..\n");
nrf_delay_ms(1000);
sd_nvic_SystemReset();
}
//power_manage();
}
• Скомпилируйте проект, и прошейте код в свое устройство. • В RTT Viewer перейдите в меню Input -> Sending и кликните Send on Enter (иначе при каждой нажатой Вами клавише будет происходить отправка соответствующего символа). • Введите букву r в текстовое поле и нажмите Enter. Устройство перезагрузится.
[Более продвинутый вывод]
Мы только что рассмотрели вывод сырого текста с помощью функции SEGGER_RTT_WriteString(). Также есть функция с мощной поддержкой форматированного вывода, SEGGER_RTT_printf(). Чтобы её использовать, нужно дополнительно подправить проект.
• Добавьте в проект модуль SEGGER_RTT_printf.c точно так же, как мы добавляли ранее SEGGER_RTT.c.
• Убедитесь, что в проекте определен макрос NRF_LOG_USES_RTT:
#define NRF_LOG_USES_RTT 1
• Точно так же добавьте RTT_Syscalls_KEIL.c из папки C:\Keil_v5\ARM\Pack\NordicSemiconductor\Syscalls.
• Сделайте правый клик на nRF_Libraries в проекте, и выберите Options.
• В списке Software Components кликните на retarget (перенаправление), и затем на кнопку Remove.
• Перейдите в меню Project -> Options for target, и снимите галочку "Use MicroLIB".
• Отредактируйте бесконечный цикл main следующим образом:
char c =0;
for (;;) {
c = SEGGER_RTT_WaitKey(); // блокировка, пока не поступят данные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();
}
Скомпилируйте и запустите проект. Теперь вывод текста может быть разными цветами, что удобно для визуального выделения отладочных сообщений. В RTT Viewer перейдите в Terminal 0, чтобы увидеть раскрашенный текст.
Теперь функция printf перенаправляет свой вывод в RTT, поэтому для отладки стал доступен стандартный форматированный вывод. Проект ble_app_uart все еще будет работать так же, как и раньше, потому что в нем вывод app_uart_put осуществляется через последовательный порт.
Пример использования форматированного вывода printf:
Здесь первый параметр указывает, что вывод должен осуществляться в Terminal 0 утилиты J-Link RTT Viewer, второй параметр это строка для печати с форматом, и третий параметр это переменная, преобразованное в текст значение которой будет подставлено вместо "%d".
Для дополнительной информации по RTT и различным функциям см. главу 10 руководства J-Link [6].
[2 простых шага для включения вывода через RTT в IDE Keil]
Поскольку модули SEGGER_RTT.c и SEGGER_RTT_printf.c уже добавлены почти во всех примерах из SDK для Keil (они находятся в папке nRF_Segger_RTT дерева модулей проекта), то добавить вывод отладочных сообщений RTT становится очень просто. Процесс по шагам:
1. Добавьте директивой #include заголовочный файл SEGGER_RTT.h в код, где нужно использовать функции вывода через RTT.
#include "SEGGER_RTT.h"
2. Перейдите в свойства проекта (меню Project -> Options for ...), и на закладке C/C++ в поле ввода Preprocessor Symbols Define добавьте макроопределение NRF_LOG_USES_RTT.
После выполнения этих двух шагов можно в коде выполнять вывод произвольных сообщений с помощью функции SEGGER_RTT_printf. Чтобы не вводить каждый раз такое длинное имя функции, я для себя сделал удобный макрос umsg:
#include "SEGGER_RTT.h"
#define umsg(...) \
{ \
SEGGER_RTT_printf(0, __VA_ARGS__); \
}
[Перенаправление вывода NRF_LOG_INFO в RTT]
Поменяйте в файле sdk_config.h макроопределение NRF_LOG_BACKEND_SERIAL_USES_RTT, установив его в 1 (по умолчанию оно определено как 0):
// < e > NRF_LOG_BACKEND_SERIAL_USES_RTT - Если разрешено (1),
// то вывод функций NRF_LOG_ будет печататься через RTT
Как вариант это макроопределение можно установить в отдельном подключаемом файле настроек app_config.h. После этого функции NRF_LOG_INFO, NRF_LOG_DEBUG и NRF_LOG_WARNING будут выводить свои сообщения через RTT.
[Размер буфера вывода]
Среди настроечных параметров есть очень важный параметр NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE, который стоит учитывать при выводе большого количества символов через RTT. По умолчанию в файле sdk_config.h приложения он определен как 512:
#if NRF_LOG_BACKEND_SERIAL_USES_RTT
// < o > NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE - размер выходного буфера RTT.
// < i> Он должен быть равным или меньшим, чем значение \ref NRF_LOG_BACKEND_MAX_STRING_LENGTH.
// < i> Это значение используется в конфигурации Segger RTT configuration для установки
// < i> размера буфера, если он больше, чем значение размера буфера RTT по умолчанию.
4. Создать в папке pca10040\s132\config файл settings.h с таким содержимым:
#define NRF_LOG_USES_RTT 1
5. Добавить в начало external\segger_rtt\SEGGER_RTT_printf.c строчку:
#include "settings.h"
Альтернативно можно пункты 4 и 5 исключить, если добавить в поле Define символов препроцессора NRF_LOG_USES_RTT=1 (свойства проекта -> закладка C/C++ -> Preprocessor Symbols -> в поле ввода Define добавить NRF_LOG_USES_RTT=1).
Для собственного форматированного вывода:
6. sdl_config.h -> nRF_Log -> выбрать уровень вывода Off.
7. Переписать в каталог external\segger_rtt\ макрос umsg.h.
8. Добавить в любой файл #include "umsg.h" и использовать вызовы umsg.
Не забыть записать SoftDevice, если проект его использует!
Содержимое заголовка external\segger_rtt\umsg.h:
#include "settings.h"
#include "SEGGER_RTT.h"
#ifdef NRF_LOG_USES_RTT
#define umsg(...) \
{ \
SEGGER_RTT_printf(0, __VA_ARGS__); \
}
#else
#define umsg(...)
#endif
[Ток потребления]
Для приложений, у которых потребление энергии - критичный параметр (например носимые устройства BLE), в стоит иметь в виду, что включение RTT при не интенсивном выводе не добавляет дополнительный ток потребления. Просто по понятным причинам растет объем используемой памяти.
Однако если Вы подключитесь консолью J-Link, чтобы считывать отладочные сообщения, то ток потребления возрастет примерно на 3.6 мА.