| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> 【FreeRTOS】06 临界段的保护——关中断和关调度 -> 正文阅读 |
|
[嵌入式]【FreeRTOS】06 临界段的保护——关中断和关调度 |
本节来讲一讲FreeRTOS如何保护临界段,先讲临界段的概念,再讲保护临界段的方法。 1)临界段的概念简单来讲,临界段是一段执行时不允许被中断(或其他任务)打断的代码;如果被打断,就有可能运行出错。 举个例子: 假设我们有一个任务Task1需要向队列FIFO中写入数据,中断服务函数TaskISR1也需要向队列FIFO中写入数据;我们知道,FIFO的写或者读需要依次执行:写入/读出数据、更新读/写指针。 如果Task1在向FIFO中写入数据时,执行完写入数据,但还没有更新写指针,此时中断来了,TaskISR1开始运行,它也需要向FIFO中写入,而由于Task1运行时还没有更新写指针,则TaskISR写入的数据会把Task1刚刚写入的数据覆盖,然后更新写指针;这就出现了一次丢失数据的错误。 当然错误还不只如此,中断服务程序TaskISR1运行完毕后,回到Task1继续运行,它会接着被打断的位置向后执行,更新写指针;这样就相当于没有写入数据,但又更新了一次写指针。 像这个例子中的FIFO写入数据、读出数据的代码段,就是临界段;它的读写数据和更新读写指针,两个操作之间不能被打断,否则就会可能出错。 用户代码中用于任务和中断间通信的变量、不可重入函数,操作系统的某些底层代码,都有可能是临界段。如果不保护好临界段,代码就会出现各种难以排查、难以复现的bug。 而最简单的保护临界段的方法,就是关闭中断,以及关闭任务调度。也就是暂时禁止中断和其他任务执行,等到临界段执行完毕后再恢复。具体使用时,如果有可能被中断打断,则关闭中断,如果有可能被高优先级的任务打断,则可以关闭任务调度。 注意!这种“关闭”是临时的,执行完临界段之后,必须要恢复允许中断和任务调度的状态。否则,中断和其他任务就无法运行了。 此外,由于执行临界段时,关闭了任务或任务调度,系统无法响应其他的实时事件,会降低系统的实时性;所以我们编写代码时,需要准确识别临界段,使得临界段尽量短,即只把需要保护的最小的一段代码保护起来即可。 2)freeRTOS中的临界段保护freeRTOS系统中定义了保护临界段的宏: 在任务中,taskENTER_CRITICAL(),用于进入临界段前保护;taskEXIT_CRITICAL(),用于临界段执行完之后,退出保护。 查看代码调用情况,可以看到它们最后是由下述两个函数实现: 这其中除了开/关中断的代码,还有一个嵌套计数,为的是记录进入了几层“关中断”中,这样在退出临界区时,就不会错误地提前退出了。 其中的开/关中断的操作,是用嵌入的汇编代码实现的,如下所示,关闭中断时,只关闭了优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断: 由于freeRTOS提供的进入临界段的代码,本质上是关闭中断,而一旦关闭中断,则freeRTOS的任务切换功能也被禁止了,所以,它同时能够保护中断和任务间的临界段冲突。 另外,如果想要在中断服务程序中进入临界段,要使用另一套函数来保护临界段:taskENTER_CRITICAL_FROM_ISR(),用于进入临界段前保护;taskEXIT_CRITICAL_FROM_ISR( x )用于临界段执行完之后,退出保护。代码的实现是类似的,感兴趣的可以自行阅读代码。 3)保护临界段的测试实例我们以一个例子来试验freeRTOS临界段的保护功能: 首先来看不保护临界段的情况。 建立任务,我们把defaultTask任务优先级设置为普通,Task02任务的优先级设置为高,这样,Task02就可以打断defaultTask的执行: 编写一个具有临界段属性的函数,这里我们用串口来实现,就是普通地往串口寄存器中写入数据: 编写两个任务内部执行的功能,都是通过访问串口的临界段函数,打印输出;不同的是高优先级的Task2第一次运行时延迟了5ms,这样可以实现defaultTask任务先执行5ms,之后Task02打断它执行: 上述程序运行结果如下: 可以看到,串口打印的输出,DefaultTask任务首次运行时,打印了前6个字符:Defaul后,被Task02打断,进入了Task02任务的打印功能;Task02打印完毕后,才继续打印DefaultTask未完成的打印字符:tTask is running。 所以,如果临界段Uart1Send函数不加保护,它的打印输出有可能被打断,输出不正确的字符串。 如下所示修改defaultTask的代码,加入临界段保护和退出的语句: 再次运行上述程序,可以看到输出如下,输出了正确的字符串: 所以,临界段保护代码功能是有效的。 4)关闭任务调度上述的freeRTOS定义的临界区保护的操作,是直接关闭中断;而实际上,有时有的临界段中断里不访问,只用保护不被其他任务打断即可;这时也可以临时关闭任务调度功能,来实现临界段的保护。 freeRTOS系统中定义了关闭任务调度和开启任务调度的函数: vTaskSuspendAll()、xTaskResumeAll() 这两个函数也需要成对使用,关闭任务调度后,然后执行临界段,最后一定要恢复到开启调度的状态。 而且,在关闭调度后,不能再使用任何会引起任务切换的函数,如之前讲到的延时vTaskDelayUntil、vTaskDelay,以及以后会讲到的信号量、消息队列等函数;直到重新开启任务调度。 在3)中的例子,临界段访问的冲突是不同任务间引起的,我们也可以通过临时关闭任务调度来实现临界段的保护,如下所示: 这个程序的执行结果,和使用taskENTER_CRITICAL()、taskEXIT_CRITICAL()保护临界段是一样的。大家可以自行测试一下。 好了,本节的内容就到这里了。 如果觉得有用可以关注作者微信号“小白白学电子”,在公众号可以找到代码和资料下载地址: |
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
89C51单片机与DAC0832 |
基于51单片机宠物自动投料喂食器控制系统仿 |
《痞子衡嵌入式半月刊》 第 68 期 |
多思计组实验实验七 简单模型机实验 |
CSC7720 |
启明智显分享| ESP32学习笔记参考--PWM(脉冲 |
STM32初探 |
STM32 总结 |
【STM32】CubeMX例程四---定时器中断(附工 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 0:42:17- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |