51单片机 中断与定时器

时间:2024-04-13 15:50:19

51单片机,拥有两个定时器,用来中断计数,分别是T0和T1。而52单片机和51单片机的定时器是一样的,只是52比51多了一个定时器/计数器T2,它们的设置都大同小异

定时器T0与T1不同之处在于它们的工作方式3不同,方式0、1、2相同。
M1
M0
工作方式
0
0
方式0,13位定时器/计数器
0
1
方式1,16位定时器/计数器
1
0
方式2,8位自动重装的8位定时器/计数器
1
1
方式3,仅适用于T0,分成两个8位计数器,
当设置成T1时停止计数

单片机内,有其中一个特殊寄存器叫TMOD(1个字节),这是用来设置定时器工作方式的寄存器,通过软件,将其寄存器内的D0~D7位置0或1,从而达成对定时器的操作。

寄存器TMOD
 
定时器1
定时器0
位序号
D7
D6
D5
D4
D3
D2
D1
D0
位符号(是什么)
GATE
C/51单片机 中断与定时器
M1
M0
GATE
C/51单片机 中断与定时器
M1
M0
要更改定时器的工作方式,我们只用到M1、M2所以其他位一般置0,又因为我们使用的是T0的定时器,所以就有:
0000 0000
使用工作方式0
0000 0001
使用工作方式1
0000 0010
使用工作方式2
0000 0011
使用工作方式3

同理,使用定时器1时也是这么设置。注意,有时候也有使用两个定时器T0、T1的时候,那么就是0001 0001两个都是工作方式1。

51单片机 中断与定时器

可能一般就是往这里面一直计数,方式1为16位的定时器/计数器,对定时器T0(TH0和TL0)来说是分成两个寄存器,TH0为高八位,TL0为低八位,组成了16位的定时器,当低位TL0计满就向高位TH0移一个数,然后清零。

12M的晶振,它的时间周期就是1/12 us。反正机器周期是1us(应该是加1的机器周期)。51单片机是12分频的,如果选用12M晶振,如果是单指令周期的语句(例如i++),刚好是1us,其他语句好似1us的整数倍。这样计算指令时间很方便。

对于利用51单片机进行串口通讯的,一般会就会选用11.0592M晶振了,因为对于9600,4800的波特率,11.0592M晶振计算时正好可以得到整数。而且一条单指令语句也大约是1us。

计满TH0、TL0是65536us(2^16=65536),约等于65.5ms,如果要定时50ms的话,也就是50ms触发一次,可以预装65536-50000=15536us,这样就自动触发了。定时器溢出中断后,会重新从预装值开始加值加到50ms就再产生中断,从而达到了定时的目的。如果要定时1s就可以让定时器中断1000ms/50ms=20次。用软件实现就好了。

另外实际操作时,把15536对256求模:15536/256=60装入TH0中,把15536对256求余:15536%256=176装入TL0中,因为这是两个八位28*28的容器。

用定时器,就必须开启中断了,关于中断,需要用到中断允许寄存器:

IE
位序号
D7
D6
D5
D4
D3
D2
D1
D0
位符号
EA
-
ET2
ES
ET1
EX1
ET0
EX0
位地址
AFH
-
ADH
ACH
ABH
AAH
A9H
A8H

51单片机内的寄存器还蛮多的。。。

定时器中断需要的是:
总中断EA:用来开启全局中断。
ET0、1、2:各个定时器中断位。
使用中断位只用将其置1就行,例如EA=1;ET0=1;

打开了中断开关只是完成了一半,还需要定时器控制寄存器:

TCON
位序号
D7
D6
D5
D4
D3
D2
D1
D0
位符号
TF1
TR1
TF0
TR0
IE1
IT1
IE0
IT0
位地址
8FH
8EH
8DH
8CH
8BH
8AH
89H
88H

你家的寄存器也太多了吧?

使用方法也是和中断寄存器一样,定时器0运行控制位TR0:用来开启定时器0. 把TR0置1,TR0=1;就开启了定时器。

每次的中断函数就填入初值,如果不装入初值,那定时器中断服务完成后,按原理就会从0开始重新计时,并执行自己的事件就可以了。

另外:一般中断服务程序中不要写过多的处理语句,否则程序会来不及执行代码,下一次中断又来袭,结果程序久而久之就乱套了。解决方法就是多线程,不是吗。看下面

51单片机 中断与定时器

说这样就没关系。。。

方式0(13位定时器),的用法和方式1的用法一样,但是值得注意的是:

51单片机 中断与定时器

方式0是13位的定时器,它的低位TL0是五位的(兼容8048单片机的13位定时器,现在很少用到了),所以它的总值是28*25=8192。它能装的值也不能那么多了,于是就装入5ms:
TH0(8192-5000)/32;
TL0(8192-5000)%32;
32是5位寄存器的容量。
所以要中断200次才能达到1s。
这个方式0可以用来做短时间中断。

方式0和方式1,当计数溢出后,计数器变为0,所以要反复重新装填初值,这会影响定时精度。但是方式2可以解决这个问题。

低位TL0是8位定时器,而TH0是常数缓冲器,当低位TL0溢出时,在溢出标志位TF0置1的同时,自动将高位TH0的常数重新装入TL0中,让TL0从初值开始重新计数,这样就不用人为软件重新装入初值带来的误差,从而提高精度。

51单片机 中断与定时器

由于两个是分开的,所以计算初值可以不用求余取模:
              TL0=总值-要计数的个数;
              TH0=总值-要计数的个数;

以11.0592MHz为晶振,那么机械周期为12x(1/11059200)≈1.085us,以计时1s为例,当要计250个数时耗时1.0851x250=271.275us,再来算计时1s要用多少次,即1000000/271.275≈3686次。

在设计51系列的单片机系统时,一般选用11.0592MHz的晶振而不选用12MHz的晶振,为什么?12M的晶振,振荡频率明显高于11.0592M的,按理说12MHz的晶振可以提高单片机的性能,而且整数更好算,那么我们为什么不用12M的呢?这个问题与单片机的串口波特率有关。由于在进入中断、装值、出中断这个过程中,很容易产生时间上的微小误差,当多次操作时微小误差不断累积,终会产生错误。而方式2,由于当定时器计满溢出后单片机自动为其装载初值,并且无需进入中断服务程序进行任何处理,这样定时器溢出的速率就会相对更稳定。说白了,用12M晶振会有8%的误差,用11M晶振的误差为0.

理论上,要使采集的数为正确的,则第8位必须正确(采样16次,取第7,8,9次),则允许发送一字节累加误差不能超过50%,单个的位误差不能超过50/10*100% = 5%,由此可知在9600的波特率下,使用12MHz的晶振时,单个位超过5%,必定会出现传错的位。使用12MHz的晶振在2400波特率下还是可以忍受的,但是超过2400后通信变得极不可靠。(为了保证有效通讯,根据电、传输介质等的物理特性结合串口设备的使用要求,确定RS232最大传输速率只能是115200,然后逐级二分得到57600、28800、19200。。。。为适应这些速率设计的相应的晶振频率。不管哪种说法都是先有波特率在有晶振频率的,也就是,波特率是前人在试验下得出的最佳的通信速率,我们使用的时候直接用现有的通信速率就行,但是要注意单片机使用的晶振频率,使用时会不会使串口产生误差,如果误差太大就会产生通信错误。)

以上还是根据方式2来讨论的,因此对于串口传输来说,必须使用自动方式,方式2,用方式1误差就不知道差到哪里去了。

用11.0592晶振的原因是51单片机的定时器导致的,用51单片机的定时器做波特率发生器时,如果用11.0592Mhz的晶振,根据公式算下来需要定时器设置的值都是整数;如果用12Mhz晶振,则波特率都是有偏差的。用12MHz,最高也就4800,而且有0.16%误差率,但在允许范围,所以没多大影响。

如果用于串口通信,建议选用11.0592M的或22.184M,选择晶振最主要还是参照说明书。

对单片机而言一个机器周期等于12个时钟周期,51单片机串口工作在方式1(自己控制装载的16位)上时,给串口使用的时钟频率要先除2,再除16,为什么要除2呢?因为实际上对于单片机的串口及外部的通信模块来说,单片机的晶振频率即使在12分频后,依然太快,所以先除2,降低串口模块所使用的的时钟频率。为什么要除16呢?因为在串口通信中为了保证所接收的数据的正确性,先对每位信号采集16次,再取其中的7、8、9次,如果有两次是高电平,就认定这一位是1,如果有2位是低电平,就认定这一位是0,所以,公式中频率要除16,。至于为什么要除12是因为公式中的频率Fosc是晶振频率(固件发出的原始频率),但是单片机所使用的的频率是经过了12分频的。所以对单片机而言一个机器周期等于12个时钟周期

1个时钟周期  =1晶振周期 = 1Fosc(如常见的外接12M晶振,那它的时钟周期=1/12M。)

1个机器周期 =  12*(1Fosc)(8051系列单片机的机器周期=12*时钟周期。之所以这样分是因为单个时钟周期根本干不了一件完整的事情(如取指令、写寄存器、读寄存器等),而12个时钟周期就能基本完成一项基本操作了。)

定时器T1的计数值每经过一个机器周期加1,即每经过12*(1Fosc)秒,TL1加1,当TL1等于256就溢出,TH1将值重新赋给TL1,TL1开始重新计数。

方式3不同于其他三个方式,它只能用于T0,也就是定时器0,和方式2差不多,也是把TL0、TH0分成两个独立的寄存器,但是TH0也参与计数,也就是两个独立的8位定时器/计数器。

TL0计数溢出后置位TF0,并申请中断,之后重装。但是由于TL0占用了TR0和TF0,所以TH0只能占用定时器T1的TR1和TF1。所以同时要用定时器T1的话,一定不要用在有中断的场合(emmm,可能就是波特率发生器吧),当然,T1同样可以正常工作在方式0、1、2下。通常这种情况,T1都被用来当做串行口的波特率发生器。波特率发生器的作用是从输入时钟转换出需要的波特率clk,即波特率时钟频率。所以这时候T1更多表示一个时钟功能。T表示Timer,反正就是这么个概念,干什么都行。

52单片机的中断级别
中断源
默认中断级别
序号(c语言)
入口地址(汇编)
INT0-外部中断0
最高
0
0003H
T0-定时器0中断
第2
1
000BH
INT1-外部中断1
第3
2
0013H
T1-定时器1中断
第4
3
001BH
T1/R1串行口中断
第5
4
0023H
T2-定时器2中断
最低
5
002BH

注意TH0是直接占据T1的中断,就连终中断号也占据了。

用法就是这样的。

说了这么多,其实最保险的做法,就是用外置定时器,因为单片机本身定时器作用来做时钟是不准的,单片机内部指令执行也是要花一些时间的,而DS1302是专门的时钟芯片,DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿,可以在断电的时候 继续计时,一节纽扣电池做备用电源工作一两年,操作简便等多种功能。这么香怎么能不用呢?