玩MCU的朋友,不管你是刚入门的新手,还是已经做了几年项目的大佬,"中断"这个词肯定不陌生。
打开任何一个MCU的工程,你都能看到各种各样的中断处理函数:串口中断、定时器中断、DMA中断、外部引脚中断……它们散布在代码的各个角落,像是随时待命的消防员,等着某个硬件事件一发生就立刻冲上去处理。
但不知道你有没有认真想过一个问题:CPU明明正在执行main函数里的代码,中断到底是怎么"打断"它的?打断之后,CPU又是怎么知道该跳到哪个函数去处理?处理完了,它又是怎么回到原来的位置继续跑的?
如果你能把这几个问题想清楚,很多嵌入式开发中让人头疼的"偶发性bug"——比如串口丢数据、定时器抖动、系统莫名其妙跑飞——你都能从根源上找到原因。
这篇文章,把MCU中断的底层机制从头到尾捋一遍。不钻太深的硬件细节,但也不会停留在"中断就是个回调"这种表面理解上。读完之后,你应该能建立起一个比较完整的认知。
先搞清楚一个根本问题:中断和普通函数调用有什么不一样?
很多人刚开始学嵌入式的时候,会下意识地把中断当成一种"特殊的函数调用"。毕竟从代码层面看,中断处理函数确实长得很像普通函数:有函数名,有函数体,里面也可以调用别的函数。
但这两者之间有一个本质区别:调用者不同。
普通函数调用,是你的业务代码主动发起的。比如你在main循环里写了一句 handle_uart(),CPU执行到这一行的时候,才会跳进这个函数去执行。整个流程是确定的、可预测的——你完全知道CPU什么时候会走到这里。
中断不一样。中断是硬件事件发起的,跟你的业务代码没有任何关系。你的主循环可能正在读传感器、刷新屏幕、处理通信协议,突然之间,串口收到一个字节,或者定时器计数到了溢出值,硬件就会"通知"CPU:"嘿,这边有紧急事情需要你处理一下!"
CPU收到这个通知后,会暂停当前正在执行的代码,跳到一个事先约定好的入口函数去处理这个事件,处理完了再回来继续跑之前被打断的代码。
这个过程完全不受你的主循环控制。你不知道它什么时候来,不知道它会打断你正在做的哪件事,甚至不知道它会不会和其他中断撞在一起。
这就是中断最核心的特征:它不是你的代码主动发起的,而是硬件事件触发的跳转——什么时候来、打断谁,你说了不算。
理解了这一点,你就能明白为什么中断函数通常有固定的命名规则。比如 SysTick_Handler、USART1_IRQHandler、TIM2_IRQHandler 这些名字不是程序员随便起的,它们必须和芯片启动文件里定义的入口一一对应。因为CPU不是通过函数名来找到中断入口的,它是通过一张叫做"向量表"的地址映射表来查找的。
来源: 整理文章为传播相关技术,网络版权归原作者所有,如有侵权,请联系删除。
我要赚赏金
