【学习FreeRTOS】第9章——FreeRTOS任务调度

news2024/11/19 19:43:17

1.开启任务调度器

vTaskStartScheduler()

作用:用于启动任务调度器,任务调度器启动后, FreeRTOS 便会开始进行任务调度【动态创建任务为例】

  • 创建空闲任务
  • 如果使能软件定时器,则创建定时器任务
  • 关闭中断,防止调度器开启之前或过程中,受中断干扰,会在运行第一个任务时打开中断
  • 初始化全局变量,并将任务调度器的运行标志设置为已运行
  • 初始化任务运行时间统计功能的时基定时器 【可选】
  • 调用函数 xPortStartScheduler()

xPortStartScheduler()

作用:该函数用于完成启动任务调度器中与硬件架构相关的配置部分,以及启动第一个任务

  • 检测用户在 FreeRTOSConfig.h 文件中对中断的相关配置是否有误
  • 配置 PendSV 和 SysTick 的中断优先级为最低优先级
  • 调用函数 vPortSetupTimerInterrupt()配置 SysTick(清空计数值、配置节拍频率、重装载值、启动计数与中断)
  • 初始化临界区嵌套计数器为 0
  • 调用函数 prvEnableVFP()使能 FPU
  • 将FPCCR寄存器的[31:30]置l,这样在进出异常时,FPU的相关寄存器就会自动地保存和恢复(M4/M7)
  • 调用函数prvStartFirstTask() 启动第一个任务

2.启动第一个任务

prvStartFirstTask()

__asm void prvStartFirstTask( void ) { 
	/* 8字节对齐 */ 
	PRESERVE8 ldr r0, =0xE000ED08 	/* 0xE000ED08为VTOR地址 */ 
	ldr r0, [ r0 ] 					/* 获取VTOR的值 */ 
	ldr r0, [ r0 ]					/* 获取MSP的初始值 */ 
	
	/* 初始化MSP */ 
	msr msp, r0
	
	/* 使能全局中断 */ 
	cpsie i 
	cpsie f 
	dsb 
	isb 
	
	/* 调用SVC启动第一个任务 */ 
	svc 0 
	nop 
	nop 
}

执行过程为:

  • 获取MSP的初始值(栈顶地址)
  • 将MSP重新赋值为栈底指针(让MSP回到原点,启动任务一去不复返)
  • 使能全局中断
  • 使用SVC指令,传入系统调用信号,出发SVC中断vPortSVCHandler ()
  • 关于MSP指针
    程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时,MCU 会自动更新 SP 指针,ARM Cortex-M 内核提供了两个栈空间
    主堆栈指针(MSP)它由 OS 内核、异常服务例程以及所有需要特权访问的应用程序代码来使用。
    进程堆栈指针(PSP)用于常规的应用程序代码(不处于异常服务例程中时)。
    在裸机中,程序全部使用MSP,在FreeRTOS中,中断使用MSP(主堆栈),中断以外使用PSP(进程堆栈)
  • 关于0xE000ED08
    0xE000ED08是VTOR(中断向量表)的地址,向量表的第一个是 MSP 指针,取 MSP 的初始值的思路是先根据向量表的位置寄存器 VTOR (0xE000ED08) 来获取向量表存储的地址;在根据向量表存储的地址,来访问第一个元素,也就是初始的 MSP。

vPortSVCHandler ()

__asm void vPortSVCHandler( void ) 
{ 
	/* 8字节对齐 */ 
	PRESERVE8 
	/* 获取任务栈地址 */ 
	ldr r3, = pxCurrentTCB 		/* r3指向优先级最高的就绪态任务的任务控制块 */ 
	ldr r1, [ r3 ] 				/* r1为任务控制块地址 */ 
	ldr r0, [ r1 ] 				/* r0为任务控制块的第一个元素(栈顶) */ 
	
	/* 模拟出栈,并设置PSP */ 
	ldmia r0 !, { r4 - r11 } 	/* 任务栈弹出到CPU寄存器 */ 
	msr psp, r0 				/* 设置PSP为任务栈指针 */ 
	isb 

	/* 使能所有中断 */ 
	mov r0, # 0 
	msr basepri, 

	/* 使用PSP指针,并跳转到任务函数 */ 
	orr r14, # 0xd 
	bx r14 }

运行过程为:

  • 获取优先级最高的就绪任务的TCB,并取其栈顶元素pxTopOfStack
  • 模拟出栈,将寄存器值出栈至CPU寄存器,并设置PSP指针
  • 开启中断
  • 线与0xd,将r14设置为线程模式并使用PSP
  • 跳转到任务的任务函数中运行,CPU自动出栈R0-xPSR等寄存器(M4:若EXC_RETURN使用FPU,则恢复浮点单元)

M4的vPortSVCHandler () ,除了手动出栈r4-r11外,还有r14,这是因为M4等系列支持FPU,需要该变量进行判别
M4的vPortSVCHandler () ,不需要线与0xd,因为在初始化时,已经对EXC_RETURN进行赋值了,不需要再线与
一般情况下,使用动态创建任务,第一个启动的任务是软件定时器任务
注意:SVC中断只在启动第一次任务时会调用一次,以后均不调用

在这里插入图片描述
在这里插入图片描述

开启任务调度器及启动第一个任务总结

在这里插入图片描述

3.任务切换

任务切换的本质:就是CPU寄存器的切换(又称上下文切换),在PendSV中断服务函数中完成 主要分为两步:

  • 需暂停任务A的执行,并将此时任务A的寄存器保存到任务堆栈,这个过程叫做保存现场
  • 将任务B的各个寄存器值(被存于任务堆栈中)恢复到CPU寄存器中,这个过程叫做恢复现场

触发PendSV中断方式

  • 滴答定时器中断调用
  • 执行FreeRTOS提供的相关API函数:portYIELD()
  • 本质:通过向中断控制和状态寄存器 ICSR 的bit28 写入 1 挂起 PendSV 来启动 PendSV 中断

在这里插入图片描述
在这里插入图片描述

PendSV中断服务函数xPortPendSVHandler()

  • 进入中断,使用PSP自动压栈
  • 当前的psp是正在运行的任务的栈指针,读取当前PSP进程指针,存入r0(M4还要考虑FPU压栈)
  • 手动压栈,并将最终结果封存至pxTopOfStack,方便下次恢复
  • 屏蔽中断
  • 调用vTaskSeitchContext(),获取当前最高优先级任务的任务控制块
  • 使能中断
  • 从最高优先级的TCB中获取pxTopOfStack,并手动出栈
  • 更新切换后的任务的的栈指针给PSP
  • PSP负责自动出栈
  • bx r14 执行新任务函数

查找最优先级任务vTaskSwitchContext( )

通过这个函数完成:taskSELECT_HIGHEST_PRIORITY_TASK( )

  • 使用硬件方式(本文使用)
  • 使用软件方式
 #define taskSELECT_HIGHEST_PRIORITY_TASK()
{
	UBaseType_t uxTopPriority;
	portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );
	configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0);
	listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );
}

前导置零指令
所谓的前导置零指令,大家可以简单理解为计算一个 32位数,头部 0 的个数。通过前导置零指令获得最高优先级

#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities )      uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )

在这里插入图片描述

获取最高优先级任务的任务控制块
通过该函数获取当前最高优先级任务的任务控制块

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
{
	List_t * const pxConstList = ( pxList );
	( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
	if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ){
		(pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
	}
	( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/886133.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

99%的Python用户都不知道的f-string隐秘技巧

f-string想必很多Python用户都基础性的使用过,作为Python3.6版本开始引入的特性,通过它我们可以更加方便地向字符串中嵌入自定义内容,但f-string真正蕴含的功能远比大多数用户知道的要丰富,今天我们就来一起get它们~ 「最基础用法…

JVS开源基础框架:平台基本信息介绍

JVS是面向软件开发团队可以快速实现应用的基础开发脚手架,主要定位于企业信息化通用底座,采用微服务分布式框架,提供丰富的基础功能,集成众多业务引擎,它灵活性强,界面化配置对开发者友好,底层容…

MySQL性能分析之慢查询日志查看

一、背景 MySQL的慢查询日志是MySQL提供的一种日志记录,他用来记录在MySQL中响应的时间超过阈值的语句,具体指运行时间超过long_query_time(默认是10秒)值的SQL,会被记录到慢查询日志中。 慢查询日志一般用于性能分析时开启,收集慢SQL然后通过explain进行全面分析,一…

GPU Dissolve(GPU 消散)学习GPU Instancing

一:摘要 通过制作一个模型GPU消散效果来学习GPU Instancing 也就是实例化。 目标效果是杨超大佬文章《GPU shatter》里面的消散效果如图: Tags:模型顶点分裂(Mesh Vertex Splitting), 实例化绘制(GPU Instancing Drawing)&#x…

【100天精通python】Day36:GUI界面编程_高级功能操作和示例

专栏导读 专栏订阅地址:https://blog.csdn.net/qq_35831906/category_12375510.html 一、GUI 高级功能 1 自定义主题和样式 自定义主题和样式可以让你的GUI应用程序在外观方面更加出色。在使用Tkinter时,你可以使用ttkthemes库来应用不同的主题和样式。…

一文搞懂Spring是如何解决Bean循环依赖的?

一.什么是Bean循环依赖 循环依赖是指Bean对象循环引用,是两个或多个Bean之间相互持有对方的引用,循环依赖有2中表现形式 第一种相互依赖,就是A依赖B,B又依赖A 第二种是自我依赖,就是A依赖自己形成自我依赖 对象引用…

上山取石,下江取锦。诗人秋浦啸傲,新津樵唱。江南山水秀美,水乡文化流长。而水,则是这些山水风景的灵魂所在。 水,雨露滋润万物生长的泉源。 它潺潺流淌于山间溪涧,涓涓细流化成了青山的眼泪。水顺势而下&a…

Unity如何制作声音控制条(控制音量大小)

一:UGUI制作 1. 首先在【层级】下面创建UI里面的Slider组件。设置好它对应的宽度和高度。 2.调整Slider滑动条的填充颜色。一般声音颜色我黄色,所以我们也调成黄色。 我们尝试滑动Slider里面的value。 a.滑动前。 b.滑动一半。 c.滑动完。 从以上滑动va…

Openai中的tokens怎么估计

大规模语言模型(LLM)的出现给自然语言处理领域带来了变革的可能性,Openai开放了chatgpt的API,方便了开发人员使用LLM的推理能力,注册时赠送5美元的使用额度,有效期3个月。 如果想便捷的使用chatgpt的API&a…

判断推理

六哥爱学习呀 产品经理 不是说我努力学习我就一定可以通过考试,所以是推不出,类似数学中充分必要性 8 回复 发布于 2019-08-07 16:28 官方解析: 当丙的范围足够大时,可能与甲相交或完全包含甲,在此情况下,有…

【数据结构】顺序队列模拟实现

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

ansible 修改远程主机nginx配置文件

安装ansible brew install ansible 或者 pip3 install ansible 添加远程主机 设置秘钥 mac登录远程主机 ssh -p 5700 root192.168.123.211 ssh localhost #设置双机信任 ssh-kyegen -t rsa #设置主机两边的ssh配置文件 vi /etc/ssh/sshd_config/ PermitRootL…

C++写文件,直接写入结构体

C写文件,直接写入结构体 以前写文件都是写入字符串或者二进制再或者就是一些配置文件,今天介绍一下直接写入结构体,可以在软件参数较多的时候直接进行读写,直接将整个结构体写入和读取,看代码: #include&…

除了ping你用过traceroute吗

一、检查两个计算机间的网络是否通 在检查两个机器的网络通不通,我们经常使用的命令是ping 但是当ping不通时,我们就不知道网络是哪里不通了,只能找网管排查。 这里介绍一个检查网络工具 traceroute 二 、TraceRoute是什么 TraceRoute的中文…

驱动 - 20230816

练习 1.编写LED灯的驱动,可以控制三个灯,应用程序中编写控制灯的逻辑,要使用自动创建设备节点机制 驱动头文件 ledHead.h #ifndef __HEAD_H__ #define __HEAD_H__#define PHY_GPIOE_MODER 0X50006000 #define PHY_GPIOE_ODR 0X50006014 #d…

toB营销如何从品牌营销转向获客营销?

“解构纷享新营销,赋能用户新增长”,这是2023年下半年,纷享销客践行“以客户成功定义成功”价值观,针对企业用户市场营销领域的全国巡回研讨会,希望把纷享销客在成长路上经历的、收获的经验、踩过的“坑”与用户共享&a…

Postman如何做接口测试:什么?postman 还可以做压力测试?

我们都知道, postman 是一款很好用的接口测试工具。不过 postman 还可以做简单的压力测试,而且步骤只需要 2 步。 首先,打开 postman, 编写接口的请求参数。 然后,点击右下方的 runner 运行器,把需要测试的接口拖动到…

序列模型和循环网络

Sequence Modeling and Recurrent Networks Sequence modeling tasks 在以往的模型中,各个输入之间是独立分布的 x ( i ) x^{(i)} x(i) 之间是相互独立的,同样输出 y ( i ) y^{(i)} y(i)之间也是相互独立的。 但是在序列模型中,输入输出是…

应用开源框架平台,实现流程化办公!

如今,实现流程化办公,管理好数据资源是很多企业的共同想法。如果采用传统的办公方式显然无法实现这一愿望。利用开源框架平台,可以管理好数据资源,为企业提高办公协作效率,进入流程化办公。流辰信息是专业的低代码技术…

如何使用Python编写小游戏?

大家好,我是沐尘而生,如果你是一个热爱编程的小伙伴,又想尝试游戏开发,那么这篇文章一定能满足你的好奇心。不废话,让我们马上进入Python游戏开发的精彩世界吧! Python游戏开发的魅力 编写小游戏不仅仅是锻…