banner
Multi-Threaded Tests for nRF7002 DK and nRF9151 DK Boards in Zephyr RTOS.

В статье рассказано о работе плат nRF7002 DK и nRF9151 DK в многопоточных тестах в Zephyr RTOS. Из документации известно, что nRF7002 DK содержит две системаы на кристалле (SoC) nRF5340, каждая из которых имеет по два ядра Arm® Cortex®-M33. Один чип (SoC) используется как хост-микроконтроллер (host microcontroller), второй чип (отладочный) служит для программирования и отладки приложения. Одно ядро хост-микроконтроллера предназначено для выполнения прикладных программ (прикладной процессор), другое (сетевой процессор) обеспечивает работу протоколов Bluetooth, Thread, Zigbee и других. Комплект разработчика nRF9151 DK имет один процессор Arm Arm® Cortex®-M33 работающий на частоте 64 МГц. Исходные коды тестов основаны на публикациях в GitHub от компании Nordic Semiconductor.
На рисунке 1 показаны исходный код и результат первого теста на плате nRF9151 DK. Код содержит два потока thread0_id и thread1_id с одинаковым приоритетом. Потоки созданы статически. Каждый поток (задача в терминах FreeRTOS) имеет функцию потока void thread0(void) строка 11 и void thread1(void) строка 16 соответственно. Для вычисления времени работы потоков использованы функции time_stamp = k_uptime_get() и delta_time = k_uptime_delta(&time_stamp). Первая из них запоминает время старта потока, вторая вычисляет время работы потока. Между этими функциями вызывается функция k_msleep(20). Функция printk() выводит на экран сообщения о времени работы каждого потока.
Несколько слов о потоках и функции k_msleep(). В документации сказано: "В любой момент времени поток может находиться в одном из следующих состояний: выполняется (Running), готов к выполнению (Runnable), не готов к выполнению (Non-runnable). Планировщик (Scheduler) не включает «Non-runnable» поток в алгоритм планирования. k_sleep() изменяет состояние потока с «Running» на «Non-runnable» до истечения тайм-аута, а затем изменит его на «Runnable». Это означает, что поток не будет кандидатом в алгоритме планировщика до истечения тайм-аута."
На рисунке 2 показаны исходный код и результат второго теста на плате nRF7002 DK. Здесь в состав каждого потока включена функция static inline void emulate_work(). Эта функция эмулирует работу процессора. В состав функции входит цикл оператора for от 0 до 300000. Функция k_sleep(20) перенесена в конец цикла.
На рисунке 3 показаны исходный код и результат третьего теста на плате nRF7002 DK. В тесте создаются и запускаются потоки thread0_id и thread1_id с приоритетами 2 и 3 соответственно. Кроме того, в строке 44 функцией k_work_queue_start() запускается поток очереди задач (Workqueue threads) с приоритетом 4. Вместо выполнения функции emulate_work() в цикле while, эта функция отправляется как элемент задания в очередь задач, используя функцию k_work_submit_to_queue() (строка 50).
В документации на Zephyr RTOS Workqueue определяется так: «Workqueue - это объект ядра, который использует выделенный поток для обработки рабочих элементов (work items) по принципу "первый пришел - первый вышел" (FIFO). Каждый рабочий элемент обрабатывается путем вызова функции, указанной в рабочем элементе. Рабочая очередь обычно используется ISR (Interrupt Service Routine) или потоком с высоким приоритетом для передачи несрочной обработки потоку с более низким приоритетом, чтобы не влиять на обработку, зависящую от времени.»
Это тридцатая статья из цикла "Real-Time Object Recognition". Первые двадцать девять опубликованы здесь: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 и 29.

1. Тест № 1.

Исходный код первого теста.
Test 1 Test 1 Test 1
Вывод первого теста.
Test 1

Рисунок 1.

Как работает первый тест? Сначала запускается функция void thread0(void). Затем, в цикле while(1) вызывается функция k_msleep(20). Это приводит к тому, что поток "засыпает" и планировщик переводит первый поток в состояние "Non-runnable". Далее планировщик запустит на выполнение второй поток - функцию thread1(void). Функция k_msleep(20) во втором потоке переведет и второй поток в состояние "Non-runnable". Через 20 мс. после старта "проснется" первый поток и выведет на экран длительность "сна". Планировщик переведет первый поток в состояние "Runnable" и запустит его на выполнение. Далее все переходы состояний первого и второго потоков будут повторяться.

2. Тест № 2.

Исходный код второго теста.
Test 2 Test 2
Вывод второго теста.
Test 1

Рисунок 2.

Как работает второй тест? Первой запускается функция первого потока void thread0(void), затем в строке 23 функция emulate_work(). Примерно через 87 мс. функция закончит работу, после чего будут вызваны функции k_uptime_delta(&time_stamp), printk(), k_msleep(20). Вызов k_msleep(20) приведет к тому, что первый поток "заснет" на 20 мс. Далее планировщик запустит на выполнение второй поток - функцию thread1(void). Работа второго потока аналогична работе первого. Через 20 мс. "проснется" первый поток, однако до окончания работы второго потока ~ (77 mc.) начало нового цикла первого потока будет задержано.

3. Тест № 3.

Исходный код третьего теста.
Test 3 Test 3
Вывод третьего теста.
Test 1

Рисунок 3.

Работа третьего теста хорошо описана в курсе (GitHub repository for this course) от компании Nordic Semiconductor. В тестах курса используется плата nRF54L15 DK работающая на частоте 128 MHz. Выходные данные работы потоков из курса Nordic Semiconductor и теста №3 совпадают по смыслу. Распечатка теста показывает, что thread0 не затрачивает время на обработку функции emulate_work(). Однако продолжительность работы thread1 в тесте №3 в два раза больше чем у nRF54L15 DK. Вероятно, это свидетельствует о том, что тестируемая плата работает на частоте 64 МГц.
4. Отладка и графическое отображение работы системы.
Приведенные в статье тесты очень просты. В реальных проектах применяют и другие примитивы выполнения (execution primitive). Например: cooperative threads, Meta-IRQ, system threads, logging threads, обработчики прерываний (ISR), очереди сообщений (Message Queue). Для глубокого изучения оборудования от Nordic Semiconductor и Zephyr RTOS предлагается курс Beyond the basics (За пределами основ). Этот курс содержит более сложные примеры. В качестве инструмента для визуального отображения и отладки работы системы в курсе рекомендуется nRF Connect for VS Code. Вот пример проекта с применением окна просмотра потоков.

Тесты можно загрузить.
Консультации через e-mail: veresov@jdesign.ru или телефон: +7 921-215-45-70.
Евгений Вересов.
01.03.2026 года.