RT-Thread学习

发布于:2024-04-18 ⋅ 阅读:(32) ⋅ 点赞:(0)

RT-Thread三个版本——标准版本

RT-Thread,全称是Real Time-Thread,顾名思义,它是一个嵌入式实时多线程操作系统,基本属性之一是支持多任务:一个处理器核心在某一时刻只能运行一个任务,由于每次对一个任务的执行时间很短,任务与任务之间通过任务调度器进行非常快速地切换。

RT-Thread系统中,任务是通过线程实现的,RT-Thread中的线程调度器也就是以上提到的任务调度器。

RT-Thread主要采用C语言编写,浅显易懂,方便移植。
它把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好

针对资源受限的微控制器(MCU)系统,可通过方便易用的工具,裁剪出仅需要3KB Flash、1.2KB RAM内存资源的NANO版本(NANO版本是RT-Thread官方于2017年7月份发布的一个极简版内核)。

而对于资源丰富的物联网设备,RT-Thread又能使用在线的软件包管理工具,配合系统配置工具,实现直观快速地模块化裁剪,无缝地导入丰富的软件功能包,实现类似Android的图形界面及触摸滑动效果、智能语音交互效果等复杂功能。

相较于Linux操作系统,RT-Thread体积小,成本低,功耗低,启动快速,除此以外RT-Thread还具有实时性高、占用资源小等特点,非常适用于各种资源受限(如成本、功耗限制等)的场合。

虽然32位MCU是它的主要运行平台,实际上很多带有MMU、基于ARM9、ARM11的甚至Cortex-A系列级别CPU的应用处理器在特定应用场合也适合使用RT-Thread。

NANO版本是标准版本的极简内核版本,SMART版本是在标准版本上增加了用户态创造而来,所以学习标准版本是学习RT-Thread的基础。

Env工具:Env工具用于对源码功能进行配置或裁剪,可以生成MDK/IAR/GCC工程,需要配合使用。

RT-Thread简介

RT-Thread是一款完全由国内团队开发维护的嵌入式实时操作系统,具有完全的自主知识产权。经过16个年头的沉淀,伴随着物联网的兴起,它正演变成一个功能强大、组件丰富的物联网操作系统。

RT-Thread的架构

近年来,物联网(Internet Of Things,IoT)概念广为普及,物联网市场发展迅猛,嵌入式设备的联网已是大势所趋。
物联网操作系统是指以操作系统内核为基础,包括如文件系统、图形库等较为完整的中间件组件,具备低功耗、安全、通信协议支持和云端连接能力的软件平台,RT-Thread就是一个IoT OS。

RT-Thread与其他很多RTOS如FreeRTOS主要区别之一是,它不仅仅是一个实时内核,还具备丰富的中间层组件
在这里插入图片描述

  • 内核层:RT-Thread内核,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等。libcpu/BSP(芯片移植相关文件/板级支持包)与硬件密切相关,由外设驱动和CPU移植构成。
  • 组件与服务层:组件是基于RT-Thread内核之上的上层软件,例如虚拟文件系统、FinSH命令行界面、网络框架、设备框架等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。
  • RT-Thread软件包:运行于RT-Thread物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。开放的软件包平台,存放了官方提供或开发者提供的软件包,这也是生态的重要组成部分。
    生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。

内核基础

从软件架构入手讲解实时内核的组成与实现,内核的组成部分,系统如何启动,内存分布情况以及内核配置方法。

RT-Thread内核介绍

内核是一个操作系统核心,负责管理系统的线程、线程间通信、系统时钟、中断及内存等。
在这里插入图片描述
内核处于硬件层之上,内核部分包括内核库、实时内核实现。
内核库是为了保证内核能够独立运行的一套小型的类似C库的函数实现子集。这部分根据编译器的不同自带C库的情况会有些不同,当使用GNU GCC编译器时,会携带更多的标准C库实现。

C库:也叫C运行库,它提供了类似strcpy、memcpy等函数,有些也会包括printf、scanf函数的实现。内核库仅提供内核用到的一小部分C库函数实现,为了避免与标准C库重名,这些函数前面会添加rt_前缀。

线程管理

线程是RT-Thread操作系统中最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占之外,系统的其它部分是可以抢占的,包括线程调度器自身。

支持256个线程优先级(也可以通过配置文件更改为最大支持32个或8个线程优先级,针对STM32默认配置是32个线程优先级),0优先级代表最高优先级,最低优先级留给空闲线程使用;同时它也支持创建多个具有相同优先级的线程,相同优先级的线程采用时间片的轮转调度算法进行调度,使每个线程运行相应时间;另外调度器在寻找那些处于就绪状态的具有最高优先级的线程时,所经历的时间是恒定的系统也不限制线程数量的多少,线程数目只和硬件平台的具体内存相关。

时钟管理

RT-Thread的时钟管理以时钟节拍为基础,时钟节拍是RT-Thread操作系统中最小的时钟单位。
RT-Thread的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。
第二类是周期触发定时器,这类定时器会周期性地触发定时器事件,直到用户手动的停止定时器否则将永远持续执行下去。

另外,根据超时函数执行时所处的上下文环境,RT-Thread的定时器可以设置为HARD_TIMER模式或者SOFT_TIMER模式。

通常使用定时器定时回调函数(即超时函数),完成定时服务。用户根据自己对定时处理的实时性要求选择合适类型的定时器。

线程间同步

RT-Thread采用信号量、互斥量与事件集实现线程间同步。
线程通过对信号量、互斥量的获取与释放进行同步;互斥量采用优先级继承的方式解决了实时系统常见的优先级翻转问题。
线程同步机制支持线程按优先级等待方式获取信号量或互斥量。
线程通过对事件的发送与接收进行同步。
事件集支持多事件的或触发和与触发,适合于线程等待多个事件的情况。

线程间通信

RT-Thread支持邮箱和消息队列等通信机制。
邮箱中一封邮件的长度固定为4字节大小;消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。
邮箱效率较消息队列更为高效。
邮箱和消息队列的发送动作可安全用于中断服务例程中。
通信机制支持线程按优先级等待方式获取。

内存管理

RT-Thread支持静态内存池管理及动态内存堆管理。
当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的;当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉(即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。等待的时间取决于申请内存块时设置的等待时间参数),当其它线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒。

动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存系统的内存管理算法以及面向大内存系统的SLAB内存管理算法。

还有一种动态内存堆管理叫做memheap,适用于系统含有多个地址且不连续的内存堆。使用memheap可以将多个内存堆“粘贴”在一起,让用户操作起来像是在操作一个内存堆。

I/O设备管理

RT-Thread将PIN、IIC、SPI、UART等作为外设设备,统一通过设备注册完成。
实现了按名称访问的设备管理子系统,可按照统一的API界面访问硬件设备。
在设备驱动接口上,根据嵌入式系统的特点,对不同的设备挂接相应的事件。当设备事件触发时,由驱动程序通知给上层的应用程序。

RT-Thread启动流程

RTT支持多种平台和多种编译器,而rtthread_startup()函数是RTT规定的统一启动入口。
执行顺序是:系统先从启动文件开始运行,然后进入RTT的启动函数rtthread_startup(),最后进入用户入口函数main()。

在这里插入图片描述
以MDK-ARM为例,用户程序入口为main()函数,位于main.c文件中。
系统启动后先从汇编代码开始运行,然后跳转到C代码,然后跳转到C代码,进行RTT系统启动,最后进入用户程序入口函数main()。

为了在进入main()之前完成RTT系统功能初始化,我们使用了MDK的扩展功能 S u b Sub Sub$ 和 S u p e r Super Super$。

s u b sub sub m a i n 先调用一些补充在 m a i n 之前的功能函数,再调用 main先调用一些补充在main之前的功能函数,再调用 main先调用一些补充在main之前的功能函数,再调用super$$main转到main()函数执行,这样可以让用户不用去管main()之前的系统初始化操作。

在components.c中定义了这段代码:

int $Sub$$main(void){
	rtthread_startup();
	return 0;
}
int rtthread_startup(void){
	rt_hw_interrupt_disable();

	/*板级初始化:需要在该函数内部进行系统堆的初始化*/
	rt_board_init();

	/*定时器初始化*/
	rt_system_timer_init();

	/*调度器初始化*/
	rt_system_scheduler_init();

	/*用户创建一个main线程*/
	rt_application_init();

	/*定时器线程初始化*/
	rt_system_timer_thread_init();

	/*空闲线程初始化*/
	rt_thread_idle_init();

	/*启动调度器*/
	rt_system_scheduler_start();

	/*不会执行至此*/
	return 0;
}

这部分启动代码,大致可以分为四个部分:

  1. 初始化与系统相关的硬件;
  2. 初始化系统内核对象,例如定时器、调度器、信号;
  3. 创建main线程,在main线程中对各类模块依次进行初始化;
  4. 初始化定时器线程、空闲线程,并启动调度器。

启动调度器之前,系统所创建的线程在执行rt_thread_startup()后并不会立马运行,它们会处于就绪状态等待系统调度;待启动调度器之后,系统才转入第一个线程开始运行,根据调度规则,选择的是就绪队列中优先级最高的线程。

rt_hw_board_init()中完成系统时钟设置,为系统提供心跳,串口初始化,将系统输入输出终端绑定到这个串口,后续系统运行信息就会从串口打印出来。

main() 函数是 RT-Thread 的用户代码入口,用户可以在 main() 函数里添加自己的应用。


网站公告

今日签到

点亮在社区的每一天
去签到