一、RT-Thread简介
RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于家电、消费电子、医疗设备、工控等领域大量使用的 32 位 ARM 入门级 MCU 的场合。
下图是 RT-Thread Nano 的软件框图,包含支持的 CPU 架构与内核源码,还有可拆卸的 FinSH 组件:
架构
支持架构:ARM:Cortex M0/ M3/ M4/ M7 等、RISC-V 及其他。
功能:线程管理、线程间同步与通信、时钟管理、中断管理、内存管理。
Nano 的特点
- 简单
1、下载简单
RT-Thread Nano 以软件包的方式集成在 Keil MDK 与 CubeMX 中,可以直接在软件中下载 Nano 软件包获取源码,获取方式详见 基于 Keil MDK 移植 RT-Thread Nano 与 基于 CubeMX 移植 RT-Thread Nano 。
同时也提供 下载 Nano 源码压缩包 的途径,方便在其他开发环境移植 RT-Thread Nano,如 基于 IAR 移植 RT-Thread Nano。
2、代码简单
与 RT-Thread 完整版不同的是,Nano 不含 Scons 构建系统,不需要 Kconfig 以及 Env 配置工具,也去除了完整版特有的 device 框架和组件,仅是一个纯净的内核。
3、移植简单
由于 Nano 的极简特性,使 Nano 的移植过程变得极为简单。添加 Nano 源码到工程,就已完成 90% 的移植工作。
在 Keil MDK 与 Cube MX 中还提供了 Nano 的软件包,可以一键下载加入到工程。另外,在 RT-Thread Studio 中可以基于 Nano 创建工程直接使用。以下是使用不同开发环境时,可以选择移植或使用 Nano 的方法:
在 RT-Thread Studio 上使用 RT-Thread Nano
基于 KEIL MDK 移植 RT-Thread Nano
基于 CubeMX 移植 RT-Thread Nano
基于 IAR 移植 RT-Thread Nano
移植 RT-Thread Nano 到 RISC-V
4、使用简单
RT-Thread Nano 在使用上也非常简单,带给开发者友好的开发体验。
易裁剪:Nano 的配置文件为 rtconfig.h,该文件中列出了内核中的所有宏定义,有些默认没有打开,如需使用,打开即可。具体的配置可见 Nano 版块的 RT-Thread Nano 配置 教程。
易添加 FinSH 组件:FinSH 组件 可以很方便的在 Nano 上进行移植,而不再依赖 device 框架,只需要对接两个必要的函数即可完成 FinSH 移植。
自选驱动库:可以使用厂商提供的固件驱动库,如 ST 的 STD 库、HAL 库、LL 库等,可以自行选择。
完善的文档:包含 内核基础、线程管理 (例程)、时钟管理 (例程)、线程间同步 (例程)、线程间通信 (例程)、内存管理 (例程)、中断管理,以及 Nano 版块的移植教程。
- 小巧
资源占用小:对 RAM 与 ROM 的开销非常小,在支持 semaphore 和 mailbox 特性,并运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸,RAM 占用约 1K 左右,ROM 占用 4K 左右。
Nano 资源占用情况举例:在运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸。以下是基于 Cortex M3 的 MDK 工程编译结果(优化等级 3)。
Total RO Size (Code + RO Data) 4000 ( 3.91kB)
Total RW Size (RW Data + ZI Data) 1168 ( 1.14kB)
Total ROM Size (Code + RO Data + RW Data) 4092 ( 4.00kB)
注:如果需要丰富的组件、驱动以及软件包等功能,则建议使用 RT-Thread 完整版。
- 开源免费(Apache 2.0)
RT-Thread Nano 实时操作系统遵循 Apache 许可证 2.0 版本,实时操作系统内核及所有开源组件可以免费在商业产品中使用,不需要公布应用程序源码,没有潜在商业风险。
二.CubeMX添加RT-Thread操作系统组件
- 获取RT-Thread Nano软件包
URL:https://www.rt-thread.org/download/cube/RealThread.RT-Thread.pdsc
- 安装RT-Thread
参考:https://blog.csdn.net/qq_36075612/article/details/107309750
三.CubeMX新建工程
选用的是STM32F103C8T6。
SYS:
RCC:
引脚PA6和PA7用于点亮LED灯:
串口USART1:
时钟树:
配置RT-Thread组件:
NVIC:
生成代码:
Keil打开工程代码。
四.创建任务
主要是两个LED灯任务,一个每0.5秒变化一次,一个每1秒变化一次。
在Application/USER文件夹下新建app_rt_thread.c文件,并添加以下代码:
#include "rtthread.h"
#include "main.h"
#include "stdio.h"
struct rt_thread led1_thread;
rt_uint8_t rt_led1_thread_stack[128];
void led1_task_entry(void *parameter);
//初始化线程函数
void MX_RT_Thread_Init(void)
{
//初始化LED1线程
rt_thread_init(&led1_thread,"led1",led1_task_entry,RT_NULL,&rt_led1_thread_stack[0],sizeof(rt_led1_thread_stack),3,20);
//开启线程调度
rt_thread_startup(&led1_thread);
}
//主任务
void MX_RT_Thread_Process(void)
{
printf("Hello RT_Thread!!!");
rt_thread_delay(2000);
}
//LED1任务
void led1_task_entry(void *parameter)
{
while(1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6, GPIO_PIN_RESET);
rt_thread_delay(500);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6, GPIO_PIN_SET);
rt_thread_delay(500);
}
}
找到Middlewares/RT-Thread/RTOS/kernel文件夹下的board.c文件,修改串口USART2(RT默认串口)为USART1。
在usert.c文件中添加代码:
/* USER CODE BEGIN 0 */
#include "stdio.h"
/* USER CODE END 0 */
.
.
.
/* USER CODE BEGIN 1 */
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 1 */
在main.c文件中添加代码:
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
extern void MX_RT_Thread_Init(void);
extern void MX_RT_Thread_Process(void);
在main函数中添加代码:
/* USER CODE END PTD */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_RT_Thread_Init();
main函数的while循环中添加以下代码作为一个新线程:
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_7);
rt_thread_delay(1000);
编译运行:
五、总结
本次实验完成的是RT-thread Nano通过CubeMX在stm32f103上的移植,总的来说整个过程还是比较顺利。裸机程序采用中断和查询结合的方式可以解决大部分应用,但随着工程的复杂,裸机方式的缺点就暴露出来了:
- 必须在中断 (ISR) 内处理时间关键运算 内处理时间关键运算 :ISR ISR 函数变得非常复杂,并且需要很长执行时间 。
- ISR ISR嵌套可能产生不预测的执行时间和堆栈需求。
- 超级循环和 ISR 之间的 数据交换是通过全局共享变量进行:应用程序的员必须确保数据一致性 。
- 超级循环可以与系统计时器轻松同步,但:如果系统需要多种不同的周期时间,则会很难实现 。
- 超过超级循环周期的耗时函数需要做拆分。
- 增加软件开销,应用程序难以理解 。
- 超级循环使得应用程序变得非常复杂,因此难以扩展 : 一个简单的更改就可能产生不预测副作用 ,对这种副作用进行分析非常耗时。
超级循环概念的这些缺点可以通过使用实时操作系统 (RTOS) 来解决。
多任务系统或者说RTOS的实现,重点就在这个调度器上,而调度器的作用就是使用相关的调度算法来决定当前需要执行的任务。如上图所示的那样,创建了任务并完成OS初始化后,就可以通过调度器来决定任务A,任务B和任务C的运行,从而实现多任务系统。另外需要初学者注意的是,这里所说的多任务系统同一时刻只能有一个任务可以运行,只是通过调度器的决策,看起来像所有任务同时运行一样。为了更好的说明这个问题,再举一个详细的运行例子,运行条件如下:
- 使用抢占式调度器。
- 1个空闲任务,优先级最低。
- 2个应用任务,一个高优先级和一个低优先级,优先级都比空闲任务优先级高。
- 中断服务程序,含USB中断,串口中断和系统滴答定时器中断。
六.参考链接
STM32的RT-Thread-Nano移植
基于 STM32CubeMX 添加 RT-Thread 操作系统组件(一)- 详细介绍操作步骤