前言
本文记录了将RT-Thread 移植到STM32F429(ARM Cortex-M4内核)的过程,以及移植过程中的关键点和注意事项。
一、什么是内核移植?
RT-Thread的内核移植就是使得RT-Thread内核在不同的芯片架构、不同板卡上运行起来,其中内核包括:线程管理和调度,线程间同步和通信、内存管理、中断管理、定时器管理等。
二、怎样进行内核移植?
内核移植分为CPU架构移植和BSP移植两部分。
2.1 CPU架构移植
RT-Thread提供libcpu抽象层适配不同CPU架构(例如:Cortex-M、ARM920T),libcpu层向上对内核提供统一的接口。
2.1.1 实现全局中断开关
RT-Thead 提供了一系列线程间同步和通信机制来解决临界区问题,这些机制都要依赖libcpu中提供的全局中断开关函数。
2.1.2 实现线程初始化
栈初始化时,初始化函数手动构造一个上下文内容,作为每个线程第一次执行的初始值。上下文在栈里的排布如下:
2.1.3 实现上下文切换
RT-Thead的libcpu抽象层为了适应不同CPU架构实现了3种线程切换函数,原因是不同的CPU架构,线程之间的上下文切换和中断到线程的上下文切换,上下文寄存器部分可能是有差异的。(Cortex-M架构的上下文切换统一使用PendSV异常实现)。
在线程环境下调用线程切换函数可以马上进行切换,但是在中断环境下需要等待中断处理函数完成之后才能切换,因此不同的CPU架构可能会有不同的上下文切换实现。硬件进入PendSV中断之前自动保存了from线程的PSR,PC,LR,R12, R3-R0寄存器,然后PendSV里保存from线程的R11~R4寄存器,以及恢复to线程的R4~R11寄存器,最后硬件在退出PendSV中断之后,自动恢复to线程的R0~R3,R12、LR、PC、PSR寄存器。Cortex-M处理器架构中线程之间的上下文切换如下图所示:
中断模式下线程切换:
2.1.3.1 实现PendSV中断
在Cortex-M3 CPU架构中,上下文切换是依靠PendSV异常实现的,PendSV中断处理函数是 PendSV_Handler(),处理函数中完成线程切换的实际工作,代码实现涉及到寄存器,一般是用汇编语言编写,具体流程图如下:
2.1.4 实现时钟节拍
时钟节拍是整个系统的心跳,在时钟节拍的基础上可以实现相同优先级线程的时间片轮转调度,定时器,延时函数等。对Cortex-M而言,libcpu移植要做的就是保证rt_tick_increase()函数在时钟节拍的中断处理函数SysTick_Handler()中被调用。调用周期在rtconfig.h 中可配置。
void SysTick_Handler(void)
{
/* enter interrupt */
rt_interrupt_enter();
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
}
2.2 BSP移植
2.2.1 为什么要进行BSP移植
大多实际工程中,板卡上不仅有CPU还会搭载不同的外设,比如最基本的GPIO,UART。RT-Thread提供了BSP抽象层,所谓的抽象层也就是统一接口,BSP移植主要工作就是适配这些抽象层的接口。
2.2.2 BSP移植的主要工作
实现一个基本的BSP主要工作如下:
1.初始化CPU内部寄存器、设定RAM工作时序。
2.实现时钟驱动及中断控制器驱动,完善中断管理
3. 实现串口和GPIO驱动。
4.初始化动态内存堆,实现动态堆内存管理。
三、移植RT-Thread
3.1 移植前准备
3.1.1 准备裸机代码&RT-Thread源码
准备1份裸机代码工程,这里用的是M4的流水灯代码;
下载1份rt-thread源码包,这里用的是rt-thread nano 3.0.3;
3.1.2 将rt-thread源码整合到STM32的裸机工程
-
从rt-thread源码包中拷贝rtconfig.h和board.c 到user文件夹;
rtconfig.h: 配置RT-Thread的功能
board.c :存放硬件相关的初始化函数 -
添加rt-thread源码到裸机工程目录
新建rtt/source和rtt/port目录,其中rtt/source用于存放src文件夹的内容,rtt/ports存放libcpu/arm/cortex-m4文件夹的内容。 -
整理KEIL工程下的目录分类
将不同属性的文件归类添加到对应的group,如下图所示:
4. 将rt-thread源码中需要用到的头文件添加到开发环境
3.2 硬件适配
3.2.1 修改平台相关的文件
这里使用的是ARM Cortex-M4 内核,主要修改rtconfig.h 和board.c ,修改rtconfig.h是为了配置RT-Thread相关参数,比如优先级个数,每秒的tick数量等,大部分参数可以使用默认值。 board.c 中主要是硬件的初始化,RT-Thread启动后会调用 函数 rt_hw_board_init(),当然硬件初始化也可以放到main.c中。board.c中还实现了SysTick_Handler(),主要用于实现tick的累加。tick是操作系统最小的时间单位, RT-Thread中所有和时间相关的事情都在SysTick中断服务函数里处理。
3.3 BSP功能实现
3.3.1 实现串口重映射到rt_kprintf()函数
rt_kprintf()是内核服务类的函数,该接口主要用于打印调试信息,方便开发者调试。需要把控制台重映射到rt_kprintf(),控制台可以是串口、CAN、USB、以太网等输出设备,一般我们选择串口。对于开发者而言,重定向的工作内容就是实现rt_kprintf()中的 rt_hw_console_output()接口。
四、代码修改总览
4.1 github上提交记录
本人在github上推送了整个移植过程,提交commit使用小步快跑的原则,移植过程非常清晰,可以访问下面的github地址了解。
https://github.com/cuimingyang/rt-thread_demos
branch: rt_thread_cortex-m3
总结
以上,简单介绍了RT-Thread移植要做的主要工作和详细步骤,后续会移植更多的RTOS,比如FreeRTOS、LiteOS或者Linux。