到目前为止,我在C中有一个我在向量表中定义的硬故障处理程序:
.sect ".intvecs"
.word _top_of_main_stack
.word _c_int00
.word NMI
.word Hard_Fault
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
....
....
....
我们的一项测试通过写入非现有地址来触发硬故障(故意)。测试完成后,处理程序返回调用函数,皮质从故障中恢复。值得一提的是处理程序没有任何参数。
现在我正在编写一个真正的处理程序。 我为堆栈框架创建了一个结构,以便我们可以在发生故障时打印PC,LR和xPSR:
typedef struct
{
int R0 ;
int R1 ;
int R2 ;
int R3 ;
int R12 ;
int LR ;
int ReturnAddress ;
int xPSR ;
} InterruptStackFrame_t ;
我定义了C中的硬故障处理程序:
void Hard_Fault(InterruptStackFrame_t* p_stack_frame)
{
// Write to external memory that I can read from outside
/* prints a message containing information about stack frame:
* p_stack_frame->LR, p_stack_frame->PC, p_stack_frame->xPSR,
* (uint32_t)p_stack_frame (SP)
*/
}
我创建了一个汇编函数:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP ; store pointer to stack frame
BL Hard_Fault ; go to C function handler
POP {R0-R7} ; pop out all stack frame
MOV PC, R5 ; jump to LR that was in the stack frame (the calling function before the fault)
.endasmfunc
现在是时候说我没有操作系统,所以我不必检查LR的位[2],因为我肯定知道我使用MSP而不是PSP。
程序编译并正常运行,我使用JTAG确保所有寄存器恢复到所需的值。
当执行最后一个命令(MOV PC, R5
)时,PC返回到正确的地址,但在某些时候,调试器指示M0被锁定在硬故障中并且无法恢复。
我不明白使用C函数作为处理程序或调用C函数的汇编函数之间的区别。
有谁知道这是什么问题?
最终,我将使用一个会阻塞处理器的断言功能,但我希望它是可选的,直到我的决定。
答案 0 :(得分:0)
解释" old_timer"评论:
当在Cortex上输入异常或中断处理程序时,LR寄存器具有特殊值。
通常只需跳转到该值就可以从异常处理程序返回(通过将该值写入PC寄存器)。
然后,Cortex CPU会自动弹出堆栈中的所有寄存器,并重置中断逻辑。
当直接跳转到存储在堆栈中的PC时,你会破坏一些寄存器而你不会恢复中断逻辑。
因此,这不是一个好主意。
相反,我会这样做:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
B Hard_Fault
修改强>
使用B
指令可能无效,因为"距离"允许B
指令比BL
指令更有限。
然而,您可以使用两种可能性(不幸的是,我不确定这些是否肯定有用)。
第一个将返回到进入汇编程序处理程序时在LR
寄存器中传递的地址:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
PUSH {LR}
BL Hard_Fault
POP {PC}
第二个将间接进行跳转:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
LDR R1, =Hard_Fault
MOV PC, R1
编辑2
您不能使用LR,因为它包含EXC_RETURN值。 ...你必须从堆栈中读取LR,并且必须从堆栈帧中清除堆栈,因为被中断的程序不知道存储了一个帧。
根据Cortex M 3 手册,您必须通过将三个EXC_RETURN值中的一个写入PC寄存器来退出异常处理程序。
如果您只是跳转到存储在堆栈框架中的LR
值,那么您将保留在异常处理程序中!
如果在程序期间发生了一些愚蠢的事情,CPU会认为异常处理程序内发生异常并且它会挂起。
我认为此时Cortex M 0 的工作方式与M 3 的工作方式相同。
如果要在异常处理程序期间修改某些CPU寄存器,可以修改堆栈帧。当您将EXC_RETURN值写入pop
寄存器时,CPU将自动PC
堆栈帧中的所有寄存器。
如果要修改堆栈帧中不存在的寄存器之一(例如R5
),可以在异常处理程序中直接修改它。
这显示了中断处理程序的另一个问题:
指令POP {R0-R7}
会将R4
寄存器R7
设置为不与已中断的程序匹配的值。根据C代码,R12
也将被销毁。这意味着在程序被中断时,这四个寄存器突然改变,而程序没有为此做好准备!