STC89C52定时器/计数器的使用 一、寄存器 1. 数据寄存器
TLx[1]8AH8BH不可位寻址复位清0 8位寄存器保存计数值的低8位。
THx8CH8DH不可位寻址复位清0
8位寄存器保存计数值的高8位。
工作原理计数时从TL开始加1计数计满后想TH进位直至TH溢出置TF标
志然后申请中断CPU进行中断处理。
2. 模式选择寄存器TMOD89H不可位寻址复位清0
高4位用于控制定时器1低4位用于控制定时器0
GATE—门控制位
=0TC[2]的启停仅由寄存器TCON中的TRx控制
=1TC的启停由外部中断引脚的电平状态和TCON中的TRx共同控制。
C/T—模式选择位
=0定时器对内部机器周期[3]计数
=1计数器对外部输入计数由Tx[4]引脚输入
注意计数模式下从采样到计数值更新需要2个机器周期共24个时钟周期因此
时钟频率为f MHz时最高计数频率为1/2f MHz。
M1M0—工作方式选择位
=00方式013位TH全用TL低5位
=01方式116位THTL全用
=10方式28 位自动重装载定时器当溢出时将 TH 存放的值自动重装入 TL
=11方式3仅适用于T0。定时器 0 此时作为双 8 位TC。TL0 作为一个 8 位TC
通过标准定时器 0 的控制位控制。TH0 仅作为一个 8 位定时器由定时器 1 的控制位控
制。 T1停止计数。
注意在方式2中计数溢出后CPU会自动将THx中的值装入TLx。因此在定时器
启动前在THx和TLx中装入的初值必须是相同的以保证计数的准确性。
3. 控制寄存器TCON88H可位寻址复位清0
位符号
TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
位地址
8FH 8EH 8DH 8CH 8BH 8AH 89H 88H
高位在前。后4位用于外部中断。
TFx—Tx溢出标志位
Tx计数溢出时硬件置1并申请中断。进入中断服务子程序后硬件自动清零。如果
不使用定时器中断而采用软件查询的方法则需要软件清零。
TRx—Tx运行控制位
置0时关闭Tx。 当GATE=0时TRx=1启动Tx
当GATE=1时TRx=1且INTx=1时启动Tx。
IEx—外部中断x请求标志
接收到外部中断后置1中断响应后硬件清零。
ITx—中断触发方式选择位
ITx=0引脚INTx[5]上低电平触发中断
ITx=1引脚INTx上下降沿触发中断。
注意采用低电平触发方式时低电平必须持续到该中断被 CPU 响应同时在该中断
服务程序执行完之前外部中断源必须被清除INTx要变高电平否则将产生另一次中断。
4. 中断使能寄存器IEA8H可位寻址复位清0
STC89C52中断使能寄存器IE
位符号
EA Reserved ET2 ES ET1 EX1 ET0 EX0
位
地址
AFH -- ADH ACH ABH AAH A9H A8H
高位在前。所有位均是置1开中断置0关中断。
EA—全局中断总中断使能位
ETx—TCx中断使能位
ES—串行口中断使能位
EXx—外部中断x使能位
注意使能某一中断时必须将EA置1。ET2为52系列所有51系列没有TC2。
5. 中断优先级寄存器IPB8H可位寻址服务清0
STC89C52中断优先级寄存器IP
位符号
Reserved Reserved Reserved PS PT1 PX1 PT0 PX0
位地址
-- -- -- BCH BBH BAH B9H B8H
所有位均是置1为高优先级置0为低优先级。
PS—串行口中断优先级控制位
PT1—TC1中断优先级控制位
PX1—外部中断1优先级控制位
PT0—TC0中断优先级控制位
PX0—外部中断0优先级控制位
同优先级的中断请求按默认顺序响应。
【说明】
1. 本文里类似的x均可取值为0或1。
2. TC指Timer/Counter即定时器/计数器。
3. 机器周期是单片机的基本操作周期一个机器周期内单片机完成一项基本操作如取指
等。一个机器周期包含12个时钟周期。时钟周期是时钟频率的倒数。
4. T0对应引脚P3.4T1对应P3.5。
5. INT0对应P3.2引脚INT1对应P3.3引脚。
二、定时器的使用 由于定时器都是由初值计数直至溢出因此最重要的就是设置计数器的初值。
假设需要定时器产生一次中断的事件为t计算初值的步骤如下 1. 计算机器周期
Tm = 12×T
T为时钟周期是时钟频率的倒数。
2. 计算需要计数的个数
需要计数的个数为N=t/Tm
3. 装填数据寄存器
方式013位THx=(213 – N)/25TLx=(213 – N)%25
方式116位THx=(216 – N)/28TLx=(216 – N)%28 实际的初值即为2n – Nn为定时器位数只需将其放入TH和TL中即可。对TL计数范围取模即为TL的初值对其取整则为TH的初值。
编程时的步骤
写定时器程序时需要对定时器及中断寄存器进行初始化过程如下
1. 设置TMOD以确定Tx的工作方式
2. 计算初值并将初值写入TH和TL
3. 允许中断如果使用中断方式需要对寄存器IE中的位进行赋值
4. 置位TRx启动定时或计数。
三、中断服务子程序 STC89C52单片机的中断级别及中断向量表
中断源 默认中断级别 中断号C语言用 入口地址汇编用
INT0—外部中断0 最高
0 0003H
T0—TC0中断 第二
1 000BH
INT1—外部中断1 第三
2 0013H
T1—TC1中断 第四
3 001BH
TI/RI—串行口中断 第五
4 0023H
T2—定时器2中断 最低
5 002BH
C51的中断函数格式如下
void functionName() interrupt InterruptNum [using groupNum ]
{……}
黑体为关键字斜体为可编辑项方括号内的为可选
项。
函数名functionName可以是任何合法的标识符。中断号InterruptNum是编译器
识别不同中断的唯一标识一定不能有误。工作组groupNum指示这个中断函数使用单
片机内存中4组工作寄存器中的哪一组由于编译器会自动分配常省略不写。
可以在中断函数中为定时器重装初值这样就可以实现间隔一定时间的中断。这种方
法会出现累积误差减小误差的有效方法是使用方式2即初值自动重装的定时器方式。
注意中断函数中一定不要写过多的处理语句否则当前中断尚未处理完下一次中断
又会到来这样就会丢失中断。因此中断服务子程序要高效、简洁。能在主程序中完成
的操作不要在中断函数中完成。
注定时器的溢出率指该定时器溢出的频率是两次计数溢出相差时间的的倒数。
四、例程 可将以下代码直接复制到.c文件中。
#include
#define uint unsigned int
#define uchar unsigned char
uchar temp; //需要显示的数值用于分解为百位、十位、个位数字的原始数值
uchar num; //计时辅助变量
uchar bai; //百位数字
uchar shi; //十位数字
uchar ge; //个位数字
sbit dula=P2^6; //段选锁存器的使能位
sbit wela=P2^7; //位选锁存器的使能位
uchar code table[]={ //数码管编码表三个数码管均接到P0口code型占用程序空
间不会占用内存空间
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void display(uchar,uchar,uchar);
void delay_ms(uint);
void init();
void main()
{
init();//初始化子程序
while(1)
{
if(num==20) //num=20时已过去了20*50ms=1s时间12MHz即每
秒执行一次下面的程序
{
num=0; //num清零重新计时
temp++;
if(temp==120)
{
temp=0; //在000-119轮流显示
}
bai=temp/100;
shi=temp%100/10;
ge=temp%10; //将3位整数分解成3个单独的数字
}
display(bai,shi,ge);
} }
/**************延迟1ms12MHz晶振***************/
void delay_ms(uint xms) //unsigned int为16位整数因此参数xms的值最大为65535
{
uint i,j;
for(i=xms;i>0;i--)
for(j=250;j>0;j--);
}
/*************数码管动态显示函数************/
void display(uchar bai,uchar shi,uchar ge)
{
/***********在第一个数码管上显示百位数字**********/
dula=1; //段选锁存器直通
P0=table[bai]; //送段选数据百位数据
dula=0; //段选锁存器锁存
P0=0xff; //"消影"避免打开位选时位选数据受之前段选数据的影响
造成乱码显示
wela=1; //位
选锁存器直通
P0=0xfe; //送位选数据0xfe表明选中第一个数码管
wela=0; //位选锁存器锁存
delay_ms(1);
/**********在第二个数码管上显示十位数字**********/
dula=1;
P0=table[shi]; //送十位数据
dula=0;
P0=0xff;
wela=1;
P0=0xfd; //打开第二个数码管
wela=0;
delay_ms(1);
/**********在第三个数码管上显示个位数字*********/
dula=1;
P0=table[ge]; //送个位数据
dula=0;
P0=0xff;
wela=1;
P0=0xfb; //打开第三个数码管
wela=0;
delay_ms(1);
}
/************初始化函数**************/
void init() {
wela=0;
dula=0;
temp=0; //初始化数码管
/****定时器初始化****/
TMOD=0x01; //模式选择T0、T1均为定时器T1为方式013位实
际本程序未使用T1T0为方式116位
TH0=(65536-50000)/256;
TL0=(65536-50000)%256; //装填初值计50,000个数的时间是
50,000*12*(1/12M)= 50(ms)。12MHz情况下恰好为50ms。
EA=1; //开总中断
ET0=1; //使能定时器T0中断
TR0=1; //启动定时器T0
}
/*********中断服务子程序**********/
void timer0() interrupt 1 //定时器T0中断服务子程序
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256; //重装计数器
num++; //约每50ms将num自增1。
}