当前位置: 首页 > news >正文

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、如果使能软件定时器,则创建定…...

【小鱼闪闪】单片机开发工具——米思齐软件下载安装(图文)

浏览器打开网址 mixly.org, 在软件平台选择mixly离线版。 最新版本为3.0,会支持audinio, ESP32、ESP8266 , 可以选择下载安装器或者完整版。 这里选择下载安装器,下载后运行“一键更新.bat”,即可自动下载最新版本的M…...

MFC开发,给对话框添加垂直滚动条并解决鼠标滚动响应的问题

无论在使用QT或者MFC进行界面开发时,都会出现在一个对话框里面存在好多的选项,导致对话框变得非常长或者非常大,就会显现的不美观,在这种情况下通常是添加一个页面的滚动条来解决这个问题,下面我们就来介绍给MFC的对话…...

动态规划DP 最长上升子序列模型 导弹防御模型(题目分析+C++完整代码实现)

概览检索 动态规划DP 最长上升子序列模型 导弹防御系统 原题链接 AcWiing 187. 导弹防御系统 题目描述 为了对抗附近恶意国家的威胁,R国更新了他们的导弹防御系统。 一套防御系统的导弹拦截高度要么一直 严格单调 上升要么一直 严格单调 下降。 例如&#xff0…...

LevelDB 源码阅读:写入键值的工程实现和优化细节

读、写键值是 KV 数据库中最重要的两个操作,LevelDB 中提供了一个 Put 接口,用于写入键值对。使用方法很简单: leveldb::Status status leveldb::DB::Open(options, "./db", &db); status db->Put(leveldb::WriteOptions…...

药店药品销售管理系统的设计与实现

标题:药店药品销售管理系统的设计与实现 内容:1.摘要 摘要:本文介绍了药店药品销售管理系统的设计与实现。该系统旨在提高药店的运营效率和管理水平,通过信息化手段实现药品销售、库存管理、财务管理等功能。本文详细阐述了系统的需求分析、设计思路、技…...

人格分裂(交互问答)-小白想懂Elasticsearch

通过交互式追问了解一个中间件 ? 啥是Elasticsearch ! 分布式搜索和分析引擎 ? 为啥是分布式搜索,单体难道用不了吗 ? 实际上是说这个东西可以分布式部署 ! 单机可用但扩展性差,分布式通过分片、副本和负载均衡实现海量数据存储与高并发处理 ? 提…...

【论文投稿-第八届智能制造与自动化学术会议(IMA 2025)】HTML, CSS, JavaScript:三者的联系与区别

大会官网:www.icamima.org 目录 前言 一、HTML(超文本标记语言):网页的骨架 HTML 的作用: 例子: 总结: 二、CSS(层叠样式表):网页的外观设计 CSS 的…...

python | OpenCV小记(一):cv2.imread(f) 读取图像操作(待更新)

python | OpenCV小记(一):cv2.imread(f)读取图像操作 1. 为什么 [:, :, 0] 提取的是第一个通道(B 通道)?OpenCV 的通道存储格式索引操作 [:, :, 0] 的解释常见误解 1. 为什么 [:, :,…...

网络工程师 (9)文件管理

一、树形目录结构 (一)定义与构成 树形目录结构由一个根目录和若干层子文件夹(或称为子目录)组成,它像一棵倒置的树。这棵树的根称为根文件夹(也叫根目录),从根向下,每一…...

Java中的线程池参数(详解)

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {} 此构造方法的参数如下&#xff1a; int corePoolSize&…...

2 MapReduce

2 MapReduce 1. MapReduce 介绍1.1 MapReduce 设计构思 2. MapReduce 编程规范3. Mapper以及Reducer抽象类介绍1.Mapper抽象类的基本介绍2.Reducer抽象类基本介绍 4. WordCount示例编写5. MapReduce程序运行模式6. MapReduce的运行机制详解6.1 MapTask 工作机制6.2 ReduceTask …...

如何用函数去计算x年x月x日是(C#)

如何用函数去计算x年x月x日是? 由于现在人工智能的普及,我们往往会用计算机去算,或者去记录事情 1.计算某一年某一个月有多少天 2.计算某年某月某日是周几 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threadin…...

开发过程中如何减少属性注释?

一、注释冗余 举个例子&#xff0c;我们在开发项目中肯定会有状态字段&#xff0c;现在有个工单状态枚举 StatusEnum.java package cn.zxj.note;/*** author: Administrator* since: 2025/1/30 14:40* description:*/ public enum StatusEnum {TO_BE_SUBMITTED(1,"待提交…...

NX/UG二次开发—CAM—快速查找程序参数名称

使用UF_PARAM_XXX读取或设置参数时,会发现程序中有一个INT类型参数param_index,这个就是对应程序中的参数,比如读取程序余量,则param_index = UF_PARAM_STOCK_PART,读取程序的加工坐标系则param_index = UF_PARAM_MCS等等。 你需要读取什么参数,只要只能在uf_param_indic…...

socket实现HTTP请求,参考HttpURLConnection源码解析

背景 有台服务器&#xff0c;网卡绑定有2个ip地址&#xff0c;分别为&#xff1a; A&#xff1a;192.168.111.201 B&#xff1a;192.168.111.202 在这台服务器请求目标地址 C&#xff1a;192.168.111.203 时必须使用B作为源地址才能访问目标地址C&#xff0c;在这台服务器默认…...

访问CMOS RAM

实验内容、程序清单及运行结果 访问CMOS RAM&#xff08;课本实验14&#xff09; 代码如下&#xff1a; assume cs:code data segment time db yy/mm/dd hh:mm:ss$ ;int 21h 显示字符串&#xff0c;要求以$结尾 table db 9,8,7,4,2,0 ;各时间量的存放单元 data ends cod…...

解决AnyConnect开机自启动问题

文章目录 一、问题描述二、解决方案 (Windows)1.开启-设置2.点击“应用”3.点击“启动”&#xff0c;选择“关” 三、参考文章 一、问题描述 学校指定的VPN总是开机自启动&#xff0c;然而 设置-Preferences 中却没有取消开机自启的选项。 似乎开机自启是必然的&#xff0c;我…...

芯片AI深度实战:进阶篇之vim内verilog实时自定义检视

【痛点】 传统Verilog开发中,工程师不断"编码→仿真→查错"的循环。本文整合AST解析与Vim编辑器,在编码阶段即实现: ✔️ 自动标记逻辑问题 ✔️ AI+ 发现涉及多模块逻辑错误 ✔️ 强制代码风格 【解决方案】 1️⃣ 基于AST的精准模式匹配 - 深度集成…...

数据结构实战之线性表(一)

一.线性表的定义和特点 线性表的定义 线性表是一种数据结构&#xff0c;它包含了一系列具有相同特性的数据元素&#xff0c;数据元素之间存在着顺序关系。例如&#xff0c;26个英文字母的字符表 ( (A, B, C, ....., Z) ) 就是一个线性表&#xff0c;其中每个字母就是一个数据…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...