ARM Cortex-M异常模型与TI CC26x0事件总线实战解析
1. 异常模型嵌入式系统的“神经系统”在嵌入式开发的世界里异常处理机制就像是整个系统的“神经系统”。它负责感知和处理所有突发状况无论是外部按键触发的中断还是内部运算产生的除零错误。这套机制决定了系统在面临意外时是能优雅地恢复还是直接“宕机”。ARM Cortex-M架构的异常模型以其高效、可预测和标准化的设计成为了现代嵌入式微控制器MCU的基石。它不仅仅是一套硬件机制更是我们构建稳定、可靠、实时响应系统的底层保障。理解它你才能从“写代码让灯闪烁”的层面深入到“设计一个永不崩溃的系统”的层面。很多开发者尤其是刚接触ARM Cortex-M的朋友常常把“中断”和“异常”混为一谈。实际上在Cortex-M的语境里中断IRQ是异常的一个子集。你可以把异常想象成一个总称它涵盖了所有能让处理器暂停当前任务、跳转到特定处理程序的事件。这些事件包括外部中断IRQ来自GPIO、定时器、串口等外设的信号。系统异常如系统定时器SysTick、可挂起的系统调用PendSV、超级用户调用SVC等这些通常由操作系统内核使用。故障Fault如硬故障HardFault、总线故障BusFault、用法故障UsageFault这是系统最后的“安全网”和“诊断器”。这套模型的核心价值在于实时性和可靠性。通过优先级仲裁高优先级的任务如电机紧急停止信号能立即抢占低优先级任务如LED闪烁。通过向量表跳转处理器能以最小的延迟找到对应的处理函数。而故障处理机制则确保了即使软件有bug如访问非法地址系统也能捕获错误进入预设的故障处理程序而不是彻底失控。2. Cortex-M异常机制深度拆解2.1 异常类型与向量表处理器的“应急电话本”当异常发生时处理器第一件事就是问“我该找谁处理”答案就在向量表Vector Table里。你可以把它看作一个预先定义好的“应急电话本”里面记录了所有异常处理程序的入口地址。根据你提供的TI CC26x0/CC13x0手册内容其异常类型和向量表布局是标准的Cortex-M风格但也有一些芯片特有的细节。我们结合通用原理和该芯片的具体情况来看1. 核心异常类型解析复位Reset 向量号1最高优先级-3。这不是普通的异常而是系统的起点。上电或复位后处理器从向量表的第一项0x00000000取出初始栈指针MSP从第二项0x00000004取出复位程序的地址并开始执行。此时处理器处于特权线程模式。不可屏蔽中断NMI 向量号2优先级-2。顾名思义无法通过常规中断屏蔽寄存器如PRIMASK关闭。通常用于处理极端紧急的硬件错误如看门狗定时器溢出在CC26x0中WDT_NMI事件可配置为触发NMI。硬故障HardFault 向量号3优先级-1固定不可配置。它是所有故障的“最后防线”。当其他可配置故障如总线故障、用法故障被禁用或者故障处理程序自身又发生故障时都会升级Escalate为硬故障。这是调试中最常见的故障入口。可配置故障包括内存管理故障MemManage CC26x0未实现、总线故障BusFault 向量号5和用法故障UsageFault 向量号6。它们的优先级可配置默认0。总线故障处理内存访问错误如访问不存在的地址用法故障处理指令执行错误如执行未定义指令、除零、非法未对齐访问。系统异常SVCall向量号11由SVC指令触发。在操作系统中应用程序通过执行SVC指令来请求内核服务如任务切换、设备驱动调用实现用户态到内核态的切换。PendSV向量号14可挂起的系统服务请求。它是为操作系统进行上下文切换Context Switching而量身定做的。操作系统可以将其优先级设为最低然后在没有其他异常运行时触发PendSV从而安全地进行任务切换。SysTick向量号15系统定时器中断。为操作系统提供周期性的“心跳”是任务调度的时间基准。外部中断IRQ 向量号16及以上这就是我们通常编程中接触最多的中断对应具体的外设如GPIO、UART、Timer等。在CC26x0中从向量号16IRQ0开始对应具体的硬件事件例如向量号16是GPIO边沿检测中断。2. 向量表重定位默认向量表固定在地址0x00000000。但在实际系统中我们往往将程序放在Flash中而将向量表重定位到RAM或其它地址以便动态修改。这是通过向量表偏移寄存器VTOR实现的。手册指出VTOR的值必须512字节对齐即低9位为0范围在0x00000200到0x3FFFFE00之间。在启动代码中我们经常看到这样的操作// 假设将向量表重定位到 0x20000000 处的RAM SCB-VTOR 0x20000000;注意重定位后所有异常处理函数的地址都必须是有效的并且指向Thumb指令地址最低位为1。如果向量表地址无效或处理函数地址错误将直接导致硬故障。2.2 异常优先级与嵌套谁更重要谁先处理异常不是先来后到而是谁优先级高谁先处理。Cortex-M使用负数和正数表示优先级数值越小优先级越高。固定优先级复位-3、NMI-2、硬故障-1拥有最高的固定优先级无法更改。可配置优先级其他所有异常包括IRQ的优先级都可以配置。在CC26x0中可配置优先级范围为0-73位。默认值都是0。优先级分组Priority Grouping是一个高级特性用于在具有大量中断的复杂系统中进行更精细的控制。它将一个优先级数值如8位划分为组优先级Preemption Priority和子优先级Sub-priority两个字段。组优先级决定中断能否相互抢占。只有更高组优先级的中断才能抢占当前正在执行的中断。子优先级当多个中断同时 pending 且组优先级相同时用于决定它们的服务顺序。例如假设优先级字段为8位我们通过AIRCR寄存器设置优先级分组为“第7位为组优先级第0位为子优先级”即抢占优先级占高1位子优先级占低7位。那么中断A优先级 0x80 (二进制 1000 0000)组优先级1子优先级0。中断B优先级 0x00 (二进制 0000 0000)组优先级0子优先级0。虽然A的数值0x80大于B的0x00但A的组优先级1低于B的组优先级0因此B的优先级更高可以抢占A。异常嵌套是抢占的直接结果。当处理器正在执行一个低优先级异常的处理程序时如果发生了一个更高优先级的异常处理器会将当前上下文寄存器等压栈。转而执行高优先级异常的处理程序。高优先级异常处理完毕后返回并继续执行被抢占的低优先级异常处理程序。这种机制保证了紧急事件能得到即时响应。2.3 异常进入与返回现场的保存与恢复异常处理的核心是透明性处理完异常后被中断的程序应该像什么都没发生过一样继续运行。这全靠精密的现场保存与恢复机制。1. 异常进入Entry当满足条件的异常发生时处理器硬件会自动执行以下操作以中断IRQ为例完成当前指令除非是复位处理器会完成当前正在执行的指令。压栈Stacking将8个寄存器压入当前使用的栈对于中断通常是主栈MSP。这8个寄存器包括xPSR程序状态寄存器、PC返回地址、LR链接寄存器、R12、R3、R2、R1、R0。这个8字的结构称为异常栈帧。取向量从向量表中读取对应异常处理函数的地址。更新寄存器将LR链接寄存器更新为一个特殊的EXC_RETURN值如0xFFFFFFF9这个值包含了返回后应使用的栈指针MSP或PSP和处理器模式线程模式或处理模式信息。同时将PC更新为异常处理函数的地址开始执行中断服务程序ISR。2. 异常返回Return异常处理函数执行完毕后需要通过特定的指令序列将EXC_RETURN值加载到PC触发异常返回序列。常见的方式是BX LR ; 当LR中存放的是EXC_RETURN值时或者从栈中弹出PCPOP {PC} ; 或 LDMIA SP!, {..., PC}处理器检测到PC被加载了EXC_RETURN值后会出栈Unstacking将之前压入栈的8个寄存器值弹出恢复现场。恢复执行用恢复的PC值即被中断指令的下一条指令地址继续执行原程序。3. 尾链Tail-Chaining优化这是一个重要的性能优化。假设异常A刚处理完异常B已经在pending状态且满足执行条件。如果按照常规流程处理器需要先出栈恢复A的现场然后立即又为B压栈。尾链优化跳过了这个不必要的出栈/压栈循环直接开始执行异常B的处理程序显著减少了异常切换的开销。4. 迟到Late-Arriving优化另一个优化发生在异常进入的压栈阶段。如果在为异常A压栈的过程中一个更高优先级的异常B发生了处理器会立即转向为异常B取向量但压栈操作会继续完成因为压栈的上下文对A和B是通用的。这样高优先级异常B能得到更快的响应。2.4 故障处理系统的“黑匣子”与“安全气囊”故障处理是异常模型中最体现系统健壮性的部分。当程序跑飞、访问非法内存或执行非法指令时故障机制是防止系统彻底崩溃的最后屏障。1. 故障类型与寄存器硬故障HardFault终极故障。其状态寄存器HFSR中的FORCED位若为1表明是其他故障升级而来VECTTBL位为1则表示在读取向量表时出错非常严重的启动问题。总线故障BusFault由内存访问错误引起。状态寄存器BFSR8位的位包括IBUSERR: 指令预取错误。PRECISERR: 精确数据总线错误故障地址存储在BFAR寄存器中非常有用。IMPRECISERR: 不精确数据总线错误通常与写缓冲有关故障地址未知。STKERR/UNSTKERR: 异常进入/退出时压栈/出栈错误。用法故障UsageFault由指令执行错误引起。状态寄存器UFSR16位的位包括UNDEFINSTR: 执行了未定义的指令。INVSTATE: 尝试非法状态切换如用BX指令切换到ARM状态Cortex-M只支持Thumb。INVPC: 非法的EXC_RETURN值栈或程序状态被破坏。NOCP: 访问了不存在的协处理器Cortex-M没有协处理器。UNALIGNED: 未对齐的内存访问某些情况下可配置为触发故障。DIVBYZERO: 除零错误需在CCR寄存器中使能。2. 故障升级Escalation这是理解故障处理的关键。一个可配置故障如总线故障在以下情况下会升级为硬故障该故障的处理程序被禁用未使能。该故障的处理程序在执行时又发生了同类型或更低优先级的故障无法自抢占。其他异常处理程序执行时发生了优先级等于或低于当前执行异常的故障。3. 锁死Lockup最严重的情况是在硬故障处理程序执行期间又发生了硬故障。此时处理器会进入锁死状态。对于CC26x0锁死状态会触发系统复位。这意味着如果你的硬故障处理函数本身也存在严重错误如访问非法内存系统将重启。实操心得调试故障的“三板斧”第一时间看LR进入故障处理程序后首先查看LR链接寄存器的值。如果它是合法的EXC_RETURN值如0xFFFFFFF9说明是从某个异常/中断返回时出的问题。如果它是一个代码地址那这个地址很可能就是触发故障的指令的下一条指令地址是定位问题的关键线索。仔细分析故障状态寄存器依次读取CFSR组合故障状态寄存器包含BFSR和UFSR、HFSR、MMFSR如果支持以及BFAR。这些寄存器会明确告诉你故障类型和地址。例如BFSR的PRECISERR位为1且BFAR有值那就可以直接去检查BFAR这个地址的访问权限。检查栈内容在故障处理程序中检查当前的栈指针SP。回溯栈帧找到被压入栈的PC、LR等寄存器值可以帮助你重建故障发生前的调用链。在Keil或IAR的调试器中通常有“Fault Reports”窗口自动解析这些信息务必善用。3. CC26x0/CC13x0事件总线Event Fabric实践TI的CC26x0/CC13x0系列无线MCU在标准Cortex-M异常模型之上引入了一个强大的硬件模块事件总线Event Fabric。它不是一个传统意义上的外设而是一个高度灵活的片上互连网络用于在不同外设和子系统之间路由事件信号而无需CPU干预。3.1 事件总线架构解析芯片内部的“信号高速公路”传统的中断模型是“外设 - NVIC - CPU”。事件总线在此基础上增加了“外设 - 事件总线 - 其他外设/DMA/NVIC”的路径。这带来了几个核心优势降低CPU负载外设间可以直接通信触发动作如ADC采样完成直接触发DMA搬运再触发定时器CPU可以休眠。提高实时性硬件级的事件路由比软件响应更快、更确定。增强灵活性通过配置可以将几乎任何外设产生的信号事件路由到几乎任何能接收事件的外设订阅者。CC26x0/CC13x0包含两个主要的事件总线域MCU事件总线位于MCU电源域连接主要的外设如GPIO、UART、SPI、Timer、DMA、射频核心等。AON事件总线位于常开Always-On电源域连接低功耗相关模块如唤醒控制器WUC、实时时钟RTC、模拟外设AUX。即使MCU主核休眠AON域仍可工作并响应事件。这两个域通过MCU事件总线作为AON事件总线的一个订阅者进行交互。AON域的事件可以路由到MCU域从而唤醒主CPU。事件类型事件是高电平有效的信号。它可以是硬件中断传统的外设中断信号。软件事件由软件写特定寄存器如SWEV.SWEV0产生的事件。DMA触发信号用于触发DMA传输。3.2 事件路由配置实战以触发DMA为例让我们通过一个具体场景来理解事件总线的配置使用GPTimer通用定时器的匹配比较事件自动触发ADC采样并通过DMA将采样结果搬运到内存整个过程无需CPU参与。这个场景涉及多个订阅者和事件源事件源GPTimer的匹配比较事件例如GPT0A_CMP事件号0x3D。订阅者1ADC的采样触发输入。ADC可以配置为由特定事件触发开始转换。订阅者2µDMA微直接内存访问的通道触发输入。DMA通道可以配置为由特定事件触发一次传输。配置步骤详解步骤1配置事件源GPTimer0A首先我们需要设置GPTimer0在匹配比较时产生一个内部事件信号。// 1. 使能GPTimer0外设时钟假设使用Power驱动 PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH); PRCMLoadSet(); while(!PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH)); PRCMPeripheralRunEnable(PRCM_PERIPH_GPT0); PRCMLoadSet(); // 2. 配置GPTimer0A为32位周期性定时器并设置比较匹配动作 GPTimerConfigure(GPT0_BASE, GPT_CFG_32BIT_CPT_UP); GPTimerLoadSet(GPT0_BASE, GPT_A, 0xFFFFFFFF); // 设置装载值 GPTimerMatchSet(GPT0_BASE, GPT_A, 0x0000FFFF); // 设置匹配值决定触发频率 // 关键配置设置TimerA在匹配时的动作这里我们配置为“产生事件” // GPTimerActionSet()函数可能封装了TAMR.TCACT寄存器的配置。 // 我们需要将TCACT字段设置为产生事件而非中断。具体值需查手册。 // 假设 GPT_ACT_EVENT 代表产生事件 GPTimerActionSet(GPT0_BASE, GPT_A, GPT_ACT_EVENT); // 3. 使能定时器 GPTimerEnable(GPT0_BASE, GPT_A);步骤2在事件总线中将GPTimer事件路由到ADC触发输入MCU事件总线有一系列选择寄存器EVENT:xxx_SEL用于将某个订阅者的输入连接到特定的事件源。 我们需要找到ADC触发输入对应的事件选择寄存器。假设ADC的触发输入对应“订阅者Y”的输入0。// 查找数据手册中ADC触发事件的选择寄存器例如可能是 EVENT:ADCTRIGSEL // 假设 GPT0A_CMP 的事件枚举值是 0x3D HWREG(EVENT_BASE EVENT_O_ADCTRIGSEL) 0x3D; // 将ADC触发源设置为GPT0A_CMP事件步骤3配置ADC以外部事件为触发源// 配置ADC控制寄存器设置触发源为外部事件而非软件触发或始终转换 // 例如设置 ADC:CTL.TRIG_SRC EXT_EVENT HWREG(ADC0_BASE ADC_O_CTL) | ADC_CTL_TRIG_SRC_EXT;步骤4将同一个GPTimer事件也路由到DMA通道触发DMA通道也有其触发事件选择寄存器。// 假设我们要使用DMA通道0其触发事件选择寄存器是 UDMA0:CH0_TRIGSEL // 同样将其设置为 GPT0A_CMP 事件 HWREG(UDMA0_BASE UDMA_O_CH0_TRIGSEL) 0x3D;步骤5配置DMA通道// 配置DMA通道0为基本模式传输ADC结果寄存器到内存数组 // 设置源地址为ADC结果寄存器目标地址为内存数组传输大小为字word // 设置仲裁大小一次触发传输的数据量并使能通道 tDMAControlTablePtr-channelControl UDMA_CHCTL_DSTINC_32 | // 目标地址递增 UDMA_CHCTL_SRCINC_NONE | // 源地址不递增 UDMA_CHCTL_DST_SIZE_32 | // 传输大小32位 UDMA_CHCTL_SRC_SIZE_32 | UDMA_CHCTL_ARBSIZE_1 | // 每次触发传输1个数据 UDMA_CHCTL_XFERMODE_BASIC; // 基本模式 tDMAControlTablePtr-srcEndAddr (void*)(ADC0_BASE ADC_O_FIFO); // ADC数据FIFO地址 tDMAControlTablePtr-dstEndAddr (void*)(adcSampleBuffer[BUFFER_SIZE-1]); // 分配通道并设置通道属性 uDMAChannelAssign(UDMA_CH0_ADC); // 假设宏定义了ADC对应的DMA通道 uDMAChannelAttributeEnable(UDMA_CH0_ADC, UDMA_ATTR_USEBURST); // 使用突发传输 uDMAChannelControlSet(UDMA_CH0_ADC, UDMA_CHCTL_...); // 更详细的配置 uDMAChannelEnable(UDMA_CH0_ADC); // 使能DMA通道步骤6启动整个链路// 启动GPTimer它将开始计时 GPTimerEnable(GPT0_BASE, GPT_A); // 此后每当GPTimer0A计数值达到匹配值 // 1. GPTimer0A产生 CMP 事件。 // 2. 事件总线将该事件同时发送给ADC的触发输入和DMA通道0的触发输入。 // 3. ADC收到触发开始一次转换。 // 4. DMA通道0收到触发等待ADC转换完成通常ADC完成会产生另一个“数据就绪”事件该事件应预先配置为DMA的请求信号这里为简化假设DMA触发即开始传输。实际更复杂可能需要ADC的“数据就绪”事件作为DMA请求源。 // 整个过程CPU无需干预可以进入低功耗模式。 PRCMSleepEnter();注意事项与避坑指南事件编号Event Number这是配置的关键。必须严格查阅芯片的《技术参考手册》TRM在“Event Fabric”或“Interrupts and Events”章节找到类似Table 4-6. MCU Event Fabric Input Events的表格确认你使用的事件源如GPT0A_CMP对应的确切十六进制编号如0x3D。事件类型事件总线传递的是电平信号。对于DMA触发需要确认外设产生的是否是适合DMA的脉冲信号。有些事件可能需要外设内部配置为“产生DMA请求”模式。竞争条件如果多个订阅者监听同一个事件它们会同时被触发。要确保逻辑正确例如ADC转换时间是否与DMA传输速度匹配。低功耗联动这是事件总线的精髓。在配置外设、DMA和事件路由后务必确保在CPU进入睡眠例如PRCMSleepEnter()前所有相关的外设时钟、事件通路都已正确使能。一个常见的错误是事件路由配置好了但产生事件的外设如定时器没有启动或者接收事件的外设如ADC没有配置为事件触发模式。3.3 软件事件与动态路由除了硬件事件事件总线还支持软件事件。通过写SWEV寄存器软件事件寄存器的特定位可以手动产生一个事件这个事件可以像硬件事件一样被路由到任何订阅者。例如产生软件事件0HWREG(EVENT_BASE EVENT_O_SWEV) | EVENT_SWEV_SWEV0;这个事件事件号0x64可以被路由到DMA作为软件触发或者路由到另一个外设作为启动信号。这在需要软件同步多个硬件操作的场景下非常有用。动态路由意味着你可以在运行时改变事件的选择寄存器从而动态改变系统的响应行为。例如在系统运行的不同阶段可以将同一个ADC采样完成事件路由到不同的DMA通道以填充不同的数据缓冲区。4. 中断与异常编程的常见陷阱与调试技巧理解了原理和配置在实际编程中依然会踩坑。下面是我在多年开发中总结的一些典型问题和解决方法。4.1 优先级配置错误导致的中断“饿死”或“嵌套混乱”问题现象高优先级中断频繁发生导致低优先级中断永远得不到执行饿死。或者中断嵌套行为不符合预期低优先级中断打断了高优先级中断。排查思路确认NVIC优先级分组使用NVIC_SetPriorityGrouping()函数CMSIS标准或在启动时检查SCB-AIRCR寄存器。确保整个项目对优先级分组的设置是一致的。不同的库或代码模块如果设置了不同的分组会导致优先级判断错乱。检查具体中断优先级使用NVIC_SetPriority(IRQn, priority)设置的priority参数是经过分组编码后的值。你需要清楚当前分组下这个数值对应的抢占优先级和子优先级各是多少。一个常见的错误是认为设置优先级为1的中断比优先级为2的中断“更优先”但在某些分组下可能恰恰相反。注意默认优先级所有可配置中断的默认优先级都是0。如果你没有显式配置所有中断的抢占优先级都相同它们之间不会嵌套只会按硬件中断号顺序排队尾链。解决方案在系统初始化早期统一设置一次优先级分组并记录下来。为中断分配优先级时绘制一个简单的优先级表格明确哪些中断需要抢占哪些不需要。对于绝对不允许被打断的关键代码段可以使用__disable_irq()和__enable_irq()或操作PRIMASK寄存器进行全局中断屏蔽但要非常小心关闭中断的时间必须极短。4.2 栈溢出引发的诡异硬故障问题现象系统运行一段时间后随机进入硬故障且故障地址BFAR看起来是合法的或者故障发生在完全不相干的函数中。查看栈指针SP发现其值已不在预定义的栈内存范围内。根本原因线程栈或中断栈空间不足。异常压栈、局部变量、函数调用都会消耗栈空间。如果中断嵌套层次很深或者某个函数递归调用或无意中的巨大局部数组就会导致栈溢出破坏栈之外的内存可能是堆、静态变量区或其他任务栈从而引发各种难以定位的内存错误最终以硬故障收场。调试与预防使用调试器查看栈使用情况在IAR或Keil中可以在内存窗口观察栈区域的边界。通常栈顶初始值附近会被填充特定的模式如0xCDCDCDCD运行一段时间后被使用的栈区域会被改写。通过观察改写区域的边界可以估算最大栈深。增加栈大小在启动文件或链接脚本中增大STACK段的大小。这是最直接的方法但会浪费RAM。静态分析对于深度递归调用尽量改为迭代。避免在函数内定义过大的局部数组特别是作为缓冲区考虑使用全局或静态分配或者从堆分配。使用MPU内存保护单元如果芯片支持Cortex-M的MPU可以配置MPU将栈区域设置为“不可执行”并在栈底部设置一个保护页Guard Page。一旦栈溢出触及保护页会立即触发内存管理故障帮助你快速定位问题而不是等到数据被破坏后才出现随机错误。4.3 中断服务程序ISR设计不当问题症状系统响应变慢其他中断延迟增加。偶尔丢失数据如串口丢包。在ISR内调用不可重入函数导致数据损坏。黄金法则快进快出ISR应该只做最必要、最紧急的工作例如清除中断标志、从硬件寄存器读取数据到缓冲区、发送一个信号量或设置一个任务就绪标志。复杂的处理应该交给任务Thread来完成。避免阻塞操作绝对不要在ISR中使用delay()、等待某个循环条件、或调用可能阻塞的API如某些OS的task_sleep。注意可重入性如果ISR和主循环或其他中断共享全局变量或硬件资源必须使用临界区保护如暂时关中断或原子操作。避免在ISR中调用printf、malloc等非可重入的库函数。正确清除中断标志一定要在ISR开始时或结束时清除触发该中断的外设标志位。否则退出ISR后中断标志依然有效会导致处理器立即再次进入该ISR形成“中断风暴”使系统卡死。4.4 向量表相关错误问题现象程序一上电或一触发中断就进入硬故障HFSR寄存器的VECTTBL位被置位。可能原因及解决VTOR设置错误在重定位向量表后VTOR寄存器指向的地址不是有效的内存区域或者没有进行512字节对齐。确保重定位的地址在有效RAM/Flash范围内并且是512的整数倍。向量表内容错误重定位后没有正确地将原向量表的内容拷贝到新地址。或者新的向量表中的处理函数地址无效例如函数已被编译器优化掉或者地址计算错误。在启动代码中确保拷贝操作正确无误。中断处理函数未实现即使你没有使用某个中断也必须在向量表中为其提供一个默认的处理函数通常是一个死循环或空函数。如果向量表项是0NULL触发该中断时处理器会尝试跳转到0地址执行必然导致错误。标准的做法是提供一个Default_Handler弱函数所有未使用的中断都指向它。// 在启动文件或中断向量表中 void Default_Handler(void) { while(1); // 或者调用一个故障处理函数 } // 将未使用的中断向量指向 Default_Handler4.5 低功耗模式下的中断与事件唤醒在CC26x0这类低功耗MCU中让CPU进入睡眠Sleep或深度睡眠Deep Sleep是常态。此时中断和事件是唤醒系统的关键。常见问题配置了中断但CPU无法被唤醒。排查清单外设时钟与电源域在CPU进入低功耗模式前产生中断的外设所在的电源域必须保持开启例如对于AON域的外设如RTC它始终有电对于MCU域的外设需要配置为在相应低功耗模式下保持运行。同时外设的时钟也必须使能。NVIC中断使能不仅要在外设级使能中断如设置UART0:IMSC寄存器还必须在NVIC级使能该中断使用NVIC_EnableIRQ()。唤醒使能对于某些深度睡眠模式可能需要在电源管理控制器PRCM中额外使能特定外设的中断作为唤醒源。例如在TI的驱动库中可能需要调用PRCMLowPowerClockSourceSet()和PRCMPeripheralWakeupEnable()。事件总线配置如果你希望通过AON域的事件如RTC比较事件来唤醒MCU那么必须确保AON事件总线正确配置将RTC事件路由到了MCU事件总线。MCU事件总线将该事件路由到了作为唤醒源的订阅者如系统CPU的唤醒输入。在MCU的NVIC中使能了对应的外部中断。中断标志在进入低功耗前确保清除了外设的中断标志位否则可能一进入睡眠就被 pending 的中断立即唤醒。调试低功耗唤醒问题一个有效的方法是使用静态电流测量和调试器结合。先测量系统进入睡眠后的电流如果电流没有降到预期值说明有外设没关如果电流降下去了但无法唤醒则重点检查上述的配置链。可以在唤醒中断的ISR入口处设置一个断点或者翻转一个GPIO来验证中断是否真的触发了。