ATmega328的Timer1停止时是否触发输入捕获中断?
ATmega328 Timer1停止时的输入捕获中断触发问题
核心结论
当Timer1处于停止状态(预分频器未使能,计数器不计数)时,ICP1引脚上的下降沿仍然会触发TIMER1 CAPTURE中断,同时会将当前TCNT1的值(手动清零后即为0x0000)锁存到ICR1寄存器中。
原理依据
ATmega328的输入捕获触发逻辑与计数器运行状态是解耦的:
- 捕获事件的检测由ICP1引脚的边沿变化直接触发,只要满足三个条件:
- 捕获中断使能位
TICIE1(TIMSK1寄存器)已置1 - 全局中断标志
SREG.I已置1(通过sei()开启) - 捕获触发边沿配置正确(
TCCR1B.ICES1设为0,对应下降沿触发)
- 捕获中断使能位
- 无论计数器是否在运行,边沿事件都会触发中断请求,同时锁存当前TCNT1的值到ICR1——哪怕计数器处于停止状态,TCNT1保持初始值(手动清零后就是0x0000),这个值依然会被正常锁存。
针对DCC信号测量的优化实现
你的优化思路完全可行,具体步骤可以这样做:
- 初始化配置:
- 清零TCNT1寄存器:
TCNT1 = 0; - 配置ICP1引脚为输入(默认就是输入,无需额外配置,除非之前设为输出)
- 设置捕获触发边沿为下降沿:
TCCR1B &= ~(1 << ICES1); - 开启捕获中断:
TIMSK1 |= (1 << TICIE1); - 开启全局中断:
sei(); - 此时Timer1保持停止状态(TCCR1B的CS12~CS10都设为0)
- 清零TCNT1寄存器:
- 中断服务程序(ISR):
ISR(TIMER1_CAPT_vect) { // 读取ICR1,自动清除ICF1中断标志 uint16_t capture_val = ICR1; // 第一次捕获时,capture_val为0x0000,此时启动Timer1 static uint8_t timer_started = 0; if (!timer_started) { // 设置预分频为fCLK/8,启动计数器 TCCR1B |= (1 << CS11); timer_started = 1; } else { // 后续捕获的capture_val就是脉冲时长,直接处理DCC信号 process_dcc_pulse(capture_val); } // 清零TCNT1,为下一次捕获做准备 TCNT1 = 0; } - 注意事项:
- 必须手动清零TCNT1,避免初始值不为0的情况
- 读取ICR1会自动清除ICF1标志,无需额外写1清零
- 确保在第一次捕获后才启动定时器,避免计数器提前运行导致初始值偏差
内容的提问来源于stack exchange,提问作者Laura
相关产品推荐
相关产品推荐

