You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 08:01:46