# 引言 在嵌入式开发中,微秒级定时需求(如传感器脉冲、协议时序)常依赖 `delayMicroseconds()`,但它会阻塞 CPU,无法处理其他任务。AVR 的 CTC 模式提供了一种硬件定时方案:计数器与比较寄存器匹配时自动清零并触发中断,无需软件干预。然而,Arduino 封装层隐藏了硬件细节,直接操作寄存器时若忽略边界条件,会导致定时器失效或系统崩溃。本文以 ATmega328P(Uno)为例,剖析 CTC 模式的边界条件,并给出可落地的代码。 # CTC 模式原理与边界条件 ## 1. 硬件机制 CTC 模式下,定时器计数器(TCNTn)从 0 递增,与比较寄存器(OCRnA)匹配时,TCNTn 自动清零,并可选触发输出比较中断(OCFnA)。定时周期由公式决定: ``` T_cycle = (OCRnA + 1) * prescaler / F_CPU ``` 例如,F_CPU=16MHz,prescaler=1,OCRnA=15 时,周期为 1μs。 ## 2. 关键边界条件 - **比较值上限**:OCRnA 是 8 位(Timer0/2)或 16 位(Timer1)。8 位定时器在 prescaler=1 时,最小周期为 1μs(OCRnA=15),但最大周期仅 16μs(OCRnA=255)。若需更长周期,必须增大 prescaler,但会牺牲分辨率。 - **中断延迟**:中断响应有固定开销(约 20-40 个时钟周期),若定时周期过短(如 1μs),中断服务程序(ISR)尚未完成,下一次匹配已发生,导致中断丢失或嵌套。经验法则:ISR 执行时间必须小于定时周期的 50%。 - **计数器溢出**:CTC 模式在匹配时清零,不会产生溢出中断(TOVn),但若 OCRnA 设置为 0,则每个时钟周期都匹配,产生连续中断,造成中断风暴。 - **与 Arduino 核心库冲突**:Arduino 的 `millis()` 依赖 Timer0 的溢出中断,若修改 Timer0 的 CTC 配置,会破坏时间基准。因此,微秒定时器应优先使用 Timer1 或 Timer2。 # 配置步骤(以 Timer1 为例) 1. **选择定时器**:Timer1 是 16 位,适合微秒级且不干扰 `millis()`。 2. **设置比较值**:根据目标周期计算 OCR1A。 3. **配置模式**:设置 TCCR1B 的 WGM13:10 = 4(CTC 模式)。 4. **设置预分频**:TCCR1B 的 CS12:10 选择时钟源,如 CS10=1 表示无分频。 5. **使能中断**:TIMSK1 的 OCIE1A 置 1。 6. **编写 ISR**:在中断中处理任务,并清除标志位(硬件自动清除)。 # 完整代码示例 以下代码实现一个 10μs 周期的非阻塞定时器,在 ISR 中翻转 LED,同时主循环执行其他任务。 ```c #include #define LED_PIN 13 void setup() { pinMode(LED_PIN, OUTPUT); // 配置 Timer1 为 CTC 模式,周期 10μs(16MHz,无分频) cli(); // 关闭全局中断 TCCR1A = 0; // 清零控制寄存器 A TCCR1B = 0; // 清零控制寄存器 B TCNT1 = 0; // 清零计数器 OCR1A = 159; // 10μs * 16MHz - 1 = 159 TCCR1B |= (1 << WGM12); // CTC 模式(WGM13:10 = 4) TCCR1B |= (1 << CS10); // 无分频,启动定时器 TIMSK1 |= (1 << OCIE1A); // 使能比较匹配中断 sei(); // 开启全局中断 } ISR(TIMER1_COMPA_vect) { // 注意:ISR 应尽量短,避免影响定时精度 digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // 翻转 LED } void loop() { // 主循环可执行其他任务,不被阻塞 delay(100); // 模拟其他工作 } ``` # 注意事项与工程实践 - **避免使用 `digitalWrite` 在 ISR 中**:`digitalWrite` 有额外开销,建议直接操作端口寄存器,如 `PORTB ^= (1 << PB5)`。 - **计算比较值**:使用公式 `OCR = (period * F_CPU / prescaler) - 1`,注意整数溢出,使用 `uint16_t` 或 `uint32_t` 计算。 - **处理长周期**:若需超过 65ms(Timer1 最大周期),可结合计数器变量,在 ISR 中累加,实现软件扩展。 - **中断优先级**:AVR 不支持嵌套中断,若其他中断(如串口)耗时较长,会延迟 CTC 中断,导致抖动。可临时关闭其他中断,但需权衡。 - **测试边界**:在 1μs 周期下,ISR 执行时间可能超过周期,导致中断丢失。建议用示波器验证实际波形,并调整周期或优化 ISR。 - **使用 Timer2 的注意事项**:Timer2 是 8 位,且其预分频器与 Timer0 不同,但 Arduino 核心库未使用 Timer2,可安全使用。 # 总结 CTC 模式是 AVR 上实现微秒级软件定时器的利器,但边界条件(比较值上限、中断延迟、冲突)决定了系统的可靠性。通过合理选择定时器、精确计算比较值、精简 ISR,并避开 Arduino 核心库的依赖,你可以在不阻塞主循环的前提下,获得稳定的微秒级时序。记住:硬件定时器是资源,边界条件是约束,工程实践是平衡的艺术。