书名:FreeRTOS实战 用STM32+SEGGER 打造工业级实时嵌入式系统 第2版
ISBN:978-7-115-70404-7
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 [美] 吉姆·尤尔(Jim Yuill)
彭恩·林德(Penn Linder)
译 王士喜 张嘉旸
责任编辑 傅道坤
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
随着嵌入式技术飞速发展,实时操作系统已成为嵌入式产品开发的核心基础,FreeRTOS凭借轻量化、高可靠性、易用性等优势,被广泛应用于各类单片机项目中。在实际开发中,如何合理运用RTOS任务、通信组件、同步机制,结合硬件外设驱动完成系统搭建,并做好内存管控、问题调试,是嵌入式开发人员必须掌握的核心技能。
本书围绕FreeRTOS展开全面讲解,共分为5个部分。第1部分介绍RTOS、配套开发板与开发工具,讲解裸机开发中经典的超级循环结构,为后续学习筑牢基础;第2部分聚焦FreeRTOS核心能力,详解调度器、任务、同步机制与任务间通信相关API;第3部分结合硬件实战,讲解基于FreeRTOS开发UART、USB等外设驱动的方法,演示任务、中断、DMA的协同使用,并讲解松耦合架构设计;第4部分讲解FreeRTOS内存管理、多核/多处理器应用以及系统故障排查与调试技巧;第 5 部分为附录,提供开发工具速查、配套参考资料,以及FreeRTOS项目的创建与安装方法。
本书内容由浅入深、理论结合实战,既适合嵌入式系统入门者从零学习RTOS与FreeRTOS开发,也可供具备一定经验的嵌入式工程师参考,用于提升FreeRTOS项目开发、驱动设计与系统调试能力。
Jim Yuill(吉姆·尤尔),资深计算机系统工程师,拥有30余年的行业经验,涉足操作系统开发、网络安全研发、网络系统编程及高效教学等多个领域。他持有计算机科学博士学位,博士论文研究方向为网络安全,其博士论文在业内具有很高的引用量。
Penn Linder(彭恩·林德),现任IVEK公司高级电气工程师,该公司专注于工业泵与控制器的研发制造。自1995年在宾夕法尼亚州立大学修读第一门嵌入式控制器课程起,他便对嵌入式系统产生了浓厚的兴趣。此后,他始终投身于这一领域,先后在多家企业负责嵌入式系统相关的软硬件产品设计工作。他参与研发的产品涵盖照明控制设备、医疗设备及工业自动化设备等。在职业生涯早期,他主要为8位微控制器(MCU)编写裸机汇编代码;近年来,则使用C语言与FreeRTOS,为32位ARM Cortex-M系列处理器开发嵌入式软件。
John Taylor(约翰·泰勒),拥有30余年嵌入式开发经验,曾任职于英格索兰(Ingersoll Rand)、开利(Carrier)、艾伦-布拉德利(Allen-Bradley)、日立电信(Hitachi Telecom)、艾默生(Emerson)、AMD等企业及多家初创公司,历任固件工程师、技术负责人、系统工程师、软件架构师及软件开发经理等职。他曾为多款产品开发固件,包括暖通空调(HVAC)控制系统、电信同步光网络(SONET)节点设备、IoT终端、通信芯片微码及医疗设备等。此外,他是两本嵌入式开发相关图书的作者,以及 5 项美国专利的共同发明人,还拥有数学与计算机科学学士学位。
谨将本书献给我的两位挚友,他们也是我的恩师,感谢他们不可或缺的帮助、深远的智慧与榜样力量,他们是E.C. Pasour博士(北卡罗来纳州立大学,已退休)与G.I. Wilson上校(美国海军陆战队,已退休)。
——Jim Yuill
谨将本书献给所有开辟嵌入式之路的先驱:你们依靠汇编语言、翻得卷边的微控制器参考手册、用于调试的LED与闲置引脚,以及一部用于拨打技术支持热线的旋转拨号电话,在有限的内存(RAM)与只读存储器(ROM)面前寸土必争。愿你们的热爱与坚韧,成为下一代开发者的精神旗帜。
——Penn Linder
本书将为你提供在嵌入式系统(尤其是运行于微控制器[MCU]的系统)中使用实时操作系统(RTOS)所需的各类实用知识。如果你希望通过实操示例和实际硬件学习如何实现此类系统,那么本书正是为你而作!
本书聚焦FreeRTOS——一款市场领先的开源RTOS。书中通过25个示例程序,生动呈现其功能与应用。
本书将详细阐释FreeRTOS的工作原理,包括其核心功能与API。书中所有讲解均配有示例程序,并对这些示例程序给出详细说明。同时,本书还将展示如何利用FreeRTOS构建实际应用系统,例如MCU外设的驱动程序。
示例程序运行于意法半导体(ST)开发板(需单独购买,型号为STM32 NUCLEO-F767ZI)。本书将介绍一套开发工具,运行示例程序时会用到这些工具,包括IDE STM32CubeIDE,以及SEGGER公司的Ozone与SystemView调试工具。其中,SystemView凭借其与FreeRTOS高度适配的特性而极具实用价值,它可以图形化方式直观展示FreeRTOS的多任务运行状态。
你将通过本书获得FreeRTOS及相关开发工具的实操经验。借助开发板与工具,你可以运行示例程序,分析其运行行为,并通过修改代码进行实验。此外,本书还提供了在开发板上安装FreeRTOS的详细步骤,帮助你从零开发自己的程序。
对于已熟悉此类开发板和工具的工程师,可仅阅读本书内容,无须实际使用开发板与工具。本书将完整呈现示例程序代码,并以插图的形式展示工具的使用方法。
本书面向嵌入式系统工程师,同时也适用于无嵌入式系统经验,但具备扎实系统架构知识的程序员(如系统程序员)。具备C编程语言的基础知识将有助于更好地阅读本书。
本书受众范围广泛,涵盖大学高年级学生至资深工程师。本书为经验较少的读者提供了入门指导内容,经验丰富的读者可跳过这部分内容——入门指导在本书中占比相对较小。
本书共16章,分为5个部分。
第1部分:RTOS、开发板与工具简介
第1章介绍RTOS,包括其使用场景。
第2~3章介绍本书配套的开发板(需单独购买)、开发工具、示例程序,以及它们的使用方法;同时为嵌入式系统硬件与开发工具的新手提供入门指导。
第4~5章介绍超级循环(super-loop),阐释RTOS的必要性及其优势。第5章包含超级循环程序的创建教程,专为嵌入式系统新手程序员设计。
第2部分:FreeRTOS调度器、任务与核心API
第6~7章介绍FreeRTOS的核心组件——调度器与任务。
第8~9章介绍FreeRTOS用于任务同步与通信的API,例如信号量(semaphore)与消息队列(queue)。
第3部分:利用FreeRTOS构建系统
第10~12章介绍如何在实际系统中运用FreeRTOS的功能,尤其是如何创建MCU外设的驱动程序。
第13章介绍如何利用任务处理串行数据流,特别是来自MCU USB控制器的数据。
第4部分:系统设计与故障排除
第14~15章介绍FreeRTOS的内存管理,以及FreeRTOS在多处理器与多核系统中的应用。
第16章介绍基于FreeRTOS的应用程序及开发工具的故障排除技巧。
第5部分:参考资料与FreeRTOS安装说明
附录A提供开发工具的“速查手册”(cheat sheet)。
附录B列出开发工具与硬件的参考文档(如用户指南)[1]。
[1] 附录B包含大量文件名、设备名称及对应资源链接,文本条目繁多,不便于纸质排版与翻阅检索。为方便读者快速查找条目,附录B不以纸质正文呈现,改为独立电子文档,读者可前往异步社区对应页面下载使用。
附录C介绍如何在开发板上安装FreeRTOS。
对于多数读者而言,亲手实操书中的示例程序是掌握相关技能的关键,包括使用调试工具观察和分析程序的运行行为。实操需准备好开发板,安装开发工具并下载示例程序。
对于资深工程师而言,仅阅读本书内容而不运行示例程序可能是更高效的学习方式。
本书将阐释示例程序的核心部分。阅读时,建议同时打开IDE,查看完整代码清单并深入探索示例程序的细节。
要熟练掌握本书内容,推荐下面两种方法。
○ 尝试修改示例程序以进行实验。
○ 利用书中介绍的FreeRTOS功能,从零创建自己的基于FreeRTOS的程序。附录C将介绍如何为开发板创建IDE项目并安装FreeRTOS。
要运行本书示例程序,需使用STM32 NUCLEO-F767ZI开发板。未来若本书支持更多硬件型号,相关信息将在本书的GitHub代码库中更新,地址如下:
https://github.com/PacktPublishing/Hands-On-RTOS-with-Microcontrollers-Second-Edition
开发工具的相关介绍及使用方法将在第 3 章中详细说明。本书编写过程中使用的工具均为Windows 11版本,这些工具也提供macOS和Linux版本。尽管我们未对这两个版本进行全面测试,但预计它们可正常配合本书使用。
示例程序可从本书的GitHub代码库下载,相关说明同样见第3章。
本书提供如下资源:
• 本书附录B;
• 本书配套源代码及彩图文件;
• 本书思维导图;
• 异步社区7天VIP会员。
要获得以上资源,您可以扫描下方二维码,根据指引领取。

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区(https://www.epubit.com/),按书名搜索,进入本书页面,单击“发表勘误”,输入勘误信息,单击“提交勘误”按钮即可(见下图)。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

我们的联系邮箱是fudaokun@ptpress.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们。
如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出版图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”(www.epubit.com)是由人民邮电出版社创办的 IT 专业图书社区,于 2015年8月上线运营,致力于优质内容的出版和分享,为读者提供高品质的学习内容,为作译者提供专业的出版服务,实现作者与读者在线交流互动,以及传统出版与数字出版的融合发展。
“异步图书”是异步社区策划出版的精品 IT 图书的品牌,依托于人民邮电出版社在计算机图书领域40余年的发展与积淀。异步图书面向IT行业以及各行业使用IT技术的用户。
第1部分 RTOS、开发板与工具简介实时系统的实现方式与应用场景多种多样。本书聚焦于如何使用实时操作系统(RTOS)在微控制器(MCU)上创建实时应用程序。
本章首先概述RTOS的定义,让读者了解具有实时需求的各类系统范围。在此基础上,我们将探讨实现实时性能的多种不同方式,同时概述可能用到的系统类型(如硬件、固件和软件)。最后,我们将讨论如何选择RTOS,以及在MCU应用中何时建议使用RTOS、何时完全无须使用RTOS。
对于不熟悉MCU的读者,可在第2章中查阅相关入门指导内容。
简而言之,本章涵盖以下主题:
○ 究竟什么是“实时”;
○ 硬件与软件在实时系统中的作用;
○ RTOS的定义;
○ 实际应用中的实时系统;
○ 选择RTOS。
实时系统是具有时序要求的系统。当实时系统未能满足时序要求时,即被视为失效。时序失效的定义方式(以及系统失效带来的后果)可能存在很大差异。需要特别注意的是,实时要求的差异可能极为显著,既体现在时序要求的速度上,也体现在未满足实时截止时间时后果的严重程度上。
每个实时系统对事件和/或信号的响应能力都存在限制。系统的这种响应能力被称为系统的带宽。例如,在设计信号采集链路时,每一级滤波器所对应的系统带宽必须足够宽,使目标信号能够通过,从而保障信号的完整性。如果滤波器对应的系统带宽设计不当,系统将无法对部分输入信号提供预期的实时响应。
系统的硬件和软件都具有有限的带宽。所有软件都运行在资源有限的硬件上。例如,MCU的时钟频率限制了它在给定时间内可执行的指令数量。指令执行的速度还受MCU架构的限制,该架构决定了执行每条指令所需的时钟周期数。软件实时响应事件的能力会受到所使用的算法、所采用的操作系统(OS)(如有)以及对操作系统功能(如多任务和时序功能)的有效利用情况的影响。
硬件和软件的局限性使得实时系统的设计既具有挑战性,又充满乐趣。具有挑战性,是因为设计者通常必须深入研究MCU架构的细节,包括硬件外设寄存器和汇编代码指令;充满乐趣,则源于理解硬件的工作复杂原理所带来的满足感,以及能够编写利用这些原理的软件所获得的成就感。
平衡这些局限性,并理解硬件与软件之间的权衡取舍,最终实现一个可正常工作的实时系统,这既是一门科学,也是一门艺术。
为了说明可能遇到的时序要求范围,我们来看几个从模数转换器(ADC)获取读数的不同系统。
首先来看一个低带宽控制系统。该控制系统是电烙铁的一部分,用于维持烙铁头的温度(见图1.1)。我们关注的系统组件包括MCU、ADC、传感器和加热器。
MCU是控制系统的核心,负责以下工作:
○ 通过ADC从温度传感器获取读数;
○ 运行闭环控制算法(以维持烙铁头的恒定温度);
○ 根据需要调节加热器的输出。
相关组件如图1.1所示。

图1.1 MCU控制的电烙铁
典型电烙铁的带宽需求相当低。例如,由于烙铁头的温度变化不会很快,MCU可能每秒仅需采集50个ADC样本(50Hz)。负责调节加热器(以维持恒定温度)的控制算法运行速度可能更低,或许每秒仅5次(5Hz)。
在此,我们快速了解一下ADC的时序要求。通过这种方式,我们能直观感受设计实时系统时需要应对的部分时间约束。图1.2所示为MCU与ADC之间的一种常见接口。

图1.2 带外部ADC的MCU
ADC会发出一个信号,告知MCU其已完成模拟信号的转换,且数字量已准备就绪,可供MCU读取。MCU必须在下次转换开始前,通过通信通道读取转换后的数字量,否则该结果将被覆盖。在需要进行新的读数前,MCU最多有20ms的时间将数据从ADC传输至内部存储器,如图1.3所示。同时,MCU还需以5Hz(200ms)的频率运行控制算法,计算加热器输出的更新值。这两种情况(尽管速度并不快)都属于实时要求的示例。

图1.3 50Hz自由运行的ADC
我们已经了解了低带宽控制系统,接下来看看对带宽要求高得多的其他控制系统。例如,网络分析仪或示波器可能需要以数十GHz的速率读取ADC的数据!原始的ADC读数通常会被转换到频域,并每秒数10次以图形化方式显示在高分辨率面板上。为了实现正常工作,这类系统需要巨大的数据处理带宽,且必须遵守极为严格的时序要求。
在可能的带宽要求范围中间,你会发现诸如闭环运动控制器之类的系统。为了在快速运动的系统中提供稳定性,这类控制器通常需要以数百Hz至数十kHz的频率执行其比例-积分-微分(PID)控制回路。那么,“实时”到底有多快?显然,仅从上述ADC示例就能看出,这取决于具体的需求。
在前面提到的部分场景中(如示波器或电烙铁),未满足时序要求会导致性能下降或报告错误数据。对于电烙铁而言,这可能表现为温度控制不佳(进而可能损坏元器件);对于示波器来说,错过截止时间会导致读数错误,这属于系统失效。过多的不准确读数会使设备被视为不可靠,进而导致销量下滑——这一切都源于实时要求未能得到持续满足。
在其他系统中,例如无人驾驶飞行器(UAV)的飞行控制或工业过程控制中的运动控制,未能及时运行控制算法可能会导致更严重的物理事故,如坠毁。在这种情况下,后果可能危及生命。
幸运的是,我们可以采取一些措施来避免所有这些失效场景。
确保系统实现预期功能的最简单的方法之一,是在满足要求的前提下尽可能简化系统(有人将此称为KISS[Keep It Simple,Stupid!]原则)。这意味着要克制住将简单任务复杂化的冲动。如果一台烤面包机的用途只是烤一片面包,就不必为其添加显示屏并让它播报天气;只需让它在正确的时间内启动加热元器件即可。多年来,这项简单任务的实现完全不需要任何代码或可编程设备。
作为开发人员,当我们遇到问题时,往往会立刻想到使用手边的MCU并开始编码。然而,产品的某些功能完全不需要代码即可妥善处理(如果产品包含机电组件,这一点尤为重要)。车窗的控制其实并不需要MCU运行轮询循环,通过驱动器启动电机并通过传感器反馈来关闭电机;实际上,仅需几个机械开关和二极管就能完成这项任务。如果某个系统确实需要反馈报告机制(例如车窗卡住时需要发出错误信号),那么可能别无选择,只能采用更复杂的解决方案。但作为工程师,我们的目标应始终如一——以尽可能简单的方式解决问题,避免添加额外的复杂性。
如果一个问题仅通过硬件就能解决,那么在启用MCU之前,应首先与团队探讨这种可能性;如果通过简单的while循环对传感器的状态进行轮询就能处理问题,那就直接轮询传感器的状态——可能无须编写中断服务程序(ISR)(本书后续章节将详细讨论)。如果设备的功能单一,在很多情况下功能完备的RTOS反而会成为阻碍,因此这类情况下无须搭载RTOS。
在依托软硬件实现系统实时响应特性的过程中,存在诸多取舍权衡。下文将介绍各类实时系统中软硬件常见的部署形式。同时需注意,下述各类系统可相互组合,以子系统形式协同运行。这类异构子系统可部署在产品级、板卡级乃至芯片级(该实现方式详见第15章)。
最初的实时系统是纯硬件的,当存在极严格的容差要求和/或快速的时序要求时,它通常是最佳解决方案。该类系统可通过分立数字逻辑、模拟元器件、可编程逻辑或专用集成电路(ASIC)实现。硬件的其他选择包括可编程逻辑器件(PLD)、复杂可编程逻辑器件(CPLD)和现场可编程门阵列(FPGA)。基于硬件的实时系统应用范围广泛,从模拟滤波器、闭环控制、简单状态机到复杂的视频编解码器均可覆盖。如果在设计时考虑功耗优化,ASIC的功耗可比基于MCU的解决方案更低。总体而言,硬件的优势在于能够并行且即时地执行操作(当然,这是一种简化的说法),而单核MCU仅能提供并行处理的假象。
实时硬件开发通常具有如下缺点:
○ 非可编程设备的灵活性不足;
○ 所需的专业知识通常不如软件/固件开发人员普及;
○ 功能完备的可编程设备(例如大型FPGA)成本较高;
○ 定制ASIC的开发成本高昂。
就本书而言,裸机固件(bare-metal firmware)泛指一切不搭载现成实时操作系统(RTOS)的固件。设计者需要自行开发原本由RTOS提供的各类功能固件。部分工程师的观点更为严苛,认为纯正的裸机固件不应调用任何现成的函数库(如厂商提供的硬件抽象库),该观点同样具备一定道理。采用裸机方案的优势在于,用户代码能够全权掌控硬件所有资源。主循环程序的执行仅会被外部中断打断,随即交由中断服务程序(ISR)处理。在这种情况下,想要切换CPU控制权,仅有两种途径:一是当前中断服务程序执行结束,二是触发更高优先级的中断。
当需要执行的任务数量较少且相对简单,或者仅需执行一个整体任务时,裸机固件实现的优势尤为突出。如果固件聚焦核心功能且遵循最佳实践,鉴于ISR之间的交互相对较少(在某些情况下甚至没有ISR),确定性性能一般较容易测量和保证。
面对负载较重的MCU,或ROM、RAM资源极度受限的MCU,裸机开发是唯一可行的方案。在裸机开发的初期,MCU的资源极为有限,因此采用裸机编程实属无奈之举。然而,随着半导体行业日趋成熟,小型MCU的存储容量不断提升。如今市面上已有大量存储资源充足且成本低廉的设备可供选择,这使得我们无须局限于裸机编程。通常情况下,使用RTOS会更为合理,接下来我们将对此进行探讨。
当裸机实现在异步处理事件时变得愈发繁杂,其功能便开始与RTOS提供的功能有所重叠。需要明确的一点是,使用RTOS而非尝试自行构建线程安全系统,可直接受益于RTOS提供商完成的所有测试工作。
你还将有机会用上凝聚过往实战经验的成熟代码——如今市面上的许多RTOS均已发展多年。在此期间,研发团队持续优化适配、扩充功能,让这类系统稳定可靠,可灵活适配各类应用场景。使用这类成熟的RTOS,可为你的项目开发提供高质量的起点。
基于RTOS的固件与裸机固件的区别在于,它运行在MCU的调度内核之上。对基于RTOS的固件而言,“固件”这一术语特指一类功能精简的RTOS——这类系统通常仅占用少量的闪存空间(如64KB、256KB等),并提供调度内核供任务使用。它与裸机固件的相似之处在于,开发人员通常必须非常熟悉MCU提供的硬件外设和寄存器。而RTOS能够屏蔽部分底层细节,同时提供可供应用固件调用的时序调度机制。
借助RTOS,调度器和各类RTOS原语能让任务产生独占处理器的错觉(第6章将详细讨论)。使用 RTOS 可使系统在后台执行其他复杂任务的同时,仍能对最重要的事件做出及时响应。
不过,多个任务在处理器中并发运行也存在一些弊端。任务之间进行数据共享时,必须合理运用同步机制,以避免死锁、优先级反转等问题。本书后续章节将讲解各类同步方法,只是这会增加代码的复杂性。中断处理通常会使用任务信号机制,以尽可能快地处理中断,并将尽可能多的处理工作推迟到任务中执行。如果处理得当,即便系统中存在诸多复杂交互,该解决方案也能出色地维持复杂系统的响应能力。
基于RTOS的软件与基于RTOS的固件的区别在于,此类软件运行在功能更完备的RTOS之上,这类RTOS拥有与通用操作系统相近的各类功能,涵盖硬件驱动程序、引导加载程序、应用程序、文件系统等。事实上,部分通用OS也推出了实时衍生版本,如RTLinux。
这类RTOS通常需要更大容量的内存(例如1MB及以上)以及具备特定功能的处理器。例如,RTOS 可能会利用处理器的内存管理单元(MMU),还能提供应用程序加载、标准化的驱动框架等功能。基于RTOS开发的应用软件的复杂度往往较高,需要各类内部系统与外部系统之间进行大量交互。使用这类RTOS的优势在于可直接享用其软硬件一体的全套完备功能。
在硬件方面,通常会使用性能更强的处理器,为应用程序提供更强的事件处理能力(带宽)。例如,设备可搭载多核处理器,且内核运行主频更高,配备吉字节(GB)级别的内存和持久性存储器。如需外接外设硬件,只要已有配套驱动,直接加装扩展卡即可实现。
在软件方面,有着种类繁多的开源方案与厂商自研的专属方案,可用于网络协议栈、用户界面(UI)开发、文件处理等各类场景。借助这些成熟方案,开发最终产品的某些功能所需的时间将大幅缩短。即使具备丰富的功能与各类可选的组件,内核的实现方式仍能确保关键任务不会被无限期阻塞,而这种情况在通用OS中时有发生。因此,与基于RTOS的固件一样,此类系统仍可实现确定性性能。
与基于RTOS的软件类似,标准OS具备开发者所需的各类函数库与完备功能,但并未严格侧重满足时序需求。一般而言,基于传统OS实现的系统,其确定性行为会差得多(在安全关键型场景中更是完全无法依赖)。例如,你想必多次遇到过计算机程序弹出“未响应”的情况。试想如果这台计算机同时还在控制烤面包机的计时器,最后很容易把面包烤焦。
如果实时性要求较为宽松,且未能按时达成期限也不会造成灾难性后果,那么只要谨慎选择运行的软件栈并控制其资源使用,标准OS也能满足使用需求。带有PREEMPT_RT补丁的Linux内核就是这类RTOS的典型示例。
至此,我们已阐述了多种利用硬件和软件实现实时系统的方案,接下来需要明确界定RTOS的具体含义,尤其是基于MCU的RTOS。
RTOS是一种经过精心设计的OS,旨在为开发人员提供实时机制。通用OS(如Windows、Linux和macOS)的创建目的是提供一致的编程环境,屏蔽底层硬件细节,简化程序编写与维护工作。它们为应用开发人员提供了多种不同的原语,例如线程(thread)和互斥锁(mutex)。
线程是拥有自己的栈和运行上下文的软件片段。线程与任务类似,均能独立运行。互斥锁是OS提供的原语,为线程提供了同步其处理过程的方式。
应用程序软件可利用线程(或任务)和互斥锁实现更复杂的行为。例如,可以创建一个多线程程序,实现对共享数据的受保护访问,如图1.4所示。

图1.4 多线程共享内存
上述应用程序并未实现线程和互斥锁原语,仅对其进行调用。线程和互斥锁的实际实现由OS负责。这具有以下几个优势:
○ 应用程序的代码更简洁;
○ 易于理解——无论开发人员是谁,都使用相同的原语,因此更容易理解不同人员编写的代码;
○ 硬件的可移植性更好——采取适当的预防措施后,代码可在OS支持的任何硬件上运行,无须修改。
在上述示例中,互斥锁用于确保在任何给定时间,只有一个线程能访问共享数据。对于通用OS而言,每个线程会无限期地等待互斥锁可用,然后再继续访问共享数据。这正是RTOS与通用OS的不同之处。在RTOS中,所有阻塞型系统调用都是有时间限制的,它不会无限期等待互斥锁,而是允许指定最大延迟时间。例如,在通用OS中,如果线程1尝试获取互斥锁,即便100ms甚至100s后仍未获取到,它也会继续等待互斥锁可用——哪怕永远等待下去。
在RTOS的实现中,等待互斥锁可用的最长时间由开发人员指定。开发人员可以设定线程1必须在100ms内获取互斥锁。如果101ms后线程1仍未获取到互斥锁,它将收到“未在指定时间内获取互斥锁”的通知。这被称为超时(timeout),其设置目的是帮助构建确定性系统。
能够在指定的时序要求内执行特定代码段的OS,可被视为RTOS。这一定义涵盖了相当多的系统。
使用RTOS的应用程序通常有两个显著特征:错过实时截止期限的可接受频次,以及错过该期限所造成后果的严重程度。使用RTOS的各类应用程序通常分为三类——硬实时系统(hard real-time system)、固实时系统(firm real-time system)和软实时系统(soft real-time system)。
| 注意:不必过分纠结于硬实时系统、固实时系统与软实时系统之间的差异。这些术语的定义在行业内尚未达成共识。真正重要的是,你要明确自己系统的需求,并设计出能够满足这些需求的解决方案! |
硬实时系统必须100%满足所有截止期限要求。若系统未能达到任一截止期限,即被视为失效。这并不是说硬实时系统失效就一定会对人造成伤害,仅表示系统只要错过一次截止期限,就属于失效。
如果失效会导致人员伤亡或重大财产损失,则这类失效通常归为安全关键失效。此外,也存在与安全无关的硬实时系统。
硬实时系统的示例可见于医疗设备,例如心脏起搏器以及参数控制极为严格的控制系统。以心脏起搏器为例,若其未能在正确的时刻发出电脉冲(错过截止期限),可能会导致患者死亡(这也是心脏起搏器被定义为安全关键型系统的原因)。
相比之下,若计算机数控(CNC)铣床的运动控制系统未能及时响应命令,可能会导致刀具切入加工件的错误位置,进而损坏工件。在这两个案例中,一个失效导致了人员死亡,另一个则使金属材料沦为废料——但两者都是因单次错过截止期限引发的系统失效。
与硬实时系统不同,固实时系统需要几乎所有截止期限都能达标。如果视频和音频暂时失去同步,通常不会被视为系统失效,但可能会让视频观看者感到不满。
在大多数控制系统中(类似前文提到的电烙铁示例),若少数几次采样的读取时间略超出指定范围,系统通常不会损坏。如果某个控制系统的ADC自动采集新样本,而MCU未能及时读取,该样本将被新的样本覆盖。这种情况偶尔发生并无大碍,但如果过于频繁,将破坏温度稳定性。然而,其他系统则无法容忍样本丢失。例如,电机控制回路即使仅丢失少数几次样本,也可能导致电机失控。这凸显了开发人员必须理解系统的实时要求,并据此进行设计的重要性。
在必须满足截止期限的频次要求上,软实时系统最为宽松。这类系统往往仅做出尽力而为的时限保障。即便未能按时完成任务,软实时系统也不认为是失效,只是容易让用户体验不佳。
汽车的定速巡航系统就是典型的软实时系统,它不存在严格的规格要求或明确的预期。驾驶员通常不会要求车速在N秒内收敛到设定速度的±X英里/小时(或公里/小时)范围内。他们仅期望在合理情况下(如无陡坡),控制系统大多数情况下能最终将车速维持在接近预期的水平。但当遇到陡坡需要踩油门才能爬坡时,他们可能仍会感到不满!
接下来,我们将介绍目前市面上的各类RTOS,以及本书使用的RTOS。
在实际场景中,实时系统的需求往往是硬实时、固实时和软实时的结合体。例如,前文提到的电烙铁示例中,热失控安全监控可能属于硬实时要求,温度测量与控制回路属于固实时要求,而用户显示屏更新则属于软实时要求。使用RTOS可更轻松地实现这类混合实时要求,但在系统设计时必须密切关注这些约束条件。
市场上存在多种RTOS,它们在功能、适配的处理器架构和适用规模上差异显著。面向8~32位MCU的小型RTOS包括FreeRTOS、Keil RTX、Micrium µC/OS、ThreadX等众多产品。这类RTOS适用于微控制器,其最基础的功能是提供一个紧凑的实时内核。当从MCU扩展到32位和64位应用处理器时,可用的RTOS包括风河VxWorks(Wind River VxWorks)、风河Linux(Wind River Linux)、绿山完整性操作系统(Green Hills’ Integrity OS),甚至是带有PREEMPT_RT内核扩展的Linux。这些功能完备的操作系统提供了丰富的软件选择,既能满足实时调度需求,也能处理通用计算任务。即便我们刚刚列举的这些操作系统,也只是冰山一角。各类规模的RTOS都有免费和付费解决方案(部分付费产品价格远超10000美元)。
那么,既然有免费的解决方案,为何还要选择付费产品呢?免费RTOS与付费解决方案的主要区别在于安全认证、中间件和客户支持。由于RTOS能提供高度确定性的执行环境,因此常被用于复杂的安全关键型应用。这类系统需要确定性操作,因为它们必须始终表现出可预测的行为。确保代码在固定时间内响应事件,是保证系统行为一致性的关键一步。大多数安全关键型应用受到监管,并有各自的管理机构和标准,例如适用于航空领域的DO-178B和DO-178C标准,以及适用于工业应用的IEC 61508 SIL 3和ISO 26262 ASILD标准。为了降低安全关键型认证的成本,设计者通常采用以下两种方法之一。
○ 将系统代码设计得极其简单,不使用RTOS,这样可以通过数学方法证明系统将持续稳定运行,不会出现故障。
○ 选用已通过认证流程的商业RTOS作为起点。例如,威腾斯坦安全实时操作系统(WITTENSTEIN SafeRTOS)是FreeRTOS的衍生版本,已获得工业、医疗和汽车领域的应用认证。
中间件在复杂系统中也可能是极为重要的组成部分。中间件是运行在用户代码(应用开发人员编写的代码)与底层(如RTOS或裸机固件)之间的代码。付费解决方案的一个核心价值在于,其生态系统提供了一套预集成的高质量中间件(如文件系统、网络协议栈、GUI框架、工业协议等),可最大限度地减少开发工作量,降低项目整体风险。
选择使用中间件而非自行开发,是为了减少内部开发团队编写的原始代码量。这既降低了风险,也减少了团队的总工作时间。因此,根据项目复杂度、进度要求等因素,这可能是一项值得的投资。
付费解决方案通常还会提供来自固件供应商的不同级别的客户支持。雇用和留住工程师的成本很高,管理者最不愿看到的就是一屋子工程师都在为工具难题发愁,而非解决需要处理的核心问题。能够通过邮件或电话快速获取专家支持,能显著提高团队效率,缩短项目周期,让工作环境更和谐。
| 注意:FreeRTOS既提供付费支持与培训服务,也有可集成的付费中间件解决方案。此外,市面上也存在开源及其他免费的中间件组件,本书后续章节将对其中部分组件进行讨论。 |
面对众多的可选方案,你可能会疑惑:为何本书仅围绕一款MCU型号和一种RTOS展开?原因有多个,其中一个是我们将要介绍的大部分概念几乎适用于所有现存的RTOS,这就如同良好的编码习惯不受编程语言限制一样。通过聚焦于一款MCU对应的一种RTOS实现,我们能够更深入地探讨相关主题,而这在试图涵盖所有替代方案的情况下是难以实现的。
FreeRTOS是MCU领域最受欢迎的RTOS实现之一,应用范围极为广泛。它已问世超过15年,已被移植到数十种平台上。如果你曾与熟悉RTOS编程的资深底层嵌入式系统工程师交流过,你肯定听说过FreeRTOS。通过专注学习FreeRTOS,你将具备扎实的基础,以便在需要时快速将FreeRTOS相关知识迁移到其他硬件平台,或切换到其他RTOS。
我们选择FreeRTOS的另一个原因是它完全免费!FreeRTOS基于MIT许可证分发。
图1.5展示了FreeRTOS在典型ARM固件栈中的位置。栈(stack)指构成系统的所有不同层级的固件组件,以及它们相互叠加的结构关系。此处的“用户”指的是使用FreeRTOS的开发人员(而非嵌入式系统的最终用户)。

图1.5 典型的ARM FreeRTOS固件栈
以下是一些值得注意的要点:
○ 无论底层硬件移植如何实现,用户代码都能访问相同的FreeRTOS API;
○ FreeRTOS不会阻止用户代码使用厂商提供的驱动程序、CMSIS(Cortex微控制器软件接口标准)或原始硬件寄存器。
硬件层面统一的标准化API意味着,代码无须频繁重写即可轻松在不同硬件目标平台间迁移。代码能够直接控制硬件的特性,也使得在必要时编写极高效率的代码成为可能(但需以可移植性为代价)。
在知道RTOS是什么之后,接下来让我们深入探讨RTOS的使用时机,以及在适当的情况下如何确定所需的RTOS类型。
有时,当人们首次接触“实时操作系统”这一术语时,会错误地认为RTOS是在嵌入式系统中实现实时行为的唯一方式。虽然这种想法完全可以理解(尤其是考虑到其名称),但与事实相去甚远。有时,我们应将RTOS视为一种潜在的解决方案,而非适用于所有情况的万能方案。一般而言,基于MCU的RTOS要成为特定问题的理想解决方案,其复杂度需达到始终均衡——既不过于简单,也不过于复杂。
如果面临的问题极为简单(例如监控两种状态,当两种状态同时存在时触发警报),解决方案可以是直接的硬件方案(如“与”门)。在这种情况下,没有必要进一步将问题复杂化,因为“与”门方案速度极快、确定性高且可靠性极强,同时开发耗时也非常短。
再考虑另一种情况:只需执行一两个任务(例如控制电机速度,并通过编码器确保电机行驶正确的距离)。这当然可以通过分立的模拟和数字硬件实现,但如果需要可配置的行驶距离,就会增加一定的复杂度。此外,调整控制回路的系数可能需要调节电位器设置(甚至可能需要为每块电路板单独调节),按照当今的制造标准,这在部分或大多数情况下是不可接受的。因此,在硬件解决方案方面,我们可以选择使用复杂可编程逻辑器件(CPLD)或现场可编程门阵列(FPGA)来实现运动控制算法并跟踪行驶距离。这两种器件在此类场景下都非常适用,因为该算法规模可能足够小,可适配CPLD;但在某些情况下,FPGA的成本可能过高。MCU也经常用于处理此类问题。如果内部团队缺乏硬件语言或工具链相关专业知识,那么裸机MCU固件解决方案可能是一个不错的选择。
假设问题更为复杂,例如某设备需要控制多个不同的执行器(如电机或电磁阀),读取多种传感器的数据并将这些值存储在本地存储器中,或许该设备还需要接入某种网络,如以太网、Wi-Fi、控制器局域网(CAN)等。RTOS能够很好地解决这类问题。由于存在多个或多或少需要异步完成的任务,RTOS带来的额外复杂度无疑是值得的。
部分RTOS任务的优先级较低但复杂度较高(如网络处理和文件系统处理),其他任务则具有时间关键性(如控制执行器和读取传感器数据)。RTOS可帮助我们确保低优先级、高复杂度的任务不会干扰时间关键性任务。此外,在许多情况下,某些控制系统通常需要按明确的时间间隔运行——这正是RTOS的优势所在。
再考虑一个与上述类似但更复杂的系统:该系统有多项网络需求,例如提供Web服务、在复杂的企业环境中处理用户身份验证、将文件推送至需要不同网络文件协议的多个共享目录等。这种复杂度水平虽然可以通过RTOS实现,但同样需根据团队可用资源来判断——由于所需的许多复杂软件栈已经存在,将其交给功能完备的操作系统(无论是RTOS还是通用OS)处理可能更为合适。有时,也可能采用多核方案:其中一个内核运行实时RTOS,另一个内核运行通用OS。
至此,显然不存在一种适用于所有情况的绝对正确的实时解决方案。每个项目和团队都有其独特的需求、背景、技能组合和应用场景,这些因素共同决定了最终的选择。选择问题的解决方案需要考虑诸多因素,重要的是保持开放的心态,选择在当时最适合自己团队和项目的方案。
本章详细探讨了实时系统相关的内容。我们定义了实时系统,并给出了现实世界中实时系统的一些示例。至此,你应该理解实时系统具有不同的时序要求,以及未能满足这些时序要求时会带来不同的后果。
随后,我们介绍了利用硬件和软件实现实时系统的多种方式。我们发现,部分实时系统仅通过硬件即可满足时序要求,部分实时系统仅在通用OS上运行软件就能满足时序要求。而介于这两种极端情况之间的,正是本书的核心焦点:依赖RTOS并运行在MCU上的应用程序。
在深入理解实时系统之后,我们定义了RTOS,并阐述了它与通用OS的区别。我们还探讨了硬实时系统、固实时系统和软实时系统之间的差异。需要记住的是,实时系统的时序要求决定了要实现预期性能所需的RTOS类型。
我们讨论了现有RTOS的类型,以及本书选择FreeRTOS的原因。FreeRTOS成熟稳定,在工业界应用广泛,而且完全免费!此外,学习FreeRTOS也是全面理解RTOS的一种有效途径。
最后,我们探讨了使用RTOS的时机,以及在适合的情况下如何确定所需的RTOS类型。
后续章节将介绍FreeRTOS的工作原理及使用方法,并通过运行在意法半导体(ST)开发板上的示例程序进行说明。接下来的两章将介绍开发板,以及我们将用于运行示例程序的开发工具(如IDE)。