目录
定时/计数器的主要特性
- MCS-51系列中的51子系列有两个16位的可编程定时/计数器:定时/计数器T0和定时/计数器T1,52子系列有3个,还有一个定时/计数器T2.
- 每个定时/计数器既可以对系统时钟计数实现定时,也可以对外部信号计数实现计数功能,通过编程设定来实现。
- 每个定时/计数器都有多种工作方式,其中T0有4种工作方式,T1有三种工作方式,T2有三种工作方式。通过编程设定工作于某种工作方式
- 每一个定时/计数器定时计数时间到时产生溢出,重置相应的溢出标置位,溢出可通过查询或中断方式处理
- 定时器可以替代长时间的非定时器延时函数,提高CPU运行速度和处理效率,可以让操作系统来执行多任务。
注意:
- 单片机的单位:us(微秒)
- 定时计数器在任何一个时刻使用,只能使用其中的一种功能(要么定时、要么计数)
- 51单片机的定时器属于单片机的内部资源,其电路连接和运转均在单片机内部完成
51单片机定时器的内部框图
注意:TH0/1和TL0/1的作用是存放各个定时器的初值,TH为高位,TL为低位,TH和TL共同作用一起计初值。
定时/计数器T0、T1的结构
组成:加法计数器、方式寄存器TMOD、控制寄存器TCON和内部总线组成
- 加法计数器:16位加法计数器用于对系统时间计数实现定时或对外部计数信号计数实现计数
- 方式寄存器TMOD:用于设定T0和T1的工作方式
- 控制寄存器TCON:用于对定时/计数器的启动、停止进行控制
- 内部总线:各部分之间的连接和信息传送
加法计数器
他是加一计数器,当我们启动定时器后,在每个机器周期的到来,那么初值寄存器自动加一,直到计满溢出形成计时器中断。
方式寄存器TMOD
标志位作用
- GATE:门控位,用于控制定时/计数器的启动是否受外部中断请求信号的影响
- C/T:选择使用计数器还是定时器;当C/T=1时工作于计数方式,当C/T=0时工作于定时方式
- M1和M0的共同作用决定了定时器的工作方式,工作方式如下
注意:TMOD是不可位寻址的,若要改变里面的单个配置,需要将TMOD的8位一起定义
控制寄存器TCON
- TF0(1):定时/计数器T0(1)的溢出标志位,当定时/计数器T1计满时,由硬件使他置位为1,若中断允许则触发T1中断。进入中断处理后由内部硬件电路自动清除(不是中断需手动清零)
- TR0(1):定时/计数器T0(1)的启动位,可由软件置位或清零,当TR0(1)=1时启动;TR0(1)=0时停止
- IT0(1):外部中断0/1触发方式控制位。IT0或IT1被设置为0,电平触发方式;IT0或IT1被设置为1,边沿触发方式
- IE0(1):外部中断0或1的中断请求标志位
定时计数器的工作方式
方式0
前言:十三位定时/计数器方式。
理解:由图可知左上这部分是用来提供时间的,只要时间到了C/T杠那么就会给定时器一个计数脉冲,而时钟脉冲最初的来源有2个,一个是系统时钟,一个就是外部接口Tx;当他由外部引脚来提供时钟的时候,我们的定时器就是一个计数器(因为外部引脚每来一个脉冲【下降沿有效】就会加一,就相当于一个计脉冲的计数器)
门控位作用:
- 若gate为0,通过非门为1,那么1与任何数都得任何数,那么定时器的开启和关闭(S2处开关)都由TRx控制
- 若gate为1,通过非门为0,那么0或任何数都得任何数,再通过与门就可知,定时器的开启或关闭由INTx和TRx双重控制
注意:
- 计数值为N与初值X的关系:X=8192-N/(12/fosc)
- 上次计数完,计数值为0,要重复计数需要重置初值 。
方式1
前言:16位定时/计数器方式
注意:
- 计数值为N与初值X的关系:X=65536-N/(12/fosc)
- 上次计数完,计数器值为0,要重复计数需要重置初值。
方式2
前言:8位自动重置定时/计数器
注意:
- 计数值为N和初值X关系:X=256-N/(12/fosc)
- 上次计数完,计数器自动重置初值,不需要用户重置(TL设定了初值后,当产生中断请求后,初值又被自动赋给TL了)
- 该方式的重置初值的方式就是啊把TH的初值再次赋给TL,所以初始化时TH也需要赋和TL一样的值以方便TH向TL的传递
方式3
前言:两个8位定时/计数器(只有T0有)
注意:这种方式使用很少,就不再多写了。
定时器原理
总结:当我们启动定时器时,在每个机器周期的到来,初值寄存器自动加1,直到计满溢出。
机器周期相当于12个震荡周期,我们拿foscMHz的震荡频率算,震荡周期=1/fosc*10^6s也就是1/fosc微秒us,由此算出一个机器周期12/fosc微秒;若初值寄存器为16位,那么需要过65536个机器周期才会溢出,也就是65536us,若我们想要50ms就计算一次,我们需要将初值寄存器赋值,使得处置寄存器有50000us的剩余空间,因此初值寄存器我们赋值了65536-N/(12/fosc)=初值X(将这个数复制到TH的高8位和TL的低8位),N为我们要计数的微秒数即50000us;这里,初值寄存器再过50ms就会溢出,然而对于工作方式1来说,你只设了一次初值,那么下次过50ms溢出了,但是定时器还在运行,下次的初值就为0了,也就意味着再过65536us才会第二次溢出,为了解决这种情况,我们因此必须不停的重置初值才可以形成定时器每隔50ms自动溢出的情况。
初值X=65536-N/(12/fosc)理解
说了单片机会在每个机器周期到来,初值寄存器自动加一,那么我们要设置50ms就需要设置一定的初值,使计算机通过特定的时间计满溢出;比如我们想要设50ms,那么50000/机器周期含义为你要经历过多少个机器周期才能到50ms,此时我们只要通过65536-50000/(12/fosc)就是初值(从65536中掐去占用50ms的时钟周期个数,那么下次计数溢出就是50ms后)
定时计数器编程
- 根据要求选择方式,确定方式控制字,写入方式控制寄存器TMOD
- 根据要求计算定时/计数器的计算值,再由计算值求得初值,写入初值寄存器
- 根据需要开放定时/计数器中断(后面需要编写中断服务程序)
- 设置定时/计数器控制寄存器TCON值,启动定时/计数器开始工作
- 等待定时/计数时间到,到则执行中断服务程序;如用查询处理则编写查询程序判断溢出标志,溢出标志等于1则进行相应的处理
定时器案例
中断案例
需求:每秒使数码管中的数字自动+1
电路图
keil文件
#include "reg51.h"
unsigned char s[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char count=0,num=0;
#初始化定时器
void inittimer(){
#配置TMOD使用定时器0,工作方式为方式1
TMOD=0x01;//0000 0001
#设置初值,50ms
TH0=(65536-50000)/256; //向右移动8位
TL0=(65536-50000)%256; //向左移动8位
ET0=1; //开启定时器0的中断
EA=1; //开启总中断
TR0=1; //开启定时器0
}
void display(){
P2=s[num];
if(num==10){
num=0;
}
}
void main()
{
inittimer();
while(1){
display();
};
}
#使用定时器中断
void time_isr() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
#定时1s
if(count==20){
num++;
count=0;
}
}
结果:每秒数码管中的数字自动加一。
定时器使用案例
需求:通过定时器来设置流水灯。
电路图
keil文件
#include "reg51.h"
void timer_init(){
TMOD=0x01; //使用定时器0,工作模式为1
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TR0=1; //开启定时器0
}
void main()
{
timer_init();
while(1)
{
unsigned int i=0,k=0,led=0x01;
for(i=0;i<8;i++){
P2=~led; //使灯亮
//定时器定的值循环20次后灯位改变(也就是1s)
while(k<20){
while(TF0==0); //等待定时时间到
//重置初值
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TF0=0; //清溢出(因为不是中断,硬件不会清溢出)
k++;
}
k=0;
//流水灯实现
led=led<<1;
}
}
}
计数器案例
中断案例
前言:
- 对于计数器来说,其只能连接P3.4/P3.5;因为只有这两个口是可以复用为计数器的。
- 计数器主要对外部事件进行计数,通过引脚把外部事件的信号传进来,在计数器功能中,外部事件以计数脉冲来体现,而且要求必须是下降沿,来一个下降沿计一个数(计数器功能对应的引脚只接收下降沿有效)。
- 需求:每按3次,数码管值加1
电路图
keil文件
#include "reg51.h"
unsigned char s[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char num=0;
void initcounter(){
#配置计数器0,工作方式为2
TMOD=0x06; //0000 0110
TH0=256-3; //作用为重置TL
TL0=256-3; //赋初值
ET0=1; //开启计数器0的中断
EA=1; //开启总中断
TR0=1; //打开计数器0
}
void display()
{
P2=s[num];
if(num==10){
num=0;
}
}
void main()
{
initcounter();
while(1){
display();
}
}
void counter_isr() interrupt 1
{
num++;
}
结果:每按三次按钮,数码管中的数字自动加一(其实前两次按了有用只不过显示的是原数字)。
计数器使用案例
需求:每按六次按钮,数码管中的数字自动加一;电路图和上面的电路图一样。
keil文件
#include "reg51.h"
unsigned char s[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned int n=0;
void counterinit(){
#配置计数器0,工作方式2
TMOD=0x06;
TH0=256-6; //重置TL
TL0=256-6; //置初值
TR0=1; //打开计数器
}
void display()
{
if(n==10){
n=0;
}
P2=s[n];
}
void main()
{
counterinit();
while(1)
{
display();
while(TF0==0); //等待计数器计数溢出
n++;
TF0=0; //清溢出
}
}