【学习日记】【FreeRTOS】调度器函数实现详解

这篇具有很好参考价值的文章主要介绍了【学习日记】【FreeRTOS】调度器函数实现详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

本文主要是对于 FreeRTOS 中调度器函数实现的详细解释,代码大部分参考了野火 FreeRTOS 教程配套源码,作了一小部分修改。

一、MSP 和 PSP

Cortex-M有两种栈空间,主堆栈和进程堆栈。

  • MSP 用于系统级别和中断处理的堆栈
    • MSP 用于保存中断发生时的堆栈状态以及在特殊操作(例如任务切换)期间的堆栈状态。MSP 在启动时会被设置为合适的内存地址,并在系统级代码运行期间始终保持不变。
  • PSP 用于任务级别的堆栈
    • 用于保存任务执行期间的局部变量、函数调用、参数等。在任务切换时,任务的 PSP 被保存,并加载下一个任务的 PSP。每个任务有自己独立的堆栈空间,并且在任务切换时,PSP 的值会发生变化。

FreeRTOS中:中断用MSP,中断以外用PSP。

二、调度器函数逻辑

【学习日记】【FreeRTOS】调度器函数实现详解,RTOS,学习,RTOS

三、调度器函数详解

1.vTaskStartScheduler()

  • 本函数为调度器的启动函数
  • pxCurrentTCB 是一个在 task.c 定义的全局指针,用于指向当前正在运行或者即将要运行的任务的任务控制块
  • 目前没有使用优先级,所以手动指定第一个运行的任务
  • 调用 xPortStartScheduler() 启动调度器
void vTaskStartScheduler( void )
{
    /* 手动指定第一个运行的任务 */
    pxCurrentTCB = &Task1TCB;
    
    /* 启动调度器 */
    if( xPortStartScheduler() != pdFALSE )
    {
        /* 调度器启动成功,则不会返回,即不会来到这里 */
    }
}

2.xPortStartScheduler()

  1. 配置PendSV 和 SysTick 的中断优先级为最低
  2. 调用函数 prvStartFirstTask()启动第一个任务
BaseType_t xPortStartScheduler( void )
{
	/*
	PendSV是一个用于低优先级任务切换的软件中断。
	通过触发PendSV中断,可以请求处理器在合适的时
	间切换到更高优先级的任务。PendSV中断具有最低
	的中断优先级,因此可以在其他中断处理完成后立
	即执行。*/
    /* 配置PendSV 和 SysTick 的中断优先级为最低 */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

	/* 启动第一个任务,不再返回 */
	prvStartFirstTask();

	/* 不应该运行到这里 */
	return 0;
}

3.prvStartFirstTask()

  • 用于初始化启动第一个任务的环境,主要是重新设置MSP指针,并使能全局中断

  • 调度器启动函数xPortStartScheduler( void )调用:

  • prvStartFirstTask函数:

  1. PRESERVE8 指令保留 8 字节栈对齐
  2. 取出向量表起始地址对应的内容
  3. 使用向量表起始地址对应的内容设置主堆栈指针msp的值
  4. 使能全局中断
  5. 使用 dsb 和 isb 指令确保数据和指令同步
  6. 调用SVC去启动第一个任务
/*
 * 参考资料《STM32F10xxx Cortex-M3 programming manual》4.4.3,百度搜索“PM0056”即可找到这个文档
 * 在Cortex-M中,内核外设SCB的地址范围为:0xE000ED00-0xE000ED3F
 * 0xE000ED008为SCB外设中SCB_VTOR这个寄存器的地址,里面存放的是向量表的起始地址,即MSP的地址
 */
__asm void prvStartFirstTask( void )
{
	/*使用 PRESERVE8 指令保留 8 字节栈对齐*/
	PRESERVE8

	/* 在Cortex-M中,0xE000ED08是SCB_VTOR这个寄存器的地址,
       里面存放的是向量表的起始地址,即MSP的地址 */
//	向量表通常是从内部 FLASH 的起始地址开
//	始存放,那么可知 memory:0x00000000 处存放的就是 MSP 的值。
	ldr r0, =0xE000ED08
	ldr r0, [r0]	//把 0xE000ED08 处向量表起始地址取出
	ldr r0, [r0]	//取出向量表起始地址对应的内容

	/* 设置主堆栈指针msp的值 */
	msr msp, r0
    
	/* 使能全局中断 */
	cpsie i	//开中断 PRIMASK=0
	cpsie f	//开异常 FAULTMASK=0
	
	/*使用 dsb 和 isb 指令确保数据和指令同步*/
//1. dsb 指令:dsb 指令用于确保数据的同步。它会强制在 dsb 指令之
//	前的所有数据访问和加载操作完成,然后再继续执行 dsb 指令后面
//	的指令。这样可以确保所有数据操作在 dsb 指令之前都已经完成,
//	避免数据争用和不一致性的问题。
//2. isb 指令:isb 指令用于确保指令的同步。它会刷新处理器流水线中
//的指令,并确保在 isb 指令之前的所有指令都已经执行完毕,然后再继
//续执行 isb 指令后面的指令。这样可以确保流水线中的指令执行顺序与
//程序中的顺序一致,避免指令重排和乱序执行带来的问题。
	dsb
	isb
	
    /* 调用SVC去启动第一个任务 */
//	"Supervisor Call"(超级用户调用),
//	用于从用户模式(通常是应用程序运行的模式)
//	切换到特权模式(通常是操作系统内核运行的模式)
//	执行一段特权代码,以执行一些需要特权级别权限的操作或服务
	svc 0  //服务号 0表示 SVC 中断,接下来将会执行 SVC 中断服务函数
	
	nop
	nop
}
  • 关于Cortex-M中三个中断屏蔽寄存器
    【学习日记】【FreeRTOS】调度器函数实现详解,RTOS,学习,RTOS

4.vPortSVCHandler()

  • 本函数为 SVC 的中断服务函数

  • SVC(Supervisor Call)

  • SVC 中断的中文翻译可以是"特权调用中断"或者"监管者调用中断"。这两个翻译都可以准确地表达SVC中断的含义。

    • "特权调用中断"强调了SVC中断的特权级别,即它是由特权级别较高的软件(通常是操作系统内核)触发的中断。这是因为SVC指令只能在特权级别上进行访问和执行。

    • "监管者调用中断"则在翻译中更加明确了SVC中断的作用,即充当操作系统或监管者与用户程序之间的通信桥梁。通过SVC中断,用户程序可以请求操作系统提供特定的服务或资源。

  • 一种可能的 SVC 中断形式如下:

void vPortSVCHandler(uint32_t *pulSVCHandlerTopOfStack)
{
    // 从寄存器中提取SVC操作码
    uint8_t ucSVCNumber = ((uint8_t *)pulSVCHandlerTopOfStack[6])[-2];

    // 根据操作码执行相应的系统调用
    switch (ucSVCNumber) {
        case 0:
            // 执行系统调用0
            // ...
            break;
        case 1:
            // 执行系统调用1
            // ...
            break;
        // ...
        default:
            // 未知的系统调用
            // ...
            break;
    }
}

  • 在调度器函数实现中,SVC中断内容如下:
  1. 加载 TCB 到 r0,以 r0 为基地址,将栈里面的内容加载到 r4~r11 寄存器
  2. 开启所有中断
  3. 设置 r14 寄存器,以使用 PSP 出栈,进入线程模式,返回 Thumb 状态
  4. 如果异常返回,则 bx r14 进入 Thumb 状态,并且栈中的剩下内容将会自动加载到CPU寄存器
  • 这段代码的作用是将要运行的任务的上下文保存到任务的堆栈中,并将任务堆栈的栈顶指针更新到处理器堆栈指针(PSP)寄存器。它还执行了其他一些操作,如设置basepri寄存器的值为0,以确保所有中断都没有被屏蔽。最后,通过设置r14寄存器的值,使得CPU在从SVC中断服务退出时能够正确恢复任务的上下文。
//SVC中断函数
__asm void vPortSVCHandler( void )
{
    extern pxCurrentTCB;	//1. 加载要运行的 TCB 的指针
    
    PRESERVE8

	ldr	r3, =pxCurrentTCB	//2. 加载要运行的 TCB 的指针的地址到 r3
	ldr r1, [r3]			//3. 加载要运行的 TCB 的指针到 r1
	ldr r0, [r1]			//4. 加载 TCB 到 r0,目前 r0 的值等于第一个任务堆栈的栈顶
	ldmia r0!, {r4-r11}		//5. 以 r0 为基地址,将栈里面的内容加载到 r4~r11 寄存器,同时r0会递增
	msr psp, r0				//6. 将r0的值,即任务的栈指针更新到 psp
	isb						//7. 等待指令同步
	mov r0, #0 
	msr	basepri, r0         //8. 设置basepri寄存器的值为0,即所有的中断都没有被屏蔽
	orr r14, #0xd           //9. 当从SVC中断服务退出前,通过向r14寄存器最后4位按位或上0x0D,
                            //   使得硬件在退出时使用进程堆栈指针PSP完成出栈操作并返回后进入线程模式、返回Thumb状态
    
	bx r14                  //10. 异常返回,这个时候栈中的剩下内容将会自动加载到CPU寄存器:
                            //    xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
                            //    同时PSP的值也将更新,即指向任务栈的栈顶
}

执行成功后,PSP 的指向(图片来自野火):
【学习日记】【FreeRTOS】调度器函数实现详解,RTOS,学习,RTOS

ARM 状态和 Thumb 状态详解

在 ARM 架构中,ARM 状态和 Thumb 状态是指处理器运行的不同工作模式。这些模式决定了处理器执行代码的指令集。

  • ARM 状态:
  1. 在 ARM 状态下,处理器执行 ARM 指令集。这些指令集是 32 位宽度的。
  2. ARM 状态提供了更高的代码密度和更强大的功能,可以执行更复杂的指令。
  3. ARM 状态下的指令集包括了更多的寄存器和更多的数据处理指令。
  4. ARM 指令使用的是 32 位的寄存器。
  5. 进入 ARM 状态可以使用跳转指令 bx。
  • Thumb 状态:
  1. 在 Thumb 状态下,处理器执行 Thumb 指令集。这些指令集是 16 位宽度的,它们可以通过压缩来提供更好的代码密度。
  2. Thumb 状态下的指令集相对于 ARM 状态来说更为紧凑,但功能上略有限制。
  3. Thumb 指令使用的是 16 位的寄存器,这些寄存器只能存放 16 位的数据。
  4. 进入 Thumb 状态可以使用跳转指令 bx。

中断向量函数名称宏定义替换

Startup_ARMCM3.s
【学习日记】【FreeRTOS】调度器函数实现详解,RTOS,学习,RTOS

为了使中断向量函数符合 Cortex-M3 的中断向量表中的向量名称,需要进行宏定义替换

FreeRTOSConfig.h

1 #define xPortPendSVHandler PendSV_Handler
2 #define xPortSysTickHandler SysTick_Handler
3 #define vPortSVCHandler SVC_Handler

后记

如果您觉得本文写得不错,可以点个赞激励一下作者!
如果您发现本文的问题,欢迎在评论区或者私信共同探讨!
共勉!文章来源地址https://www.toymoban.com/news/detail-639383.html

到了这里,关于【学习日记】【FreeRTOS】调度器函数实现详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【学习日记】【FreeRTOS】FreeRTOS 移植到 STM32F103C8

    本文基于野火 FreeRTOS 教程,内容是关于 FreeRTOS 官方代码的移植的注意事项,并将野火例程中 STM32F103RC 代码移植到 STM32F103C8。 两个下载链接: 官 网 代码托管 Source:内核源码,我们需要的主要内容 根目录:这里的 C 文件都是由 C 语言编写的,所以在各种单片机上是通用的

    2024年02月11日
    浏览(46)
  • 【RTOS】快速体验FreeRTOS所有常用API(4)队列

    快速体验FreeRTOS所有常用API(1)工程创建 快速体验FreeRTOS所有常用API(2)任务管理 快速体验FreeRTOS所有常用API(3)同步与互斥 快速体验FreeRTOS所有常用API(4)队列 快速体验FreeRTOS所有常用API(5)信号量、互斥量 快速体验FreeRTOS所有常用API(6)事件组 快速体验FreeRTOS所有常

    2024年01月15日
    浏览(41)
  • [RTOS]rtthread,freeRTOS,uCOS等系统简单对比

    在上一篇《嵌入式操作系统浅谈》中,大概介绍了我自己再工作中使用到的如rt-thread,freeRTOS,uCOS,luatos等系统,在这一篇文章中,我简单说一下这些操作系统一些特点,还有就是我们平时的工作中什么场景下用哪种系统比较好。 csdn里面很多大神对上面的一些系统都做过了

    2023年04月15日
    浏览(29)
  • 【RTOS】快速体验FreeRTOS所有常用API(2)任务管理

    快速体验FreeRTOS所有常用API(1)工程创建 快速体验FreeRTOS所有常用API(2)任务管理 快速体验FreeRTOS所有常用API(3)同步与互斥 快速体验FreeRTOS所有常用API(4)队列 快速体验FreeRTOS所有常用API(5)信号量、互斥量 快速体验FreeRTOS所有常用API(6)事件组 快速体验FreeRTOS所有常

    2024年01月15日
    浏览(41)
  • 【RTOS】快速体验FreeRTOS所有常用API(1)工程创建

    快速体验FreeRTOS所有常用API(1)工程创建 快速体验FreeRTOS所有常用API(2)任务管理 快速体验FreeRTOS所有常用API(3)同步与互斥 快速体验FreeRTOS所有常用API(4)队列 快速体验FreeRTOS所有常用API(5)信号量、互斥量 快速体验FreeRTOS所有常用API(6)事件组 快速体验FreeRTOS所有常

    2024年01月20日
    浏览(40)
  • FreeRTOS任务调度与任务切换 | FreeRTOS八

    目录 说明: 一、任务调度器 1.1、开启任务调度器函数 1.2、任务调度器实现步骤 1.3、xPortStartScheduler()函数 二、启动一个任务 2.1、prvStartFirstTask()函数 2.2、vPortSVCHandler()函数 三、任务切换 3.1、任务切换触发途径 3.2、PendSV中断触发任务切换步骤 关于内容: 1)以下内容多为概

    2023年04月15日
    浏览(35)
  • 【踩坑日记】STM32 USART 串口与 FreeRTOS 冲突

    笔者使用 FreeRTOS 创建了两个任务,使两颗 LED 以不同频率闪烁,但是在加入串口 USART 部分代码后, LED 不能正常工作了。 硬件:STM32F103C8T6、ST-Link 软件:KEIL5 代码来源:野火 FreeRTOS 例程 说明,野火的教程并不适用于 F103C8,笔者对其进行了移植,一定程度上是因为移植出现了

    2024年02月11日
    浏览(40)
  • Freertos 任务调度

    文章目录 前言 一、任务调度 二、任务优先级 三、任务状态           用51单片机或者stm32进行裸机开发时, 一般都是在 main 函 数里面用 while(1) 做一个大循 环来完成所有的处理。而Freertos是一个抢占式的实时多任务操作系统,可以并发处理各个任务。   Freertos支持三种任

    2024年02月15日
    浏览(38)
  • 【09】FreeRTOS的时间片调度

       同等优先级任务 轮流地享有相同的 CPU 时间( 可设置 ), 叫时间片, 在FreeRTOS中,一个时间片就等于SysTick 中断周期 。在源码中,SysTick中断服务函数1ms中断一次,一个时间片就是1ms,滴答定时器SysTick的中断周期可以自己设置。 时间片调度运行举例: 运行条件: 1、创建

    2024年02月09日
    浏览(29)
  • 初识FreeRTOS入门,对FreeRTOS简介、任务调度、内存管理、通信机制以及IO操作,控制两个led不同频率闪烁

    当代嵌入式系统的开发越来越复杂,实时性要求也越来越高。为了满足这些需求,开发者需要使用实时操作系统(RTOS),其中一个流行的选择是FreeRTOS(Free Real-Time Operating System)。本篇博客将详细介绍FreeRTOS的特性、任务调度、内存管理、通信机制以及一些示例代码。 FreeR

    2024年02月14日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包