定时计数器
51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)
这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。
当工作在定时模式时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。
可以算出工作在定时模式时最高单次定时时间:65535*1.085us
当工作在计数器模式时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1
使用定时-计数器
(一)启动定时计数器
T0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)
总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)
(二)设定定时计数器的工作模式
这一步由寄存器TMOD(不可位寻址)控制
第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数
第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式
M1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:
M1 |
M0 |
工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) |
0 |
0 |
13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:213−1=8191 |
0 |
1 |
16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 |
1 |
0 |
8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 |
1 |
1 |
如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到(28−1=255),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) |
(三)查询定时计数器是否溢出
上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
#include <reg52.h> #include <intrins.h>
#define uint unsigned int #define uchar unsigned char
sbit DU = P2^6; sbit WE = P2^7;
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
void delay(uint z) { uint x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); }
void display(uchar i) { uchar bai, shi, ge; bai = i / 100; shi = i % 100 / 10; ge = i % 10; P0 = 0XFF; WE = 1; P0 = 0XFE; WE = 0; DU = 1; P0 = tabel[bai]; DU = 0; delay(5);
P0 = 0XFF; WE = 1; P0 = 0XFD; WE = 0; DU = 1; P0 = tabel[shi]; DU = 0; delay(5);
P0 = 0XFF; WE = 1; P0 = 0XFB; WE = 0; DU = 1; P0 = tabel[ge]; DU = 0; delay(5); }
void T0_init() { TR0 = 1; TMOD = 0X01; TH0 = 0x4b; TL0 = 0xfd; }
void main() { uchar mSec, Sec; T0_init(); while(1) { if(TF0 == 1) { TF0 = 0; TH0 = 0x4b; TL0 = 0xfd; mSec++; if(mSec == 20) { mSec = 0; Sec++; } } display(Sec); if(Sec > 10) Sec = 0; } }
|
方式1初值计算方法,假如定时t(单位:us),使用T0:
则:
1 2 3
| TH0 = (65535 - t / 1.085) / 256 TL0 = (65535 - t / 1.085) % 256 四舍五入取整后转换为16进制
|
中断系统
主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断
引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。
51子系列存在5个中断源:
外部中断源2个
-
INT0——由P3.2端口线引入,低电平或下降沿引起。
-
INT1——由P3.3端口线引入,低电平或下降沿引起。
这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制
内部中断源3个
-
T0——定时器/计数器0中断,由T0回零溢出引起。
-
T1——定时器/计数器1中断,由T1回零溢出引起。
-
TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。
这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。
使用中断
如何使用中断,有3大步骤:
(一)允许产生中断
要允许产生中断首先要配置中断允许寄存器IE和XICON
(可位寻址表示每一位都已经用变量定义好了,比如第7位是EA)
总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断
(二)设置什么时候响应中断
什么时候响应中断由控制寄存器TCON控制
TFx:当计数值溢出时,就会向cpu发出中断请求,然后自动进入对应的中断处理函数,然后TFx由硬件自动清0
其它IEx, ITx也差不多是这样
(三)产生中断后你要干什么——中断处理函数
中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:
格式如下:
1 2 3 4 5
| void 函数名() interrupt 中断入口号 { }
|
中断优先级:
如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念
中断优先级级别越高,就越先被执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
#include <reg52.h> #include <intrins.h>
#define uint unsigned int #define uchar unsigned char
sbit DU = P2^6; sbit WE = P2^7; sbit key_s2 = P3^0; sbit key_s3 = P3^1; uchar num; uchar mSec, Sec;
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};
void delay(uint z) { uint x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); }
void display(uchar i) { static uchar wei; P0 = 0XFF; WE = 1; P0 = SMGwei[wei]; WE = 0; switch(wei) { case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break; case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break; case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break; } wei++; if(wei == 3) wei = 0; }
void T0_init() { EA = 1; ET0 = 1; TR0 = 1; TMOD = 0X01; TH0 = 0xED; TL0 = 0xFF; }
void main() { T0_init(); while(1) { if(key_s2 == 0) { delay(20); if(key_s2 == 0) { if(num != 120) num++; while(!key_s2); } } if(key_s3 == 0) { delay(20); if(key_s3 == 0) { if(num > 0) num--; while(!key_s3); } } } }
void timer0() interrupt 1 { TH0 = 0xED; TL0 = 0xFF; display(num); }
|