定时器
使用 STC89c52 来示例
简介
C51中的定时器和计数器是同一个硬件电路支持的,通过寄存器配置不同,就可以将他当做定时器或者计数器使用。
确切的说,定时器和计数器区别是致使他们背后的计数存储器加1的信号不同。当配置为定时器使用时,每经过1个机器周期,计数存储器的值就加1。而当配置为计数器时,每来一个负跳变信号(信号从P3.4 或者P3.5引脚输入),就加1,以此达到计数的目的。
标准C51有2个定时器/计数器:T0和T1。他们的使用方法一致。C52相比C51多了一个T2 。
术语解释
- 定时器和计数器,电路一样 。
- 当使用定时器时,靠内部震荡电路数数。
- 当使用计数器时,数外面的信号,读取针脚的数据。
前置知识
- 什么是晶振 ?
- 什么是时钟周期 ?
- 什么是机器周期 ?
晶振
晶振 ( 晶体震荡器 ) ,又称数字电路的“心脏”,是各种电子产品里面必不可少的频率元器件。也就是 1 秒震荡多少次,也就是 “时钟频率”,使用 fosc 表示 。STC89c52 为 11.0592 MHz。
时钟周期
时钟周期也称为振荡周期,定义为时钟频率的倒数(T时 = 1 / fosc S)。时钟周期是计算机中最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。时钟周期是一个时间的量。更小的时钟周期就意味着更高的工作频率。
机器周期
机器周期也称为 CPU周期 。在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶
段(如取指、译码、执行等),每一阶段完成一个基本操作。完成一个基本操作所需要的时间称为
机器周期。一般情况下,一个机器周期由若干个时钟周期组成。 具体由多少个时钟周期组成需要看其对应的手册。
思考?如何求得一个机器周期的时间?
STC89c52 的一个机器周期 为 12个时钟周期。
- 获取对应的时钟频率
- 求出时钟周期
- 根据时钟周期求得机器周期
时钟频率为 : 11.0592 MHz = 1105926 HZ
时钟周期为 :1 / 11059200 S
机器周期为 : 12 / 110592600 S = 12 000 000
/ 11059200 us = 1.085 us
编写一个定时器
STC89c52 手册定时器部分如下(使用内部震荡电路实现)
我们可以得到如下信息:
TCON中
- 定时器有 2 个 (定时器0 和定时器 1,我们看定时器 0)
- TF0 为中断溢出标识 。由硬件置 1 ,可以由硬件或软件置 0 。
- TR0 为运行控制位。由软件置位和清零。TR0 为 1 时才允许 T0 计数 。
- IE0 和 IT0 与中断相关(这里暂时不用)
TOMD中
1. TOMD.3 定时器0,需要TR0 = 1 才开启 。
2. C / T 定时器,计数器转换 。
3. M1 已经 M0 为配置定时器模式,手册已经为我们举例。我这里使用0 1 模式全部使用 , 也就是16位,最大数为 65536。
代码编写
了解完上面的基本信息开始写代码
需求:写一个定时器,每1秒闪烁一次led 灯。
#include "reg52.h"
sbit led = P3^7 ;
/*
分析
16位最大能表示的时间为
计数 * 机器周期 = 65535 * 1.085us = 71105.475 us = 71.105475 ms
1S = 1000 ms
不能直接计数那么我们使用 10ms 结合数据统计来获取 1S
1. 10ms的定时器
2. 统计 100 次 决定led 开,关。
如何去定义10ms 定时器,将需要计数次数设置为x
x * 1.085us = 10000 us
x = 9216 (约等于)
65536 - 9216 = 56320 (这里就得出TL0和TH0计数器的初始值)
*/
void initTimer()
{
// 配置定时器模式
TMOD &= 0xF0 ;
TMOD |= 0x01 ;
//初始值
TL0 =0x00;
TH0 =0xDB;
// 运行控制位
TR0 = 1 ;
// 溢出标志位
TF0 = 0 ;
}
void main()
{
int number = 0 ;
initTimer();
while(1){
// 100次 10ms 就是 1 S
if(number == 100){
number = 0;
led = !led;
}
// 溢出一次代表 10ms
if( TF0 == 1 ){
// 手动清零
TF0 = 0 ;
// 重置
TL0 =0x00;
TH0 =0xDB;
// 统计
number++;
}
}
}
其它使用案例
SG90 舵机使用
舵机的控制信号为周期是20ms的脉宽调制(PWM) 信号,其中脉冲宽度从0.5ms-2.5ms,相对应舵盘的位置为0-180度,呈线性变化。常见的舵机有 90°,180°,360°。我们这里使用 180°。
PWM周期
需要通过信号线往舵机输入PWM波,才能驱动舵机转动
PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右
以下是一个周期内高电平所占的时间:
0.5ms————0度; 2.5% 对应函数中占空比为250
1.0ms————45度; 5.0% 对应函数中占空比为500
1.5ms————90度; 7.5% 对应函数中占空比为750
2.0ms————135度; 10.0% 对应函数中占空比为1000
2.5ms————180度; 12.5% 对应函数中占空比为1250
代码实现
需求让舵机 从0°-90°循环转动
#include "reg52.h"
sbit sg90 = P1^1 ;
// 计数
int cnt= 0 ;
// 旋转角度
int number = 1 ;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
/*
初始化
*/
void Timer0Init(void) //500微秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA = 1 ; // 开启中断
ET0 = 1 ; // 开启中断
}
void main()
{
Delay1000ms();
Timer0Init();
while(1){
// 旋转0°
number = 1 ;
Delay1000ms();
Delay1000ms();
// 旋转90°
number = 3 ;
Delay1000ms();
Delay1000ms();
}
}
/*
定时中断处理
*/
void PWM() interrupt 1
{
TF0 = 0 ;
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
if( cnt < number ){
sg90 = 1 ;
}
if( cnt > number){
sg90 = 0 ;
}
if( cnt >= 40 ){
cnt = 0 ;
sg90 = 1;
}
cnt++;
}
HC-SR04超声波模块
接口定义
- vcc 5v
- Trig 控制端
- Echo 接收端
- Gnd 接地
手册使用说明
超声波时序图
计算原理
让它发送波:给Trig端口至少10us的高电平
开始发送波:Echo信号由低电平跳转到高电平
接收返回波:Echo信号由高电平跳转回低电平
计算时间 : Echo引脚维持高电平的时间!
开始发送波,启动定时器,接收到返回波,停止计时器
计算距离 :测试距离=(高电平时间*声速(340m/s))/2
代码实现
当见检测到 10cm 内有遮挡点亮 led
#include "reg52.h"
sbit trig = P1^5;
sbit echo = P1^6 ;
sbit led = P3^7 ;
/*
1. 给 trig 口至少 10us 高电平
2. 等待 echo 高电平 ,并同时开启定时器
3. 等待 echo 低电平 ,并同时关闭定时器
4. 计算数据
5. 距离 = time * 340m / s / 2
*/
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void initHC()
{
trig = 0 ;
trig = 1 ;
Delay10us();
trig = 0 ;
}
void Timer0Init(void) //1000微秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x00; //设置定时初值
}
void main()
{
double dis = 0 ;
double time = 0 ;
Timer0Init();
while(1){
initHC();
// 监听echo 升为高电平
while(echo == 0 );
//定时器0开始计时
TR0 = 1;
// 监听echo 升为低电平
while(echo == 1 );
// 关闭定时器
TR0 = 0 ;
// 计算距离
time = (TH0 * 256 + TL0 ) * 1.085 ; // us
dis = time * 0.017 ;
if(dis < 10){
led = 0;
}else{
led = 1;
}
TL0 = 0x00; //设置定时初值
TH0 = 0x00; //设置定时初值
}
}
好啦,一些基本使用就到这里啦。希望对你有帮助。