中断与时钟
59 2025-08-24 09:45
下面把“中断”与“时钟”这两个概念放在一起做一个系统化梳理,并给出在 Arduino(以及更一般的 MCU)场景里它们是怎样配合、怎样配置、有哪些易踩的坑。你可以把它当成一份速查笔记。
-
中断的本质
-
定义:打断当前 CPU 指令流,强制跳到固定入口(向量表)执行一段 ISR,再返回。
-
触发源:
– 外部引脚(attachInterrupt、CHANGE/RISING/FALLING/LOW)
– 片上外设(ADC 完成、串口收到字节、定时器溢出、比较匹配、看门狗复位等)
– 异常事件(地址越界、除零、NMI) -
关键寄存器:全局中断使能位(AVR 的 I 位、ARM 的 PRIMASK),以及每个外设自己的中断使能/标志位。
-
时钟在 MCU 中的角色
-
时钟 = 驱动所有逻辑的“心跳”。
– 源:内部 RC、外部晶振、PLL 倍频、内部 32 kHz RTC 时钟。
– 分配:CPU 时钟、外设总线时钟、定时器计数时钟、ADC 时钟…… -
时钟频率决定了:
– 指令执行速度(1 Hz = 1 条指令 / 秒,8 MHz ≈ 8 MIPS)。
– 定时器分辨率(16 位定时器在 8 MHz 下,1 tick = 0.125 µs)。
– 串口波特率、SPI/I²C 速率等。
-
“定时器中断”——把时钟变成中断
-
工作流程:
时钟 → 预分频器(1/8、1/64 …)→ 计数器 TCNTx → 溢出/比较匹配 → 置位中断标志 → CPU 若全局中断使能则跳转到 ISR。 -
典型用法:
– 产生周期任务(1 kHz PID 控制,每 1 ms 一次)。
– 软件 PWM、Tone、Servo、IR 发射。
– 时间戳/软件 RTC(millis()/micros() 在 AVR 上就是 T0 溢出中断)。
-
Arduino 常见时钟与中断 API 速查 | 需求 | AVR 芯片 | ESP32 | 备注 | |---|---|---|---| | 取毫秒时间戳 | millis() | 同 | 内部 Tick 计数器,32 位 49 天回绕 | | 取微秒时间戳 | micros() | 同 | AVR 每 4 µs 一计,ESP32 每 1 µs | | 周期中断 | Timer1/TimerOne 库 |
hw_timer_t*
+timerAttachInterrupt
| ESP32 有 4 组 64 位通用定时器 | | 外部引脚中断 | attachInterrupt(pin, ISR, mode) | 同 | ESP32 所有 GPIO 都可 | | 精确延时 |delayMicroseconds()
| 同 | 忙等,关中断会失真 | -
中断延迟 & 抖动
-
进入延迟:CPU 完成当前指令 + 向量跳转,AVR 约 3–5 时钟周期;ESP32 带 cache/flash 抖动更大。
-
抖动源:更高优先级中断抢占、ISR 内关中断、flash 擦写(ESP32)。
-
对策:
– ISR 里只做“标记/写队列”,重活放loop()
。
– 关键代码用cli()
/sei()
或portENTER_CRITICAL()
开关中断。
– ESP32 把 ISR 放 IRAM:void IRAM_ATTR onTimer(){...}
。
-
时钟精度 & 温漂
-
内部 RC:±1 %–5 %,随温度/电压漂移。
-
外部晶振:±20 ppm(0.002 %),一年漂移约 10 分钟。
-
若需长期计时:
– 接 32.768 kHz 晶振 + RTC 模块(DS3231、PCF8523)。
– 或用 NTP/ GPS 定期校时。
-
一个最小可运行示例(AVR Timer1 1 kHz CTC)
cpp
复制
#include <TimerOne.h>
volatile bool tick = false;
void onTimer() { // 中断上下文
tick = true; // 只做标记
}
void setup() {
Timer1.initialize(1000); // 1000 µs 周期
Timer1.attachInterrupt(onTimer);
Serial.begin(115200);
}
void loop() {
if (tick) {
tick = false;
static uint32_t cnt = 0;
Serial.println(cnt++);
}
}
-
代码要点:
–volatile
保证编译器不优化掉跨上下文访问。
–loop()
里无阻塞操作,否则中断来了也来不及响应。
一句话总结
时钟 = 心跳源;中断 = 让 CPU 在特定“心跳点”或“外部事件”发生时停下手里活去干别的。
把时钟切成等长节拍,就能用“定时器中断”做出任何周期任务;把外部信号接到引脚,就能用“外部中断”立即响应。二者共同构成了 MCU 实时性的根基。
时钟 = 心跳源;中断 = 让 CPU 在特定“心跳点”或“外部事件”发生时停下手里活去干别的。
把时钟切成等长节拍,就能用“定时器中断”做出任何周期任务;把外部信号接到引脚,就能用“外部中断”立即响应。二者共同构成了 MCU 实时性的根基。
全部评论