chengzepeng 发表于 2012-9-11 23:18:58

菜鸟必看之16位定时器的正确填充方法

特别注意事项:
1,在进行写定时器时会停止计数,填充的时候需要补偿1。
2,进入定时器中断时需要时间,填充前必须读出再减去进入中断需要的时间。
3,计算填充值时需要时间,填充计算过程中需要考虑。

例子:

活动填充值:#include<at89x51.h>

#define uint8_t unsigned char
#define uint16_t unsigned int

#define K1 P1_0
#define K2 P1_1
#define L0 P2_0

uint16_t T_D,T_X;

void delay_ms(uint8_t xms)                // 晶体为12M时循环一次为1mS
{
        uint16_t xus;
        for(;xms;--xms)
        {       
                for(xus=124;xus;--xus);
        }
}

void int_t0(void) interrupt 1
{
        T_X = T_D+TL0+14;        // T_X为临时变量,T_D为定时器填充值,14为补偿值,TL0当前值为进入中断所需时间
        TH0 = T_X>>8;
        TL0 = T_X;
        P2_0 = !P2_0;                       
}

void main(void)
{
        TMOD = 0x01;        // T0设置为16位定时器
        TH0 = 0xFC;
        TL0 = 0x22;
        ET0 = 1;                 // 开T0中断
        EA = 1;                        // 开总中断
        TR0 = 1;                // 打开T0定时器
        T_D = 65536-1000;
        T_X = 0;
        while(1)
        {
                if(!(K1&&K2))
                {
                        delay_ms(100);
                        if((!K1)&&(T_D<5000))
                        {
                                T_D = T_D+100;       
                        }
                        else if((!K2)&&(T_D>100))
                        {
                                T_D = T_D-100;                                       
                        }
                        while(!(K1&&K2));                               
                }
        }
}固定填充值50mS(12M):#include<at89x51.h>

#define uint8_t unsigned char
#define uint16_t unsigned int

#define K1 P1_0
#define K2 P1_1
#define L0 P2_0

void int_t0(void) interrupt 1
{
        TH0 = (65536-50000)/256;                // 定时50mS进入中断
        TL0 = (65536-50000)%256+2+TL0;// 加上补偿时间
        P2_0 = !P2_0;                       
}

void main(void)
{
        TMOD = 0x01;        // T0设置为16位定时器
        TH0 = (65536-50000)/256;        // 定时50mS进入中断
        TL0 = (65536-50000)&256;
        ET0 = 1;                 // 开T0中断
        EA = 1;                        // 开总中断
        TR0 = 1;                // 打开T0定时器
        while(1);
}

Lu.Shi 发表于 2012-9-12 13:37:43

void int_t0(void) interrupt 1

{

      TH0 = (65536-50000)/256;                // 定时50mS进入中断

      TL0 = (65536-50000)%256+2+TL0;// 加上补偿时间

      P2_0 = !P2_0;                        

}

楼主能不能讲讲上面对TL0赋初值时的补偿时间啊,什么是补偿时间?为什么加上补偿时间?为什么补偿时间是加上2+TL0?
谢谢了!!

chengzepeng 发表于 2012-9-12 15:19:32

Lu.Shi 发表于 2012-9-12 13:37 static/image/common/back.gif
void int_t0(void) interrupt 1

{


定时器溢出后会重新计数,并且触发中断,
从主函数进入中断需要时间,
在中断读出TL0 也就是进入中断的时间,
+2的计算和写入消耗的时间补偿,
如果TL0需要填充的是0,
那么在中断里面就不需要加了,
要写--TL0;因为写TH0时会导致TL0加2,所以需要减去一个!

jz701209李 发表于 2012-9-12 15:46:11

路过学习一下!!!!!!

Lu.Shi 发表于 2012-9-12 16:13:28

chengzepeng 发表于 2012-9-12 15:19 static/image/common/back.gif
定时器溢出后会重新计数,并且触发中断,
从主函数进入中断需要时间,
在中断读出TL0 也就是进入中断的时 ...

那按照您的意思,是指每次进入中断里面都要在写入TH0和TL0耗费时间,所以对TL0要加上补偿时间,这个我理解了,但是为什么加的是2呢?还有为什么在中断中对TL0重新写入初值时还要加上TL0呢??

linucos 发表于 2012-9-12 16:23:33

代码挺漂亮不错!

chengzepeng 发表于 2012-9-12 16:42:15

本帖最后由 chengzepeng 于 2012-9-12 16:43 编辑

Lu.Shi 发表于 2012-9-12 16:13 static/image/common/back.gif
那按照您的意思,是指每次进入中断里面都要在写入TH0和TL0耗费时间,所以对TL0要加上补偿时间,这个我理 ...

建议你用个仿真器测试下,在中断后的第一句做断点!
主函数就用死循环什么也不干,
中断后看到TL0就是4或者5,这个就是中断进入所需的时间,如果主函数更复杂的话需要时间更长!

加上2的原因是在计算加的时候就消耗了两个指令时间!

Lu.Shi 发表于 2012-9-12 16:49:18

chengzepeng 发表于 2012-9-12 16:42 static/image/common/back.gif
建议你用个仿真器测试下,在中断后的第一句做断点!
主函数就用死循环什么也不干,
中断后看到TL0就是4或 ...

明白了,谢谢指点。。

568581185 发表于 2012-9-12 16:56:11

{:smile:}路过学习一下

zhdo1983 发表于 2012-9-12 21:27:33

mark                           

a794001114 发表于 2012-9-13 01:11:09

mark,51定时器

chengzepeng 发表于 2012-9-13 12:31:19

a794001114 发表于 2012-9-13 01:11 static/image/common/back.gif
mark,51定时器

这个可不单单是51定时器,
PIC或者AVR以及STM都通用,
只要是需要填充的定时器都要进行计数补偿!

Sullivan 发表于 2012-9-13 13:47:38

chengzepeng 发表于 2012-9-13 12:31这个可不单单是51定时器,PIC或者AVR以及STM都通用,只要是需要填充的定时器都要进行计数补偿! ...

如果TL0恰好是255你岂不悲剧了?如果再加上溢出判断你岂不更悲剧了?

chengzepeng 发表于 2012-9-13 14:32:13

本帖最后由 chengzepeng 于 2012-9-13 14:35 编辑

Sullivan 发表于 2012-9-13 13:47 static/image/common/back.gif
如果TL0恰好是255你岂不悲剧了?如果再加上溢出判断你岂不更悲剧了?

所以这个就得看着办啦,
计算的时候你没预算到会溢出?
对于有可能会溢出的先在TH0预先加1,然后在TL0补偿处理!

如果你说你要填充TH0 = 0xFF; TL0 = 0xFF;
那么只能很遗憾的告诉你:建议你换个更快更高端的单片机(除非定时器前面有比指令周期还多的N分频,否则估计地球都没有能在一个指令时间中断又退出并且还有别的时间去干别的)。


定时器使用常识:

不要赋值超过中断时间的底线,
另外还得留下一段时间给主函数,
否则你的中断永远在循环进入!

假设进入定时器中断时间约10个指令周期,你的主函数处理预留20指令周期,中断处理最久为20指令周期,
那么你的定时周期不得小于50指令周期,如果定时的时间过短那么就会出错。

zn_dmu 发表于 2013-8-15 10:42:49

楼主你好,我想问一个关于avr定时器的问题
我在一个程序里每667us产生一个int0中断,在此中断里,给定时器T1赋初值,使其定时一个可变时间,
然后在T1溢出中断里把T1关闭,令某一管脚置高,同时给T3赋初值,使其定时一定时间,
在T3溢出中断里,把T3关闭,同时把输出管脚清零,这样可以使输出信号与输入产生一个相位差。
我的问题是,我在主函数的大循环里,加入一个按键检测,使检测到按键的时候改变定时器T1的初值,这样应该可以使相位可调。
但是在按键的时候输出没有明显变化,好像是按键检测没有作用。想问你一下 我的问题处在哪里,另外还有一个问题,我可以直接这样赋值tcnt1=65355?

chengzepeng 发表于 2013-8-17 16:57:11

zn_dmu 发表于 2013-8-15 10:42 static/image/common/back.gif
楼主你好,我想问一个关于avr定时器的问题
我在一个程序里每667us产生一个int0中断,在此中断里,给定时 ...

AVR的没有深入研究,现在正在玩STM8,那个所有定时器都有自动重载(实际是溢出限制值),只要更改这个寄存器就可以实现可循环定时,还可以强制中断并重载(归0)计数器。

一匹狼 发表于 2013-8-18 08:40:03

不错,学习一下

millwood0 发表于 2013-8-18 09:24:54


      TH0 = (65536-50000)/256;                // 定时50mS进入中断
      TL0 = (65536-50000)%256+2+TL0;// 加上补偿时间vs.
      TH0 += (-50000)/256;                // 定时50mS进入中断
      TL0 += (-50000)+2;// 加上补偿时间

zn_dmu 发表于 2013-8-18 11:03:05

chengzepeng 发表于 2013-8-17 16:57 static/image/common/back.gif
AVR的没有深入研究,现在正在玩STM8,那个所有定时器都有自动重载(实际是溢出限制值),只要更改这个寄 ...

问题解决了,是我犯了个低级错误。。谢谢了

梁国俭 发表于 2013-8-19 05:52:59

谢谢楼主的分享
页: [1]
查看完整版本: 菜鸟必看之16位定时器的正确填充方法