如何写代码 —— 编程内功心法
2026/7/2 4:30:20
这是一个用于ARMv8-M 架构的 C 语言内联汇编函数,用于读取IPSR(中断程序状态寄存器)的值。
staticinlineuint32_tgetipsr(void)static inline:内联函数,编译器会将函数体直接插入调用处,避免函数调用开销uint32_t:返回32位无符号整数getipsr:获取IPSR寄存器值的函数__asm____volatile__("\tmrs %0, ipsr\n":"=r"(ipsr)// 输出操作数:// 输入操作数(空):"memory");// 破坏描述mrs %0, ipsrmrs:Move from System Register,从系统寄存器读取到通用寄存器%0:引用第一个操作数(ipsr变量)ipsr:要读取的系统寄存器"=r" (ipsr):输出操作数
=r:表示输出到寄存器,=表示只写,r表示通用寄存器(ipsr):对应的C变量"memory":破坏描述(clobber)
在ARMv8-M架构中,IPSR(Interrupt Program Status Register)寄存器包含:
当前中断/异常号
常见中断号示例
// 示例1:判断当前是否在中断上下文中uint32_tget_current_exception(void){returngetipsr();// 返回当前异常号}// 示例2:检查是否处于线程模式intis_in_thread_mode(void){return(getipsr()==0);}// 示例3:调试中断信息voiddebug_interrupt_info(void){uint32_tipsr_value=getipsr();if(ipsr_value!=0){printf("当前处于中断/异常处理中,编号: %u\n",ipsr_value);}}这个函数在嵌入式系统开发中常用于:
IPSR(Interrupt Program Status Register)是ARM Cortex-M系列处理器中的一个特殊功能寄存器,属于程序状态寄存器(PSR)的一部分。
IPSR最核心的功能是存储当前正在执行的中断或异常处理程序的编号。
// IPSR的位域|31-9|8-0||保留|异常编号(0-255)|IPSR = 0:处理器处于线程模式(Thread Mode)
IPSR ≠ 0:处理器处于处理器模式(Handler Mode)
| 编号 | 异常类型 | 优先级 | 说明 |
|---|---|---|---|
| 0 | 无异常 | - | 线程模式 |
| 1 | Reset | -3(最高) | 复位 |
| 2 | NMI | -2 | 不可屏蔽中断 |
| 3 | HardFault | -1 | 硬件错误 |
| 4 | MemManage | 可编程 | 存储器管理错误(MPU) |
| 5 | BusFault | 可编程 | 总线错误 |
| 6 | UsageFault | 可编程 | 指令/数据使用错误 |
| 7 | 保留 | - | - |
| 8-10 | 保留 | - | - |
| 11 | SVCall | 可编程 | 系统服务调用(SVC指令) |
| 12 | Debug Monitor | 可编程 | 调试监控器 |
| 13 | 保留 | - | - |
| 14 | PendSV | 可编程 | 可挂起的系统调用 |
| 15 | SysTick | 可编程 | 系统节拍定时器中断 |
| 16+ | IRQ0-IRQ239 | 可编程 | 外部中断 |
// 获取当前异常信息voiddebug_current_exception(void){uint32_texception_num=__get_IPSR();switch(exception_num){case0:printf("线程模式\n");break;case2:printf("NMI中断发生!\n");break;case3:printf("HardFault!需要检查错误地址\n");SCB->CFSR;// 读取配置故障状态寄存器break;case11:printf("系统调用(SVC)\n");break;default:if(exception_num>=16){printf("外部中断IRQ%d\n",exception_num-16);}break;}}// 确保某些关键操作不被中断打断voidcritical_operation(void){uint32_told_ipsr=__get_IPSR();if(old_ipsr!=0){// 已经在中断上下文中,不能再调用某些函数return;}// 执行关键操作__disable_irq();// ... 关键代码 ...__enable_irq();}// 防止中断嵌套过深#defineMAX_NESTING_DEPTH5uint32_tinterrupt_nesting_count=0;voidIRQ_Handler(void){uint32_tipsr=__get_IPSR();uint32_tirq_num=ipsr-16;// 转换为IRQ编号interrupt_nesting_count++;if(interrupt_nesting_count>MAX_NESTING_DEPTH){// 中断嵌套过深,可能是优先级配置错误system_panic("中断嵌套溢出");}// 处理中断...interrupt_nesting_count--;}// 在RTOS中判断上下文voidscheduler(void){uint32_tipsr=__get_IPSR();if(ipsr==0){// 线程上下文,可以正常调度switch_thread();}elseif(ipsr==14){// PendSV中断,专门用于上下文切换perform_context_switch();}else{// 其他中断,延迟调度SCB->ICSR|=SCB_ICSR_PENDSVSET_Msk;}}完整的PSR寄存器(32位) | 31-27 | 26-25 | 24 | 23-16 | 15-10 | 9 | 8-0 | | APSR | 保留 | EPSR.T | 保留 | EPSR.ICI/IT | EPSR.Q | IPSR |// 可以读取,但不能直接写入uint32_tcurrent_exception=__get_IPSR();// 正确__set_IPSR(0);// 错误!编译器会报错当异常处理程序执行完毕后,处理器自动从堆栈恢复PSR,IPSR自动清零(返回线程模式)。
当发生中断嵌套时:
通过调试器可以查看IPSR的值:
// 在GDB中查看(gdb)p/x $ipsr $1=0xf// SysTick中断正在执行// 在MDK/Keil中Register窗口 → Core Registers → PSR → IPSR字段#defineIN_INTERRUPT()(__get_IPSR()!=0)#defineIN_THREAD_MODE()(__get_IPSR()==0)if(IN_INTERRUPT()){// 使用中断安全的函数interrupt_safe_function();}else{// 可以使用普通函数normal_function();}// 统计在各种异常中花费的时间uint32_ttime_in_exceptions[256]={0};uint32_tlast_timestamp=0;uint32_tlast_exception=0;voidSysTick_Handler(void){uint32_tnow=get_timestamp();uint32_tcurrent_exception=__get_IPSR();// 计算在上一个异常中花费的时间uint32_telapsed=now-last_timestamp;time_in_exceptions[last_exception]+=elapsed;last_exception=current_exception;last_timestamp=now;// ... 其他处理 ...}IPSR是理解ARM Cortex-M处理器异常/中断系统的关键寄存器:
掌握IPSR的使用,是嵌入式系统开发和调试的基本功之一。