RT-Thread RTOS Печать
Добавил(а) microsin   

Операционная система для микроконтроллеров RT-Thread начала свое развитие в 2006 году. Это открытая, нейтральная, основанная на широком комьюнити разработчиков операционная система реального времени (real-time operating system, RTOS).

RT-Thread приветствует поддержку всех разработчиков, и если у вас есть любые идеи, советы по развитию или вопросы по использованию RT-Thread, то можете связаться с разработчиками RT-Thread, информация доступна на официальном сайте RT-Thread, Github, Twitter, LinkedIn, Youtube, Facebook, Medium. Вопросы можно публиковать в секции решения проблем (раздел issue) репозитория или форума RT-Thread.

Website | Github | Twitter | LinkedIn | Youtube | Facebook | Medium

Если вы заинтересованы в RT-Thread, хотите присоединиться к команде разработки RT-Thread, либо предоставить свой код, то посетите страничку Code Contribution Guide site:rt-thread.io.

Основные среды разработки и компиляторы (IDE), поддерживаемые RT-Thread:

MDK KEIL
IAR
GCC

Скрипты и утилиты конфигурирования и компиляции работают на Python-скриптах SCons [2].

RT-Thread написана в основном на языке C и придерживается стандартов API POSIX [3], что упрощает понимание её интерфейса и портирование на различные архитектуры микроконтроллеров (MCU) и модули/платы разработчика на их основе. Разработчики стараются придерживаться объектно-ориентированных методов программирования при создании систем реального времени, что делает код элегантным, структурированным, модульным и легко адаптируемым под требоывания пользователя.

У RT-Thread есть версии Standard и Nano. Для MCU-систем с ограниченными ресурсами версия ядра NANO потребует только 3 килобайта Flash и 1.2 килобайта RAM. Многофункциональные устройства IoT с большим объемом памяти могут использовать версию Standard, онлайн-утилиту управления пакетами и утилиты конфигурирования системы. Это упрощает применение богатого набора различных библиотек для создания таких сложных функций, как графический интерфейс в стиле Android со слайдинг-эффектами тачскрина, голосовое управление и т. д.

[Архитектура RT-Thread]

RT-Thread это не только ядро реального времени, но также и множество различных функциональных компонентов.

RT Thread architecture

Показанная на рисунке выше архитектура включает:

RT-Thread kernel: слой ядра, основная часть RT-Thread, где реализованы объекты операционной системы, такие как модуль поддержки потоков (multi-threading) и распределение между ними процессорного времени (scheduling, планировщик), а также модули семафоров (semaphore), очередей сообщений (message queue), управление памятью, программные таймеры и т. д. Библиотеки libcpu и BSP (Chip Migration Related Files/Board Support Package) осуществляют привязку к конкретной аппаратуре и состоят из драйверов периферийных устройств и низкоуровневых подпрограмм поддержки особенностей портирования CPU.

Components and Service Layer: слой компонентов и служб реализует промежуточный высокоуровневый интерфейс поверх RT-Thread kernel. Здесь представлены виртуальный файловые системы (virtual file system, VFS [4]), интерфейс командной строки FinSH [5], поддержка сетевых коммуникаций и протоколов (network frameworks) и многое другое. Описание некоторых других компонентов см. в статье [6].

RT-Thread software package: программный пакет общего назначения, предназначенный для запуска на основе RT-Thread устройств IoT различного назначения. Пакет включает в себя документацию, исходный код или библиотечные файлы. RT-Thread предоставляет открытую платформу для пакетов, доступных официально или от сторонних разработчиков. Пакеты составляют важную функциональную составляющую экосистемы RT-Thread, значительно сокращая время разработки конечных устройств. RT-Thread поддерживает более 370 программных пакетов.

Примечание переводчика: к сожалению некоторые программные пакеты, такие как например стек BLE для микроконтроллеров BEKEN или библиотека OTA для загрузчика с поддержкой шифрования, могут поставляться без исходного кода (например, в виде файла библиотеки *.a). Исходный код для таких библиотек может быть предоставлен по дополнительному соглашению с разработчиками (например на платной основе).

[Общая структура каталогов файлов RT-Thread]

Пакет исходного кода RT-Thread содержит следующие директории:

Папка Описание
BSP Board Support Package, пакет поддержки платы - готовый код для адаптации ресурсов поддерживаемых плат разработчика.
components Библиотеки программных компонентов, таких как оболочка консоли finsh shell, файловая система, стек протокола и т. д.
documentation Контекстная документация, описывающая стиль кодирования, функции библиотек, doxygen, и т. п.
examples Примеры кода приложений.
include Файлы заголовка API-функций RT-Thread kernel.
libcpu Код портирования на поддерживаемые CPU, такие как ARM/MIPS/RISC-V и т. д.
src Файлы исходного кода RT-Thread kernel.
tools Файлы скриптов для системы сборки RT-Thread (command build tool).

RT-Thread портирована на больше чем 200 плат разработчика. Большинство библиотек BSP поддерживают популярные среды разработки (IDE), такие как MDK (Keil), IAR, и почти все BSP поддерживают компилятор GCC. Обычно также предоставляются готовые примеры приложений, на основе которых пользователи могут создать свой собственный проект. Каждый имеет унифицированную структуру каталогов и файлов с файлом README.md, содержащим базовое описание BSP, как можно быстро начать его использовать.

[Поддерживаемые архитектуры]

ARM Cortex-M0/M0+: чипы наподобие тех, что поставляет компания ST Microelectronics.
ARM Cortex-M3: чипы производителей наподобие ST, Winner Micro, MindMotion, и т. д.
ARM Cortex-M4: чипы производителей наподобие ST, Nuvton, NXP, GigaDevice, Realtek, Ambiq Micro, и т. д.
ARM Cortex-M7: чипы производителей наподобие ST, NXP.
ARM Cortex-M23: чипы производителей наподобие GigaDevice.
ARM Cortex-M33: чипы производителей наподобие ST.
ARM Cortex-R4.
ARM Cortex-A8/A9: чипы производителей наподобие NXP.
ARM7: чипы производителей наподобие Samsung.
ARM9: чипы производителей наподобие Allwinner, Xilinx, GOKE.
ARM11: чипы производителей наподобие Fullhan.
MIPS32: чипы производителей наподобие loongson, Ingenic.
RISC-V: чипы производителей наподобие Hifive, Kendryte, Nuclei.
ARC: чипы производителей наподобие SYNOPSYS.
DSP: чипы производителей наподобие TI, Analog Devices.
C-Sky.
x86.

[Поддерживаемые IDE и компиляторы]

Основные компиляторы/среды разработки, поддерживаемые RT-Thread:

RT-Thread Studio IDE (Windows, на основе Eclipse) [7].
MDK KEIL (Windows).
IAR (Windows).
GCC (Windows, Linux).

RT Thread Studio IDE

[Env Tool]

На ранних стадиях разработки команда RT-Thread также создала дополнительный инструмент конфигурирования Env. Это утилита работает на основе тексто-графического интерфейса TUI (Text-based user interface). Разработчики могут использовать утилиту Env для конфигурирования и создания проектов GCC, Keil MDK и IAR.

RT Thread Env Tool

См. также руководство пользователя [8] и вводное видео [9].

[Быстрый старт]

Примеры основаны на платах STM32F103 BluePill [10] и Raspberry Pi Pico, см. репозитории [11, 12], вводное обучающее видео [13].

Симулятор. RT-Thread BSP может быть напрямую загружен и скомпилирован для любой поддерживаемой платы или чипа. Однако также есть возмож запуска симуляции RT-Thread на основе qemu-vexpress-a9 BSP, без использования аппаратного устройства. Симулятор работает на основе QEMU, что доступно на Windows и Ubuntu [14].

Исходный код и утилиты RT-Thread можно скачать на Github [15].

Env Tool. Утилита Env [8] может помочь в конфигурировании и создании шаблонов проектов (скрипты для Linux/MacOS/Windows). Встроенная система menuconfig [2] обеспечивает простое конфигурирование ядра, компонентов и программных пакетов [6]. На Windows также доступна графическая утилита конфигурирования проекта и управления пакетами RT-Thread.

[Лицензия]

RT-Thread это открытое ПО, лицензируемое под Apache License Version 2.0 since v3.1.1. License. Эту информацию лицензирования можно в основном увидеть в начале модулей кода:

/* Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 * ...
 */

Базовые вопросы

Q1. Загрузил SDK, все в порядке, компиляция проходит успешно. Однако не могу запустить программу, в чем проблема?

Если используете Keil + RT-Thread Env, то первое, что вам следует сделать после загрузки исходного кода, это пройти процесс конфигурирования через menuconfig.

Если используете RT-Studio IDE, то нужно открыть Settings перед созданием проекта; пройдите через все элементы конфигурации, отмените ненужное, оставив только ядро (kernel).

Сначала убедитесь, что система нормально запускается в минимальной конфигурации, используя для этого простейшую программу наподобие мигания светодиодом. После этого пошагово добавляйте нижележащие устройства или другие пакеты, проверяя работу на каждом шаге (menuconfig -> компиляция -> проверка).

Q2. Загрузил SDK, все в порядке, компиляция проходит успешно. Однако программа показывает Hard Fault on Thread, в чем проблема?

Сделайте все то же самое, что перечислено в ответе на Q1.

Q3. Загрузил SDK, но есть проблемы с компиляцией.

Сделайте все то же самое, что перечислено в ответе на Q1.

Kernel

Q1. Как правильно определить RT_NAME_MAX?

Чем меньше RT_NAME_MAX, тем меньше будет использоваться памяти. Например, если существует 100 объектов kernel, и имя объекта занимает 8, то всего на это будет потрачено 800 байт. В структуре rt_object находится определение объекта, где есть определение имени длиной RT_NAME_MAX байт и две переменные типа rt_uint8_t. RT_NAME_MAX может быть определено как 2n + 2.

Q2. RT_DEBUG

Не включайте отладку ядра (kernel debugging), если не понимаете, необходимо ли это в вашей ситуации.

Q3. Как правильно определить размер стека потока?

Этот вопрос имеет большое отношение к приложениям. и если это только система с минимальным ядром (minimal kernel system), потоком ожидания (idle thread), без разрешенных прерываний и работающих программ, то 256 для стека достаточно. Если добавляется код программы, вместе с механизмами блокировки, очередей, передачи сообщений, то лучше размер стека потока увеличить до 1024 или даже больше (подбирается опытным путем).

Q4. Как быстро вычислить значение, возвращаемое GET_PIN?

Мы знаем, что группирование GPIO чипов обычно начинается с PA, потом идут PB, PC, PD, PE, ... PZ. Также каждая такая группа портов содержит либо 16 бит, либо 8 бит (соответствует 16 IO и 8 IO). Упрощенная формула для GET_PIN для 16-битных портов:

(X - A) * 16 + n

Т. е. для A10 получится 10. Для C9 получается 2*16+9=41. H1 будет 7*16+1=113.

Для 8-битных портов формула будет:

(X - A) * 8 + n.

Q5. Не понимаю, чем отличаются hard timers, soft timers и hardware timers.

RT-Thread kernel определяет программные таймеры (software timer). Они, в отличие от аппаратных таймеров (hardware timer), не требуют задействования отдельного периферийного устройства timer, как и для реализации разных других функций типа сравнение интервалов (comparison) и захват сигнала (capture). Программный таймер просто задает интервал времени таймаута, и запускает callback-функцию при истечении этого интервала.

Программный таймер, определяемый RT-Thread, также делится на два типа "hard timer" и "soft timer". Первый тип выполняет свою callback-функцию в прерываниях SysTick. Этот тип используется для встроенных таймеров потока, и может использоваться также на уровне приложения, однако следует иметь в виду, что callback-функции вызываются из контекста прерываний.

Второй тип, который работает в потоке, может использоваться, когда слой приложения не требует высокой точности отслеживания интервала времени. Однако следует отметить, что поток, который определяет таймер, и поток, который выполняет callback-функцию таймера, являются двумя разными потоками.

Q6. Сколько памяти достаточно запросить для пула очереди сообщений (Message Queue)?

Очередь сообщений может создаваться разными способами.

rt_err_t rt_mq_init (rt_mq_t mq,
                     const char *name,
                     void *msgpool,
                     rt_size_t msg_size,
                     rt_size_t pool_size,
                     rt_uint8_t flag);
 
rt_mq_t rt_mq_create (const char *name,
                      rt_size_t msg_size,
                      rt_size_t max_msgs,
                      rt_uint8_t flag);

Если вы создаете очередь сообщений с помощью rt_mq_create, то пул очереди сообщений будет вычислен автоматически по размеру тела сообщения msg_size и максимальной длине очереди max_msgs.

Если для инициализации очереди сообщений используется rt_mq_init, то память пула msgpool для сообщений должна быть предоставлена пользователем. В этом случае следует обратить внимание на размер структуры pool_size. Необходимый объем пула памяти можно вычислить по формуле:

(RT_ALIGN(msg_size, RT_ALIGN_SIZE) + sizeof(struct rt_mq_message*)) * max_msgs

Здесь msg_size это размер тела сообщения, и max_msgs это максимальная емкость очереди сообщений.

Q7. Замечания по использованию очереди сообщений (message queue).

Хотя несколько API-функций наподобие rt_mq_send, rt_mq_urgent и rt_mq_recv имеют параметры размера size, обратите особое внимание, что передаете строго одинаковые значения аргумента, соответствующие msg_size, в rt_mq_init и rt_mq_create. Не следует менять размер параметра size.

Т. е. не используйте очередь сообщений, чтобы напрямую посылать данные слишком большого размера.

Q8. Объяснение смысла макроса INIT_xxx_EXPORT.

Отличительная особенность RT-Thread в том, что в функции main не делается никакая начальная конфигурация, для этого существует отдельный поток. Другие потоки автоматически запускаются через INIT_APP_EXPORT. Поначалу это слегка запутывает пользователя.

Всего RT-Thread определяет 6 процессов запуска (startup processes).

1. Подпрограммы инициализации платы, вызываемые в функции board_init(). Определение:

#define INIT_BOARD_EXPORT(fn)     INIT_EXPORT(fn, "1")

2. Подпрограммы инициализации pre/device/component/env/app, вызываемые в init_thread (чисто программная инициализация). Определение:

#define INIT_PREV_EXPORT(fn)      INIT_EXPORT(fn, "2")

3. Инициализация устройства. Определение:

#define INIT_DEVICE_EXPORT(fn)    INIT_EXPORT(fn, "3")

4. Инициализация компонента (dfs, lwip, ...). Определение:

#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")

5. Инициализация окружения (монтирование диска, ...). Определение:

#define INIT_ENV_EXPORT(fn)       INIT_EXPORT(fn, "5")

6. Инициализация приложения (приложение rtgui, и т. п. ...). Определение:

#define INIT_APP_EXPORT(fn)       INIT_EXPORT(fn, "6")

INIT_BOARD_EXPORT запускается перед запуском планировщика (scheduler). Здесь происходит процесс начальной инициализации конфигурации периферии.

Остальные процессы запускаются основным потоком (в standard-версии RT-Thread, если используется поток main) после того, как запущен планировщик.

Эти процессы не полностью фиксированы, и кое-что можно подстроить. Например, можно перенести инициализацию LCD из DEVICE в BOARD, и сделать инициализацию emwin в PREV. Некоторые очереди сообщений также инициализируются в процессе ENV.

В большинстве случаев все инициализации можно провести в описанных выше процессах 1 .. 6. Однако существует вероятность возникновения конфликта.

Этой теме посвящен топик PR #5194 в RT-Thread Github Repository, где находятся некоторые полезные ссылки, и многие разработчики находят здесь советы по решению своих проблем.

Рационально сделать запуск устройств и компонентов на одном уровне, разрешая их зависимости с помощью мьютексов. В этом случае нужно подстроить порядок кода.

Q9. Как реализовать пробуждение потока с помощью rt_thread_suspend и rt_thread_resume?

Лучше всего избегать таких сценариев. В RT-Thread может быть 2 возможности, возникающие при переходе потока в состояние приостановки (suspend): в одном случае он сразу получит процессорное время, в другом случае он будет ждать освобождения ресурса, пока ресурс заблокирован. Не существует полного и прозрачного способа, чтобы потоки знали текущее взаимное состояние.

Предположим, что поток A хочет явно приостановить поток B, но A не знает, отказался ли в настоящий момент B от CPU, или он покидает ресурс для приостановки, или ресурc доступен и B пробуждается, выходя из состояния приостановки. Так или иначе, опасно переводить в приостановку другой поток, не зная его текущее состояние.

Единственный допустимый вариант может быть, когда поток B загружен больше всего, и сам не будет проявлять инициативу для блокировки, чтобы оказаться от процессорного времени. Кроме того, приоритет B относительно низкий, и тогда высокоприоритетный поток A может приостановить B при определенных условиях. Но тогда поток B должен повлиять на поток ожидания (idle thread).

Фактически необходимого функционала можно достичь более безопасными методами, используя традиционные механизмы синхронизации потоков - ожидание на семафоре или очереди. Поток B приостанавливает сам себя, ожидая поступления сигнала от A; поток A тогда разбудит поток B с помощью сигнала (публикуя семафор или ставя объект в очередь).

Q10. Команды list_thread (или ps) не могут проверить состояние потока?

Ошибки потока в столбце Error не могут иметь какой-либо ссылки. Здесь 0 это нормальное состояние, -2 показывает таймаут, и когда вы выполните rt_thread_mdelay, он превратится в -2. Однако это не означает ошибку.

Столбец status представляет текущее состояние потока. Однако из-за того, что команда list_thread (или ps) выполняется в контексте потока tshell, поток tshell по определению работает (состояние running). Поток ожидания (idle thread) не может быть приостановлен, он показывает состояние готовности (ready). Другие потоки могут быть в состоянии ready, но в большинстве случаев при нормальном сценарии они могут быть в состоянии приостановки (suspend), ожидая публикации семафора или очереди. Однако это не означает, что другие потоки приостановлены, и не управляются планировщиком.

Q11. Может ли таймер выполнять долгие операции?

Как упоминалось выше, существует 3 разновидности таймеров в RT-Thread, и у каждого свои характеристики.

Hardware timer: его callback-функция вызывается в прерывании, так что не рекомендуется, чтобы она выполняла какие-либо длительные действия.

Hard timer: также выполняет свои callback-функции в прерываниях, и не рекомендуется в них напрямую выполнять длительные операции.

Soft Timer: выполняется потоком таймера (timer thread), который вызовет callback-функции, и теоретически здесь могут выполняться долгие операции. Timer thread также является обычным потоком, у него есть собственный стек потока, свой приоритет, и т. п. Если некоторые операции независимы, то размещение их в определенном потоке совпадет с выполнением в потоке таймера.

Однако способ, которым текущий timer thread обрабатывает soft-таймеры, не подходит для выполнения длительных операций. Для этого требуются некоторые изменения.

Необходимо соответствующим образом подстроить приоритет timer thread относительно других потоков. Следует иметь в виду, что если создано несколько soft-таймеров, то длительное выполнение callback-функции одного из этих soft-таймеров может повлиять на работу других soft-таймеров, чего избежать не получится.

[Ссылки]

1. RT-Thread document center site:rt-thread.io.
2RT-Thread SCons.
3. POSIX Standard and RT-Thread Implementation site:club.rt-thread.io.
4RT-Thread Virtual File System.
5Консоль FinSH.
6RT-Thread RTOS и её компоненты.
7RT-Thread Studio IDE.
8. User Manual of Env site:rt-thread.io.
9. Get Started with RT-Thread Env Tool site:youtube.com.
10Программирование STM32 Blue Pill через USB.
11. rt-thread/bsp/stm32/stm32f103-blue-pill site:github.com.
12. rt-thread/bsp/raspberry-pico  site:github.com.
13. RT-Thread RTOS Tutorial #1 | Introduction of RT-Thread site:youtube.com.
14QEMU: быстрый старт.
15RT-Thread GitHub.

. RT-Thread FAQ site:club.rt-thread.io.
https://github.com/RT-Thread/rt-thread