[51单片机] 定时器调度任务,时间都去哪儿了?

[复制链接]
4295|25
 楼主| 651927693 发表于 2014-5-2 10:49 | 显示全部楼层 |阅读模式
本帖最后由 651927693 于 2014-5-2 10:53 编辑

写了一个程序,里面设计数码管的动态显示,抛弃了之前延时的做法,一律采用时标。但是却发现时间“跑”了,时间到底去哪儿了呢?
我的定时周期是50us,定时中断函数如下:
  1. void timer0(void) interrupt 1 using 0
  2. {
  3.     flag_50us = 1;
  4. }
我用逻辑分析仪测试过,定时准确。
还有一个时标函数,是检查是否到0.5s,如下:
  1. void half_second_check(void)
  2. {
  3.     if(flag_50us == 1)
  4.     {
  5.         flag_50us = 0;
  6.         num_50us++;
  7.         if(10000 == num_50us)
  8.         {
  9.             num_50us = 0;
  10.             flag_half_second = 1;
  11.             P30 = ~P30;            
  12.         }
  13.     }
  14. }

我的主程序如下:
  1. void main(void)
  2. {
  3.         int num = 0;
  4.         timer_init();
  5.        

  6.         P0 = 0x00;
  7.         while(1)
  8.         {       
  9.                 half_second_check();
  10.                
  11.                 if(1 == flag_half_second)
  12.                 {
  13.                         flag_half_second = 0;
  14.                         num ++;
  15.                         if(999 == num)
  16.                                 num = 0;
  17.                        
  18.                 }
  19.                
  20.                 led_display(num/100, (num%100)/10, num%10, 2);
  21.         }
  22. }

注意到我注释了最后一个函数led_display这个函数,这种情况下,测试时间,0.5s,十分准确。
但是当我取消注释时,时间就变成了4.44s
于是很理所当然地认为应该是led_display这个函数的执行时间太长。但是这个程序本身并不长呀,应该不会超过50us:
  1. void led_display(uint num0, uint num1, uint num2, uint dot)
  2. {
  3.         static unsigned int segs = 0;
  4.         if(num_50us%10000 == 0)
  5.         {                  
  6.                 if(segs == 0)
  7.                 {
  8.                         P32 = 1;
  9.                         if(0 == dot)
  10.                                 P1 = led_code[num0]-32;
  11.                         else
  12.                                 P1 = led_code[num0];
  13.                         P34 = 0;
  14.                         P33 = 1;
  15.                         P32 = 1;
  16.                 }
  17.                
  18.                 else if(segs == 1)
  19.                 {
  20.                         P34 = 1;
  21.                         if(1 == dot)                          
  22.                                 P1 = led_code[num1]-32;        
  23.                         else
  24.                                 P1 = led_code[num1];
  25.                         P33 = 0;
  26.                         P34 = 1;
  27.                         P32 = 1;
  28.                 }
  29.                 else
  30.                 {
  31.                         P33 = 1;
  32.                         if(2 == dot)
  33.                                 P1 = led_code[num2] - 32;
  34.                         else
  35.                                 P1 = led_code[num2];
  36.                         P32 = 0;
  37.                         P33 = 1;
  38.                         P34 = 1;
  39.                         segs = -1;
  40.                 }
  41.                 segs++;
  42.         }
  43. }

这个程序就是数码管动态显示的程序,这一句if(num_50us%10000 == 0)用来指定扫描的间隔,现在是每0.5s扫描一次(显示效果自然不好,我这里只是为了调试)。如果到了0.5s,则执行函数,否则直接退出。按理说对时间影响不大呀?
大家帮忙分析分析。

ningling_21 发表于 2014-5-2 12:06 | 显示全部楼层
50US中断一次,时间都用来进出中断了...
受不了了 发表于 2014-5-2 12:06 来自手机 | 显示全部楼层
50us,除了锅巴都没有饭了
 楼主| 651927693 发表于 2014-5-2 12:37 | 显示全部楼层
就是说进出中断的时间也很长喽。我设置成50us主要是要生成一定频率的方波信号。
 楼主| 651927693 发表于 2014-5-2 12:48 | 显示全部楼层
但是为什么我注释掉led_display这个函数之后,定时就十分准确呢?
 楼主| 651927693 发表于 2014-5-2 12:52 | 显示全部楼层
我把50us改成了200us之后,注释了led_display之后测得的时间是2s,准确。取消注释后定时周期为3.8s,确实有所改善。但是是什么原因造成这样的结果呢?
受不了了 发表于 2014-5-2 13:03 来自手机 | 显示全部楼层
呵呵,2楼都给你解释了,在定时器中断不太频繁时堆栈操作所耗时间可以忽略不计,而你这个50us显然就不是了
 楼主| 651927693 发表于 2014-5-2 13:57 | 显示全部楼层
受不了了 发表于 2014-5-2 13:03
呵呵,2楼都给你解释了,在定时器中断不太频繁时堆栈操作所耗时间可以忽略不计,而你这个50us显然就不是了 ...

但是为什么我还是可以得到50us的精确延时呢?0.5s的时标也是正确的呀。
gx_huang 发表于 2014-5-2 14:02 | 显示全部楼层
你模拟一下主程序循环一圈需要的时间,自然就明白问题在哪里了。
 楼主| 651927693 发表于 2014-5-2 14:33 | 显示全部楼层
gx_huang 发表于 2014-5-2 14:02
你模拟一下主程序循环一圈需要的时间,自然就明白问题在哪里了。

什么意思?我的主程序会花很多时间吗?
ningling_21 发表于 2014-5-2 15:02 | 显示全部楼层
定时器0中断中没有赋初始值...
sfesdm 发表于 2014-5-2 16:02 | 显示全部楼层
51一般用12MHz的晶振比较多,如果是12分频的话,就是说,每条指令的执行时间是1uS。
中断时,CPU会进行入栈操作,中断返回时,CPU又会进行出栈操作,入栈、中断执行、出栈用的时间大概都有20uS了,你每50uS中断一次,只剩下30uS给主程序。而主程序还要调用两个函数,而且还要进行“%”和“/”这些耗指令的操作,试问,你还有多少时间可以执行真正需要执行的指令?
既然你只要0.5S的间隔时间,建议可以把定时中断时间加大点,改成1mS,应该都没问题了,改成10mS中断,更是绰绰有余。
linqing171 发表于 2014-5-2 16:09 | 显示全部楼层
那个 "==" 太绝对了吧
一旦主循环一个函数超了时间,计数过头了怎么办? 计数循环回来再相等?
ZG11211 发表于 2014-5-2 17:22 | 显示全部楼层
本帖最后由 ZG11211 于 2014-5-2 17:34 编辑

尽量将中断外的程序运行时间控制在下一次中断到来之前结束,也就是洗干净屁股坐等下一个中断来临,而不是这次还没做完,中断又来催你了,最后的结果就是手忙脚乱。
如楼主的帖子描述,第一次50us标志位翻转后肯定能执行的,但是如果程序在顺序执行过程中被中断打断后,标志位再次翻转,然后会继续上次未完成的任务,而不会进入50us处理程序,也就是本次50us标志位翻转了却未被执行,只有等到上次的顺序任务完成后才会再次检查该标志位是否为真,不知道这样解释楼主是否明白。
另外,单片机在执行if(num_50us%10000 == 0)此类的求余或除法运算时是非常耗时的,你只是需要一个定时扫描时序而已,完全可以改用2,4,8,16或32这样的整数倍,然后采用位移操作来得到你的结果。
ayb_ice 发表于 2014-5-2 20:27 来自手机 | 显示全部楼层
首先要判断50us cpu执行多少条指令,这些指令又能做多复杂的算法
一般来说,这个中断太快了
 楼主| 651927693 发表于 2014-5-2 20:48 | 显示全部楼层
ningling_21 发表于 2014-5-2 15:02
定时器0中断中没有赋初始值...

初值赋过值了,只是没有贴出来。
 楼主| 651927693 发表于 2014-5-2 20:53 | 显示全部楼层
sfesdm 发表于 2014-5-2 16:02
51一般用12MHz的晶振比较多,如果是12分频的话,就是说,每条指令的执行时间是1uS。
中断时,CPU会进行入栈 ...

谢谢您耐心的回复。原来中断是要花费这么多的时间的啊?不过还有个问题,我自己能通过实验的方式得到具体的时间吗?
 楼主| 651927693 发表于 2014-5-2 20:56 | 显示全部楼层
linqing171 发表于 2014-5-2 16:09
那个 "==" 太绝对了吧
一旦主循环一个函数超了时间,计数过头了怎么办? 计数循环回来再相等? ...

没有理解。。。您能再说得详细点吗?
 楼主| 651927693 发表于 2014-5-2 21:05 | 显示全部楼层
ZG11211 发表于 2014-5-2 17:22
尽量将中断外的程序运行时间控制在下一次中断到来之前结束,也就是洗干净屁股坐等下一个中断来临,而不是这 ...

您的意思是这样吗:
首先无论如何,中断服务函数总是会按照我设置的时间来周期性的中断,如果没有更高级的中断,这个是强制性的,是十分准确的。于是,它也会周期性的置位我的flag_50us。但是这个flag_50us是否被我的主循环里的函数捕捉到,就要看我两次判断flag_50us是否为1之间执行代码的时间了。如果这个时间小于我的周期值,那么完全没有问题。但是如果中间执行代码的时间比较长,就有可能出现flag_50us被置位很多次了我都没有捕捉到。

另外,因为编写上层的程序多了,忘了一些51的知识,忽略了/、%这样的运算是很花时间的。
总之,谢谢。
 楼主| 651927693 发表于 2014-5-2 21:07 | 显示全部楼层
ayb_ice 发表于 2014-5-2 20:27
首先要判断50us cpu执行多少条指令,这些指令又能做多复杂的算法
一般来说,这个中断太快了 ...

对的,确实太快了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

26

主题

544

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部