[牛人杂谈] PendSV中断以及其用法

[复制链接]
 楼主| 小灵通2018 发表于 2025-1-24 12:23 | 显示全部楼层 |阅读模式
PendSV(Pendable Service Call)是ARM Cortex-M系列处理器特有的异常类型,专门为实时操作系统(RTOS)设计,用于任务切换或其他需要延后执行的低优先级任务。
用途:PendSV主要用于上下文切换(Context Switch),即在多任务系统中,切换当前运行任务到下一个任务。
优点:它的优先级最低,不会干扰高优先级中断。通过软件触发,提供灵活性和精确控制。不需要额外的硬件支持,可以高效实现任务切换。

 楼主| 小灵通2018 发表于 2025-1-24 12:23 | 显示全部楼层
PendSV中断的特点

最低优先级:
它的优先级是可编程的,但通常被设置为最低,以避免干扰其他中断。
在Cortex-M中,优先级通过NVIC(Nested Vectored Interrupt Controller)配置。

软件触发:

PendSV不能由硬件触发,只能通过软件设置触发(写入SCB中的ICSR寄存器)。

中断延后:
PendSV可以被挂起,直到高优先级中断处理完成后再执行。
它的“挂起特性”使得它非常适合实现任务切换,而不会影响关键实时任务。

与SysTick的配合:
SysTick定时器用于产生周期性中断,触发调度器运行。
PendSV负责执行实际的任务切换。
 楼主| 小灵通2018 发表于 2025-1-24 12:25 | 显示全部楼层
PendSV的触发机制
触发PendSV中断:
向SCB->ICSR寄存器的PENDSVSET位写入1即可触发PendSV中断。
  1. SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;  // 设置PendSV挂起位
清除PendSV挂起:
PendSV中断执行后,硬件会自动清除挂起状态。
也可以通过软件清除挂起状态:
  1. SCB->ICSR |= SCB_ICSR_PENDSVCLR_Msk;  // 清除PendSV挂起位
设置优先级:
通过NVIC_SetPriority函数设置PendSV为最低优先级:
  1. NVIC_SetPriority(PendSV_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0xFF, 0xFF));


 楼主| 小灵通2018 发表于 2025-1-24 12:25 | 显示全部楼层
PendSV的用法
在RTOS中,PendSV通常用于任务调度和任务切换,以下是一个典型的流程:

任务切换的大致步骤
SysTick中断触发:定时器(如SysTick)触发中断,唤醒调度器。
调度器决定下一个任务:根据任务优先级、状态等信息选择下一个任务。
PendSV中断触发:调度器触发PendSV中断。
执行任务切换:
保存当前任务的上下文(寄存器、堆栈指针等)。
加载下一个任务的上下文,切换任务。


 楼主| 小灵通2018 发表于 2025-1-24 12:26 | 显示全部楼层
以下是一个典型的PendSV中断服务例程(Cortex-M架构下):
  1. void PendSV_Handler(void) {
  2.     // 保存当前任务上下文
  3.     __asm volatile (
  4.         "MRS R0, PSP\n"                  // 获取当前任务的进程堆栈指针 (Process Stack Pointer)
  5.         "STMDB R0!, {R4-R11}\n"          // 保存低寄存器(R4-R11)到堆栈
  6.         "LDR R1, =current_tcb\n"         // 加载当前任务控制块的地址
  7.         "LDR R2, [R1]\n"                 // 获取当前任务的TCB地址
  8.         "STR R0, [R2]\n"                 // 保存当前任务的堆栈指针到TCB
  9.     );

  10.     // 加载下一个任务上下文
  11.     __asm volatile (
  12.         "LDR R2, =next_tcb\n"            // 加载下一个任务控制块的地址
  13.         "LDR R2, [R2]\n"                 // 获取下一个任务的TCB地址
  14.         "LDR R0, [R2]\n"                 // 获取下一个任务的堆栈指针
  15.         "LDMIA R0!, {R4-R11}\n"          // 恢复低寄存器(R4-R11)
  16.         "MSR PSP, R0\n"                  // 恢复进程堆栈指针
  17.         "BX LR\n"                        // 返回下一个任务
  18.     );
  19. }
 楼主| 小灵通2018 发表于 2025-1-24 12:26 | 显示全部楼层
配合调度器触发PendSV
RTOS的调度器通常在决定切换任务后触发PendSV。例如:
  1. void task_switch(void) {
  2.     // 触发PendSV中断,进行任务切换
  3.     SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
  4. }

 楼主| 小灵通2018 发表于 2025-1-24 12:27 | 显示全部楼层
PendSV的作用与SysTick配合示例
以下示例演示了如何结合SysTick和PendSV实现任务调度:
  1. #include "stm32f4xx.h"

  2. void SysTick_Handler(void) {
  3.     // 调度器逻辑:决定是否需要切换任务
  4.     if (should_switch_task()) {
  5.         SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;  // 触发PendSV中断
  6.     }
  7. }

  8. void PendSV_Handler(void) {
  9.     // 保存当前任务上下文并切换到下一个任务
  10.     save_context();
  11.     schedule_next_task();
  12.     restore_context();
  13. }
 楼主| 小灵通2018 发表于 2025-1-24 12:27 | 显示全部楼层
总结
触发条件:

PendSV由软件触发(SCB->ICSR寄存器),不能由硬件自动触发。
优先级设置:

确保PendSV优先级最低(通常与调度器无关的中断优先级较高)。
任务切换效率:

PendSV的主要作用是保存当前任务上下文并加载下一个任务的上下文,效率对RTOS性能至关重要。
与SysTick配合:

SysTick定时器负责定时触发调度器运行,调度器触发PendSV完成实际任务切换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

157

主题

1727

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部