概念基础
单片机延时基础
之前的延时我们都使用的是利用C语言的多次的空循环进行延迟操作,这样的缺点是:CPU时间被占用无法进行其他任务,导致系统效率降低。而且延时时间越长,该缺点越明显,所以,这种延时操作只适用于短暂延时,或简单的项目。
而定时器/计数器的使用,实现了单片机对时间的有效控制。单片机中有多个定时/计数器,都可以实现定时和计数的功能。
定时/计数器结构
16位寄存器T0、T1分别由TH0、TL0和TH1、TL1四个8位计数器组成 ,其中两个定时器的区别为:
- T0(定时器0)可分成2个独立的8位定时器,而定时器1则不能;
- T1(定时器1)可作为串口的波特率发生器,而定时器0则不能。
还有两个特殊功能寄存器用于控制定时/计数器:
- TMOD,工作方式控制寄存器,确定工作方式和功能;
- TCON,工作状态控制寄存器,控制定时/计数器的启动、停止及溢出标志。
定时/计数器控制
工作方式寄存器TMOD
低四位(0,1,2,3)用于T0,高四位用于T1:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
功能 | GATE | $C/\overline{T}$ | M1 | M0 | GATE | $C/\overline{T}$ | M1 | M0 |
GATE:门控位,用于控制定时器的启动是否受外部中断源的影响。当GATE = 0时,只要使用软件将TCON中的TR0或TR1置为1,就可以启动定时/计数器工作;当GATE = 0时,要用软件将TCON中的TR0或TR1为1,同时外部中断的引脚INT0/1也为高电平,才可以启动定时/计数器。即此时多了一个启动条件:INT0/1引脚需为高电平。
$C/\overline{T}$:定时/计数器模式选择位,当$C/\overline{T}=0$ 为定时模式;$C/\overline{T}= 1$为计数模式。
M1/M0:工作方式设置位,定时/计数器有四种工作方式:
M1M0 工作方式 说明 00 方式0 13位定时/计数器 01 方式1 16位定时/计数器 10 方式2 8位自动重装定时/计数器 11 方式3 T0分成两个独立的定位/计数器,T1此方式停止计数
定时/计数器工作方式
方式0:为13位定时计数器:由TL0的低5位(高3位未用)和TH0的8位组成。TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。
计数个数与计数初值的关系:$X = 2^8-N$
方式1:为16位定时计数器:由TL0作为低8位,TH0 作为高8位,组成了16位加1计数器 。
计数个数与计数初值的关系为:$X=2^{16}-N$
方式2:8位自动重装初值定时/计数器
计数个数与计数初值的关系为:$X=2^8-N$
方式3:只适用于定时/计数器T0,定时器T1处于方式3时相当于TR1=0,停止计数。
工作方式3将T0分成为两个独立的8位计数器TL0和TH0 。
工作状态寄存器TCON
位序 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位名称 | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
TF1:T1溢出标志位
当T1计数满溢出时,硬件将TF1置1,并申请中断。接入服务程序之后,其将自动清零;T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件的效果一样。
TR1:T1运行控制位
TR1 = 1:启动定时器;TR1 = 0:关闭计时器,由软件控制。
TF0:T0溢出标志位
功能同TF1,工作对象是T0。
TR0:T0运行控制位
功能同TF1,工作对象是T0。
IE1:外部中断1请求标志位
IT1:外部中断1触发方式选择位
IE0:外部中断0请求标志位
IT1:外部中断0触发方式选择位
定时/计数器原理
定时/计数器的实质是加1计数器,由高8位和低8位2个寄存器组成。加1计数器有两个计数脉冲来源:一是由系统的时钟振荡输出脉冲经12分频后送来;二是由T0或T1引脚输入的外部脉冲源。
每来一个脉冲,计数器加1,当加到计数器全为1的时候,再输入一个脉冲使计数器归零,且计数器的溢出时TCON中的TF0或TF1置1,向CPU发出中断请求(定时/计数器中断允许时)。
如果定时/计数器处于定时模式,则表示定时时间结束;计数模式即计数值已满。所以,溢出时计数器的值减去计数器的初值即为加1计数器的计数值。
当设置为定时器模式时,加1计数器是对内部机器周期计数(1个机器周期相当于12个震荡周期,即**计数频率为晶振频率的$1/12$**)。计数个数乘以机器周期$T_{cy}$就是定时时间t。
设置为计数器模式时,外部事件计数脉冲由T0或T1引脚输入到计数器。在每个机器周期的S5P2期间采样T0,T1引脚电平。当某周期采样到一高电平输入,而下一周期又采样到一低电平时,则计数器加1,更新的计数值,在下一个周期的S3P1期间装入计数器。由于检测一个从1到0的下降沿需要两个机器周期,因此要求被采样的电平至少要维持一个机器周期。当晶振频率为12MHz时,最高计数频率不超过1/2MHz,即计数脉冲的周期要大于$2\mu{s}$。
CPU 时序的有关知识
- S5P2: 代表第5状态中的第二节拍
机器周期:一般也叫CPU周期。 表示从内存读取一条指令字的最短时间,它是指令周期的最小单位。它等于单指令的周期长度 。 在51单片机中 一个机器周期=6状态周期=12时钟周期。
指令周期:一条指令包括1个或多个机器周期。所有的C语言代码最后都会编译成汇编代码来执行,而执行一条汇编指令需要的机器周期就叫做指令周期
状态周期:在8051单片机中把一个时钟周期定义为一个节拍(用P表示),二个节拍定义为一个状态周期(用S表示)。一个状态周期等于两个时钟周期。
时钟周期:又叫振荡周期, 是指为单片机提供时钟信号的振荡源的周期( 晶振频率的倒数 )。
定时/计数器操作
步骤:
- 选择工作方式(设置M1,M0)
- 选择控制方式(设置GATE)
- 选择模式:定时器还是计数器(设置C/T)
- 给定时/计数器赋初值(设置$TH_X$和$TL_x$)
- 开启定时器中断(设置ET0和ET1)
- 开启总中断(设置EA)
- 打开计数器(设置TR1和TR0)
配置计数器:
1
2
3
4
5
6
7
8void TimerConfiguration(){
TMOD = 0X01; //定时器T0工作方式1
TH0 = 0X3c; //设置初始值
TL0 = 0X0B0;
EA = 1; //打开总中断
ET0 = 1; //打开定时器T0中断
TR0 = 1; //启动定时器T0
}定时器T0初值计算(晶振为12MHz)
51单片机内部时钟频率是外部时钟的12分频, 也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说你用的是12MHz的晶振,那么单片机内部的时钟频率就是$12MHz\over12$ = 1MHz,当你使用12MHz的外部晶振的时候,机器周期=$1 \over1MHz$=$1\mu{s}$。
当我们需要定时50ms时,则计数值为${50ms}\over{1{\mu}s}$=50000,所以初值 = 最大值计数值 - 50000 = 15536 (3cb0),即$TH_x$ =0x3c,$TL_x$= 0xb0。
程序代码
LED按定时器T0设定的时间周期闪烁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sbit led = P1 ^ 0;
void t0_init(){
TMOD = 0X01; //设置定时器T0工作方式1
TH0 = 0X3c; //设置初始值
TH0 = 0Xb0;
EA = 1; //打开总中断
ET0 = 1; //打开定时器T0中断
TR0 = 1; //启动定时器T0
}
void main(){
led = 1;
t0_init();
while(1);
}
void t0() interrupt 1{
TH0 = 0X3c; //设置初始值
TL0 = 0Xb0;
led = ~led;
}LED按500ms/次的精确频率闪动
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
sbit led = P1 ^ 0;
int i = 0;
void t1_init(){
TMOD = 0X10; //设置定时器T1工作方式1
TH0 = 0X3c; //设置初始值
TH0 = 0Xb0;
EA = 1; //打开总中断
ET1 = 1; //打开定时器T1中断
TR1 = 1; //启动定时器T1
}
void main(){
led = 1;
t1_init();
while(1){
if(i == 10){
led = ~led;
i = 0;
}
}
}
void t1() interrupt 3{
TH0 = 0X3c; //设置初始值
TL0 = 0Xb0;
i++;
}