Cortex-M3 双堆栈指针(MSP&PSP)
- Cortex-M3内核中有两个堆栈指针(MSP & PSP),但任何时刻只能使用到其中一个。
- 复位后处于线程模式特权级,默认使用MSP。
- 通过SP访问到的是正在使用的那个指针,可以通过MSR/MRS指令访问指定的堆栈指针。
- 通过设置CONTROL寄存器的bit[1]选择使用哪个堆栈指针。CONTROL[1]=0选择主堆栈指针;CONTROL[1]=1选择进程堆栈指针。
- Handler模式下,只允许使用主堆栈指针MSP。
双堆栈指针在OS中的应用
典型的OS环境中,MSP和PSP的用法如下: MSP用于OS内核和异常处理。 PSP用于应用任务。 M3有两种mode:分别是handler mode和thread mode。有两种权限,分别是特权级和用户级。 那么经过组合后理论上有四种状态。
状态 | 使用条件 |
---|
特权级 handler mode | 异常/中断 | 用户级 handler mode | 无 | 特权级 thread mode | 应用 | 用户级 thread mode | 应用 |
特权级下用MSP,用户级用PSP。 特权级下权限很大,用户级下会有限制。
裸机下,一般不需要区分。MSP就足够用了,因为单线程的程序没必要用2个堆栈,但值得注意的一点就是,单片机进入中断时必定是用MSP,即使你当前使用的是PSP,在进入中断时单片机也会调整过来使用MSP,中断返回时再恢复, 应用在操作系统中,MSP给内核,PSP给用户。
双堆栈指针的初始化
系统复位时从0x00000000处读出MSP的初始值。 在OS初始化时,对PSP进行初始化。
PSP指针在不同任务间切换
用任务A的SP执行入栈操作,并保存任务A的SP。 设置PSP指向任务B的栈空间,用任务B的SP执行出栈,随后开始执行任务B。
HardFault_Handler异常
STM32出现硬件错误可能有以下原因:
(1)数组越界操作;
(2)内存溢出,访问越界;
(3)堆栈溢出,程序跑飞;
(4)中断处理错误;
1、异常发生时,内核将R0~R3、R12、Return address、PSR、LR寄存器依次入栈,其中Return address即为发生异常前PC将要执行的下一条指令地址,因此在堆栈中反数第三个字即为出错位置。发生异常之后可首先查看LR寄存器中的值,确定当前使用堆栈为MSP或PSP,然后找到相应堆栈的指针,并在内存中查看相应堆栈里的内容。
2、默认的HardFault_Handler处理方法是B .将它改成BX LR直接返回的形式。然后在这条语句打个断点,一旦在断点中停下来,说明出错了,然后再返回,就可以返回到出错的位置的下一条语句那儿。 这个有时候可能需要在反汇编模式下调试,因为可以是程序跑飞一会儿才出现HardFault_Handler。
3、还是将中断函数修改,打印中断时的一些信息: HardFault_Hander()定义如下:
void HardFault_Handler(void)
{
uint32_t r_sp ;
r_sp = __get_PSP();
PERROR(ERROR,Memory Access Error!);
Panic(r_sp);
while (1);
}
发生异常之后可首先查看LR寄存器中的值,确定当前使用堆栈为MSP或PSP,然后找到相应堆栈的指针,并在内存中查看相应堆栈里的内容。
如果LR寄存器中的值是0xFFFFFFF9
应该去看MSP的地址,找到该地址的地址然后如下图所示打开内存,输入上面找到的寄存器地址,右键选择以long型查看地址如下所示: 然后查看这个地址向下数六个long地址,为什么是6个long地址呢,因为由于异常发生时,内核将R0~R3、R12、Returnaddress、PSR、LR寄存器依次入栈,其中Return address即为发生异常前PC将要执行的下一条指令地址;大概是0x08xxxxxx这样开始的即为出错的代码位置,然后可以反汇编查看,如下图所示:
如果LR寄存器中的值是0xFFFFFFFD
[1] 在复杂的情况下,即使定位了异常发生位置也很难容易的改正错误,要学会使用Watch窗口对发生错误的指针变量进行跟踪;
[2]在问题不明晰的情况下,尝试分析反汇编代码,就自己遇到的,部分情况下的异常发生在BL等跳转指令处,BL跳转到了不合法的内存地址产生异常
如果还找不到问题,注释部分函数,一个函数一个函数排查了,看看是哪个函数导致的,然后在锁定问题原因。
实际操作
|