[活动] 颁奖:跟着cruelfox,打卡学习FreeRTOS的获奖者们、他们的学习分享

nmg   2020-9-14 16:40 楼主

活动详情:点此查看

首先,特别感谢cruelfox在赶公司项目的同时,及时给出了每站思考任务。cruelfox的对每站任务解读见帖子最后

同时也要感谢网友们的积极参与,欢迎大家跟帖反馈对本活动的意见感受和建议,我们都会认真查看并在策划相关活动时参考、借鉴。综合cruelfox推荐意见,本次活动评选结果如下。

 

优秀打卡奖

*以下网友更新个人信息后,跟帖告知已更新信息。我们将按照论坛资料里的相关邮寄信息,安排礼品邮寄

abc9981 所获奖品:小米蓝牙耳机Air
我的学号 所获奖品:小米蓝牙耳机Air
superstar_gu 所获奖品:小米插排

 

他们分享的笔记如下:

网友用户名 第一站:应用场景 第二站:栈—任务切换的关键 第三站:任务状态及切换 第四站:任务间的通信 第五站:中断与任务切换 第六站:实验-串口后台打印 第七站:大作业
abc9981 应用:数据网关(数据集中器)
功能:故名思意,主要功能就是汇总终端设备数据,统一上报之后台,根据终端设备的类型不同,数据网关将调用的硬件资源也是不一样的
例如:2路串口,1路无线,1路iic采集数据,1路spi数据存储,同时还要处理后端发送的指令,及定时上传数据至后台
在没有rtos情况下,可按流程进行数据处理,但是数据采集过程中肯定会出现通信超时等情况导致资源分配不均,以及无法及时处理后台数据等问题
引入rtos架构
1、能合理分配等待的空闲时间的资源
2、代码简单化,在硬件资源独立的情况下只需要考虑当前任务的处理逻辑
3、机动性更高了,当需要添加一路数据采集是,直接添加一个任务,并不影响其他任务运行
具体任务划分
任务1,串口1数据采集,由于串口资源属于硬件资源,为了减少硬件资源在不同任务间调用可能会出问题,所以独立开一个任务
任务2,串口2数据采集,
任务3,无线数据采集
任务4,iic数据采集
任务5,数据汇总及数据存储
任务6,后台通讯处理
个人觉得这章应该是RTOS的核心

正常裸奔基本不考虑堆栈问题,只有调用中断的时候会调用到栈的功能,正常使用比较多的也是定义变量,动态内存使用的也比较少

当使用了RTOS就无时不刻的在调用堆栈,TCB基本就是动态获取堆,任务切换就要一直压栈出栈,同时还要考虑到系统中断的堆栈调用

这里RTOS针对各任务的整个堆栈操作,就要是由任务调度器来完成,整个过程大概就是

保存任务1现场,恢复任务2现场,保存任务2现场,恢复任务1现场不断的切换

 

什么是现场呢,就是CPU运行状态寄存器

51与contex-m核其实比较大的区别就是现场的不同,51的资源相较于contex-m的资源要少,cpu调用的寄存器也少,可以理解为现场小,所以在保存现场的数据上,contex-m是会比51要大一些的

 

以上为个人理解,如果理解有误,请大家指正,其实看这章也是挺懵的,查了很多资料,希望大家相互交流共同进步
思考题: 在FreeRTOS任务程序中,常使用 xTaskDelay() 函数来达到延时执行的目的。若在某一个任务里调用了 xTaskDelay(20), 想等待20个时间单位,结果这一回延时操作却超过了25个时间单位。请分析有可能是哪些原因造成的?

答:首先这个xTaskDelay(20),20个时间单位肯定是硬吃的,毋庸置疑,主要是要分析后面这个多出来的5个时间单位被谁吃了。

1、被更高优先级抢占,就是当前任务到等到19个时间单位时,有一个高优先级任务抢占资源,运行了6个时间单位才释放资源

2、被同优先级抢占,当前任务到等到19个时间单位时,有一个同优先级任务正好在就绪态,肯定时可以接手资源运行6个时间单位再释放资源

3、被中断吃了,不管是裸奔还是,上rtos硬件中断始终优先级是最高的,正常编程过程中,我们是不会再中断中做了很多东西,但不排除有些人在中断中写了应用代码,甚至写了延时函数
本次学习主要针对的是任务间通讯
正常来说想计算器这样的程序一般裸奔是很好解决了,但是为了更好的了解本章的内容做了以下修改
原本的裸奔扩展为几个任务
任务一,扫描键盘,识别键盘输入,将数据传输至运算任务,与显示任务,优先级最高(由于该程序是计算器,使用到的按键会比较多,而且并无其他实时任务占用资源,所以这里使用扫描键盘的做法,节省硬件资源)
任务二,运算任务,存储任务一传输过来的数据,进行存储或运算,具体更具传输指令触发动作
任务三,显示任务,显示其他任务传输过来的数据,及指令触发动作

 

根据本章学习到的内容定义了三个方案

 

方案一:任务通知
个人理解,每个任务都有相应的任务通知,应该是可以触发某个任务的任务通知,如果以上不成立,该方案不可行,

任务一,实时在扫描,识别到触发信号,通知到任务二,与任务三,执行相应动作
任务二,等待任务通知,并执行相应动作,运算结果发送至任务三
任务三,等待任务通知,并执行相应动作

 

方案二:队列
任务一,实时在扫描,识别到触发信号发送队列数据到任务二
任务二,做相应触发动作,并发送队列数据到任务三
任务三,根据指令执行相应动作

 

方案三:信号量
前提定义全局变量
任务一,实时在扫描,识别到触发信号,发送与任务二的信号量
任务二,识别信号量,获取相应数据,并发送与任务三的信号量
任务三,识别信号量,获取相应数据,并触发相应动作
他的回复含代码和图片,直接点击看帖回复比较方便:https://bbs.eeworld.com.cn/forum.php?mod=redirect&goto=findpost&ptid=1138152&pid=3005191&fromuid=530227 在FreeRTOS环境下,要设计一个UART接收指定数量字符的函数,可以用怎样的途径?请描述你的思路,并分析优缺点,以及还可能怎么改进。
审题:UART接收指定数量字符,然后触发后续任务
方案一:中断收到UART数据,将UART数据存入消息队列,统计数量,达到数量任务通知或信号量触发后续任务
方案二:中断收到UART数据,将UART数据存入环形缓冲区,统计数量,达到数量任务通知或信号量触发后续任务
方案三:中断收到UART数据,将UART数据存入DMA,统计数量,达到数量任务通知或信号量触发后续任务

方案一,可能导致数据丢失,方案二,解决方案一的数据丢失问题,方案三,释放了CPU
https://bbs.eeworld.com.cn/thread-1139972-1-1.html

第二题,设计考虑,后期有时间在验证补充
我的学号 用简易示波器的设计进行思考,在我看来示波器可以分为三个软件相关的模块:

1. 数据实时性采样,示波器需要根据已设定的采样精度和存储深度对数据进行完整采集,实时性要求高;

2. 数据存储和处理,需要有足够的空间存储数据并能够根据需要进行信号处理,保证数据处理速度和存储速度赶上采样速度,避免数据丢失甚至混淆;

3. 数据重现,重现的终端可以是屏幕或者上位机,数据刷新时间不能太慢;

此外的考量还有:

信号正常显示和触发功能可以设计成两种不同的工作模式;

如果考虑多通道示波器,还需要做到通道间的采样数据平行,互不干扰;

如果期望通过U盘,串口或者网口等介质取走数据,则有文件操作方面的要求;

另外诸如整机运行时内部自检,采集到的数据是否需要补偿,数据存储失败如何处理等,这些也是每个周期运行时需要考虑的问题

功能方面,能靠硬件完成的事情MCU 就不要参与,留下宝贵的算力资源参与数据处理和任务调度;例如在硬件采集和显示的读写空档进行数据存储和处理,这需要精准的时间测量;不使用实时系统也能实现上述功能,但弊端有:1.不利维护,代码复杂度高;2.不利改进;若有新功能的添加,需要重新考虑程序架构;3.可移植性差;不同平台,甚至是不同元器件带来的延时,都会影响到原本的时序安排.

 
感觉这期难度稍微有些高,ARM 的指令集不常用也就忘得差不多了

直觉上的概念是:无论8 bit  或32 bit 的单片机,放在RTOS 进行任务的切换,实际上做的都是同样的工作:A任务切换到B任务时,把当前A任务相关的变量的值,函数的执行位置等信息压入到A 相关的堆栈中;然后开启B任务生成变量和执行程序;切换到A任务时,同样需要将当前B任务相关的变量的值,函数的执行位置等信息压入到B 相关的堆栈中,然后从A 的堆栈调出之前的信息使用,如此反复;

至于C51 和STM32 对这个操作的差异,由于架构不同记载信息的寄存器会不同,此外可能用不同的机制进行实现。具体有多不同,还需要再查看多些资料。。。。
1.存在比原本任务更高优先级的任务,在原本任务准备开始延时被就绪,且执行时间为5个时间单位;

2.存在和原本任务同等优先级的就绪任务,不同任务穿插用了 5 个时间单位;

3.硬件有问题,导致计时不准
可以分为三个任务进行:
任务一负责实时监听按键输入信息,正确地读取信息的输入是很重要的,而信息输入的时机是不可测的。可以通过中断的方法让监听事件处在就绪状态,按键输入后将信息传递给储存计算任务;

任务二负责输入信息的存储和计算;考虑存输入信息快且多的情况,或者需要进行的运算比较复杂,可以专门开辟一个存储信息和进行计算的任务;这个任务在输入信息达到一定数量后被启用执行,执行完毕后输出结果交给显示用的任务三,再将自身挂起等待通知;

任务三负责结果输出,该任务实时性要求不高,主要响应任务二的通知,将计算结果输出到1206,完毕后将自身挂起等待通知。
该宏函数主要用于上下文切换的申请,本质上是通过操作中断控制和状态寄存器(ICSR)的相关位置实现 PendSV 异常;然后再PendSV 异常中设置其他任务的上下文返回,这样在高优先级异常处理完毕后会立即进入PendSV 异常,该异常又将开启新的任务

 
在数据收发的时序是已知的情况下,直接在程序里边定时查看取走UART 接收寄存器内容即可;
如果数据的接收拾不定时的,最简单的方法是不停地查询,但这样无疑会浪费系统宝贵的资源;
可以采用中断的方法,同样是分为两个任务进行,任务一负责接收中断的响应和接收数据,任务二由任务一唤醒,取走数据并进行后期的处理,在DMA 加入的情况下能够省下更多时间;使用系统存在版主提到的数据刷新过快,任务切换开销过多反而得不偿失的可能


选择了第二个题目,个人的一点愚见
https://bbs.eeworld.com.cn/thread-1139991-1-1.html
superstar_gu

由于单片机程序运行都是单线程,所以多任务应用必须应用中断。但是,在并发的多任务调度,CPU空闲管理,操作系统(比如RTOS)相对于中断,就比较有优势。我构想下列案例:

逆变器控制

任务A:现场电流/电压传感器数据实时采样

任务B:逆变器PWM占空比计算与调节

任务C:上位机通讯

应用中断,选择PWM中断优选,依次调用任务A,任务B,任务C,然后等待下有一个PWM中断信号,

优点是:单线程,方便调试,CPU任务节点方便跟踪,

缺点是:CPU”空闲“时间较长,实时性略差。

改用操作系统(比如RTOS),三个任务A,任务B,任务C”并行“执行,

(1) CPU得到充分利用,任务深入改进

比如,任务A,数据采集,加大采集数据的深度与广度,提前预防采集数据丢失,增加电压/电流失调或故障预警。

(2) 外界交互“实时性”

比如,任务C,由于任务脆片化,缩小时间周期,逼近实时性

(3) 需要提升CPU性能

任务B,”中断“处理单线程将所有资源最大化偏向任务B,这是操作系统任务处理难以达到的。但是操作系统在任务的PWM处理算法,尤其人工智能算法会有优势,因为多变量多任务是操作系统的优势。

(4) 操作系统RTOS任务调动的多样性

操作系统的任务调动多样性,决定着模式选择多,下列两幅图,一幅为任务优先,一幅为时间片管理。

本案以任务优先考虑,依次为:故障(任务A预警),任务B,任务A,任务C,




 
堆栈这部分我还要深入了解。
以c51为例,
1. 我们需要知道两个任务占用内存大小,这样才能给任务分配内存。
2. 创建任务堆栈和tcb,初始化。
3.  任务调度,全局句柄获取tcb地址,读取任务1堆栈指针,同时保存任务2堆栈值;
4. 由于任务堆栈是连续的,堆栈先进后出,任务调度过程中,内存任务逐渐释放。
vTaskDelay()是相对延时函数,任务每次延时都是从调用延时函数vTaskDelay()开始算起的,延时是相对于这一时刻开始的。如果执行任务A的过程中发生中断,那么任务执行的周期就会变长。
(1)高级别的任务抢占

(2)任务A与任务B,甚至更多任务同时执行,也会造成任务A的延时函数延时

(3)任务本身在,调度时的损失时间,如阻塞,挂起至执行,也会导致延时。
   

1. 键盘扫描任务 

        使用时间片进行周期性任务调动,每隔一段时间执行一次,单次周期执行任务完成,给计算器程序发送消息队列

    2. 计算器程序任务

        使用 getchar() 函数和 putchar() 函数分别进行输入输出,周期性任务调动,每隔一段时间执行一次,每次周期执行任务完成,若不为空,给显示屏接口任务发送消息队列

     3. 显示屏接口任务   

        等待消息队列任务
执行系统调用,比如普通任务可以使用taskYIELD()强制任务切换,中断服务程序中使用portYIELD_FROM_ISR()强制任务切换。

portYIELD_FROM_ISR函数的参数,如果为pdTRUE,退出中断就会切换到高优先级任务执行。那是因为这个portYIELD_FROM_ISR函数在参数为pdTRUE时,会调用portYIELD()函数强制上下文切换。如果为pdFALSE,不执行上下文切换。
任务:FreeRTOS环境下,要设计一个UART接收指定
数量字符的函数
(1) 创建UART 接受任务
(2) UART接受函数RX,考虑采用系统中断实现,
(3) 若字符数量比较长,考虑利用队列实现。
xRxedChars=xQueueCreate(uxQueueLength,(signedchar)sizeof(signedchar));
(4)UART接受函数
ISR( USART0_RX_vect )
{
signed char cChar;
signed portBASE_TYPE xHigherPriorityTaskWoken;

    cChar = UDR0;

    xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken );

}

上述方法基于字符队列与中断实现,UART存取过程中完全占用CPU,效率不高,资源利用率差。
DMA存取更好。
简单做了STM32F107如何移植FreeRTOS并创建任务:https://bbs.eeworld.com.cn/thread-1139796-1-1.html

 

 

瓜分红包奖

共5人参与,奖池内共100元,最终按时完成的网友为3名,平均会瓜分到34元。

 

@我的学号,@superstar_gu @abc9981 挑战成功,论坛微信公用号“helloeeworld1”将与您联系,通过微信转账的方式转给您。

@俺还活着,差2站没有打卡

@role_2099  差6站没有打卡

 

 

附cruelfox对各站打卡任务解读:

第二站思考题:任务切换时对堆栈的操作,和不同硬件体系下开销的比较。要使多个任务交替运行,各自不知道其它任务的存在,必须在被动任务切换时做好现场保存和恢复的工作。从软件角度,现场包括了所有的变量,其中全局变量、静态局部变量是编译时分配的,不受任务管理影响;局部变量分情况,有的是存在堆栈中,有的仅使用寄存器。堆栈是任务私有的,但寄存器是公用的,所以任务切换必须把A任务运行时所有的寄存器保存起来,再把任务B已保存的寄存器恢复。使用堆栈来进行寄存器保存--恢复是方便的,主要原因是硬件有这个机制,比如在发生中断的时候一部分寄存器是自动保存的。

 

基本上任务切换的开销,对堆栈操作这部分就是保存和恢复寄存器。具体是哪些寄存器就跟硬件平台相关了。比如ARM Cortex-m系列,有16个32-bit通用寄存器,除了切换任务时(进入中断触发)自动保存的以外,需要RTOS代码来主动保存。Cortex-m4有FPU的专用寄存器,如果不同任务都用到FPU,还需要把FPU的寄存器保存到堆栈上。在硬件角度或者说汇编语言思路的软件角度,保存现场就是保存可能用到的寄存器。那么,大家再想想,各种片上设备的寄存器要不要保存?

 

32位平台和8位平台的差别蛮大的。按规律讲,通用寄存器数量越多的话,保存和恢复对堆栈操作引起的开销就越大。虽然寄存器多,C语言程序运行效率可能会高,对RTOS切换则是不利。像6502, 6809, STM8这些8位平台寄存器就少,保存的数量少。AVR虽然是8位机,却有32个通用寄存器不得不保存。8051不是好处理的,一方面R0等寄存器是分bank的,要不要都保存?另一方面,8051的堆栈只能使用前128 byte的地址空间,存储不够呀。怎么办呢?硬要做还得把整个堆栈和扩展SRAM空间来回拷贝,开销不是一般的大。

 

为了减少任务切换时对堆栈操作的开销,可以约定限制寄存器的使用,这需要编译器的支持。FreeRTOS已经port过的那些平台是可以放心使用的,不过同一平台也可能见到不同的版本,就是因为支持的硬件特性有差别。

 

第三站思考题:xTaskDelay()实际延迟超长的原因。 第一个许多网友都答到了,是被更高优先级的任务抢占,所以在延迟条件满足,变为就绪状态之后还要等待高优先级的任务离开运行状态。第二个,同一优先级的任务在运行,但具体还是要看的:(a)如果是非抢占式调度,其实跟优先级就没有关系了,只要另外的一个任务没有交出控制权,调用xTaskDelay()的函数只能等着。(b)抢占式,时间片轮转,如果有多个同优先级的任务在ready状态,每个运行1个tick时间,也是有可能因为“排队”多等了5个tick. (c)抢占式,但不使用时间片轮转,那么指定延迟结束,也要等同优先级其它任务交出控制权。

 

第三种情况,正在执行的任务禁用了调度器,这时候无论如何都不会发生任务切换。不过,长时间禁用调度器不是多任务环境下的好方法。第四种,该任务被其它任务给挂起了,到后来才恢复。这是少见的可能性。第五种,有网友说因为中断时间占用,的确不能排除,但中断服务程序使用大量时间本身不是好的设计。

 

第四站的思考题:方法是容易的。我想提的是,对于程序的移植,应尽量用原有的代码。这个题目中,原程序用了putchar()和getchar()进行输入输出,我们就将这两个函数重新编写(替换C标准库的函数),保留原来的调用参数就行了。可以使用队列、信号量等来做,在键盘任务和显示模块任务中分别配合getchar()和putchar()工作。

 

第五站思考题:关于portYIELD_FROM_ISR(), 我想让各位分析得更透彻的……为什么要有这么一个宏调用,而不是默认使用? 很重要的一点是,中断引起任务切换是经常的事情,但是不是总要切换。这给ISR提供了一个选择,只有在需要的时候才申请切换,而如果硬件信号处理完毕但不值得切换任务(可以后续同样的中断累积了比较多的数据再触发任务去处理)那就保持现在任务执行。

 

其次,ISR程序里面可以使用FreeRTOS的许多xxxxFromISR()的API来和任务通信,或者是操作通信对象。平常的这类操作(由任务调用)是可能引起任务切换的,但ISR进行这类操作时,只是获得一个是否需要切换任务的hint——通过一个参数得到,并不进行任务切换。是否要切换,仍然由ISR自己决定。因此,ISR可以根据情况,在末尾使用portYIELD_FROM_ISR()来申请切换任务。

 

由于portYIELD_FROM_ISR()使用软中断来实现,它引起任务切换是在ISR处理完成之后的(如果还有别的ISR,按照优先级都执行之后,才会进行任务调度),多个触发任务切换的事件源,最后汇总到一次任务切换进行调度,减少了调度器的操作。

回复评论 (6)

已更新信息,通过本次学习也算是入门了FreeRTOS,浅尝FreeRTOS,后续有时间还是要深入了解

点赞  2020-9-14 17:21

感谢

点赞  2020-9-14 17:40

非常羡慕坚持打卡的同学们,有时间和精力坚持学习,值得敬佩!

点赞  2020-9-15 08:54

信息确认,感谢EE 和 CRUELFOX 组织这次活动;纸上得来终觉浅,还是需要找个实际的项目练练手

君应有语,渺万里层云,千山暮雪,知向谁边?
点赞  2020-9-28 09:01
恭喜恭喜
点赞  2020-9-28 23:54

确认信息

点赞  2020-10-13 11:58
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复