打印
[51单片机]

定时器调度任务,时间都去哪儿了?

[复制链接]
3579|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
651927693|  楼主 | 2014-5-2 10:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 651927693 于 2014-5-2 10:53 编辑

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

我的主程序如下:
void main(void)
{
        int num = 0;
        timer_init();
       

        P0 = 0x00;
        while(1)
        {       
                half_second_check();
               
                if(1 == flag_half_second)
                {
                        flag_half_second = 0;
                        num ++;
                        if(999 == num)
                                num = 0;
                       
                }
               
                led_display(num/100, (num%100)/10, num%10, 2);
        }
}

注意到我注释了最后一个函数led_display这个函数,这种情况下,测试时间,0.5s,十分准确。
但是当我取消注释时,时间就变成了4.44s
于是很理所当然地认为应该是led_display这个函数的执行时间太长。但是这个程序本身并不长呀,应该不会超过50us:
void led_display(uint num0, uint num1, uint num2, uint dot)
{
        static unsigned int segs = 0;
        if(num_50us%10000 == 0)
        {                  
                if(segs == 0)
                {
                        P32 = 1;
                        if(0 == dot)
                                P1 = led_code[num0]-32;
                        else
                                P1 = led_code[num0];
                        P34 = 0;
                        P33 = 1;
                        P32 = 1;
                }
               
                else if(segs == 1)
                {
                        P34 = 1;
                        if(1 == dot)                          
                                P1 = led_code[num1]-32;        
                        else
                                P1 = led_code[num1];
                        P33 = 0;
                        P34 = 1;
                        P32 = 1;
                }
                else
                {
                        P33 = 1;
                        if(2 == dot)
                                P1 = led_code[num2] - 32;
                        else
                                P1 = led_code[num2];
                        P32 = 0;
                        P33 = 1;
                        P34 = 1;
                        segs = -1;
                }
                segs++;
        }
}

这个程序就是数码管动态显示的程序,这一句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主要是要生成一定频率的方波信号。

使用特权

评论回复
5
651927693|  楼主 | 2014-5-2 12:48 | 只看该作者
但是为什么我注释掉led_display这个函数之后,定时就十分准确呢?

使用特权

评论回复
6
651927693|  楼主 | 2014-5-2 12:52 | 只看该作者
我把50us改成了200us之后,注释了led_display之后测得的时间是2s,准确。取消注释后定时周期为3.8s,确实有所改善。但是是什么原因造成这样的结果呢?

使用特权

评论回复
7
受不了了| | 2014-5-2 13:03 | 只看该作者
呵呵,2楼都给你解释了,在定时器中断不太频繁时堆栈操作所耗时间可以忽略不计,而你这个50us显然就不是了

使用特权

评论回复
8
651927693|  楼主 | 2014-5-2 13:57 | 只看该作者
受不了了 发表于 2014-5-2 13:03
呵呵,2楼都给你解释了,在定时器中断不太频繁时堆栈操作所耗时间可以忽略不计,而你这个50us显然就不是了 ...

但是为什么我还是可以得到50us的精确延时呢?0.5s的时标也是正确的呀。

使用特权

评论回复
9
gx_huang| | 2014-5-2 14:02 | 只看该作者
你模拟一下主程序循环一圈需要的时间,自然就明白问题在哪里了。

使用特权

评论回复
10
651927693|  楼主 | 2014-5-2 14:33 | 只看该作者
gx_huang 发表于 2014-5-2 14:02
你模拟一下主程序循环一圈需要的时间,自然就明白问题在哪里了。

什么意思?我的主程序会花很多时间吗?

使用特权

评论回复
11
ningling_21| | 2014-5-2 15:02 | 只看该作者
定时器0中断中没有赋初始值...

使用特权

评论回复
12
sfesdm| | 2014-5-2 16:02 | 只看该作者
51一般用12MHz的晶振比较多,如果是12分频的话,就是说,每条指令的执行时间是1uS。
中断时,CPU会进行入栈操作,中断返回时,CPU又会进行出栈操作,入栈、中断执行、出栈用的时间大概都有20uS了,你每50uS中断一次,只剩下30uS给主程序。而主程序还要调用两个函数,而且还要进行“%”和“/”这些耗指令的操作,试问,你还有多少时间可以执行真正需要执行的指令?
既然你只要0.5S的间隔时间,建议可以把定时中断时间加大点,改成1mS,应该都没问题了,改成10mS中断,更是绰绰有余。

使用特权

评论回复
13
linqing171| | 2014-5-2 16:09 | 只看该作者
那个 "==" 太绝对了吧
一旦主循环一个函数超了时间,计数过头了怎么办? 计数循环回来再相等?

使用特权

评论回复
14
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这样的整数倍,然后采用位移操作来得到你的结果。

使用特权

评论回复
15
ayb_ice| | 2014-5-2 20:27 | 只看该作者
首先要判断50us cpu执行多少条指令,这些指令又能做多复杂的算法
一般来说,这个中断太快了

使用特权

评论回复
16
651927693|  楼主 | 2014-5-2 20:48 | 只看该作者
ningling_21 发表于 2014-5-2 15:02
定时器0中断中没有赋初始值...

初值赋过值了,只是没有贴出来。

使用特权

评论回复
17
651927693|  楼主 | 2014-5-2 20:53 | 只看该作者
sfesdm 发表于 2014-5-2 16:02
51一般用12MHz的晶振比较多,如果是12分频的话,就是说,每条指令的执行时间是1uS。
中断时,CPU会进行入栈 ...

谢谢您耐心的回复。原来中断是要花费这么多的时间的啊?不过还有个问题,我自己能通过实验的方式得到具体的时间吗?

使用特权

评论回复
18
651927693|  楼主 | 2014-5-2 20:56 | 只看该作者
linqing171 发表于 2014-5-2 16:09
那个 "==" 太绝对了吧
一旦主循环一个函数超了时间,计数过头了怎么办? 计数循环回来再相等? ...

没有理解。。。您能再说得详细点吗?

使用特权

评论回复
19
651927693|  楼主 | 2014-5-2 21:05 | 只看该作者
ZG11211 发表于 2014-5-2 17:22
尽量将中断外的程序运行时间控制在下一次中断到来之前结束,也就是洗干净屁股坐等下一个中断来临,而不是这 ...

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

另外,因为编写上层的程序多了,忘了一些51的知识,忽略了/、%这样的运算是很花时间的。
总之,谢谢。

使用特权

评论回复
20
651927693|  楼主 | 2014-5-2 21:07 | 只看该作者
ayb_ice 发表于 2014-5-2 20:27
首先要判断50us cpu执行多少条指令,这些指令又能做多复杂的算法
一般来说,这个中断太快了 ...

对的,确实太快了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

26

主题

544

帖子

1

粉丝