| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> FreeRTOS的调度器源码分析及系统滴答SysTick -> 正文阅读 |
|
[嵌入式]FreeRTOS的调度器源码分析及系统滴答SysTick |
1. PendSV系统调用查遍了C站上所有关于FreeRTOS调度器的分析,发现大家分析完vTaskStartScheduler()之后就戛然而止了,我就会比较迷糊,这个仅开启了调度器的调度,而FreeRTOS是一个实时操作系统,并不能体现出他的实时性在哪里,虽然已经在FreeRTOSConfig.h中设置了configUSE_PREEMPTION等于1,那他是怎么其的作用呢? project\demo\FreeRTOSConfig.h ? 刚开始对PendSV系统调用还比较陌生,读过很多的资料之后,下面是我的理解: 而PendSV就是一次SWI系统调用。我们先看SWI系统调用的结果是什么,SWI系统中断定义在中断向量表中, ?当系统发生SWI中断的时候,就会触发调用FreeRTOS_SWI_Handler函数,而FreeRTOS_SWI_Handler的处理过程如下:
根据上面的流程,可以知道, 首先它会先保存当前任务的上下文,他会继续调用到vTaskSwitchContext()函数,他是一个C函数,也是调度的关键函数,经过vTaskSwitchContext()后,当前任务已经不是上一个任务而是切换到了优先级最高的新任务,最后,将此时当前新任务的栈pop出来,继续执行,完成任务的切换工作。
挑出了其中的重要函数,首先检查栈是否溢出,然后,找出系统中优先级最高的任务,FreeRTOS的实时性问题就体现在这个地方,将pxCurrentTCB赋值一个最高优先级的任务等待调度。注意此时并没有发生切换,只是在等待调度,这个就与上面为什么要引入PendSV对应了起来! 2. FreeRTOS任务切换的场合
首先需要明确的是,FreeRTOS对于不同的架构,其实现方式是不同的。基于STM32的处理过程是?中断控制及状态寄存器ICSR(地址:0xE000_ED04),向ICSR的第28位写入1悬起PendSV(启动PendSV中断),而ARM_CA9的处理方式是,利用系统调用触发一次异常,执行SWI指令。 STM32的处理方式很多文章中都有提到,而这篇文章选择了ARM_CA9的处理方式。 2.1 系统调用系统调用就是执行FreeRTOS系统提供的相关API函数,比如,任务切换的函数taskYIELD(),或是有些调用了taskYIELD()函数的API函数。
根据系统调用的过程可以看出来,触发了一次SWI中断,之后就会执行上面第一节的函数了。 2.2?系统滴答SysTick在main函数中,任务创建完以后,就要开启调度器开始调度了,在开始调度器的函数中,有对系统滴答的注册,如下:
它会调用到tick的注册函数,而它是一个宏定义,展开后
SystemSetupSystick()是关键函数,它注册了系统滴答的中断函数,系统滴答的回调函数是FreeRTOS_Tick_Handler(),注册过程如下:
tickHandler(),就是我们的关键函数,也就是上面的FreeRTOS_Tick_Handler(),当系统每1ms tick一次,就会触发一次该函数,
这个函数很关键的函数就是xTaskIncrementTick(),更新系统的时间,并判断是否有任务到达了唤醒的时间,如果有就加入到就绪链表中,但最重要的任务是判断是否需要发生任务调度,它的返回值就是,若等于pdTRUE表示需要调度,否则不需要。这个很关键。 对STM32的处理方式是将中断控制及状态寄存器ICSR(地址:0xE000_ED04),向ICSR的第28位写入1悬起PendSV(启动PendSV中断),改变它的标志位,等待调度。而ARM_CA9则不大一样了,它只是附给一个全局变量ulPortYieldRequired的值为pdTRUE。但并没有任何调度的痕迹。 当时就很迷惑,搜索了很久才发现它的处理过程如下。 我们搜索ulPortYieldRequired,发现它会在portASM.S中调用到,
首先判断ulPortYieldRequired是否等于0,如果不等于0就会执行下面的switch_before_exit,在switch_before_exit中,终于看到了第一节中分析的函数vTaskSwitchContextConst,找出优先级最高的任务等待调度。 而问题是,他是怎么执行的呢。
看到这段代码应该就会明白了,我们的ulPortYieldRequired是在中断处理过程中得到的执行,也就是,当系统产生中断,中断执行完成之后,就会判断ulPortYieldRequired是否需要产生调度,如果需要就按第一节的过程进行处理。而这个中断恰好就是我们的系统时钟中断。 整体的过程就是,CPU每个tick就会产生一次中断,首先对系统时钟加一,并判断是否需要调度,如果需要产生调度,将ulPortYieldRequired赋值为pdTRUE。之后根据ulPortYieldRequired决定是否执行第一节中的代码。 最后的最后,此时的pxCurrentTCB已经指向了最新需要执行的任务,portSAVE_CONTEXT已经将上一个执行的任务的上下文保存了起来,portRESTORE_CONTEXT就是恢复任务的上下文,这个任务已经切换了,它就是需要执行的最新任务的上下文,当portRESTORE_CONTEXT执行完,就完成了上下文的切换了。 其实产生任务切换的函数有很多,因为FreeRTOS是一个实时操作系统,必须确保系统的实时性问题,调度是很关键的,当我们看到taskYIELD()的时候,就意味着需要调度,分析源代码时,可以关注一下这一点。 |
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
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 10:51:23- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |