作者:Lim Jia Zhi,高级嵌入式软件工程师
关键词: 编程
什么是 RTOS
实时操作系统 (RTOS) 是一种轻型操作系统,用于在资源和时间受限的设计中使多任务和任务集成变得易于执行,这在嵌入式系统中常见。此外,“实时”一词表示的是执行时间而非原始速度的可预测性/确定性,因此一个RTOS由于其确定性,通常可以证明满足硬实时性要求。
RTOS的主要概念包括以下几个:
任务
任务(也可称为进程/线程)是独立函数,以无限循环方式运行,通常每个函数只负责一个功能。任务在自己的时间(时间隔离)和内存堆栈(空间隔离)内独立运行。通过使用硬件内存保护单元 (MPU) 可以保证任务之间的空间隔离,这种保护单元会限制可访问的内存区域,并在出现违规访问时触发故障异常。通常情况下,内部外设都是内存映射的,所以MPU也可以用来限制外设访问。
任务可以处于以下不同的状态:
- 阻塞——任务正在等待一个事件(如延迟超时、数据/资源的可用性)。
- 就绪——任务随时可以在CPU上运行,但由于CPU被另一个任务使用而没有运行。
- 运行——任务被分配到CPU上运行。
调度器
RTOS中的调度器控制哪一个任务可在CPU上运行,且有不同的调度算法。通常包括如下几种算法:
- 抢占算法——如果另一个优先级更高的任务就绪,则中断任务。
- 协作算法——只有当当前正在运行的任务主动让出时,才会进行任务切换。
抢占调度可以让优先级较高的任务中断优先级较低的任务,以满足实时约束条件,但代价是在上下文切换过程会产生更多的开销。
任务间通信 (ITC)
多任务通常需要相互分享信息或事件。最简单的共享方式是直接在RAM中读/写共享全局变量,但由于竞争条件会导致数据损坏风险,因此这种方式并不可取。比较好的方法是通过setter和getter函数来读取/写入文件范围内的静态变量,通过禁用中断或在setter / getter函数内部使用互斥 (mutex) 来防止竞争条件。更洁净的方法是使用线程安全的RTOS对象,如在任务之间传递信息的消息队列。
除了信息共享外,RTOS对象还能够同步任务执行,因为任务可以被阻塞,以等待RTOS对象的可用性。大多数RTOS都有如下对象:
- 消息队列
– 传递数据的先入先出(FIFO)队列
– 可以通过复制或引用(指针)方式来发送的数据
– 用于在任务之间或中断与任务之间发送数据
- 旗语
– 可作为参考计数器,记录特定资源的可用性
– 可以是二进制或计数旗语
– 用于保护资源的使用或同步任务执行
Mutex
– 类似于二进制旗语,一般用于保护单一资源的使用(MUTual EXclusion)
– FreeRTOS mutex带优先级继承机制,以避免优先级反转问题(高优先级任务结束,等待低优先级任务的情况)
- 信箱
– 简单的存储位置,用来共享单个变量
– 可视为单一元素队列
- 活动组
– 条件组(旗语、队列、事件标志等的可用性)
– 任务可以被阻塞,可以等待特定的组合条件得到满足
– 在Zephyr中作为轮询API,在FreeRTOS中作为QueueSets
系统节拍
RTOS需要一个时基来测量时间,通常以系统节拍计数器变量的形式在周期性的硬件定时器中断内递增。有了系统节拍,应用程序只需使用一个硬件定时器,就可以维持多个基于时间的服务(任务执行间隔、等待超时、时间分片)。但是,较高的节拍率只能提高RTOS的时基分辨率,并不能使软件运行得更快。
为什么使用 RTOS
组织
应用程序总是可以用裸机的方式来编写,但随着代码的复杂程度增加,拥有某种架构将有助于管理应用程序的不同部分,从而保持其隔离。此外,通过架构化开发方式和熟悉的设计语言,新的团队成员可以更快地理解代码并投入工作。RFCOM Technologies已经使用不同的微控制器,如Texas Instruments的Hercules、Renesas的RL78和RX,以及STMicroelectronics的STM32,在不同的RTOS上开发应用。类似的设计模式让我们可以在不同的微控制器,甚至在不同的RTOS上开发应用。
模块化
分而治之。通过在不同的任务中分离功能,我们可以很容易地增加新功能,而不会破坏其他功能;但前提是新功能不会使CPU和外设等共享资源过载。没有RTOS的开发通常会处于一个无限大的循环中,所有功能都是该循环的一部分。循环内任何一个功能的改变都会对其他功能产生影响,使得软件难以修改和维护。
通信堆栈和驱动程序
许多额外的驱动程序或堆栈,如TCP/IP、USB、BLE堆栈和图形库都是针对现有的RTOS开发或移植的。应用程序开发人员可以专注于软件的应用层,大大缩短上市时间。
小提示
静态分配
针对RTOS对象使用静态分配内存方式,意味着在编译时为每个RTOS对象在RAM中保留内存堆栈。例如,xTaskCreateStatic() 是freeRTOS中的静态分配函数。这就能保证RTOS对象的成功创建,从而避免因处理可能出现的分配失败而造成麻烦,并使应用程序更加具有确定性。
在决定任务所需的堆栈规格方面,可以用规格较大的(足够大)堆栈来运行任务,然后在运行时检查堆栈的使用情况,以确定其最高水位线。也有静态堆栈分析工具可供选择。
操作系统抽象层 (OSAL) 和有意义的抽象化
就像硬件抽象层 (HAL) 一样,使用RTOS抽象层可以让应用软件轻松迁移到其他RTOS。RTOS的功能很相似,所以创建OSAL应该不会太复杂。例如:
直接使用 freeRTOS API:
if( xSemaphoreTake( spiMutex, ( TickType_t ) 10 ) == pdTRUE ) { //dosomething }
将 RTOS API 打包添加到 OSAL 中:
if( osalSemTake( spiMutex, 10 ) == true) { //dosomething }
使用在任务间通信上的抽象层,以使代码更易于阅读并尽量减少 RTOS 对象的范围。
if( isSpiReadyWithinMs( 10 ) ) { //doSomething }
此外,如果有多个SPI模块可用,这种抽象还允许程序员改变下方所用的RTOS对象(例如,从mutex到计数旗语)。OSAL和其他抽象层通过简化单元测试中的mock函数插入,同样有助于软件测试。
节拍间隔的选择
理想情况下,因为开销较少,所以节拍率越低越好。为了选择合适的节拍率,开发人员可以将应用程序中的模块时间约束情况罗列下来(如重复间隔、超时持续时间等)。如果有一些离群模块需要小的间隔,可以考虑为离群模块设置专门的定时器中断,而非增大RTOS的节拍率。如果高频功能很短(如写入寄存器,以打开/关闭LED),就可在中断服务例程 (ISR) 内完成,否则使用延迟中断处理技术。延迟中断处理是一种将中断计算推迟到RTOS任务中的技术,ISR只通过RTOS对象产生一个事件,然后RTOS任务就会被该事件解除封锁并进行计算。
抑制低功率应用的节拍
当系统较长时间处于空闲状态时,Tickless idle(无节拍闲置)会禁用节拍中断。对于嵌入式固件来说,降低功耗的一个重要方法就是让系统尽可能长时间地处于低功耗模式。通过禁用周期性节拍中断,然后设置倒计时定时器,以便在被阻塞的任务即将执行时进行中断,从而实现无节拍闲置。如果没有任务等待超时,则可以无限期地禁用节拍中断,直到发生另一个中断(例如,按钮按下)。例如,在低功耗蓝牙 (BLE) 信标的情况下,MCU 可以在广告间隔之间进入深度睡眠。如图1所示,信标大部分时间进入深度睡眠模式,功耗仅为几十微安。
总结
RTOS提供调度器、任务和任务间通信RTOS对象等功能,以及通信堆栈和驱动程序。它可以让开发人员专注于嵌入式软件的应用层,轻松快捷地设计多任务软件。但是,就像其他工具一样,必须正确使用才能发挥出更大的价值。为了创建安全、可靠和高效的嵌入式软件,开发人员应该知道什么时候使用RTOS功能及其配置方法。
———————————————————–
如有任何问题,欢迎联系得捷电子Digi-Key客服团队。
中国(人民币)客服
- 400-920-1199
- service.sh@digikey.com
- QQ在线实时咨询 |QQ号:4009201199
中国(美金)/ 香港客服
- 400-882-4440
- 8523104-0500
- china.support@digikey.com
到微信搜寻“digikey”或“得捷电子”
关注我们官方微信
并登记成会员,
每周接收工程师秘技,
赚积分、换礼品、享福利