本小节目录:
6.1,单片机中断及寄存器
6.2,单片机定时器/计数器介绍
6.3,定时器程序编写及介绍
首先必须提醒:初学单片机定时器和中断时,是比较难理解的,难于知识点多,乱,杂,但又是必须去学会的!否则,不算学会单片机!所以,耐心,坚持学下去,慢慢的总会懂的!
6.1)单片机中断及寄存器
一)什么是中断呢?
生活中例如你正在电脑旁敲击程序中,突然有人敲门,你停止敲击键盘,去开门,这个停止敲击键盘去开门就是中断。
中断的概念
CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);
CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);
待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断。
其流程图如下图。
图1 中断流程示意图
二)中断源
在52单片机中有6个中断源
中断号 优先级 中断源 中断入口地址
0 1(最高) INT0---外部中断0 0003H
1 2 T0 --- 定时器/计数器0 000BH
2 3 INT1---外部中断1 0013H
3 4 T1 ---定时器/计数器1 0018H
4 5 TI/RI 串行口中断 0023H
5 6 T2--- 定时器/计数器2 002BH
表 6-1 中断源,中断号及优先级别
52单片机的6个中断源简述如下:
INT0—外部中断0,引入端口:P3.2,触发方式:低电平、下降沿。
INT1—外部中断1,引入端口:P3.3,触发方式:低电平、下降沿。
T0—定时器/计数器0中断,触发方式:TO计数器记满归零。
T1—定时器/计数器1中断,触发方式:T1计数器记满归零。
T2—定时器/计数器2中断,触发方式:T2计数器记满归零。
TI/RI—串口中断,触发方式:串口完成一帧字符发送或接收完。
三)中断寄存器
单片机在使用中断功能时,通常需要设置两个与中断有关的寄存器:中断允许寄存器IE和中断优先级寄存器IP
所谓的中断允许寄存器就是控制各个中断是开还是关,就是要使用哪个中断,就必须将其打开,也即允许该中断。
IE在特殊功能寄存器中,字节地址为A8H,位地址分别是AFH~A8H(由高到低),由于该字节地址(A8)能被8整除(单片机中能被8整除的地址都可以位寻址),因而可进行位寻址,即可对该寄存器的每一位进行单独操作。IE复位值:0x00,各个位定义如表7-2所示。
1)IE—中断允许寄存器
表6-2 IE:中断允许寄存器
Bit |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Name |
EA |
-- |
ET2 |
ES |
ET1 |
EX1 |
ET0 |
EX0 |
Address |
AFH |
-- |
ADH |
ACH |
ABH |
AAH |
A9H |
A8H |
EA: CPU的总中断允许控制位,
EA = 1, CPU开总中断;
EA = 0,CPU屏蔽所有中断申请。
ET2:定时/计数器T2的溢出中断允许位。
ET2 = 1,允许T2中断;
ET2 = 0,禁止T2中断。
ES:串行口中断允许位。
ES = 1,允许串行口中断;
ES = 0,禁止串行口中断。
ET1:定时/计数器T1的溢出中断允许位。
ET1 = 1,允许T1中断;
ET1 = 0,禁止T1中断。
EX1:外部中断1允许位。
EX1 = 1,允许外部中断1中断;
EX1 = 0,禁止外部中断1中断。
ET0:定时/计数器T0的溢出中断允许位。
ET0 = 1,允许T0中断;
ET0 = 0,禁止T0中断。
EX0:外部中断0允许位。
EX0 = 1,允许外部中断0中断;
EX0 = 0,禁止外部中断0中断。
单片机具有两个中断优先级,即高级优先级和低级优先级,可以实现两级中断嵌套。中断优先级在特殊功能寄存器中,可以通过设置实现各个中断属于中断的哪一级,该寄存器字节地址为:B8H,也能位寻址,即可对每一位单独操作。IP复位值:0x00,各个位定义如表6-3所示。
2)IP—中断优先级寄存器
表6-3 IP:中断优先级寄存器
Bit |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Name |
-- |
-- |
-- |
PS |
RT1 |
PX1 |
PT0 |
PX0 |
Address |
-- |
-- |
-- |
BCH |
BBH |
BAH |
B9H |
B8H |
PS:串行口中断优先级控制位。
PS = 1,串行口中断为高优先级中断;
PS = 0,串行口中断为低优先级中断。
PT1:定时/计数器T1中断优先级控制位。
PT1 = 1,T1中断定义为高优先级中断;
PT1 = 0,T1中断定义为低优先级中断。
PX1:外部中断1中断优先级控制位。
PX1 = 1,外部中断1中断定义为高优先级中断;
PX1 = 0,外部中断1中断定义为低优先级中断。
PT0:定时/计数器T0的溢出中断允许位。
PT0 = 1,T0中断定义为高优先级中断;
PT0 = 0,T0中断定义为低优先级中断
PX0: 外部中断0中断优先级控制位
PX0 = 1,外部中断0定义为高优先级中断:
PX0 = 0,外部中断0定义为低优先级中断。
在单片机程序执行中
1)高优先级中断能打断低优先级中断以形成中断嵌套,
2)同优先级中断间,或低级对高级中断则不能形成中断嵌套,
3)在没有设置中断优先级情况下,按照默认中断级别执行中断,若设置中断优先级,则按设置顺序先后执行。(上表6.1中可看到各个中断优先级别)
个人感觉:很多刚学的人看到上面那些中断以及IE,IP寄存器时,肯定都蒙了,完全不知道是干什么用的,当然也包括本人刚开始学这些东西时也一样,完完全全不知道天南地北,其实,这些东西不需要去记它,只需要大概去了解,用得时候去查就可以了,然后久而久之就会记住了!当然,也包括下面的定时器的工作方式和寄存器!都不需要去死记,
6.2)单片机定时器/计数器
介绍前有几个非常重要的说明:
1,定时器和计数器的实质都是加1计数器,千万不要理解为计数器是用来计时的,定时器是用来定时的。只是定时器的加1触发源来自单片机内部,而计数器的加1触发源来自单片机外部。计数是指对外部事件进行计数,外部事件的发生以输入脉冲的形式表示,因此计数功能的实质就是对外来的脉冲进行计数。
2,补充一下C语言的“/”和“%”运算,例:73除10等于7余3,也可以看做为:73/10=7 // 求模运算; 73%10=3 // 求余运算。
3,本人所用STC89C52单片机晶振是12T的,意思是单片机将晶振12分频之后作为自己的主时钟,那么单片机运行的频率:工作频率 = 12MHz / 12 = 1MHz。机器周期 = 1 / 1MHz = 1us,当然大家可以自己算出11.0592MHz对应的机器周期(1.085us)。那要铭记的一个值就是1us。
1)定时器/计数器工作方式寄存器TMOD
该寄存器也属于特殊功能寄存器,其字节地址为:89H,该寄存器不能位寻址,定时和计数功能由控制位C/进行选择,TMOD寄存器的各位信息如表6-5所示。可以看出,2个定时/计数器有4种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各个模式下的功能如表6-4所示。
表6-4:定时器/计数器工作方式寄存器
每个定时/计数器都有4中工作方式,就是通过设置M1M0来设定,对应关系如表6-5所示。
GATE -- 门控位
GATE = 0,定时/计数器的启动和禁止仅有TRX(X = 0/1)决定。
GATE = 1,定时/计数器的启动和禁止由TRX(X = 0/1)和外部中断引脚(INT0/INT1)上的电平(必须是高电平)决定共同决定。
C/T-- 选择计数器模式还是定时器模式选择位
C/T = 1,设置为计数器模式;
C/T = 0,设置为定时器模式。
M1 M0—工作方式选择位
表6-5 TMOD:定时器/计数器工作方式设置表
M1 |
M0 |
定时/计数器工作方式 |
0 |
0 |
设置为方式0,为13位定时/计数器 |
0 |
1 |
设置为方式1,为16位定时/计数器 |
1 |
0 |
设置为方式2,8位初值自动重装的8位定时/计数器 |
1 |
1 |
设置为方式3,仅T0工作,分成两个8为计数器,T1停止 |
每个定时/计数器都有4中工作方式,就是通过设置M1M0来设定,对应关系如表6-5所示。
具体介绍:
1)工作方式0(M1M0为00)和1(M1M0为01),分别为13位和16位计数,最多能装(213)=8192和(216)=65536个数就会溢出定时器,且装的数都在,TH0,和TL0寄存器里,需要人为重装初值(具体如何使用,看下面程序介绍)
2)工作方式2(M1M0为10)为8位初值自动重装,最多能装(28)=256个数,不需要人为重装初值。
3)关于工作方式3,是将定时器T0分为两个独立的计数器,因为不常用,在此不介绍了!
2)定时/计数器控制寄存器TCON
该寄存器也是特殊功能寄存器,字节地址为88H,位地址由低到高分别是88H~8FH,该寄存器可进行位寻址。TCON的主要功能就是控制定时器是否工作、标志那个定时器产生中断或者溢出等等。复位值:0x00,其各个位的定义如表7-6所示。
表6-6TCON:定时器/计数器控制寄存器表
Bit |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Name |
TF1 |
TR1 |
TF0 |
TR0 |
IE1 |
IT1 |
IE0 |
IT0 |
Address |
8FH |
8EH |
8DH |
8CH |
8BH |
8AH |
89H |
88H |
外部中断:
IE0(IE1):外部中断请求标志位
当INT0(INT1)引脚出现有效的请求信号,此位由单片机自动置1,cpu开始响应,处理终端,而当入中断程序后由单片机自动置0.(外部中断,即外部中断相应的引脚接入低电平或下降沿信号时,中断开始响应。)
T0(IT1):外部中断触发方式控制位 //选择有效信号
IT0(IT1)=1:脉冲触发方式,下降沿有效。
IT0(IT1)=0:电平触发方式,低电平有效。
内部中断:
TF0(TF1):内部定时器/计数器溢出中断标志位
当定时器、计数器计数溢出的时候,此位由单片机自动置1,cup开始响应,处理中断,而当进入中断程序后由单片机自动置0.(内部中断实际上就是利用内部的计数器,只不过提供计数的脉冲来自单片机自身。)
TRO(TR1):定时器/计数器启动位 //启动定时器
TRO(TR1)=1; 启动定时器/计数器0
TR0(TR1)=0; 关闭定时器/计数器0
看了上面一大堆的什么中断啊,寄存器啊,EA啊,TR0啊,等等,相信菜鸟都头昏眼花了,显然,当初本人也是晕了好久好久,学单片机当初还差点断送在这里了啊,不过现在回过头了看看,其实也没有什么,也不是很难呀,一句话,用到什么去查什么,程序哪里不懂就看上面哪里,记住哪里,慢慢的日子久了,都会懂的,万里长征需多日走完的啊!
6.3)定时器程序的编写及介绍
下面以定时器0工作方式0为例介绍,别的工作方式1,2,3大同小异!懂0也会容易懂1,2,3呀!
介绍一个发光二极管以1S亮灭闪烁程序:
#include
#define Uchar8 unsigned char
#define Uint16 unsigned int
sbit Led1 = P2^0;
Uint16 Counter;
void main(void)
{
1) TMOD = 0x00; // 设置定时器0工作方式1
2) TH0 = (8192-5000)/32; // 装初值,晶振为12MHZ,定时5ms数为5000
3) TL0 = (8192-5000)%32;
4) EA = 1; // 开总中断
5) ET0 = 1; // 开定时器0中断
6) TR0 = 1; // 启动定时器0
7) while(1); // 程序在此停留等待中断的发生
}
void Time0_ISR(void) interrupt 1
{
8) TH0 = (65536-50000)/256; //重装初值
9) TL0 = (65536-50000)%256;
10) Counter++;
11) if(Counter==200) // 累计到200次,说明1秒时间到(200*5ms=1s)
{
12) Counter = 0; // 将变量Counter清0,用于下次计数
13) Led1 = ~Led1; // Led取反,LED灯闪烁
}
}
对于定时器以及中断的学习,上面已经说过,知识点是很乱,多,杂的,但不需要去背,用到了就去查,查多了,用多了就自然熟了!
详细介绍程序:
1)TMOD=0x00; 设置定时器0工作方式1,可查上面表6-4和表6-5的介绍
2)TH0 = (8192-5000)/32; // 装初值,晶振为12MHZ,定时5ms数为5000
3)TL0 = (8192-5000)%32;
a,定时器必须要有一个初值,然后开启定时器的时候,才能从那个初值开始加1计数,例如一杯水要装8192滴才能满,然后溢出杯子,假设1滴水代表1微秒,那么如果你想让杯子5000微秒满,那么就应该先往杯子里装入8192-5000=3192滴水呀!想想是否是这个道理呢?同理,如果我想让定时器满5000微秒(5毫秒)溢出,那就需装一个初值3192呀!
b,8192-5000=3192为什么对32求模和求余?
因为这里用到的工作方式0是13位定时器,为高8位TH0和低5位TL0寄存器,需要将3192分解为一个高8位的数,和一个低5位的数,装入TH0和TL0寄存器中
c, 8),9)中为什么还要重装初值呢?
上面已经介绍过,工作方式0是不能自动重装初值的,必须人为写程序去装初值,也就是说,水杯里水满后溢出,把水全部倒掉,再装入一定的水,让水把杯子重新滴满!
d,2),3)还可以写为 TH0=(8192-5000)》5;
TL0=(8192-5000)& 0x1F;
4),5),6)查上面表6-2可知,同时必须知道,定时器的使用,必须首先开启总中断,然后开定时器中断,接着开定时器。例如你回你家里的房间上网,必须首先开你家大门,然后**间的门,接着才能上网!
7)while(1); 程序执行到这里后,会一直停留等待中断的发生,中断来了就往下面执行,否则一直等待
10),11),12)Counter自加,当counter满200次时,证明1秒到了(200*5ms=1s),然后先把Counter清0 ,为什么呢?因为清0后,用于下次计数呀!然后才是LED灯闪烁。
特别注意: 中断服务程序的写法
void 函数名(void)interrupt 中断号 (using 工作组)
{
中断服务程序内容
}
关于中断函数说明如下几点,望铭记:
1) 中断函数无返回值,所以前面为void
2) 中断函数命名随便,但一定不能是C语言的关键字,如if、case等,建议大家用:Timer0_ISR、EX0_ISR等
3) 中断函数不带任何参数,所以小括号内写了void
4) 中断函数的关键词interrupt,一定要写且必须正确无误
5) 定时器的中断号见上面的表6—1,必须一一对应,确保准确无误,错了伤不起啊
6) 后面的using 工作组(),通常不写,具体依个人习惯
下面来个小总结:
在使用定时器及中断时,必须的过程:
1)对TMOD赋值,设置使用定时器几,工作方式为几
2)计算初值,将初值写入TH0,TL0或者TH1,TL1中
3)设置中断方式时,设置IE中断允许寄存器,用于开放总中断,定时器中断
4)开启TCON控制寄存器的TR1或者TR0,开始定时或者计数
即使单片机有无数次中断,但永远别对单片机的学习中断和放弃!
本系列《MCU之心路分享》博文,本人将尽自己所能,以菜鸟的身份,写下初学者最详细,最具体的入门博客,同时分享DIY的乐趣,如有误之处,欢迎拍砖,本人也很乐意接受各位大神的批评和指点,在此谢谢啦!
2013年11月21日 夜
zhujun74_602010376 2015-8-4 06:26
zhujun74_602010376 2015-8-4 06:25
用户342917 2015-4-18 14:45
DiracFatCat 2015-3-24 15:00
用户1324913 2015-3-24 13:28
DiracFatCat 2015-3-19 09:01
hot.summer_2010_438634915 2015-3-18 14:20
用户403664 2014-4-4 15:09
用户1745671 2014-4-4 10:48