手写RTOS-使用PendSV进行压栈与出栈操作

2023-05-16

学会使用PendSV中断进行压栈和出栈操作,是实现任务调度的关键。今天我们就来学习一下如何使用不超过20行的汇编实现压栈和出栈操作。

我们现在来实现这么一个例子:先把R4-R11通用寄存器的值保存到一个缓冲区里面,然后再把缓冲区里面的值恢复到R4-R11。

实现这个功能的意义在于,在切换一下个任务之前,要先把当前任务的状态先保存起来,然后把要下面要执行的任务状态从缓冲区里面恢复出来。

下面我们就要上一次创建的工程上进行本次实例的讲解

首先,我们先定义一个结构体:

typedef uint32_t StackType_t;

typedef struct tskTaskControlBlock
{
  StackType_t *pxTopOfStack;
}TCB_t;

然后定义一个结构体变量以及结构体指针:

TCB_t *pxCurrentTCB;
TCB_t CurrentTCB;

接下来,我们要对PendSV相关的寄存器进行定义和定义一个写寄存器操作:

#define NVIC_INT_CTRL                    0xE000ED22
#define portNVIC_INT_CTRL_REG            0xE000ED04
#define portNVIC_PENDSVSET_BIT           0x10000000
#define portNVIC_PENDSVSET_PRI           0xFF

#define REG_WRITE_32(ADDRESS)	         (*(volatile unsigned long *)(ADDRESS))
#define REG_WRITE_8(ADDRESS)	         (*(volatile unsigned char *)(ADDRESS))

下面我们来实现一下触发PendSV异常的函数:

void TrigetPendSVC(void)
{
  REG_WRITE_8(NVIC_INT_CTRL) = portNVIC_PENDSVSET_PRI;
  REG_WRITE_32(portNVIC_INT_CTRL_REG) = portNVIC_PENDSVSET_BIT;
}

这个函数很简单,先把PendSV中断设置为最低优先级,然后再触发发PendSV异常。

在main函数中对结构体进行初始化:

CurrentTCB.pxTopOfStack = &puxStackBuffer[1024];
pxCurrentTCB = &CurrentTCB;

最后我们来实现一下PendSV的中断函数:

__asm void PendSV_Handler(void)
{
  extern  pxCurrentTCB;            /*引用外部变量pxCurrentTCB*/
  
  ldr r1, =pxCurrentTCB;           /*读取结构体指针pxCurrentTCB地址的值*/
  ldr r2, [r1];                    /*读取结构体指针pxCurrentTCB指向的值,即CurrentTCB的地址*/
                                   /*也是CurrentTCB.pxTopOfStack的地址*/
  ldr r0, [r2];                    /*读取CurrentTCB.pxTopOfStack指针指向的地址,并赋给r0*/
  
  stmdb r0!, {r4-r11};             /*依次把r11到r4保存到r0指向的缓冲区中*/
  
  str r0, [r2];                    /*再把此时移位后栈顶指针的值赋给CurrentTCB.pxTopOfStack*/
  
  add r4, r4, #1;                  /*测试代码*/
  add r5, r5, #1;                  /*测试代码*/
  
  ldmia r0!, {r4-r11};             /*把r0指向的缓冲区中的值恢复给r11-r4*/
  bx lr;                           /*返回*/
}

现在我们来测试一下我们写的代码是否有问题,进入仿真模式:

我们停在这个触发PendSV中断的地方:

点击下一步看下能否进行中断,能够正常进入

我们再把相关的变量加入到Watch1中进行观察,看下数据是否正确:

第一步,把pxCurrentTCB指针的地址赋给r0,从下图可以看出,操作正常:

 下一步,读出pxCurrentTCB结构体指针所指针向的结构体地址的值,&pxCurrentTCB的值为0x20000004,&CurrentTCB = pxCurrentTCB = 0x20000008,操作正确:

下一步,把CurrentTCB.pxTopOfStack的值赋给r0,即r0=CurrentTCB.pxTopOfStack=&puxStackBuffer[1024];这里要说一下为什么要取puxStackBuffer[1024]的地址,这样不是数据越界了吗?是不是我写错了?其实没有写错,刚开始的时候我也不理解,看到后面指令我就明白了,因为Cotex-M3是满减栈,即操作SP指针的时候,要先把SP-1,然后再对SP指向的地址进行赋值,所以到后面还是会把第一个数写在puxStackBuffer[1023]上,不会越界。

 下一步,把r11-r4依次保存在缓冲区puxStackBuffer数组中,数据可以一一对应,见下图:

 下一步,把缓冲区压栈完成后指向的地址保存到CurrentTCB.pxTopOfStack中,以便下次恢复的时候用,可以看到CurrentTCB.pxTopOfStack的值已经变成0x20000FF0,与r0的值一致:

下面执行一下测试代码,r4=0,r5=0x20001010;我们执行一下加1运算:

下一步,恢复数据,可以看到r4,r5已经变回原来的数值了:

 到这里,使用PendSV保存上下文和恢复的完成了,下一篇说一下任务的切换。

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

手写RTOS-使用PendSV进行压栈与出栈操作 的相关文章

  • 关于RTOS中信号量、互斥量、邮箱、消息队列的一些理解

    1 信号量 信号量有两种 xff1a 计数性信号量和二值信号量 xff0c 计数性信号量可以被获取多次 xff0c 二值信号量只有0和1两种状态 xff0c 只能被获取一次 信号量可以用来对资源进行保护 xff0c 防止多个任务同时访问某个
  • UC/OS-III学习——触发PendSV中断

    UC OS III学习 触发PendSV中断 前言一 关于PendSV的基础知识二 代码1 c语言2 汇编语言 前言 PendSV典型使用场合是在上下文切换时 xff08 在不同任务之间切换 xff09 本文主要介绍触发PendSv中断的两
  • RTOS与linux区别

    一句话解释 xff1a linux是分时系统 xff0c 不过可以通过配置内核改成实时 嵌入式Linux 系统是在原来Linux的发行版本之上进行了优化和改进的 xff0c 用于嵌入式的移动终端等设备的嵌入式Linux系统现在基本上都是实时
  • CCS v6.1下完成TI-RTOS配置

    TI RTOS 是实时操作系统的TI微控制器 TI RTOS实现更快的发展不再需要开发人员编写和维护的系统软件 xff0c 如调度程序 xff0c 协议栈和驱动程序 它结合了实时多任务内核与其他中间件组件 xff0c 包括TCP IP和US
  • FreeRTOS操作系统如何设置的PendSV和SysTick优先级

    首先应该明确PendSV和SysTick的优先级应该设置为最低 xff0c 具体原因参见这一篇博客 PendSV功能 xff0c 为什么需要PendSV 设置优先级在函数port c中的xPortStartScheduler 函数中实现的
  • 中断及pendSV

    1 概述 通过任务及任务切换一节读者已经了解了任务切换的详细过程 其实要实现任务切换的功能前面讲的还远远不够 因为 PSP的操作必须在特权模式下 而用户的程序是工作在线程模式下的 所以 用户要想实现任务的切换功能就必须想办法让CPU工作在特
  • FreeRTOS任务切换——PendSV

    前言 xff1a 本文分析一下FreeRTOS任务切换相关内容 RTOS系统的核心是进行任务管理 xff0c 任务切换 本文分SVC和PendSV异常 任务切换场景 pendSV中断函数 FreeRTOS时间片调度4部分讲解FreeRTOS
  • STM32使用RTOS BootLoader跳转app进入异常中断问题

    一 问题描述 在boot中不使用RTOS xff0c 跳转到APP中 xff0c APP可以正常运行 但是boot中使用RTOS跳转到APP中 xff0c 程序配置完时钟后就会进入MemManage Handler错误中断 二 解决方法 1
  • FreeRTOS的学习(十四)——PendSV异常

    FreeRTOS的学习系列文章目录 FreeRTOS的学习 xff08 一 xff09 STM32上的移植问题 FreeRTOS的学习 xff08 二 xff09 任务优先级问题 FreeRTOS的学习 xff08 三 xff09 中断机制
  • RTOS概念介绍

    事件标志组 事件标志组是实现多任务同步的有效机制之一 也许有不理解的初学者会问采用事件标志组多麻烦 xff0c 搞个全局变量不是更简单 xff0c 其实不然 在裸机编程时 xff0c 使用全局变量的确比较方便 xff0c 但是在加上 RTO
  • STM32、FreeRTOS—— SVC 和 PendSV

    问 I went through the xTaskStartSechudler function which ends up triggering the SVC instruction and i came to know that i
  • SVC和PendSV

    转载于 xff1a http book 2cto com 201209 4625 html 1 xff0e SVC SVC xff08 Supervisor Call xff09 指令用于产生一个SVC异常 它是用户模式代码中的主进程 xf
  • STM32 Free RTOS实战

    FreeRTOS是一个开源的实时操作系统 使用的平台 xff1a 秉火STM32 Cortex M3内核开发板 xff0c Free RTOS v8 2 3 多任务流水灯 span class token builtin class nam
  • FreeRTOS 事件组

    实现功能 当任务A B完成后执行串口任务 不同任务用不同的位表示 configUSE 16 BIT TICKS 1 bitx 0 7 configUSE 16 BIT TICKS设置为0 bitx 0 23 串口任务的头文件 ifndef
  • Threadx 定时器timer

    文章目录 定时器管理结构 定时器链表 定时器激活链表 定时器工作原理 定时器API 定时器创建 tx timer create 删除定时器 tx timer delete 修改 tx timer change Threadx 操作系统定时器
  • OSAL

    OSAL为 Operating System Abstraction Layer 即操作系统抽象层 支持多任务运行 它并不是一个传统意义上的操作系统 但是实现了部分类似操作系统的功能 OSAL概念是由TI公司在ZIGBEE协议栈引入 他的意
  • Nuttx操作系统(三):构建模式

    1 1 Nuttx构建配置以及模式 Nuttx有三种不同的构建配置 FLAT构建 这种构建是所代码驻留在公共地址空间中 1 应用 内核以及board logic在一个flat地址环境中 2 所有的地址空间具有相同的属性 PROTECTED构
  • RT-Thread 内核基础(六)

    RT Thread内核配置示例 RT Thread的一个重要特性是高度可裁剪性 支持对内核进行精细调整 对组件进行灵活拆卸 配置主要是通过修改工程目录下的rtconfig h文件来进行 用户可以通过打开 关闭该文件中的宏定义来对代码进行条件
  • RT-Thread 内核基础(五)

    使用static修饰全局变量作用 限制作用域 如果全局变量前面加上 static 关键字 那么该变量的作用域将被限制在声明它的源文件中 即它将成为一个文件作用域的静态变量 其它源文件无法访问这个变量 这对于控制变量的可见性和避免命名冲突是有
  • 开始使用 Real Time Linux 编程的最佳方式是什么?

    虽然我用C实现了很多项目 但我对操作系统完全陌生 我在 Discovery board STM32 上尝试了实时 Linux 并得到了闪烁 LED 的正确结果 但我并没有真正理解整个过程 因为我只是按照步骤操作 并且无法在互联网上找到每个步

随机推荐