51单片机之定时器/计数器(Timer/Counter)
概述
定时器/计数器 实际上是一个东西,只不过因为使用功能不同而有不同的名字,因为定时器的功能更常用,后文就用定时器指代。
作为大多数单片机的都会有的一个外设(外部设备,这是相对于内核而言的),定时器的重要性显而易见。首先理解定时器概念, 你可以想象定时器就是计数的,单片机每接收到一个脉冲信号,计数值就加1。
定时器的信号来源有两个选择,单片机内部的时钟信号或者外部脉冲信号。采用内部时钟信号,因为内部时钟频率固定且已知,计数值就相当于时间(只不过单位可能不是标准的秒,毫秒,微妙而已),这种用法叫做定时器。采用外部脉冲信号的,相当于计算脉冲的个数,只要这个脉冲的频率不是太高,单片机能够检测到,这种用法叫做计数器。
相关寄存器
数据寄存器THx,TLx
每一个定时器/计数器都对应了两个数据寄存器,均为8位,THx指的是高8位,TLx指的是低8位,合在一起,可以说是16位的。
定时器/计数器0 对应 TH0,TL0;定时器/计数器1 对应 TH1,TL1。
当数据寄存器计数值只能向上计数,即只能累加,当加满溢出时,会将相应的溢出标志位置1,并从新开始计数。如果配置了中断,会跳转至中断函数,并将标志位有硬件清零。
但是到底怎样才算启动,从多少开始计数,累加到多少算溢出,和下面的寄存器有关。
定时器/计数器控制寄存器TCON
位序号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位符号 | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
高4位(D4到D7)与定时器/计数器0、1有关,具体为:
位 | 功能 |
---|---|
TR0 | 定时器/计数器0运行控制位,置为1启动,0暂停(这里的启动只能说是软件启动) |
TF0 | 定时器/计数器0溢出标志位 |
TR1 | 定时器/计数器1运行控制位,同TR0 |
TF1 | 定时器/计数器0溢出标志位,同TF0 |
低4位(D0到D3)与外部中断0、1有关,详细介绍请查看博主的中断一文
定时器/计数器工作方式寄存器TMOD
位序号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位符号 | GATE | C/ T ‾ \overline{T} T | M1 | M0 | GATE | C/ T ‾ \overline{T} T | M1 | M0 |
高4位与定时器/计数器1有关,低4位与定时器/计数器0有关。下表中x取0或1。
位 | 功能 |
---|---|
GATE | 门控位。置1,需要INTx(外部中断x对应的引脚)和TRx都为1时启动定时器x(软硬结合);置0,当TRx为1时启动定时器(软件启动) |
C/ T ‾ \overline{T} T | 功能选择位。置1,作定时器用,计数信号来内部时钟;置0,计数器,此时外部信号来源为P3.4(for计数器0),P3.5(for计数器1)。 |
M1 M0 | 模式选择位。 |
M1 M0共有4种组合方式,下面详细讲解定时器的4种模式
M1M0 | 模式 | 用途 | 计数模值/溢出值 | 备注 |
---|---|---|---|---|
00 | 模式0 | 13位定时器/计数器 | 213=8192,0xFF1F | TLx寄存器只用到低5位,即B00011111+1会清零并向THx进位 |
01 | 模式1 | 16位定时器/计数器 | 216=65536,0xFFFF | TLx,THx的每一位都用到 |
10 | 模式2 | 自动重装载8位定时器/计数器 | 28=256,0xFF | 当TLx溢出时,将THX的值装载至TLx中 |
11 | 模式3(仅对T0有效) | T0分为2个8位独立计数器,T1不工作 | 256,256 | 该用法太少见 |
注意:上表所说的计数模值/溢出值。举个例子,对于模式0,寄存器最大为0x1FFF(8191 = 213-1),因为是从0开始计数的。同样的,8位寄存器所以能装载的数最大为0xFF(255 = 28-1)。
定时器内部结构图解:
应用
假设你使用的是12Mhz的晶体,经过12分频之后的机器频率就是1Mhz,即机器周期就是1us,定时器数据寄存器加1需要1us。我们可以预先给THx,TLx赋适当的值,达到固定时间计时的目的。下面的代码利用定时器,计算毫秒数。
#include "reg52.h"
unsigned int ms=0;
void main()
{
TMOD |= 0x10; // ‘或’的方式不影响定时器0的配置,定时器1配置为无门控位,16位的定时器。
TL1=(65536-1000)%256;
TH1=(65536-1000)/256; //理解这种赋初值的方式
IE=0x88; //打开定时器1中断,EA=1,ET1=1。
TR1=1; //启动定时器1
while(1)
{
//print ms
}
}
void timer1() interrupt 3
{
TH1=(65536-1000)/256;
TL1=(65536-1000)%256; //不赋初值会从TL1 = 0,TH1 = 0开始计数
ms++; //1000us = 1ms
}
当你使用的晶体是11.0592Mhz时,12分频之后是0.9216Mhz,机器周期就是 1 0.9216 M h z ≈ 1.085 u s \frac{1}{0.9216Mhz}\approx1.085us 0.9216Mhz1≈1.085us。使用定时器计算时要注意这一点。