【专题3:从0到1写嵌入式操作系统】 之 【3.PendSV异常】

1.PendSV异常

  PendSV是系统异常,具有可编程的优先级,主要用在OS的任务切换中。可以将它配置为最低优先级(比普通任务的优先级要高,在运行任务期间发生了PendSV异常,CPU可以正常响应该异常)。

2.实现案例

  系统启动之后手动触发一个PendSV异常,然后在PendSV异常处理函数中保存R4 - R11的值到指定的栈中。
main.c

#define NVIC_INT_CTRL       0xE000ED04      // 中断控制及状态寄存器
#define NVIC_PENDSVSET      0x10000000      // 触发软件中断的值
#define NVIC_SYSPRI2        0xE000ED22      // 系统优先级寄存器
#define NVIC_PENDSV_PRI     0x000000FF      // 配置优先级

#define MEM32(addr)         *(volatile unsigned long *)(addr)
#define MEM8(addr)          *(volatile unsigned char *)(addr)

void triggerPendSVC (void) 
{
    MEM8(NVIC_SYSPRI2) = NVIC_PENDSV_PRI;   // 向NVIC_SYSPRI2写NVIC_PENDSV_PRI,设置其为最低优先级
    MEM32(NVIC_INT_CTRL) = NVIC_PENDSVSET;    // 向NVIC_INT_CTRL写NVIC_PENDSVSET,用于PendSV
}

typedef struct _BlockType_t 
{
    unsigned long * stackPtr;
}BlockType_t;

BlockType_t * blockPtr;

void delay (int count) 
{
    while (--count > 0);
}

int flag;

unsigned long stackBuffer[1024];
BlockType_t block;

int main () 
{
    block.stackPtr = &stackBuffer[1024];//从这里可以体会到ARM使用的是满减栈。
    blockPtr = █
    for (;;) {
        flag = 0;
        delay(100);
        flag = 1;
        delay(100);
        
        triggerPendSVC();
    }
}

switch.c


__asm void PendSV_Handler ()
{
	//导入变量
    IMPORT  blockPtr
    
    // 加载寄存器存储地址
    LDR     R0, =blockPtr
    LDR     R0, [R0]
    LDR     R0, [R0]

    // 保存寄存器
	//为什么只保存R4-R11,因为其他的寄存器的值硬件会自动保存到栈中(这里的栈指MSP)
    //STM的作用:批量将寄存器的数据写到R0地址处的空间中,
	//D表示地址递减,也就是一开始R0的值是栈顶,栈顶是高地址
	//B表示在加载数据之前,先将R0减4
	//RO!中的!表示当所有寄存器都加载完毕之后,将此时R0的值/0x20000FF0(此时栈的地址)写回R0
    STMDB   R0!, {R4-R11}
    
    // 将最后的地址写入到blockPtr中,一开始栈指针变量的值是数组最后一个元素的地址,
    //将寄存器中的数据写到了栈里面之后,此时栈肯定是在向下移动的,STMDB能自动移动栈指针(R0/block.stackPtr),
    //当所有寄存器的数据都写到了栈中,此时的栈地址必须更新到栈指针变量中。
	// 即,将此时的栈地址写到block.stackPtr中,该变量的值为0x20000FF0
    LDR     R1, =blockPtr
    LDR     R1, [R1]
    STR     R0, [R1]
    
    // 修改部分寄存器,用于测试
    ADD R4, R4, #1
    ADD R5, R5, #1
    
    // 恢复寄存器,这里的R0的值等于0x20000FF0,压栈和弹栈的顺序相反
    LDMIA   R0!, {R4-R11}
    
    // 异常返回
    BX      LR
	NOP
}  

总结:
  当发生任务切换/PendSV异常时,如果把保存R4-R11寄存器保存到上一个任务的栈中,然后恢复的是下一个任务的栈,此时就实现了任务切换。

猜你喜欢

转载自blog.csdn.net/qq_29083043/article/details/106243865