nRF5x: отладка с помощью RTT |
![]() |
Добавил(а) microsin |
В 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 в любой проект nRF5x (на примере проекта ble_app_uart): • Загрузите zip-архив [4] и распакуйте его. Мы добавили файл заголовка для функций RTT, но еще нужно добавить в проект модули *.c. • В Keil сделайте правый клик на папку проекта, и кликните Add Group, введите RTT для имени группы. Теперь у Вас должна работать отправка простых строк через интерфейс 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(); } • Скомпилируйте проект, и прошейте код в свое устройство. [Более продвинутый вывод] Мы только что рассмотрели вывод сырого текста с помощью функции 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: SEGGER_RTT_printf(0, "Значение переменной: %d\n", variable); Здесь первый параметр указывает, что вывод должен осуществляться в 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
//============================================================
#ifndef NRF_LOG_BACKEND_SERIAL_USES_RTT
//#define NRF_LOG_BACKEND_SERIAL_USES_RTT 0
#define NRF_LOG_BACKEND_SERIAL_USES_RTT 1
#endif
Как вариант это макроопределение можно установить в отдельном подключаемом файле настроек 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 по умолчанию.
#ifndef NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE
#define NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE 512
#endif
#endif //NRF_LOG_BACKEND_SERIAL_USES_RTT Если Вы выведете через RTT большее количество символов, чем размер буфера RTT, то это приведет к ошибочному, непредсказуемому выводу через RTT. На примере приложений из SDK 12.3.0 (nRF5_SDK_12.3.0_d7731ad), пошаговая инструкция: 1. sdl_config.h -> nRF_Log -> поставить галочку NRF_LOG_ENABLED. 2. sdl_config.h -> nRF_Log -> выбрать уровень вывода NRF_LOG_DEFAULT_LEVEL (по умолчанию Info, Debug самый подробный). 3. sdl_config.h -> nrf_log_backend -> снять галочку NRF_LOG_BACKEND_SERIAL_USES_UART и поставить галочку NRF_LOG_BACKEND_SERIAL_USES_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 мА. [Ссылки] 1. Debugging with Real Time Terminal site:nordicsemi.com. |