为了能到远方,脚下的每一步都不能少.|

南方与南

园龄:4年2个月粉丝:5关注:3

2021-02-04 17:14阅读: 5648评论: 1推荐: 0

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

计算定时器定时时间

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

    • 则时钟周期为:

      111059200

    • 则机器周期为:

      1211059200

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

    x1211059200=0.02x=18432

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

    y=6553618432=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;
    }

本文作者:南方与南

本文链接:https://www.cnblogs.com/jingpengblog/p/14373828.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   南方与南  阅读(5648)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起