单片机学习(六)定时器的使用
参考资料:https://www.bilibili.com/video/BV1Mb411e7re?p=17
一、定时器简介
- 定时器介绍:
51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成 - 定时器作用:
- (1) 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作
- (2)替代长时间的Delay, 提高CPU的运行效率和处理速度
- ...
二、STC89C52定时器资源
- 定时器个数: 3个 (
T0
、T1
、T2
),T0和T1与传统的51单片机兼容,
T2是此型号单片机增加的资源 - 注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的
三、定时器工作原理
定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔“一秒"(一个单位时间),计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间"时,计数单元就会向中断系统发出中断申请,产生"响铃提醒",使程序跳转到中断服务函数中执行。
在实现细节上:时钟部分每隔单位时间就会传递一个脉冲信号到计数部分,而计数部分会对脉冲个数进行计数,当脉冲个数达到TH0,TL0
能存储的最大数量后(即发生溢出,这里一共有16位,即只能表示0~65535这些数字,继续接收信号即溢出),TF0
将会被置一,此时就会发出中断申请,然后程序跳转到中断服务程序中执行。
1. 时钟信号的提供
可以看到,在时钟部分有两个输入来源:
- 系统时钟
T0
引脚
当我们使用T0
引脚时往往是将这个模块作为计数器记录脉冲的个数的,而不是用作定时器,故这里我们一般只考虑系统时钟的信号输入。
SYSclk
:系统时钟,即晶振周期,本开发板上的晶振为12MHz
当 C/T=0
时选择的是系统时钟作为输入,而 C/T=1
时选择的是T0
引脚作为输入,故我们需要选择第一种。
2. 中断系统简介
中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。
中断的概念
当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。
中断的优先级
实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。
中断的嵌套
当CPU正在处理一个中断源请求的时候( 执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU能够
- 暂停对原来中断源的服务程序
- 转而去处理优先级更高的中断请求源,处理完该请求
- 再回到原低级中断服务程序
这样的过程称为中断嵌套,这样的中断系统称为多级中断系统
没有中断嵌套功能的中断系统称为单级中断系统。
中断流程图
STC89C52的中断资源
-
中断源个数: 8个
- 外部中断0
- 定时器0中断
- 外部中断1
- 定时器1中断
- 串口中断
- 定时器2中断
- 外部中断2
- 外部中断3
-
中断优先级个数: 4个
-
中断号
3. 相关的寄存器
TCON寄存器
TF和TR
当计数器溢出时TF0
会被置一,TR0
用于控制T0
计时器是否计数。
TMOD寄存器
TMOD寄存器主要是用于配置我们的定时器的功能。
GATE
C/T
M1、M0
总图
4. 定时器程序中断通路配置图
四、编码实现
初始化代码
1. 配置TMOD寄存器
首先我们希望使用定时器0,我们需要配置:
GATE = 0
C/T = 0
M1,M0 = 01
所以我们可以使用与或赋值法进行赋值:
TMOD &= 0xF0; // 1111 0000
TMOD |= 0x01; // 0000 0001
tips:TMOD寄存器是不可位寻址的,故我们只能通过整体赋值的方式进行配置
2. 配置TCON寄存器
我们需要启用T0
计数器,并给它的溢出标志位TF0
置零:
TF0 = 0;
TR0 = 1;
3. 配置计数寄存器的初始值
计数器的计数范围:0~65535
大约每隔1us计数加一,故其总共定时时间为65535us
而我们希望计时1ms即发生中断,则我们可以将计数寄存器初始值为64536,因为64536离计数器溢出差值1000,所以当计时时间为1ms时即可触发中断。
而计数寄存器由两个寄存器拼接而成,则我们可以分高低位赋值:
TH0 = 64536 / 0x0100; // 高八位
TL0 = 64536 % 0x0100; // 低八位
4. 配置中断通路
即按照上一部分的第4点的图进行配置即可:
ET0 = 1;
EA = 1;
PT0 = 0;
5. 总的配置代码
void Timer0Init(void)
{
// 1.配置TMOD寄存器
TMOD &= 0xF0; // 1111 0000
TMOD |= 0x01; // 0000 0001
// 2.配置TCON寄存器
TF0 = 0;
TR0 = 1;
// 3.配置计数寄存器的初始值
TH0 = 64536 / 0x0100;
TL0 = 64536 % 0x0100;
// 4.配置中断通路
ET0 = 1;
EA = 1;
PT0 = 0;
}
中断程序代码
语法:相当于在函数后面加上 interrupt
关键字:
void 中断程序名() interrupt 中断号 {
...
}
其中中断号需要和中断源相对应。
使用STC-ISP自带的定时器计算器
打开STC-ISP烧录工具,并点击【定时器计算器】的tab:
配置如下:
然后我们即可获得相应的初始化代码了!
注意:在我们当前版本的单片机中是没有AUXR这个寄存器的,我们不需要配置此项(删除即可),然后我们还需要加入中断的配置,即【配置中断通路】部分的三行代码。
使用自动生成的代码:
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
// 配置中断通路
ET0 = 1;
EA = 1;
PT0 = 0;
}
unsigned int T0Count = 0;
void timer0_routine() interrupt 1 {
T0Count++;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
if (T0Count == 1000) {
T0Count = 0;
P2_0 =! P2_0;
}
}
五、将计时器封装成模块
新建Timer0.h
和Timer0.c
文件:
// Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0Init(void);
#endif // __TIMER0_H__
#include <Atmel/REGX52.H>
#include "Timer0.h"
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
// 配置中断通路
ET0 = 1;
EA = 1;
PT0 = 0;
}
这样我们只需要在主函数中使用Timer0Init()
,我们即可在固定的时间内调用中断程序了。