计算定时器定时时间
-
假设现在使用晶振是
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
- 将y转成 16 进制 就是 0xB800
Matlab计算
-
如果以上方程懒得手算求解的话,有个好方法就是用Matlab解一下 [滑稽].
syms x
eqn = x * 12 / 11059200 == 0.02
solve(eqn, x)
-
同样可以得到
18432
定时时间精准性调整
问题提出
- 单片机系统里,硬件进入中断需要一定的时间,大概是几个机器周期,还要进行原始数据保护,就是把进中断之前程序运行的一些变量先保存起来,专业术语叫做
中断压栈
,进入中断后,重新给定时器 TH 和 TL 赋值
,也需要几个机器周期,这样下来就会消耗一定的时间. - 虽说这点时间很短,但这所造成的时间误差是会累计的,没1秒钟都差了几个微秒,时间一久造成的误差就不可小觑了。
解决方案
-
使用软件debug进行补偿:
- keil Debug观察程序运行时间, 可以把2次进入中断的时间间隔观察出来,比较和实际定时的时间相差了几个机器周期,然后在进行定时器初值赋值的时候,进行一个调整。
- 目前我STC89C52上用的是11.0592M晶振,发现差了几个机器周期,就把定时器初值加上几个机器周期,这样相当于进行了一个补偿。
-
使用累计误差计算出来:
- 有时候,除了程序本身存在的误差外,硬件精度也有可能会影响到时钟的精度,比如晶振,会随着温度变化出现温漂现象,就是实际值和标称值要差一点。那么还可以采取累计误差的方法来提高精度:
- 比如我们可以让时钟运行半个小时或者一个小时,看看最终时间差了几秒,然后算算一共进了多少次定时器中断,把这差的几秒平均分配到每次的定时器中断中,就可以实现时钟的调整。
- 有时候,除了程序本身存在的误差外,硬件精度也有可能会影响到时钟的精度,比如晶振,会随着温度变化出现温漂现象,就是实际值和标称值要差一点。那么还可以采取累计误差的方法来提高精度:
-
这个世界上没有绝对的精确,只能在一定程度上提高精确度,但是永远不会使误差为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; }