找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7633|回复: 8
收起左侧

关于51单片机定时器计时不准确问题分析

[复制链接]
ID:475776 发表于 2019-4-9 13:54 | 显示全部楼层 |阅读模式
请教下大神们:
      我用STC89C52RC单片机的定时器0来计时NEC(红外)脉冲时出现过这种现象准确的问题。
描述:
我在外部中断0中来计算两个下降沿之间的时间,在计时1.125ms的脉冲时,定时器中的TL0、TH0中的数据小于理论计算值,而且还相差比较大(200~300个定时器周期)。但当计时13.5ms的脉冲时,定时器TL0、TH0中的数据与理论计算值接近。不知道是什么原因,有测试过晶振波形,平均是11.079MHZ与计算时用的11.0592MHZ接近。同时我还测试了NEC红外脉冲与理论脉冲宽度接近。

NEC脉冲

NEC脉冲

  1. volatile uint8_t plus_nums = 0;//记录下降沿数量
  2. volatile uint8_t nec_code[4] = {0};//保存NEC协议数据
  3. volatile uint8_t received_flag = 0;//接收完成标志
复制代码
  1. /**
  2. *功能:初始化外部中断0、定时中断0、定时器1
  3. *说明:定时器0不使能中断
  4. **/
  5. void Init_all_interrupt(void)
  6. {
  7.         TMOD = 0x01;//16位计数值
  8.         ET0 = 1;
  9.         EX0 = 1;
  10.         IT0 = 1;
  11.         TR0 = 0;
  12.         EA = 1;
  13.         P1 = 0xff;
  14. }

  15. /**
  16. *功能:解码NEC红外编码,外部中断0.
  17. *共34个下降沿

  18. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  19. *经测试理论值与实际值相差太大,且实际值偏小。猜测应该是晶振频率比11.0592MHZ更小
  20. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  21. **/
  22. void NEC_falldown_INT0(void) interrupt 0
  23. {
  24.         uint16_t temp_plus_time = 0;
  25.         #ifdef DEBUG
  26.         TEST = 0;
  27.         #endif
  28.         plus_nums++;
  29.         if(plus_nums == 1)//第一次下降沿
  30.                 {
  31.                         TH0 = 0;
  32.                         TL0 = 0;
  33.                         TR0 = 1;//开启定时器
  34.                 }
  35.         else if(plus_nums == 2)//进行“前导码判断”第一次下降沿,清零开始计时脉冲
  36.                 {
  37.                         temp_plus_time = TH0;
  38.                         temp_plus_time = temp_plus_time << 8;
  39.                         temp_plus_time = temp_plus_time | TL0;//保存脉冲数值
  40.                         TH0 = 0;
  41.                         TL0 = 0;
  42.                         if((temp_plus_time >= 0x2BB0) && (temp_plus_time <= 0x3576))//脉冲时间在(13.5ms)±10%
  43.                         {
  44.                                 #ifdef DEBUG
  45.                                 LED0 = 0;
  46.                                 #endif
  47.                         }
  48.                         else        //前导码异常--重新检测整个NEC协议序列
  49.                         {
  50.                                 //全部复位--重新开始
  51.                                 plus_nums = 0;
  52.                                 TR0 = 0;//关闭定时器0
  53.                         }
  54.                 }
  55.         else        //3-34
  56.                 {
  57.                         temp_plus_time = TH0;
  58.                         temp_plus_time = temp_plus_time << 8;
  59.                         temp_plus_time = temp_plus_time | TL0;//保存脉冲数值
  60.                         TH0 = 0;
  61.                         TL0 = 0;
  62.                         //933~1140;不知道理论计数得到的下限值会偏小很多?---!!!只有一种可能,晶振频率有问题!!!
  63.                         //650~1140---测试可用
  64.                         if((temp_plus_time >= 933) && (temp_plus_time <= 1140))//判断”逻辑0“.脉冲时间在(1.125ms)±10%
  65.                                 {
  66.                                         #ifdef DEBUG
  67.                                         LED1 = 0;
  68.                                         #endif
  69.                                 }
  70.                         //1866~2280;不知道理论计数得到的下限值会偏小很多?---!!!只有一种可能,晶振频率有问题!!!
  71.                         //1566~2280---测试可用
  72.                         else if((temp_plus_time >= 1866) && (temp_plus_time <= 2280))//判断”逻辑1“.脉冲时间在(2.25ms)±10%
  73.                                 {
  74.                                         temp_plus_time = plus_nums -3;//产生数组下标
  75.                                         //nec_arr_index / 8可以使用位域代替这个运算,提高执行效率
  76.                                         nec_code[temp_plus_time / 8] = nec_code[temp_plus_time / 8] | (0x01 << (temp_plus_time % 8));
  77.                                         #ifdef DEBUG
  78.                                         LED2 = 0;
  79.                                         #endif
  80.                                 }
  81.                         else
  82.                                 {
  83.                                         //全部复位--重新开始
  84.                                         plus_nums = 0;
  85.                                         TH0 = 0;
  86.                                         TL0 = 0;
  87.                                         TR0 = 0;//关闭定时器0,重新开始
  88.                                         for(temp_plus_time = 0;temp_plus_time < 4;temp_plus_time++)
  89.                                                 {
  90.                                                         nec_code[temp_plus_time] = 0;
  91.                                                 }
  92.                                         #ifdef DEBUG
  93.                                         LED7 = ~LED7;
  94.                                         #endif
  95.                                 }        
  96.                          //接收到了结束标志脉冲
  97.                         if(plus_nums == 34)
  98.                         {
  99.                                 plus_nums = 0;//结束了
  100.                                 received_flag = 1;//可以进行解码操作了
  101.                                 TR0 = 0;//关闭定时器0
  102.                                 #ifdef DEBUG
  103.                                 LED3 = ~LED3;
  104.                                 #endif
  105.                         }
  106.                 }
  107.         #ifdef DEBUG
  108.         TEST = 1;
  109.         #endif
  110. }

  111. /**
  112. *功能:当71.111ms后都未检测到第二个下降沿时,关闭定时器
  113. */
  114. void NEC_Plus_TIMER0(void) interrupt 1
  115. {
  116.         uint8_t i;
  117.         TR0 = 0;//关闭定时器
  118.         TH0 = 0x00;
  119.         TL0 = 0x00;
  120.         //所有全局变量清零
  121.         plus_nums = 0;
  122.         for(i = 0;i < 4;i++)
  123.                 {
  124.                         nec_code[i] = 0;
  125.                 }
  126.         #ifdef DEBUG
  127.         LED4 = 0;
  128.         #endif
  129. }
复制代码
回复

使用道具 举报

ID:451718 发表于 2019-4-9 16:58 | 显示全部楼层
TL和TH置零后开启计数器, 可能存在一些我们不知道的过程, 最好还是TL=0xff  ,TH=0xff 试试。这个从0x00 到 0xff的过程,实际上可能并不是我们想象的就是一步之遥。 以上仅是一个猜想。
楼主,我会关注你的结果哦。 我也想弄不明白,  设置0xff为初值,和设置0x00为初值是不是一样的效果。
回复

使用道具 举报

ID:96682 发表于 2019-4-9 17:00 | 显示全部楼层
选用的器件精度不够当然会出现计时不准确的问题了,玩玩就好别太较真
有的山寨手机一个月误差十分钟都有不奇怪
回复

使用道具 举报

ID:475776 发表于 2019-4-10 09:35 | 显示全部楼层
robinsonlin 发表于 2019-4-9 16:58
TL和TH置零后开启计数器, 可能存在一些我们不知道的过程, 最好还是TL=0xff  ,TH=0xff 试试。这个从0x00  ...

51定时器是向上计数的,我用TL0=0x00,TH0=0x00是为了计时NEC红外脉冲时间的。我也有测量晶振脉冲平均是11.079MHZ(5个12MHZ的脉冲、1个8MHZ脉冲)。关键点:当计时短脉冲时,非常不准确。而计时长脉冲时,很准确。我没有用定时器中断来计时,按照道理应该不会因为进入中断而产生的误差。
回复

使用道具 举报

ID:475776 发表于 2019-4-10 09:37 | 显示全部楼层
wc86110 发表于 2019-4-9 17:00
选用的器件精度不够当然会出现计时不准确的问题了,玩玩就好别太较真
有的山寨手机一个月误差十分钟都有不 ...

那你所说的精度,应该是定时器计时过短时误差大。计时长时误差小。但是我想知道的是这个误差是怎么来的?因为我定时器计时不是通过定时器中断计时的。而定时器计时是独立于CPU运行的,也就是说当进入外部中断时定时器计数还在运行
回复

使用道具 举报

ID:123289 发表于 2019-4-10 10:06 | 显示全部楼层
楼主:将的的测量原理画成流程图,你自己就会发现问题了。
会画流程图吗?
回复

使用道具 举报

ID:475776 发表于 2019-4-10 11:08 | 显示全部楼层
yzwzfyz 发表于 2019-4-10 10:06
楼主:将的的测量原理画成流程图,你自己就会发现问题了。
会画流程图吗?

好的。我试下,还有就是请问下你这里有流程图绘制的工具吗?有的话还请发下
回复

使用道具 举报

ID:475776 发表于 2019-4-11 17:13 | 显示全部楼层
yzwzfyz 发表于 2019-4-10 10:06
楼主:将的的测量原理画成流程图,你自己就会发现问题了。
会画流程图吗?

我有重新整理测量原理,也做了流程图。没发现导致这样结果的逻辑问题
回复

使用道具 举报

ID:696728 发表于 2020-4-4 08:18 | 显示全部楼层
我也遇到这个问题,100us以下定时,相差几十个us呢,不知原因为何。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表