在水一方 发表于 2018-11-12 14:54:17

基于STM32 操作系统 任务切换 原理 个人心的(基于 U/COS II 和 FreeRTOS)

本帖最后由 在水一方 于 2018-11-12 16:44 编辑

STM32的 堆栈分析,
STM32 xPSR影响的条件指令,
STM32寄存器说明:
R0-R12:通用寄存器
R0‐R12 都是 32 位通用寄存器,用于数据操作。但是注意:绝大多数 16 位 Thumb 指令只能访
问 R0‐R7,而 32 位 Thumb‐2 指令可以访问所有寄存器。
Banked R13: 两个堆栈指针
Cortex‐M3 拥有两个堆栈指针,然而它们是 banked,因此任一时刻只能使用其中的一个。
主堆栈指针(MSP),这个地址就是我们进入main 函数时使用的堆栈 查看工程的map文件 __initial_sp:
进程堆栈指针(PSP),由用户的应用程序代码使用。
R14:连接寄存器, 存储返回地址
/*在中断前未使用FPU*/
0xFFFF_FFF1 返回handler模式0xFFFF_FFF9 返回线程模式,并使用主堆栈(SP=MSP)
0xFFFF_FFFD 返回线程模式,并使用线程堆栈(SP=PSP)

/*在中断前使用FPU*/

0xFFFF_FFE1 返回handler模式   0xFFFF_FFE9 返回线程模式,并使用主堆栈(SP=MSP)
0xFFFF_FFED 返回线程模式,并使用线程堆栈(SP=PSP)

R15:程序计数寄存器指向当前的程序地址。如果修改它的值,就能改变程序的执行流特殊功能寄存器
程序状态字寄存器组(PSRs)
中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI)
控制寄存器(CONTROL)

请在 《Cortex M3权威指南》中查看 26页寄存器的意义 ,在121页查看 SVC 和 PendSV 异常

1.堆栈的初始化
①. 堆栈的定义:uint32_t TEST_TASK_STK,这里存放着 临时变量压栈信息,以及R0~R15,xPSR 寄存器 。在任务切换的时候使用
②. 堆栈的位置: STM32为 堆栈向下生长的方式,所以我们的堆栈定义位置则是 TEST_TASK_STK 这个数组的最后一位地址。
③. 堆栈的初始化:
堆栈在内存的的排列
;xPSR      状态寄存器                        栈顶
;R15      PC
;R14      LR
;R12
;R3
;R2
;R1
;R0                任务形参

;手动保存寄存器                              其他形参都在压栈中
;R14
;R11
;R10
;R9
;R8
;R7
;R6
;R5
;R4

uint32_t*stk = (uint32_t *)&TEST_TASK_STK;               /* 这里定义一个指针指向堆栈顶部*/
/* 这里的赋值顺序根据 寄存器在堆栈中的排列         */
*(stk)   = (uint32_t)0x01000000L;                                                         /* xPSR 程序状态寄存器 这一部分值PSP自动保存         */
*(--stk) = (uint32_t)task;                                                                        /* 加载任务函数         这里是我们线程函数               */
*(--stk) = (uint32_t)SX_TaskError;                                                               /* R14 (LR) 加载退出任务 错误函数 任务不能return         */
      stk -= 4;                                                                                             /* 这里存放 R12, R3, R2 and R1. 四个寄存器            */
*(--stk) = (uint32_t)p_arg;                                                                     /* R0 : 形参                     任务的入口形参               */                                                                        
*(--stk) = (uint32_t)0xfffffffdL;                                                                     /* R14默认返回线程模式,并使用线程堆栈(SP=PSP)*/
      stk -= 8;                                                                                          /* R11, R10, R9, R8, R7, R6, R5 and R4.                         */
此时堆栈的指针位置在 stk ,在任务切换的时候 加载的就是此时的stk值。

④.通过链表 把 任务的 控制块 连接到一起,实现了任务的讯轮


2.任务的启动

①.调用启动函数
SXTCBCur:当前运行任务初始化 为0
SXTCBReady :为当前就绪任务初始化为 第一个需要运行的任务

SXTCBStart                                          ; 任务启动函数
      LDR   R0, =NVIC_SYSPRI14      ; 设置 PendSV 优先级
      LDR   R1, =NVIC_PENDSV_PRI
      STR   R1,

      CPSIE   I                                        ; 使能中断
      SVC         0                                    ;触发SVC中断
      NOP

SVC_Handler                                          ; SVC_Handler异常中断处理
      LDR   R0, =SXRunning                   ; SSRunning = TRUE    全局变量 系统启动标志
      MOV   R1, #1
      STRB    R1,
      
      LDR   R0, =SXTCBCur            ; 把 SXTCBReady 任务 加载到 SXTCBCur
      LDR   R1, =SXTCBReady
      LDR   R2,
      STR   R2,

      LDR   R0,                         ; 我们将新的栈地址送到R0
      LDMIA   R0!, {R4-R11,R14}   ; 将R4-R11,R14从栈弹出加回偏移地址(回到R0 的地址),在执行BX跳转指令时,硬件能从正确的栈地址自动弹出;R0-R3,R12,LR,PSR,PC的内容

      MSR         PSP, R0                   ; 给加载 堆栈指针到 PSP中
      BX                R14                        ;跳转到第一个任务


3.任务的切换
①.任务的切换思想
    最简单的任务切换时轮训 没有优先级的,每次滴答定时器中断时从当前任务指向的下一个任务轮训查找就绪任务;有优先级的 通过优先级组,从高优先级轮训到低优先级查询人物状态。
②.任务的切换触发
      LDR   R0, =NVIC_INT_CTRL          ; 触发PendSV_Handler 异常 中断(causes context switch)
      LDR   R1, =NVIC_PENDSVSET
      STR   R1,
      BX      LR

③.任务的切换
PendSV_Handler
      CPSID   I                                                   ; 关闭中断 避免 任务切换被打断
      MRS   R0, PSP                                        ; 保存 PSP 堆栈 (PSP 堆栈为线程堆栈,MSP 堆栈为 主堆栈 main)

      TST                R14, #0x10                              ; 堆栈是否使用 FPU ?
      IT                EQ                                           ;如果使用
      VSTMDBEQ R0!,{S16-S31}                        ;要将高 16个vfp 寄存器压栈

      STMDB   R0!, {R4-R11,R14}                        ; 保存当前任务的 R4-R11,      R14的寄存器值;进入中断时 R0-R3,R12,LR,PSR,PC会自动存储到当前任务堆栈 R0地址-32

      LDR   R1, =SXTCBCur                           ; 将当前的 TCB 赋值给R1 寄存器
      LDR   R1,                                         ;
      STR   R0,                                         ; 将 R0 保存的堆栈信息 保存到 R1 中 到此 堆栈保存已经结束下面开始任务的切换

      LDR   R0, =SXTCBCur                           ; 把 SXTCBCur         赋值给 R0
      LDR   R1, =SXTCBReady                              ; 将 SXTCBReady      赋值给 R1
      LDR   R2,                                         ; 加载 SXTCBReady 到R2寄存器
      STR   R2,                                         ; 存储 SXTCBReady 到 R0寄存器

      LDR   R0,                                        ; 我们将新的栈地址送到R0
      LDMIA   R0!, {R4-R11,R14}                  ; 将R4-R11,R14从栈弹出加回偏移地址(回到R0 的地址),在执行BX跳转指令时,硬件能从正确的栈地址自动弹出;R0-R3,R12,LR,PSR,PC的内容

      TST         R14, #0x10                                       ; 堆栈是否使用 FPU ? 如果使用 要将高 16个vfp 寄存器弹出
      IT               EQ
      VLDMIAEQ R0!, {S16-S31}

      MSR   PSP, R0                                       ; 将R0中的栈地址送到PSP线程栈
      ORR   LR, LR, #0x04                           ; 这句是确保后面执行BX命令时使用的是线程栈 ;PSP,而不是主堆栈MSP
      CPSIE   I                                                       ; 开总中断
      BX      LR                                                 ; 执行中断返回,此时R0-R3,R12,LR,PSR,PC自动 ;出栈在这里任务 任务切换完毕
由于不能上传文件,放网盘了
链接:https://pan.baidu.com/s/1Z731y7d_sf3RGUWc4FzBZQ
提取码:17r6


eric2013 发表于 2018-11-12 14:59:31

谢谢楼主分享。

在水一方 发表于 2018-11-12 15:08:04

本帖最后由 在水一方 于 2018-11-12 15:21 编辑

eric2013 发表于 2018-11-12 14:59
谢谢楼主分享。
为什么我图片 和 程序 都发不上了啊,给您QQ发消息了

eric2013 发表于 2018-11-12 15:29:13

在水一方 发表于 2018-11-12 15:08
为什么我图片 和 程序 都发不上了啊,给您QQ发消息了

可以上传和提交,你可以这样,你编辑好,保存下。

我来给你提交下

在水一方 发表于 2018-11-12 16:23:17

eric2013 发表于 2018-11-12 15:29
可以上传和提交,你可以这样,你编辑好,保存下。

我来给你提交下

还是不行,上传不成功,提示我 IO 错误。

在水一方 发表于 2018-11-12 17:44:32

遇到什么问题可以,可以在本贴讨论

jinniuxing 发表于 2020-6-16 14:35:37

谢谢分享!

会飞的猪_2020 发表于 2021-5-18 11:22:24

为什么要有R0~R12这么多寄存器?
我看那些文章,PC,和SP指针我能理解,但是R0~R12这些寄存器又是做什么用的呢?

ssssssss 发表于 2021-5-21 08:13:19

我帮上传一下
页: [1]
查看完整版本: 基于STM32 操作系统 任务切换 原理 个人心的(基于 U/COS II 和 FreeRTOS)