一、实验要求
【实验目的】
1、掌握定时器控制寄存器的设置方法;掌握定时器初始化流程;
2、掌握定时器中断服务程序的编写方法。
【实验内容】
数码管上显示定时器中断计数(定时时间1s)。
【硬件要求】
连接方法:P2.0-P2.7连接一只共阳型7段LED数码管,P1.0连接至单片机P3.5(T1)引脚。
【软件实现】
编程实现实现数码管上显示定时器中断计数(定时时间1s)。
二、知识要点
(一)单片机定时器/计数器
单片机有两个16位的可编程的定时器/计数器T0和T1,可用作定时或延时,它们具有两种工作模式(计数模式和定时模式)和四种工作方式(工作方式0、工作方式1、工作方式2、工作方式3),定时器/计数器的核心部件是二进制加1计数器(TH0、TL0或TH1、TL1)。
定时器/计数器的两种工作模式都是对脉冲信号进行计数的,只是计数信号的来源不同,定时模式来自内部,而计数模式来自外部。与T0和T1相关的寄存器有:两个8位的控制寄存器TMOD和TCON、两个16位的计数器TH0、TL0或TH1、TL1。
(二)相关的特殊功能寄存器
1、工作方式寄存器TMOD
8位特殊功能寄存器TMOD用于选择定时器/计数器T0、T1的工作方式,它分为两组,高4位决定T1的工作方式,低4位决定T0的工作方式,以下是TMOD的格式:
(1)GATE为门控位,当GATE=0时,使TCON的TR0或TR1为1就可以启动定时器/计数器工作,而当GATE=1时,除了需要使TCON以外,同时还需要使外部中断引脚为高电平时才能启动定时器/计数器工作,即T0、T1的是否计数受到外部引脚输入电平的控制,其中INT0引脚控制T0,INT1引脚控制T1,可用于测量在INT0和INT1引脚出现的正脉冲宽度。
(2)C/T为定时/计数模式选择位,通过该位选择工作模式,当C/T=0时,工作模式为定时模式,即此时脉冲周期等于机器周期,计数器对机器周期计数,通过计数值就可以得到计数的时间;而当C/T=1时,工作模式为计数模式,计数器输入信号外部引脚,即计数器对外部输入引脚T0(P3.4)或T1(P3.5)的外部脉冲(负跳变)计数,允许的最高计数频率位晶振频率的1/24。
(3)M1、M0为工作方式选择位,通过该位选择工作方式,如下表,当M1、M0为不同值时对应相应的四种工作方式:
M1 | M0 | 工作方式 | 功能 |
0 | 0 | 工作方式0 | 13位计数器(TH的8位和TL的低5位) |
0 | 1 | 工作方式1 | 16位计数器 |
1 | 0 | 工作方式2 | 自动重装8位计数器 |
1 | 1 | 工作方式3 | T0分成两个8位;T1停止计数 |
不能对TMOD采用位寻址方法,只能采用字节寻址(例TMOD=0xXX)设置定时器的工作方式。
2、定时器/计数器控制寄存器TCON
定时器/计数器控制寄存器TCON控制定时器/计数器的启动与停止以及标志定时器的溢出和中断情况,对TCON既可以采用字节寻址也可以采用位寻址,当51单片机系统复位时,TCON的所有位都会清零。
TCON的格式如下,高4位用于控制定时器/计数器的启动和中断请求,低4位控制外部中断:
(1)TR1和TR0为运行控制位,等于0时停止定时器工作,等于1时启动定时器工作(例启动定时器T1工作:TR1=1);
(2)TF1和TF0为溢出中断标志位,定时器溢出由硬件自动置为1,CPU响应中断后,硬件自动清零(例TF1=0无溢出,TF1=1时溢出);
(3)IT1和IT0为外部中断INT0、INT1的触发方式控制位,由软件来置1或清0,以控制外部中断的触发方式。(例IT1=1时,外部中断INT1为下降沿触发,IT1=0时,外部中断INT1为低电平触发);
(4)IE1和IE0为外部中断的请求标志位,当引脚上出现中断请求信号(低电平或脉冲下降沿)时,硬件自动将IEX置1,从而产生中断请求标志,而当CPU响应中断时,由硬件消除(例外部中断INT0P3.2引脚有中断请求信号时,IE0置1,即IE0=1)。
(三)定时器/计数器的工作方式
定时器/计数器T0和T1的工作原理相同,唯一区别在于在工作方式3时,定时器/计数器T1停止计数。前面讲过通过TMOD的M1、M0工作方式选择位可选择工作方式。
1、工作方式0
当M1、M0为0时是工作方式0,工作方式0的计数位数为13位,由THx(x=0,1)的8位和TLx(x=0,1)的低5位(高3位未用)组成13位加1计数器,TLx(x=0,1)低5位溢出时向THx(x=0,1)进位,THx(x=0,1)计数溢出则TCON中的溢出标志位TFx(x=0,1)置1。
工作方式0的最大计数值(计数外部脉冲个数)为213=8192,即方式0的计数范围为1⁓213。
2、工作方式1
当M1、M0分别为0、1时是工作方式1,工作方式1的计数位数为16位,由THx高8位和TLx低8位构成16位加1计数器,其控制方式与操作方式与方式0相同。
工作方式1的最大计数值(计数外部脉冲个数)为216=65536,即方式1的计数范围为1⁓216。
3、工作方式2
当M1、M0分别为1、0时是工作方式2,它是自动重装初值的8位计数方式,当TLx计数溢出时,除了使TCON中的溢出标志位TFx置1,还自动打开TLx与THx之间的三态门,使THx内的内容重新转入TLx中,并继续进行计数操作,通过这个操作可以省去用户重新装入计数初值的程序从而简化定时初值的计算方法,精确地确定定时时间。
工作方式2的最大计数值(计数外部脉冲个数)为28=256,即方式2的计数范围为1⁓28。
4、工作方式3
方式3只适用于定时器/计数器T0,定时器T1工作于方式3相当于TR1=0,停止计数(可用于串行口波特率产生器),当M1、M0分别为1、1时是工作方式3,工作方式3的计数位数为8位,将T0分为两个独立的8位计数器TL0和TH0,TL0使用T0的状态控制位C/T、GATE、TR0、TF0,其既可以当计数使用也可以当定时使用,即T0的各控制位和引脚信号归TL0使用,而TH0由于只能对机器周期计数,不能作为外部计数模式,所以则只能作为定时器使用。
工作方式3的最大计数值(计数外部脉冲个数)为28=256,即方式3的计数范围为1⁓28。
一般情况下,当T1用作串行口的波特率发生器时,T0才工作在方式3。T0处于工作方式3时,T1可定为方式0、方式1和方式2,用来作为串行口的波特率发生器,或不需要中断的场合。
(四)预置定时器/计数器的初值
1、预置计数器初值
根据不同工作方式下的其最大计数值,可得到最大计数值M分别是:方式0:8192;方式1:65536;方式2:256;方式3:256。
计数脉冲由外部T0或T1引入,对外部脉冲进行计数,则初值X=M-要求的计数值=[要求的计数值]求补。
2、预置定时器初值
定时器模式下,计数器由系统时钟频率fosc经12分频后计数,则初值X=M-要求的定时值/(fosc/12)。
例、设单片机的晶振频率为fosc=12MHz,在定时器模式下:
(1)一个机器周期是多少微秒?
(2)定时器处于不同工作方式时,最大定时时间是分别是多少?
(1)因为fosc=12MHz,所以一个机器周期为1μs
(2)处于方式0时,最大定时时间为8192×1μs=8192μs
处于方式1时,最大定时时间为65536×1μs=65536μs
处于方式2时,最大定时时间为
256×1μs=256μs
处于方式3时,最大定时时间为
256×1μs=256μs。
例、设单片机的晶振频率f=12MHz,应用T0定时器的方式0,定时时间为2ms,求定时器T0的初值以及TH0、TL0的值。
X=213-2×10-3×12×106/12=8192-2000=6192=1830H=1100000110000B,
将T0的初值低5位送至TL0,则TL0=10000B=10H;
高8位送至TH0,则TH0=11000001B=0C1H。
(五)四类定时/计数方法
定时/计数方法分为四类:
1、软件延时;
2、硬件定时;
3、硬件定时+软件计数;
4、硬件定时+硬件计数;
(六)定时器/计数器的初始化过程
1、第一步
选择定时器/计数器的工作方式,即通过TMOD中的工作方式选择位M1和M0赋值实现选择。
2、第二步
预置定时/计数初值,根据定时时间/计数次数将所计算的初值写入TH0、TL0或TH1、TL1中。
3、第三步
开放定时器/计数器的中断,设置中断优先级,通过给IE寄存器中相关位赋值实现。
4、第四步
启动定时器/计数器,通过TCON中的TR1=1或TR0=1实现。
三、Proteus仿真软件画原理图
原理图如下:
四、Keil编程软件编写程序
首先和之前的外部中断实验一样,定义数组变量,将数组Tab定义为unsigned char code Tab[],定义一个无符号字符型(自动变量)数组Tab[],且该变量位于ROM中,根据共阳极数码管段码,即unsigned char code Tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};。
然后延时函数没必要说,这里简单略过,依然是延时1s:
void delay(unsigned int x) { unsigned int i,j; for(i=0;i<x;i++) for(j=0;j<125;j++); } delay(1000);
选择定时器/计数器的工作方式,通过TMOD中的工作方式选择位M1和M0赋值实现选择,这里使用定时器T0,工作方式1,即TMOD=0x01=0000 0001,然后启动定时器0,即TR0=1。
设置一个无限循环,while(1){},首先预置计数初值,因为采用定时器工作方式且晶振为12MHz,即fosc=12MHz,所以一个机器周期为12/fosc=1μs,若要计数1ms,则需计数1000次。
算出初值65536-1000,由于选择的是定时器T0且工作方式1,列出计算TH0和TL0的式子:
TH0=(65536-1000)/256; TL0=(65536-1000)%256;
这里初值/256是取其商高8位写入TH0中,%256取其余数低8位写入TL0中,写入后T0从65536-1000=64536开始加1,加了1000次,就会溢出产生中断。然后设置一个do……while循环等待定时时间完成,即do{}while(!TF0),循环体内为空,括号里的!TF0相当于为0,即直到TF0等于0时定时时间结束。
之前还要定义一个变量n来记录显示数字的个数,即unsigned char n=0;,另外还有一个if……else语句来判断0-9共十个数字是否都显示完,如下:
if(n>=9) n=0; else n++;
以下是整个程序代码:
#include<reg51.h> unsigned char code Tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //共阳极数码管段码,定义一个无符号字符型(自动变量)数组Tab[],且该变量位于ROM中 void delay(unsigned int x)//延时函数 { unsigned int i,j; for(i=0;i<x;i++) for(j=0;j<125;j++); } void main() { unsigned char n=0; //记录显示数字的个数 P2=Tab[0]; TMOD=0x01; //TMOD=0x01=0000 0001,即对应定时器T0,工作方式1 TR0=1; //启动定时器T0 while(1) //无限循环 { TH0=(65536-1000)/256; //预置计数初值,65536-1000是初始值,/256取其高8位写入TH0中,%256取其低8位写入TL0中 //写入后,T0从64536开始加1,加了1000次,就会溢出产生中断 TL0=(65536-1000)%256; do{}while(!TF0); //等待定时时间完成 if(n>=9) //判断0-9十个数字是否都显示完 n=0; else n++; P2=Tab[n]; //依次取数组Tab内的元素,即取数组中数字对应的段码 delay(1000); //调用延时函数,参数为1000,即1s } }
五、实验结果
(一)仿真结果
仿真结果如下,数码管依次显示0-9无限循环:
……,依次到数字9:
(二)连接电路
P1.0和P3.5连接,P2.0-P2.7排线与数码管连接,如下图:
(三)烧录并观察实验现象
烧录可执行文件,观察现象,数码管依次显示0-9无限循环:
……,依次到数字9: