PIC16F877A Timer1中断计时不符预期,求排查计算与配置问题
问题分析与解决方案
你的Timer1中断计时误差问题,计算过程是正确的,核心问题出在手动重载Timer1的时机带来的累积误差,以及Timer1配置中未启用自动重装载功能。
问题根源
当Timer1溢出触发中断后,从硬件置位TMR1IF标志,到CPU响应中断进入ISR,再执行TMR1 = preloadValue这段时间内,Timer1已经继续计数了多个指令周期(中断响应延迟+指令执行时间)。每次中断都会多走几个周期,100次累积后,实际定时就会比预期的1秒长,和__delay_ms(1000)的软件延时(误差相对更小)逐渐拉开差距,最终出现数分钟差1秒的情况。
解决方案
启用PIC16F877A Timer1的自动重装载功能,利用硬件自动完成计数器重载,消除手动赋值带来的延迟误差。
1. 修改Timer1初始化函数
配置Timer1的16位读写模式,设置自动重装载寄存器,并启用自动重装载逻辑:
void timer1_init() { T1CON = 0x00; // 重置T1CON寄存器 T1CONbits.T1RD16 = 1; // 允许16位读写TMR1和TMR1RL寄存器 T1CONbits.T1CKPS1 = 0; // 预分频器设置为1:1 T1CONbits.T1CKPS0 = 0; T1CONbits.TMR1CS = 0; // 选择内部时钟源(Fosc/4) // 设置自动重装载值 TMR1RL = preloadValue; // 初始化Timer1计数器 TMR1 = preloadValue; T1CONbits.TMR1ON = 1; // 启动Timer1 LED2_ON; }
2. 简化中断服务程序(ISR)
去掉手动重载TMR1的代码,仅保留计数和清中断标志的逻辑:
void __interrupt() ISR() { if(TMR1IF) { counter ++; if (counter == secCounter1) { counter = 0; LED2_TOGGLE; } TMR1IF = 0; // 清除中断标志位 } }
额外说明
__delay_ms的精度依赖于_XTAL_FREQ定义和实际振荡器频率,如果你的HS振荡器存在微小频率偏差,可能仍会有极细微的同步误差,但不会再出现数分钟差1秒的严重偏移。- 确保T1CON的
T1RD16位必须置1,否则16位寄存器的读写会失效,需分开操作TMR1RLH和TMR1RLL(不推荐)。
修改后运行,LED1和LED2的同步性会大幅提升,长时间运行也不会出现明显时差。
内容的提问来源于stack exchange,提问作者BeamString
相关产品推荐
相关产品推荐

