今天来学习一个有用的新东西——定时器与中断,虽然刚学起来这个东西会让人感觉摸不着头脑,但仔细考虑一番还是不难的。
一、定时器概述
简介:MCS51系列的单片机通常有2个16位可编程定时/计数器,即定时器T0和T1,T0和T1均可分为高8位TH和低8位TL。与定时/计数器相关的有两个特殊功能寄存器:模式控制寄存器TMOD和控制寄存器TCON,它们控制了定时器的工作,如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/af93951a18049dfdefaebbbb12497eda.png)
时间:16位的定时器/计数器实质上就是一个加1计数器。当定时器/计数器为定时工作方式时,计数器的加1信号由振荡器的12分频信号产生(1个机器周期);每过一个机器周期,计数器加1,直至计满溢出为止。因此,定时器的定时时间与系统的振荡频率有关。我们的晶振频率为12MHz,所以一次计数加一就是1us。
二、模式控制寄存器TMOD
TMOD构成:
TMOD寄存器分为两组,高四位控制定时器T1,低四位控制定时器T0,构成如下:
GATE | C/T* | M1 | M0 | GATE | C/T | M1 | M0 |
---|
如果我们使用一个定时器,那么只要设置对应的4位就可以了:
位符号 | 描述 |
---|---|
GATE | 门控位,为0时以TR1TR0来启动定时器运行,为1时依靠中断/TR启动定时器运行 |
C/T* | 为0:定时器模式 | 1时:计数器模式 |
M1+M0 | 工作方式的选择,有4中工作方式 |
定时器的工作方式:
加入我们使用的是T0定时器:
TMOD | 工作方式 | 描述 |
---|---|---|
0000 0000 | 工作方式0 | 13位定时器 |
0000 0001 | 工作方式1 | 16位定时器 |
0000 0010 | 工作方式2 | 8位常数自动重新装载器 |
0000 0011 | 工作方式3 | T0分为两个8位计数器 |
三、控制寄存器TCON
TCON构成:
TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
---|
功能描述:
低四位与外部中断有关,高四位功能如下:
TF:计数溢出标志位,定时器/计数器T0或T1溢出时,由硬件自动将TF0或TF1置为“1”,并向CPU申请中断,CPU响应中断后,自动将TF清零。
TR:计数运行控制位,TRx=1时,启动计数器/定时器;TRx=0时停止定时器/计数器工作
三、定时器的工作
定时器/计数器初始化流程:
EA:控制总中断
TRx:TCON的控制位,控制计时器Tx中断
下面是使用T0定时器定时1ms的初始化函数:
void Init()
{
TMOD = 0X01; // T0选择工作方式1
TH = (65536-1000)/256; //设定TH初始值
TL = (65536-1000)%256; //设定TL初始值
ET0 = 1; //开启定时器T0中断
EA = 1; //开启总中断
TR0 = 1; //开启定时器T0
}
这里设置了定时器模式以及定时器TH和TL的初始值,这里设置了定时1000个及其周期,即1ms。之后设置了中断,最后开启定时器。
设置计时时间
如果我们要定时时间为t ms,包含1000*t个机器周期,则我们需要为T0预先填装初始值 2^16^-1=65535
:
设置 TH = (6535 - t*1000) / 256
设置 TL = (6535 - t*1000) % 256
例如,我们需要计时1ms,则T0初始值为(65535-1*1000) = 64535,对应的TH和TL分别为252和23。
四、中断的实现
当定时器T0/T1计数计满溢出时,会导致TCON的TF置位并向CPU申请中断,CPU响应中断,执行中断函数。
中断函数:一种当有中断发生时自动执行的函数,它不需要由主函数调用执行。执行完中断函数后,定时器/计数器重新计数。
函数定义:
void funct() interupt N
{
do_sonething();
}
描述:函数名后面加上了 inerrupt N
即interrupt加上1个数字,这个数字表示中断的级别。T0定时器的中断级别高于T定时器的中断级别,级别高的中断函数优先执行
举个栗子:
void T0_process() interrupt 1
{
TH = (65535 - 1000) / 256;
TL = (65535 - 1000) % 256;
count++;
if (count == 100)
{
count = 0;
led = ~led;
}
}
描述:这个中断函数在中断时,重置了T0定时器的数值,同时,当发生了100次中断(1ms)的时候,led灯的电位变化一次,即led灯每200ms闪烁一次。
五、综合运用
先看一个使用工作方式2的计数器,TL0是8位的定时器,TH0是缓冲器,保存装载的数值。在发生中断时,自动将TH0的数值重装到TL0,TL0重新开始计数,这样可以减小软件带来的误差:
#include "STC15F2K60S2.H"
#define uint unsigned int
#define uchar unsigned char
sbit led_sel=P2^3;
sbit LED = P0^0;
uint count = 0;
void init()
{
P0M1 = 0x00; //设置推挽输出
P0M0 = 0xff;
P2M1 = 0x00;
P2M0 = 0xf0;
P0 = 0x00;
led_sel = 1;
TMOD = 0x02; //设置定时器
TH0 = (255 - 100);
TL0 = (255 - 100);
EA = 1;
ET0 = 1;
TR0 = 1;
}
void T0_process() interrupt 1
{
count++;
}
void main()
{
init();
while (1)
{
if (count == 5000)
{
count = 0;
LED = ~LED;
}
}
}
描述:在上面的程序中,我们使用了中断来计时,每一次中断时间为0.1ms,5000次就是0.5s,得到的结果就是led灯以1Hz的频率闪烁。同时可以发现,我们不需要每次中断时手动为T0重装数值。
第四篇单片机学习笔记完结,cheers ! ??