活动总览:点此查看(含活动鼓励和活动学习总内容)
本站打卡开始和截止时间:8月12日-8月14日(3天)
打卡任务:
1、阅读cruelfox干货笔记第一篇:FreeRTOS学习笔记 (1)应用场景
2、跟本帖回复思考题: 请构想一个适合单片机实现的具体的应用,描述其中可以成为“任务”的软件组成部分,并分析如果在此应用的软件中引入实时操作系统,能够带来什么样的好处。
FreeRTOS学习笔记 (1)应用场景(作者:cruelfox)
以下内容均为复制粘贴cruelfox分享“FreeRTOS学习笔记 (1)应用场景”,为了方便大家,所以就直接复制过来了,虽然是第一篇,但是全文无水,需要大家认真阅读。
NXP KW41大赛的时候被迫啃了一口FreeRTOS,我后来打算系统地学习一下它,再尝试应用到自己的DIY项目当中去。FreeRTOS只是众多的 RTOS (字面意思是实时操作系统)中的一种,因为用得广泛有是开源的,作为学习是个不错的选择。我大概地读过了它的文档,现在回头开始梳理,研究下部分的实现细节,一边写这个连载。
单片机也要用操作系统?如果以日常用的Windows, Linux, BSD这些,甚至是DOS,来代表操作系统的话,在单片机上运行操作系统时候个很荒诞的想法——因为对大多数单片机,RAM实在太少了。而 FreeRTOS 并不是要提供一个在单片机上运行软件的平台,把软件一个个安装到它上面,供用户选择运行什么,它没有用户界面;它不是一个管家,也不带任何硬件驱动程序,也没有文件系统服务提供。FreeRTOS只是一个操作系统内核,它首先提供了操作系统最重要的特性:任务调度。
也就是说,有了FreeRTOS,在单片机上实现多任务会容易一些。这里至少有两层含义,一是多任务是否一定要用RTOS才可以实现? 当然不是。对于单片机开发来讲,所有系统资源都是你的,在不同中断服务里面处理不同的任务并不是很困难的事情。第二,是否没有多任务就完全用不着RTOS? 这也得看具体情况,如何界定“任务”的概念了,一件复杂的事情在程序中也有可能划分为几个任务来处理。
还是举几个例子吧。
1. SD卡MP3音乐播放机
暂且不管用户操作界面,在播放状态下,从数据流上看播放机是这样工作的(作了一定简化):
I2S接口DMA缓冲区空闲
用剩余PCM数据填充缓冲区,请求解码下一块MP3数据
请求读取MP3文件下一段内容
定位SD卡上要读取扇区位置
SDIO控制器读命令发送
SDIO控制器接收数据完成中断
填充文件数据缓冲区
解码MP3数据,写PCM数据缓冲区
填满I2S接口的DMA缓冲区
这个过程涉及到四个关键的软件部分:
若按自顶向下的软件设计思路,I2S设备驱动以固定的节拍被唤醒,进行缓冲区PCM数据填充,因此需要定期去调用MP3解码程序。MP3解码器则根据前面一段解码操作的结果来决定是否要访问文件系统(因为MP3解码一块数据产生的PCM音频数据的量并不能刚好是I2S设备驱动请求的大小),以及需要读取多少字节的MP3文件内容,还有解码出来暂时用不到的数据也要保存起来下次用。到了文件系统这里,请求读文件的位置和长度也未必是正好SD卡上的一个扇区,所以也有缓存,而且还需要跟踪文件在SD卡上的索引。SDIO设备驱动则按照文件系统的请求读取SD卡,等待操作完成以后返回,注意它和前面的模块不同的是,有一段什么事情也不干的硬件IO等待时间。
一层层的嵌套调用关系如这样:
注意,每一次的函数调用,都代表一个完整的操作:填充一块缓冲区、解码一段MP3数据、读取一段文件,以及读取SD卡一块数据。下层的子程序被调用,完成后返回上一层的程序中继续执行。在没有异常(中断)发生的情况下,程序是不会离开这个调用关系嵌套的。
又需要注意到,上面几个列举的主要函数,虽然一次调用操作是完整的,但每次操作过后它的内部状态不一定相同。用C语言来说,就是这些函数需要有static型的局部变量,或者是自己用一些全局变量来记住上次调用后状态是怎样了。典型的就是文件系统 read_file() 这样的函数被调用后,它需要记住文件指针的位置,以便下次接着读。
如上描述实现的音乐播放器实现有一个重要的缺点:在SD卡读操作的等待时间里,CPU的执行只能停留在SD卡访问函数中,不能用来进行MP3解码的运算操作,处理能力被浪费了。在I2S设备的缓冲区填满,到下一次需要再填充这段时间 ,CPU也是处于空闲状态。其实完全可以利用这两段空闲时间,做其它的事情,比如预先解码MP3一部分数据。那么问题是,如何在一个函数执行过程中,跳出去执行其它的函数,然后再跳回来?用中断,对,但是把什么放在中断里面呢?
2. USB mass storage设备
就是利用单片机的USB片上设备,模拟一个U盘。做这样的设备,主要内容就是响应来自USB主机的各种请求。当USB主机向设备发出请求的时候,USB硬件会产生一个IRQ,随后USB的中断服务函数(IRQ handler)被执行。通常,USB驱动程序库会提供一些回调(callback)函数接口,就是由用户写一些函数,供USB IRQ handler在需要的时候调用。
作为USB mass storage设备,需要提供的回调函数必须要有读存储设备、写存储设备等,以产生实际的磁盘数据给USB,以及接收USB要求写的磁盘数据。这些函数什么时候被调用到,是应用程序无法预见的,因为它在USB中断发生之后的响应过程中。这样一来,回调函数处理U盘模拟的事务,主程序里面不用管,可以处理无关的别的工作,也就实现了多任务么!
然而,在中断环境下执行用户代码,好像不是太好的选择,因为这时候优先级同级和更低的中断就不能响应了。在实际的USB mass storage设备里面,U盘的数据要从片外存储设备中读取,比如NAND Flash,情况就会糟糕——读取数据需要等待,而这个等待完全发生在USB IRQ handler里面,USB主机的请求暂时也没法再响应了。如果主程序中的任务也需要硬件I/O中断,可能就会受到影响。
那么,作为只模拟一个U盘,不进行其它任务的应用来说,这样似乎也无关紧要:反正都是等。我亲自做过的USB全速(12Mbps)下U盘设备的表现,传输速度最多到500多kB,比12Mbps差了不少,为什么呢?
看上面这个图,USB IRQ中断响应期间,是没有数据传输的,所以12Mbps的有效带宽被浪费了一部分,U盘的吞吐率自然就达不到理论值了。回调函数在I/O等待的时候,USB主机也在等待USB设备的应答。而U盘数据准备好之后,USB硬件发送数据,CPU又无事可干直到下一次请求到来。比较合理的设计是利用USB TX的时间进行实际存储设备的I/O预读取,也就是猜测USB会继续请求读上次读了的后面的数据,那就先把数据读到内存中来,一旦猜对了,就可以缩短下次请求的响应时间,模拟U盘的读取吞吐率就提高了。要这么实现,就不能单纯地在回调函数中处理I/O任务。
3. 数据记录装置的存储管理
某个应用需要对远程设备(通过UART连接)进行 ReadChipID, ReadStatus, ReadData, EraseData, WriteData 这几项操作,以实现数据管理。这几个操作在具体的实现上,都是通过UART发送约定好格式的命令数据,然后通过解析UART收到的返回数据内容来判断操作是否成功,并接收有效的数据。
倘若把注意力放在UART发送命令这里,程序的结构可能是这样的:
这样的设计假定了远程设备会如期地发送出来应答,对UART交互过程却几乎没有容错能力。一旦交互过程中出现了意外,就无法从错误状态恢复,只能从头再来。
一旦考虑到UART通信可能出现的异常,程序的编写就变得不那么直截了当了。一种办法是将解析UART收到数据的功能写成状态机,在数据是否合乎预定的格式、对返回数据的辨识的基础上确定程序执行的状态。当到达某些状态时,就从UART发送命令。但如此写法,从读代码的角度很难看出程序执行的意图,把操作流程和通信纠错混在了一起,且不利于代码维护。
如何在保持程序流程直观的写法下,融进去通信状态识别和错误处理机制呢?按照一般的思维,这是一个单任务(线程)的程序,程序是顺序执行,都不需要借助中断。程序是在按照一个流程进行操作,但还要处理与流程关系不紧密的UART通信异常,这两项工作在时间上是交错的。倘若设想这是两个任务在进行,但是是在可以预见地相互切换着呢?好像这么说把简单事情又复杂化了……
上面三个例子都有可能借助“任务调度”实现更有效的运行时间分配。在我看来,FreeRTOS提供了一种新的程序组织方式——任务,把复杂的事情划分成独立的小块分开编写;同时又提供一些机制让这些小块功能相互协同,来实现总的目的。FreeRTOS的任务是用C语言函数来编写的,它提供一个特性,可以让几个函数看起来“同时”在执行。当然啰,只有一个CPU的情况下实际上是轮流在执行的,不过这也和普通C语言程序有重大区别。
我们知道,C语言的函数可以嵌套调用,可以递归调用。比如 funcA() 调用 funcB(),然后 funcB() 又调用 funcC(),甚至 funcC() 再调用 funcA() 都可以。但是 funcA() 必须等到 funcB() 返回之后才能继续执行它后面的内容,funcB() 调用 funcC() 也是等待 funcC() 完成了,控制权才回来。
对于FreeRTOS的任务则不是这样的了,taskA() 任务可以在执行过程中主动或者被动地交出控制权,taskB() 可以这时候获得控制权,但它并不是从头再开始执行,而是从上次交出控制权的那个地方后面继续执行。又在某个时候,taskB() 交出控制权,系统选择了执行 taskC(),然后再当 taskC() 交出控制权时,taskA() 又恢复了执行。
上面这个图简化了一点,实际上从一个任务的函数跳出来到另一个任务的函数中继续执行的过程,中间还要执行一段FreeRTOS内核的代码,也就是说是内核在负责调度下一步执行哪个任务,如何暂停当前的任务,如何恢复另一个任务。
是否多任务的调度一定要用某种RTOS来实现?也并不是,用了RTOS会有所帮助,同时资源开销会多那么一点。比如已经提到过的用中断来切换任务,已可以实现简单的多任务。又比如,把一个任务分解成多个步骤,每个步骤对应一个函数,然后在一个大的循环里面每次选择执行哪个任务的哪一步。这种多任务为非抢占式的,而用中断实现的多任务是抢占式的。
请注意,不管是否用了某种RTOS来实现,多任务操作的特点是每个任务只要没有执行完,它的状态(包括私有的数据)就必须完整保存。因为代表任务的函数(比如中断服务程序、任务的某一个步骤)一旦返回了,局部变量的作用域就消失,不能用来保存状态,所以任务状态保存要么用全局变量,要么用static的局部变量,要么用动态分配的存储。FreeRTOS的办法是:让任务函数暂停执行不需要返回,可以同时存在多个这种没有返回的函数,然后任选其中一个恢复执行。如何实现这个特性?请听我下篇分析。
阅读完了,欢迎大家跟本帖交流、回复思考题: 请构想一个适合单片机实现的具体的应用,描述其中可以成为“任务”的软件组成部分,并分析如果在此应用的软件中引入实时操作系统能够带来什么样的好处。
我认为用操作系统只在并发的程序和不丢数据的情况下。如按键要求实时响应。而串口通讯要求不丢数据。而现在的困难是大程序,太大了,根本用不了。因为一个项目是有时间要求的。若只有一两个函数变为进程。在整体代码中又怕运行出错。
单片机引入操作系统的好处是充分利用单片机的空闲时间。通过任务切换来实现同步和流程切换。比如传感器数据采集,可以按照数据采集,命令下发,数据传输,数据维护等分成多个任务,在没有收到操作命令或是不在传输时段内可以不用处理其他事情。
学习过一段时间的RTOS,任务调度、消息队列、内存管理这些原理都是清楚的,跑个简单的任务没有问题,但是一旦要应用来项目中,在复杂功能的需求下,经常不知道如何规划任务来实现功能,网上大多是偏原理类的学习资料,基本RTOS的应用设计很少,应用不到项目中,都没有动力搞下去了
引用: symic 发表于 2020-8-12 08:27 单片机引入操作系统的好处是充分利用单片机的空闲时间。通过任务切换来实现同步和流程切换。比如传感器数据 ...
系统把单片机安排的明明白白的,干活干活干活
另外也欢迎举个栗子🌰,关注&交流本站的打卡任务。
“请构想一个适合单片机实现的具体的应用,描述其中可以成为“任务”的软件组成部分,并分析如果在此应用的软件中引入实时操作系统能够带来什么样的好处。”
引用: ddllxxrr 发表于 2020-8-12 07:33 我认为用操作系统只在并发的程序和不丢数据的情况下。如按键要求实时响应。而串口通讯要求不丢数据。而现在 ...
所以有没有想到适合引入操作系统的例子哇,欢迎跟帖交流~
打卡任务:
“请构想一个适合单片机实现的具体的应用,描述其中可以成为“任务”的软件组成部分,并分析如果在此应用的软件中引入实时操作系统能够带来什么样的好处。”
简单的电机控制的项目。不知道这要分任务是否合理。可以将任务分为以下几个任务,电流采集任务,转速采集任务,PID控制输出任务,电机状态打印任务,指令接收任务。任务切换的好处在于既保证了系统的性能,每个任务又相互不干扰,都能得到充分的执行。
如果做一个室内环境监控器可以使用实时操作系统来完成,其中对各个传感器的数据可以作为一个单一的任务,显示,短信发送,等都可以分为单独的任务,这样可以方便各个任务处理自己的数据,层次分明,最大限度的利用单片机的资源。
温室大棚环境控制系统中,单片机即要采集空气温度、空气湿度、土壤湿度、二氧化碳浓度、氧气浓度、光照强度等传感器数据,还要对数据进行处理、存储、分析,然后对预先存储的农作物生命周期数据进行调用,最后对排风扇、浇水阀、遮光卷帘进行控制。上报给终端作物状态。次过程如遇到多层中断,执行会比较慢,等待操作时间过长。如用实时操作系统则可以对多任务同时进行,如采集数据后,分析数据与上报同时进行,执行操作各任务及上报同时进行。缩短执行时间。
stm32的nucleo-f334r8 开发版,想做一个通讯中转吧功能。把spi、I2c的数据图通过uart送到pc端。还没想好如何规划任务来实现这些功能。大概每个功能算一个任务吧,看看rtos能做到什么。
引用: freebsder 发表于 2020-8-12 13:46 顶楼洋洋洒洒确实写了很多,但基本没说到点子上。只讲战术不讲战略是找不到应用场景的。
欢迎大虾分享交流,给小虾们送福利哇~
引用: Rain、 发表于 2020-8-12 11:36 刚好有个项目需要用到freertos,先提前学习一下
可以提前按照楼主的思考题,思考思考这个项目架构,如果可以的哈,可以跟帖交流哇
引用: 月光疾风 发表于 2020-8-12 10:28 把手里的stm32开发板拿出来学习freertos,先先把在线升级的功能做成任务,之后再做一个数据采集
还有自己的学习计划,棒棒的,期待随时交流进度
估计很多人都有stm32的板子,希望通过此活动,能助你完成计划,
一个协议转换板子,板子上多个串口和can彼此之间通信,如果应用操作系统的话,可以省去各个串口优先级的配置与调试,就会更专注于功能本身。每个串口间的通信可为一任务
用简易示波器的设计进行思考,在我看来示波器可以分为三个软件相关的模块:
1. 数据实时性采样,示波器需要根据已设定的采样精度和存储深度对数据进行完整采集,实时性要求高;
2. 数据存储和处理,需要有足够的空间存储数据并能够根据需要进行信号处理,保证数据处理速度和存储速度赶上采样速度,避免数据丢失甚至混淆;
3. 数据重现,重现的终端可以是屏幕或者上位机,数据刷新时间不能太慢;
此外的考量还有:
信号正常显示和触发功能可以设计成两种不同的工作模式;
如果考虑多通道示波器,还需要做到通道间的采样数据平行,互不干扰;
如果期望通过U盘,串口或者网口等介质取走数据,则有文件操作方面的要求;
另外诸如整机运行时内部自检,采集到的数据是否需要补偿,数据存储失败如何处理等,这些也是每个周期运行时需要考虑的问题
功能方面,能靠硬件完成的事情MCU 就不要参与,留下宝贵的算力资源参与数据处理和任务调度;例如在硬件采集和显示的读写空档进行数据存储和处理,这需要精准的时间测量;不使用实时系统也能实现上述功能,但弊端有:1.不利维护,代码复杂度高;2.不利改进;若有新功能的添加,需要重新考虑程序架构;3.可移植性差;不同平台,甚至是不同元器件带来的延时,都会影响到原本的时序安排.