计算51单片机定时器定时时间方法


计算定时器定时时间

  • 假设现在使用晶振是 11.0592M

    • 则时钟周期为:

      \[\frac{1}{11059200} \]

    • 则机器周期为:

      \[\frac{12}{11059200} \]

  • 如果现在要定时20ms, 就是0.02秒, 要经过x个机器周期后得到0.02秒, 列出方程:

    \[x * \frac{12}{11059200} = 0.02 \\ \\ x = 18432 \]

    • x(18432 )个机器周期后刚好达到 65536(溢出)
    • 因为16 位定时器 的溢出值是 65536(因 为65535 +1 才是溢出)
  • 得到寄存器初值y

    \[y = 65536 - 18432 = 47104 \]

    • 将y转成 16 进制 就是 0xB800
      • TH0 = 0xB8
      • TL0 = 0x00

Matlab计算

  • 如果以上方程懒得手算求解的话,有个好方法就是用Matlab解一下 [滑稽].

    syms x
    
    eqn = x * 12 / 11059200 == 0.02
    
    solve(eqn, x)
    
  • 同样可以得到18432

定时时间精准性调整

问题提出

  • 单片机系统里,硬件进入中断需要一定的时间,大概是几个机器周期,还要进行原始数据保护,就是把进中断之前程序运行的一些变量先保存起来,专业术语叫做中断压栈,进入中断后,重新给定时器 TH 和 TL 赋值,也需要几个机器周期,这样下来就会消耗一定的时间.
  • 虽说这点时间很短,但这所造成的时间误差是会累计的,没1秒钟都差了几个微秒,时间一久造成的误差就不可小觑了。

解决方案

  1. 使用软件debug进行补偿:

    • keil Debug观察程序运行时间, 可以把2次进入中断的时间间隔观察出来,比较和实际定时的时间相差了几个机器周期,然后在进行定时器初值赋值的时候,进行一个调整。
    • 目前我STC89C52上用的是11.0592M晶振,发现差了几个机器周期,就把定时器初值加上几个机器周期,这样相当于进行了一个补偿。
  2. 使用累计误差计算出来:

    • 有时候,除了程序本身存在的误差外,硬件精度也有可能会影响到时钟的精度,比如晶振,会随着温度变化出现温漂现象,就是实际值和标称值要差一点。那么还可以采取累计误差的方法来提高精度:
      • 比如我们可以让时钟运行半个小时或者一个小时,看看最终时间差了几秒,然后算算一共进了多少次定时器中断,把这差的几秒平均分配到每次的定时器中断中,就可以实现时钟的调整。
  3. 这个世界上没有绝对的精确,只能在一定程度上提高精确度,但是永远不会使误差为0,只能无限接近0,如果在上两个基础上,还是觉得不够精确的话,那么建议使用时钟芯片(DS1302),通常时钟芯片计时的精度比单片机的精度要高一些。

Code

  • 精确到ms定时时间的函数

    /* 定义定时器高低位重装数据的全局变量 */
    #define CRYSTAL_VALUE 11059200 // 当前单片机所使用的晶振值
    #define MACHINE_CYCLE 12       // 当前单片机的机器周期
    
    unsigned char T0RH = 0; // T0 重载值的高字节
    unsigned char T0RL = 0; // T0 重载值的低字节
    
    /* 配置并启动 T0,ms-T0 定时时间 */
    void ConfigTimer0(unsigned int ms) {
      unsigned long tmp_value;
      //临时变量
      tmp_value = CRYSTAL_VALUE / MACHINE_CYCLE;
    
      //定时器计数频率
      tmp_value = (tmp_value * ms) / 1000;   // 计算所需的计数值
      tmp_value = 65536 - tmp_value;         // 计算定时器重载值
      tmp_value = tmp_value + 18;            // 补偿中断响应延时造成的误差
      T0RH = (unsigned char)(tmp_value>>8);  // 定时器重载值拆分为高低字节
      T0RL = (unsigned char)tmp_value;
      TMOD &= 0xF0;                   // 清零 T0 的控制位
      TMOD |= 0x01;                   // 配置 T0 为模式 1
      TH0 = T0RH;                     // 加载 T0 重载值
      TL0 = T0RL;
      ET0 = 1;                        // 使能 T0 中断
      TR0 = 1;                        // 启动 T0
    
    }
    
    /* 定时器0中断服务函数 */
    void InterruptTimer0() interrupt 1 {
      TH0 = T0RH; // 重新加载初值
      TL0 = T0RL;
    
    }
    


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM