FreeRTOS内核源码解读之-------系统启动(三)

2023-05-16

前面文章两篇文章介绍了FreeRTOS的启动过程,但是有些问题还没有解决,在本篇文章中将会逐一解决。
首先,在《FreeRTOS内核源码解读之-------系统启动(一)》中提到Cortex-M4内核中两个不同的栈指针寄存器MSP和PSP。对于不具有嵌入式OS的应用,可以在操作中只使用MSP栈指针寄存器;对于含有嵌入式OS(就像FreeRTOS)应用,异常处理(包括内核状态下)使用的是MSP,对于应用任务使用的是PSP。每一个应用任务都有自己的栈空间,那么上面这种机制是怎么实现的呢?
还有FreeRTOS是一个多任务运行的操作系统,那么当一个任务正在运行时,会被优先级更高的任务或者中断打断,FreeRTOS是怎么保证被打断的任务在重新运行时不会出现问题?换句话说,如何实现保护现场、任务切换等?
本文从这两个问题触发,具体阐述FreeRTOS内核启动过程,在下一篇介绍FreeRTOS任务调度。

  • 问题引入
  • Cortex-M4对于多任务运行的硬件架构支持特性
    一、问题引入
    通过上一篇文章《FreeRTOS内核源码解读之-------系统启动()》分析,我们知道FreeRTOS最后调用prvStartFirstTask产生一个SVC中断,产生SVC中断之后,在函数vPortSVCHandler进行有FreeRTOS内核态到用户态的切换。两个函数的代码如下:
__asm void prvStartFirstTask( void )
{
	PRESERVE8
	    /* Cortext-M3硬件中,0xE000ED08地址处为VTOR(向量表偏移量)寄存器,存储向量表起始地址*/
    ldr r0, =0xE000ED08    
    ldr r0, [r0]
    /* 取出向量表中的第一项,向量表第一项存储主堆栈指针MSP的初始值*/
    ldr r0, [r0]   
    /* 将堆栈地址存入主堆栈指针 */
    msr msp, r0
    /* 使能全局中断*/
    cpsie i
    cpsie f
    dsb
    isb
    /* 调用SVC启动第一个任务 */
    svc 0
    nop
    nop
}
__asm void vPortSVCHandler( void )
{
	PRESERVE8
	ldr	r3, =pxCurrentTCB
	ldr r1, [r3]
	ldr r0, [r1]
	ldmia r0!, {r4-r11, r14}
	msr psp, r0
	isb
	mov r0, #0
	msr	basepri, r0
	bx r14
}

首先FreeRTOS从内核态切换到用户态时,是怎样切换MSP和PSP栈指针的呢?还有FreeRTOS是怎样找到应用任务的栈以及用户编写的应用程序的呢?
在函数prvStartFirstTask主要做的内容如下:
1)将堆栈地址存入主栈指针寄存器,在中断向量表中第一项是堆栈地址;
2)开中断;
3)产生一个SVC中断。
在函数prvStartFirstTask中设置了MSP主堆栈指针。
在函数vPortSVCHandler可以看到对寄存器进行了赋值,对PSP进行了赋值、然后进行程序跳转。那么猜测关于上面的疑问可以从这几句来寻求答案。
二、应用任务栈帧
在函数vPortSVCHandler中会看到这么一句 ldr r3, =pxCurrentTCB ,pxCurrentTCB变量存放的是当前需要运行任务的控制块。代码:

ldr	r3, =pxCurrentTCB
	ldr r1, [r3]
	ldr r0, [r1]

具体作用是什么的?
首先看任务控制块中的内容(去掉各种条件编译):

typedef struct tskTaskControlBlock
{
	volatile StackType_t	*pxTopOfStack;
	ListItem_t			xStateListItem;
	ListItem_t			xEventListItem;	
	UBaseType_t			uxPriority;	
	StackType_t			*pxStack;
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];
} tskTCB;

去掉条件编译感觉好清爽,这里只是关注第一个成员变量,为了填充一下版面,请允许我这么做。关于任务控制块各成员变量的讲解,请关注我的另一篇文章 《FreeRTOS内核源码解读之-------任务创建》 。由此可知 ldr r3, =pxCurrentTCB 其实就是 ldr r3, =pxTopOfStack。那么,最后就是使得r0寄存器保存任务栈指针。
那么,任务栈里面存放的是什么呢?由于任务第一次运行,那么需要去任务创建函数中寻找一点蛛丝马迹。
任务创建函数调用过程如下:

xTaskCreate->prvInitialiseNewTask->pxPortInitialiseStack

从这里看出任务创建函数xTaskCreate,最终会调用pxPortInitialiseStack。函数pxPortInitialiseStack代码如下(下面就分析一下):

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	/* Simulate the stack frame as it would be created by a context switch
	interrupt. */

	/* Offset added to account for the way the MCU uses the stack on entry/exit
	of interrupts, and to ensure alignment. */
	pxTopOfStack--;

	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */

	/* Save code space by skipping register initialisation. */
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */

	/* A save method is being used that requires each task to maintain its
	own exec return value. */
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_EXEC_RETURN;

	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

	return pxTopOfStack;
}

代码不是很多,就一句一句的分析一下吧。
1)*pxTopOfStack = portINITIAL_XPSR:栈顶保存了xPSR,且它的值为portINITIAL_XPSR,0x01000000。其实就是一个初始状态,其中的1表示Thumb状态。因为Cortex-M只有Thumb状态。
2)*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK : portSTART_ADDRESS_MASK=0xfffffffe,pxCode保存的是任务函数的函数指针,就是我们用户编写的任务函数,这句就是保存任务跳转时的跳转地址,这里要求必须半字或者字对齐,因此需要&portSTART_ADDRESS_MASK。
3)*pxTopOfStack = ( StackType_t ) prvTaskExitError :该数值是赋值给LR寄存器,保存任务出错处理函数指针,一般不会执行这个函数,因为我们的任务都是一个不能返回的死循环,因此不会执行。
4)pxTopOfStack -= 5:留出空间用于保存寄存器 R12, R3, R2 and R1。
5)*pxTopOfStack = ( StackType_t ) pvParameters:保存任务函数的参数。
6)*pxTopOfStack = portINITIAL_EXEC_RETURN:处理器进入异常处理或者中断模式的时候,连接寄存器的数值会被更新为portINITIAL_EXEC_RETURN,当我们使用BX或者LDR这类指令时,会将该数值写入程序寄存器,是用来触发异常返回机制。
7)pxTopOfStack -= 8:保存r4~r11。
通过上面分析,我们大体画出任务栈中保存的数据如下:
在这里插入图片描述那么我们在回过来看一下函数vPortSVCHandler具体做了哪些事情:
1)将任务栈保存的R11-R4内容赋给寄存器R11-R4;
2)将portINITIAL_EXEC_RETURN赋给寄存器R14,主要是用于触发中断返回,也就是下面 bx r14 指令;
3)pvParameters 赋值给PSP寄存器。
那么现在就需要解决FreeRTOS运行用户任务时,MSP到PSP切换的问题。玄妙之处就是portINITIAL_EXEC_RETURN,上面说了将portINITIAL_EXEC_RETURN=0xfffffffd写入到R14寄存器(LR)之后,我们调用bx r14指令之后就会产生中断返回,通过查阅资料得到如下图:
在这里插入图片描述正是写入的是0xfffffffd,所以中断返回的时候是的处理器进入处理模式,并且使用进程栈PSP寄存器。
总结: 本文主要解决了SVC中断处理函数中具体执行内容,和任务创建过程中任务栈中存放的数据。以及如何从MSP栈指针切换到PSP栈指针。下一篇文章将对FreeRTOS中任务切换进行讲解。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS内核源码解读之-------系统启动(三) 的相关文章

  • 电脑开机安装流氓软件、弹广告处理办法

    今天处理了一台开机自动安装流氓软件的电脑 xff0c 而且还弹广告 现在写下处理过程 文章目录 一 卸载软件 xff08 1 xff09 控制面板卸载 xff08 2 xff09 安装路径卸载 二 禁用任务计划程序三 删除软件安装包四 修改
  • VScode中使用git进行版本控制

    VScode中使用git进行版本控制 一 安装与配置二 初始化和提交本地仓库三 提交到远程仓库四 克隆和拉取1 克隆2 拉取 五 修改后的提交六 免密提交七 其他命令1 撤销2 Git 分支命令3 暂时保存更改4 同时提交多个仓库 一 安装
  • SLAM的前世今生 终于有人说清楚了

    SLAM的前世今生 终于有人说清楚了 硬创公开课 from xff1a http www leiphone com news 201605 5etiwlnkWnx7x0zb html 2016 05 16 19 40 宗仁 0 条评论 今年
  • 使用catkin进行工程管理

    catkin是什么 这是一个管理ros代码的工程管理工具 xff0c 基于cmake xff0c 但是比cmake多更多功能 xff0c 是cmake上层的代码管理规则 ros之前采用的是ros build xff0c 现在用catkin
  • ROS-melodic 安装 及:rosdep init 和 rosdep update 失败问题解决总结

    正常安装ros流程 ros 安装 melodic sudo sh c 39 etc lsb release amp amp echo 34 deb http mirrors tuna tsinghua edu cn ros ubuntu D
  • ubuntu 安装Opencv4版本后安装Opencv3,并在项目中区分使用

    安装 xff1a ubuntu 安装OpenCV3版本后安装OpenCV4 xff0c 且多版本共存 wyyang2的博客 CSDN博客 https blog csdn net wyyang2 article details 1039894
  • Ubuntu 16.04录屏软件

    Ubuntu 16 04安装录屏软件Simple Screen Recorder https www maartenbaert be simplescreenrecorder https jingyan baidu com article
  • debug时一个非常坑爹的问题——单步调试正常但是运行没结果

    单步调试能得到正确答案 xff0c 运行不出来 本人用的是codeblocks xff0c 20 03版本 话说有一天晚上 xff0c 那个不平凡的夜 xff0c 还有不到十个小时数据结构的实验课就要上交报告了 xff0c 而自己的程序却出
  • DataX使用、同步MySQL数据到HDFS案例

    文章目录 4 DataX使用4 1 DataX使用概述4 1 1 DataX任务提交命令4 1 2 DataX配置文件格式 4 2 同步MySQL数据到HDFS案例4 2 1 MySQLReader之TableMode4 2 1 1 编写配
  • 支持期限提至10年,Ubuntu 18.04 LTS

    给技术最前线加星标 xff0c 每天看技术热点 综合自 xff1a cnBeta COM Solidot Mark Shuttleworth宣布将支持Ubuntu 18 04 LTS长达十年时间 xff0c 让LTS版本的含金量更足 xff
  • 实训第五天:我的页面布局,API接口(一言,天气预报)

    1 不用不用脚本和弹窗获取手机信息 xff0c 用微信公众平台的组件开发能力 open data用于展示微信开放的数据 部分代码 xff1a lt view class 61 34 container 34 gt lt view class
  • 这是一个失败的程序员

    写程序至今 xff0c 未入门 xff0c 什么都不懂 xff01
  • adb dumpsys命令用法

    dumpsys命令功能很强大 xff0c 能dump系统服务的各种状态 xff0c 非常有必要熟悉该命令的用法以及含义 一 概述 1 1 dumpsys命令用法 可通过dumpsys命令查询系统服务的运行状态 对象的成员变量属性值 xff0
  • matlab subs函数

    在matlab命令行查看subs函数的帮助 subs函数一共有三种使用方法 xff1a subs s old new subs s new subs s 点击 subs 的参考页可以看到下面的详细说明 第一种使用方法 说明 xff1a su
  • Docker----如何更改docker镜像的存储路径

    原文链接 Docker 如何更改docker镜像的存储路径 背景 随着docker容器已经镜像的使用 xff0c docker镜像占用大量磁盘空间 xff0c 当然可以通过不断的删除镜像或者设置定时任务删除镜像 xff0c 但是有时候还是挺
  • ROS的geometry_msgs/PoseWithCovarianceStamped Message 消息格式

    溪西创客小屋 geometry msgs PoseWithCovarianceStamped Message Raw Message Definition This expresses an estimated pose with a re
  • 线程和进程的理解

    一 介绍线程和进程 什么是线程 是程序执行的最小单位 xff0c 一个进程在执行过程中产生建多个线程 xff0c 同一进程中的 多个 线程共享同一块内存空间及系统资源 xff0c 线程数进程的一部分 xff0c 因此线程数也被称为轻量级进程
  • Windows安装tensorflow-gpu

    0 想在Windows环境安装tensorflow gpu xff0c 显卡必须是N卡 xff08 本文以3070显卡为例进行说明 xff09 1 安装好Anaconda以及Pycharm xff08 安装教程 xff1a https ww
  • 16.进程-进程间通信概述

    进程间通信 xff0c 也就是大家常说的 IPC Inter Process Communication xff0c 指的是不同的进程间进行交流 xff0c 本质上就是进程之间发送和接收数据 xff1b 本质上 xff0c 信号也是属于进程
  • PNP问题-位姿估计方法梳理(pose estimation)

    tags 单目视觉 位姿测量 目标3D精确模型已知 xff08 建立2D 3D对应关系 xff09 xff1a 点特征 P3P问题 基于针孔成像模型 Gao的方法 xff08 opencv emgucv xff09 Kneip 的 P3P

随机推荐

  • 室内无人机定位导航

    个人观点 xff1a 可研究的方向 1 静态规划方面 xff1a 将控制与定位结合起来 xff1b 修正回环检测误差 xff0c 提高算法的计算精度和执行效率 xff1b 2 动态规划方面 xff1a 用神经网络识别运动物体的行进方向 xf
  • 树莓派3b程序控制无人机 (一)——电脑连树莓派

    设备 xff1a 树莓派3b xff08 备有键盘 xff0c 鼠标 xff09 xff1b win10 x64笔记本 xff1b UAV pixhawk飞控板 etc network interfaces 的设置可参考以下链接 xff1a
  • 三星6410裸机程序开发

    网上关于S3C6410裸机程序开发都是基于RealView RVDS 也有一些是基于eclipse的 xff0c 但都没有详细介绍在eclipse中如何建立S3C6410裸机程序工程 尽管友善之臂提供的6410裸机程序示例使用了eclips
  • linux socket can程序cantool

    最近写了个自认为不错的基于linux socket can程序 xff0c 主要功能 xff1a 程序具备全部CAN功能 xff0c 包括CAN标准帧 扩展帧接收与发送 CAN总线错误判断 环回等功能适用基于LINUX SOCKET机制实现
  • Linux CAN编程详解

    Linux CAN编程详解 是一篇百度文库上的文档 xff0c 主要描述了以下内容 xff1a can总线介绍及其帧类型 xff1b Linux 系统中CAN 接口配置 xff1b Linux 系统中CAN 接口应用程序开发 xff1b L
  • c++中冒号(:)和双冒号(::)的用法和c/c++ 位域结构体

    1 冒号 xff08 xff09 用法 xff08 1 xff09 表示结构体内 位域的定义 xff08 即该变量占几个bit空间 xff09 typedef struct XXX unsigned char a 4 unsigned ch
  • CAN总线与RS485的比较

    最近一个项目总体方案设计为分布式系统 xff0c 于是在通讯上纠结于CAN总线还是RS485 因此在网上搜索一些了一些关于RS485和CAN总线的资料 xff0c 除进一步认识RS485通讯特点外 xff0c 认识了CAN总线的特点及其与R
  • Linux内核中常见内存分配函数

    1 原理说明 Linux 内核中采 用了一种同时适用于32 位和64 位系统的内 存分页模型 xff0c 对于32 位系统来说 xff0c 两级页表足够用了 xff0c 而在x86 64 系 统中 xff0c 用到了四级页表 xff0c 如
  • MII、RMII、GMII接口的详细介绍

    概述 xff1a MII Media Independent Interface 介质无关接口 或称为媒体独立接口 xff0c 它是IEEE 802 3定义的以太网行业标准 它包括一个数据接口和一个MAC和PHY之间的管理接口 数据接口包括
  • Visual Studio .NET 2003中出现“无法启动调试 没有正确安装调试器”错误的解决方法

    最近 xff0c 装了Visual Studio NET 2010后 xff0c 在Visual Studio NET 2003中进行运行调试 xff0c 突然出现 无法启动调试 没有正确安装调试器 提示 xff0c 不能向往常一样进入控制
  • ftime()函数

    ftime 函数取得目前的时间和日期 相关函数 xff1a time ctime gettimeofday 表头文件 xff1a include lt sys timeb h gt 函数定义 xff1a int ftime struct t
  • 几道经典的嵌入式C语言笔试题

    C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法 这些年 xff0c 我既参加也组织了许多这种测试 xff0c 在这过程中我意识到这些测试能为带面试者和被面试者提供许多有用信息 xff0c 此外 xff0c 撇开面试的压力不谈 xf
  • 解决eclipse中出现Resource is out of sync with the file system问题

    作者 xff1a reille 本博客网址 xff1a http blog csdn net reille xff0c 转载本博客原创文章请注明出处 本文内容概要 xff1a 解决eclipse中出现Resource is out of s
  • 代码中特殊的注释技术——TODO、FIXME和XXX的用处

    作者 xff1a reille 本博客网址 xff1a http blog csdn net reille xff0c 转载本博客原创文章请注明出处 本文内容概要 xff1a 代码中特殊的注释技术 TODO FIXME和XXX的用处 更多请
  • 个人网站梦想终实现即reille blog | velep.com成长之路

    最近用wordpress开放平台软件建立了一个属于自己的个人博客网站velep com即reille blog xff0c 中文名 xff1a reille博客 xff0c 圆了自己多年来的梦想 xff0c 感觉像是在这大千互联网里找到了属
  • PaddleX树莓派部署--神经计算棒2代

    PaddleX树莓派部署 神经计算棒2代 PaddleX支持在树莓派上插入NCS2 神经计算棒2代 通过OpenVINO部署PadlleX训练出来的分类模型 注意 xff1a 目前仅支持分类模型 仅支持Armv7hf的树莓派 前置条件 OS
  • Hbase数据结构和体系架构

    1 HBase与关系数据库比较 1 xff09 行式数据库 优点 1 数据存储在一起 2 INSERT UPDATE数据较容易 缺点 1 选择操作 xff08 select xff09 时 xff0c 即使是几行所有数据也要被读取 2 xf
  • Ubuntu安装remmina

    官方教程 xff1a https github com FreeRDP Remmina wiki sudo apt span class hljs attribute add span span class hljs attribute r
  • ros运行rviz时出现QXcbConnection: XCB error: 148错误

    原因 xff1a 由于使用了vnc远程控制下位机 xff0c rviz是一个基于opengl开发的图形插件 xff0c 需要使用理论的屏幕参数 xff08 thetis screen xff09 xff0c 使用vnc会导致屏幕参数值不对
  • FreeRTOS内核源码解读之-------系统启动(三)

    前面文章两篇文章介绍了FreeRTOS的启动过程 xff0c 但是有些问题还没有解决 xff0c 在本篇文章中将会逐一解决 首先 xff0c 在 FreeRTOS内核源码解读之 系统启动 xff08 一 xff09 中提到Cortex M4