FreeRTOS学习 --- 任务调度
开启任务调度器
作用:用于启动任务调度器,任务调度器启动后, FreeRTOS 便会开始进行任务调度
该函数内部实现,如下:
1、创建空闲任务(优先级最低)
2、如果使能软件定时器,则创建定时器任务(优先级最高)
3、关闭中断,防止调度器开启之前或过程中,受中断干扰,会在运行第一个任务时打开中断(在SVC中断中,通过设置basepri 为 0 来开启中断)
4、初始化全局变量,并将任务调度器的运行标志设置为已运行
5、初始化任务运行时间统计功能的时基定时器
6、调用函数 xPortStartScheduler()完成启动任务调度器中与硬件架构相关的配置部分,以及启动第一个任务
xPortStartScheduler()
作用:该函数用于完成启动任务调度器中与硬件架构相关的配置部分,以及启动第一个任务
该函数内部实现,如下:
1、检测用户在 FreeRTOSConfig.h 文件中对中断的相关配置是否有误
2、配置 PendSV 和 SysTick 的中断优先级为最低优先级
3、调用函数 vPortSetupTimerInterrupt()配置 SysTick
4、初始化临界区嵌套计数器为 0
5、调用函数 prvEnableVFP()使能 FPU(M3中不支持FPU浮点单元)
6、调用函数 prvStartFirstTask()启动第一个任务
prvStartFirstTask()
函数 prvStartFirstTask()用于初始化启动第一个任务前的环境,主要是重新设置 MSP 指针,并使能全局中断。
1. 首先是使用了 PRESERVE8,进行 8 字节对齐,这是因为,栈在任何时候都是需要 4 字节对齐的,而在调用入口得 8 字节对齐,在进行 C 编程的时候,编译器会自动完成的对齐的操作,而对于汇编,就需要开发者手动进行对齐。
2.获得 MSP 指针的初始值
ldr r0, =0xE000ED08 /* 0xE000ED08 为 VTOR 地址 */
ldr r0, [ r0 ] /* 获取 VTOR 的值 */
ldr r0, [ r0 ] /* 获取 MSP 的初始值 */
(1) 什么是 MSP 指针?
程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时, MCU 会自动更新 SP 指针,使 SP 指针指向最后一个入栈的元素,那么程序就可以根据 SP 指针来从栈中存取信息。对于正点原子的 STM32F1、 STM32F4、 STM32F7 和 STM32H7 开发板上使用的 ARM Cortex-M 的 MCU 内核来说, ARM Cortex-M 提供了两个栈空间, 这两个栈空间的堆栈指针分别是 MSP(主堆栈指针) 和 PSP(进程堆栈指针)。在 FreeRTOS 中 MSP 是给系统栈空间使用的,而 PSP 是给任务栈使用的,也就是说, FreeRTOS 任务的栈空间是通过 PSP 指向的,而在进入中断服务函数时,则是使用 MSP 指针。当使用不同的堆栈指针时, SP 会等于当前使用的堆栈指针。
(2) 为什么是 0xE000ED08?
0xE000ED08 是 VTOR(向量表偏移寄存器)的地址, VTOR 中保存了向量表的偏移地址。一般来说向量表是从其实地址 0x00000000 开始的,但是在有情况下,可能需要修改或重定向向量表的首地址,因此 ARM Corten-M 提供了 VTOR 对向量表进行从定向。而向量表是用来保存中断异常的入口函数地址,即栈顶地址的,并且向量表中的第一个字保存的就是栈底的地址,在 start_stm32xxxxxx.s 文件中有如下定义:
__Vectors DCD __initial_sp ; 栈底指针DCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault Handler
以上就是向量表(只列出前几个)的部分内容,可以看到向量表的第一个元素就是栈指针的初始值,也就是栈底指针。
在了解了这两个问题之后,接下来再来看看代码。首先是获取 VTOR 的地址,接着获取VTOR 的值,也就是获取向量表的首地址,最后获取向量表中第一个字的数据,也就是栈底指针了。
3. 在获取了栈顶指针后,将 MSP 指针重新赋值为栈底指针。这个操作相当于丢弃了程序之前保存在栈中的数据,因为FreeRTOS从开启任务调度器到启动第一个任务都是不会返回的,是一条不归路,因此将栈中的数据丢弃,也不会有影响。
4. 重新赋值 MSP 后,接下来就重新使能全局中断,因为之前在函数 vTaskStartScheduler()中关闭了受 FreeRTOS 的中断
5. 最后使用 SVC 指令,并传入系统调用号 0,触发 SVC 中断。
SVC中断处理函数vPortSVCHandler()
当使能了全局中断,并且手动触发 SVC 中断后,就会进入到 SVC 的中断服务函数中。 SVC的中断服务函数为 vPortSVCHandler(),该函数在 port.c 文件中有定义,具体的代码如下所示(这里以正点原子的 STM32F1 系列开发板为例,其他类型的开发板类似):
_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, # 0msr basepri,/* 使用 PSP 指针,并跳转到任务函数 */orr r14, # 0xdbx r14
}
从上面代码中可以看出,函数 vPortSVCHandler()就是用来跳转到第一个任务函数中去的,该函数的具体解析如下:
1. 首先通过 pxCurrentTCB 获取优先级最高的就绪态任务的任务栈地址,优先级最高的就绪态任务就是系统将要运行的任务。 pxCurrentTCB 是一个全局变量,用于指向系统中优先级最高的就绪态任务的任务控制块
举个例子:
定时器处理任务的任务优先级为 31,是系统中优先级最高的任务,因此当进入 SVC 中断时, pxCurrentTCB 就是指向了定时器处理任务的任务控制块。
接着通过获取任务控制块中的第一个元素,得到该任务的栈顶指针,任务控制块的相关内容
2. 接下来通过任务的栈顶指针,将任务栈中的内容出栈到 CPU 寄存器中,任务栈中的内容在调用任务创建函数的时候,已经初始化了。然后再设置 PSP 指针,那么,这么一来,任务的运行环境就准备好了。(这里的任务栈顶指针就是最下图里的pxTopOfStack)
3. 通过往 BASEPRI 寄存器中写 0,允许中断。
4. 最后通过两条汇编指令,使 CPU 跳转到任务的函数中去执行,代码如下所示:
orr r14, # 0xd
bx r14
要弄清楚这两条汇编代码,首先要清楚 r14 寄存器是干什么用的。 通常情况下, r14 为链接寄存器(LR),用于保存函数的返回地址。但是在异常或中断处理函数中, r14 为 EXC_RETURN (关于 r14 寄存器的相关内容,感兴趣的读者请自行查阅相关资料), EXC_RETURN 各比特位的描述如下表所示:

XC_RETURN 只有 6 个合法的值,如下表所示:

为此时是在 SVC 的中断服务函数中,因此此时的 r14 应为 EXC_RETURN,将 r14 与 0xd作或操作,然后将值写入 r14,那么就是将 r14 的值设置为了 0xFFFFFFFD 或 0xFFFFFFED(具体看是否使用了浮点单元),即返回后进入线程模式,并使用 PSP。这里要注意的是, SVC 中断服务函数的前面,将 PSP 指向了任务栈。
说了这么多, FreeRTOS 对于进入中断后 r14 为 EXC_RETURN 的具体应用就是,通过判断EXC_RETURN 的 bit4 是否为 0,来判断任务是否使用了浮点单元。
最后通过 bx r14 指令,跳转到任务的任务函数中执行,执行此指令, CPU 会自动从 PSP 指向的栈中出栈 R0、 R1、 R2、 R3、 R12、 LR、 PC、 xPSR 寄存器, 并且如果 EXC_RETURN 的bit4 为 0(使用了浮点单元),那么 CPU 还会自动恢复浮点寄存器。
出栈/压栈汇编指令详解

任务栈图解:

相关文章:
FreeRTOS学习 --- 任务调度
开启任务调度器 作用:用于启动任务调度器,任务调度器启动后, FreeRTOS 便会开始进行任务调度 该函数内部实现,如下: 1、创建空闲任务(优先级最低) 2、如果使能软件定时器,则创建定…...
2025年人工智能技术:Prompt与Agent的发展趋势与机遇
文章目录 一、Prompt与Agent的定义与区别(一)定义(二)区别二、2025年Prompt与Agent的应用场景(一)Prompt的应用场景(二)Agent的应用场景三、2025年Prompt与Agent的适合群体(一)Prompt适合的群体(二)Agent适合的群体四、2025年Prompt与Agent的发展机遇(一)Prompt的…...
区块链 智能合约安全 | 回滚攻击
视频教程在我主页简介和专栏里 目录: 智能合约安全 回滚攻击 总结 智能合约安全 回滚攻击 回滚攻击的本质是”耍赖” 举一个简单的例子,两个人玩石头剪刀布,输了的给对方10块钱,现在A输了,A说这把不算,重来 放在Solidity中,require()函数会检测其中的条件是否满…...
【Go语言圣经】第六节:方法
第六章:方法 6.1 方法声明 在函数声明时,在其名字之前放上一个变量,这就是声明了变量对应类型的一个方法,相当于为这种类型定义了一个独占的方法。 下例为 Point 类型声明了计算两个点之间距离的方法: package mai…...
【JavaEE进阶】图书管理系统 - 壹
目录 🌲序言 🌴前端代码的引入 🎋约定前后端交互接口 🚩接口定义 🍃后端服务器代码实现 🚩登录接口 🚩图书列表接口 🎄前端代码实现 🚩登录页面 🚩…...
TensorFlow 简单的二分类神经网络的训练和应用流程
展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括: 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中,数据准备是通过两个 Numpy 数…...
docker安装Redis:docker离线安装Redis、docker在线安装Redis、Redis镜像下载、Redis配置、Redis命令
一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull redis:7.4.0 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜像…...
Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview
Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview 文章目录 Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview1. Introduction&Abstract1. LLM面临的问题2. RAG核心三要素3. RAG taxonomy 2. Overv…...
LabVIEW透镜多参数自动检测系统
在现代制造业中,提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统,该系统能够在单一工位上完成透镜的多项质量参数检测,并实现透镜的自动搬运与分选,极大地提升了检…...
什么是Maxscript?为什么要学习Maxscript?
MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…...
Redis|前言
文章目录 什么是 Redis?Redis 主流功能与应用 什么是 Redis? Redis,Remote Dictionary Server(远程字典服务器)。Redis 是完全开源的,使用 ANSIC 语言编写,遵守 BSD 协议,是一个高性…...
128周二复盘(164)学习任天堂
1.设计相关 研究历史上某些武器数值,对一些设定进行参数修改。兼顾真实性,合理性,娱乐性。 学习宫本茂游戏思想,简单有趣-重玩性,风格化个性化-反拟真。对堆难度与内容的反思。 后续将学习岩田聪以及别的任天堂名人的…...
LeetCode:63. 不同路径 II
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:63. 不同路径 II 给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0]…...
VUE之组件通信(一)
1、props 概述:props是使用频率最高的一种通信方式,常用与:父<——>子。 若 父传子:属性值是非函数。若 子传父:属性值是函数。 父组件: <template><div class"father">&l…...
ESP32-S3模组上跑通esp32-camera(41)
接前一篇文章:ESP32-S3模组上跑通esp32-camera(40) 一、OV5640初始化 2. 相机初始化及图像传感器配置 上一回继续对reset函数的后一段代码进行解析。为了便于理解和回顾,再次贴出reset函数源码,在components\esp32-camera\sensors\ov5640.c中,如下: static int reset…...
本地部署DeepSeek R1:打造专属私人AI助手指南
在当今人工智能蓬勃发展的浪潮中,DeepSeek R1模型的本地部署为用户带来了全新的体验。它不仅能够保障数据隐私,还具备与商业AI模型相媲美的出色性能。随着计算能力的不断提升以及开源AI社区的日益壮大,用户如今可以在本地运行高性能AI模型&am…...
Redis-布隆过滤器
文章目录 布隆过滤器的特点:实践布隆过滤器应用 布隆过滤器的特点: 就可以把布隆过滤器理解为一个set集合,我们可以通过add往里面添加元素,通过contains来判断是否包含某个元素。 布隆过滤器是一个很长的二进制向量和一系列随机映射函数。 可以用来检索…...
OpenCV 版本不兼容导致的问题
问题和解决方案 今天运行如下代码,发生了意外的错误,代码如下,其中输入的 frame 来自于 OpenCV 开启数据流的读取 """ cap cv2.VideoCapture(RTSP_URL) print("链接视频流完成") while True:ret, frame cap.rea…...
【视频+图文详解】HTML基础3-html常用标签
图文教程 html常用标签 常用标签 1. 文档结构 <!DOCTYPE html>:声明HTML文档类型。<html>:定义HTML文档的根元素。<head>:定义文档头部,包含元数据。<title>:设置网页标题,浏览…...
【B站保姆级视频教程:Jetson配置YOLOv11环境(五)Miniconda安装与配置】
Jetson配置YOLOv11环境(5)Miniconda安装与配置 文章目录 0. Anaconda vs Miniconda in Jetson1. 下载Miniconda32. 安装Miniconda33. 换源3.1 conda 换源3.2 pip 换源 4. 创建环境5. 设置默认启动环境 0. Anaconda vs Miniconda in Jetson Jetson 设备资…...
【PLL】杂散生成和调制
时钟生成 --》 数字系统 --》峰值抖动频率生成 --》无线系统 --》 频谱纯度、 周期信号的相位不确定性 随机抖动(random jitter, RJ)确定性抖动(deterministic jitter,DJ) 时域频域随机抖动积分相位噪声确定性抖动边带 杂散生成和…...
游戏引擎 Unity - Unity 启动(下载 Unity Editor、生成 Unity Personal Edition 许可证)
Unity Unity 首次发布于 2005 年,属于 Unity Technologies Unity 使用的开发技术有:C# Unity 的适用平台:PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域:开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…...
侯捷 C++ 课程学习笔记:深入理解 C++ 核心技术与实战应用
目录 引言 第一章:C 基础回顾 1.1 C 的历史与发展 1.2 C 的核心特性 1.3 C 的编译与执行 第二章:面向对象编程 2.1 类与对象 2.2 构造函数与析构函数 2.3 继承与多态 第三章:泛型编程与模板 3.1 函数模板 3.2 类模板 3.3 STL 容器…...
Java的Integer缓存池
Java的Integer缓冲池? Integer 缓存池主要为了提升性能和节省内存。根据实践发现大部分的数据操作都集中在值比较小的范围,因此缓存这些对象可以减少内存分配和垃圾回收的负担,提升性能。 在-128到 127范围内的 Integer 对象会被缓存和复用…...
【C++动态规划 离散化】1626. 无矛盾的最佳球队|2027
本文涉及知识点 C动态规划 离散化 LeetCode1626. 无矛盾的最佳球队 假设你是球队的经理。对于即将到来的锦标赛,你想组合一支总体得分最高的球队。球队的得分是球队中所有球员的分数 总和 。 然而,球队中的矛盾会限制球员的发挥,所以必须选…...
python 判断复杂包含
目录 python 判断复杂包含 a和b都是拍好序的: python 判断复杂包含 a[10,13,15] b[[9,11],[11,13],[13,16]] b的子项是区间,返回b中子区间包含a其中元素的子项 if __name__ __main__:a [10, 11, 15]b [[9, 11], [11, 13], [13, 16]]# 筛选出包含…...
Teleporters( Educational Codeforces Round 126 (Rated for Div. 2) )
Teleporters( Educational Codeforces Round 126 (Rated for Div. 2) ) There are n 1 n1 n1 teleporters on a straight line, located in points 0 0 0, a 1 a_1 a1, a 2 a_2 a2, a 3 a_3 a3, …, a n a_n an. It’s possible to tele…...
css-设置元素的溢出行为为可见overflow: visible;
1.前言 overflow 属性用于设置当元素的内容溢出其框时如何处理。 2. overflow overflow 属性的一些常见值: 1 visible:默认值。内容不会被剪裁,会溢出元素的框。 2 hidden:内容会被剪裁,不会显示溢出的部分。 3 sc…...
python-leetcode-从中序与后序遍历序列构造二叉树
106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode) # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right r…...
绝对值线性化
函数中的绝对值线性化有多种方法,包括我之前的一篇博文. 前几天在小红书刷到一个帖子,一位网友提供了另外一种巧妙的方式,记录如下。 假如有一个绝对值表达式: y ∣ a x − b ∣ (1) y|ax-b|\tag{1} y∣ax−b∣(1) 令&#x…...
