(基于江协科技)51单片机入门:5.定时器
目录
定时器
中断系统
按键控制LED流水灯模式
可位寻址 和 不可位寻地址
利用定时器计时2秒闪烁一次LED代码:
利用STC - ISP获取定时器
1.流水灯主函数文件代码:
2.闹钟主函数文件代码:
定时器
定时器是外设模块,当程序执行后,时间计算的 Delay 可以完全替代,且定时器不占单片机的计算空间
定时器具有 4 种模式
最常用的是 模式1:16位定时器 / 计数器
计数器中有 TL0 和 TH0 用于计数,内存为2个字节(65535为最大值)
TL0 和 TH0 的加 有时钟给与的脉冲决定,一个脉冲 计算器+1
直到计算器到达最大值 65535 就会溢出,从新计算
溢出后计算器会向 TF0 申请中段
脉冲由SYSclk导致
SYSclk:系统时钟,即金振周期,金振频率看开发板上金振上打印的,有12MHz 有11.0592MHz的
SYSclk 被 MCU in 12T mode 作用 进行12分频
12M 变为 1M (1秒(s) = 1000 毫秒(ms) = 1,000,000 微秒(μs))
所以 1 微妙进行一次计数
11.0592 MHz / 12 = 921.6 kHz
1 微妙进行 0.921.6次计数
如果给 1 链接 T0pin 作用:计数器
如果给 0 链接SYSclk 作用:定时器
中断系统
作用:提醒程序
中断系统是为使CPU具有对外紧急事件的实时处理能力而设置的
中断系统就是打断你现在干的事情,让你先干其他事情,再会来干现在的事情
例如:你突然尿急,中断现在干的事情,先去上厕所,再回来干事情
中断你正在干的事情的原因就是中断源,尿急就是中断源
但中断源具有多个
会按优先级进行,最先相应优先级最高的中断请求
中段的资源和单片机的型号关联在一起,不同的型号可能会有不同的中段资源,例如中断源的个数不同,中断源优先级的个数不同
STC89C52有8个中断源:
外部中断0,定时器0中断,外部中断1,定时器1中断,串口中断,外部中断2,定时器2中断,外部中断3
4个中断优先级
定时器模式寄存器为 TMOD 类似以前的P2,作用控制定时器达到我们想要的模式
按键控制LED流水灯模式
可位寻址 和 不可位寻地址
可位寻址:能单独赋值(一个bit位) P2_0
不可位寻址:只能整体赋值(一个字节) P2
计数器只有溢出才发出中断申请
所以想要定时器1毫秒提醒一次,就从溢出向前减去1毫秒内计数器的次数
65535 - 1000(12MHz1毫秒计数的次数) = 64535
我们上面讲计算器的最大值为 65535 是讲计时器看为了一个整体
但实际上它是单独的两个储存
一个储存有 256
两个存储便是 256 * 256
按排位先后将高位内容存入 TL0 中
低位内容存入 TH0中
结合上文1毫秒提醒一次:
给整体赋值为64535
单个:
TL0 = 64535 / 256;(一个储存的最大值)
TH0 =64535 % 256;
利用定时器计时2秒闪烁一次LED代码:
#include <REGX52.H>void Timer0_Init()
{TMOD = 0x01;TF0 = 0;TR0 = 1;TH0 = 64535/256;TL0 =64535%256;ET0= 1;EA = 1;PT0 = 0;
}int main()
{Timer0_Init(); // 定时器模式初始化while(1){}return 0;
}
int c = 0; // 设置为全局变量,放中断函数内部会导致,一直创建再销毁,永远到不了2
void Timer0_Routine() interrupt 1
{TH0 = 64535/256; // 由于每次中断,计数器会重新开始计算TL0 =64535%256; // 为了保持中断为1毫秒一次,中断中再次赋值为64535c++;if(c>1000){c = 0;P2_0=~P2_0;}
}
由于 TMOD 寄存器为不可位寻址 且 寄存器上控制着2个定时器
直接给它赋值为 0x01 会影响到第 1 定时器
所以我们换一种写法,只控制计时器 0 二不干扰定时器 1
TMOD = 0x01 更改为 TMOD = TMOD & 0xF0;
TMOD = TMOD | 0x01;
作用:TMOD 低4位清零 高4位保持不变
将TMOD 最低位置1 高4位保持不变
利用STC - ISP获取定时器
定时器的初始化也可以利用STC - ISP 软件获取初始化的函数
有4个注意点:
1.89C52没有16位自动重载
2.定时器时钟12T是选择 .
3.在想选择6T时要在软件左侧勾选(12T够用了)
4.将配置好的第一句删除,因为89C52没有第一节的寄存器
后续加上 ET0 = 1;(单独开关)
EA =1;(总开关)
PT0 = 0;(对这个中断选择优先级)
cror 和 crol 函数:
两函数的头文件都是 <intring.h>
cror:左移函数
crol:右移函数
实现独立按键控制流水灯的思路:
利用 key(独立函数) 的返回值对中断函数的左右移操作进行判断
#include <REGX52.H>
#include "key.h"
#include "intrins.h"void Timer0_Init()
{TMOD = 0x01;TF0 = 0;TR0 = 1;TH0 = 64535/256;TL0 =64535%256;ET0= 1;EA = 1;PT0 = 0;
}unsigned char move = 1; // 流水灯左右移动决定变量,初始化为1(左移)int main()
{unsigned char key_ret = 0; // 流水灯外围if语句的保护变量,用于暂存key的返回值P2 = 0xFE; ¯Timer0_Init(); while(1){key_ret = key(); // 不直接让move 变量接收,保护move变量的值if(key_ret) {if(key_ret == 1){move = 1;}if(key_ret == 2){move = 2;}}}return 0;
}
void Timer0_Routine() interrupt 1
{static int c = 0;TH0 = 64535/256; TL0 =64535%256; c++;if(c>500){c = 0;if(move==1){P2 =_cror_(P2,1);}if(move==2){P2 =_crol_(P2,1);}}
}
定时器函数的分装(11.0592MHz)
头文件
#ifndef __TIME_H_
#define __TIME_H_
void Timer(void);
#endif
源文件
#include <REGX52.H>void Timer(void)
{TMOD &= 0xF0; TMOD |= 0x01; TL0 = 0x66; TH0 = 0xFC; TF0 = 0; TR0 = 1;ET0 = 1;EA = 1;PT0 = 0;
}
1.流水灯主函数文件代码:
#include <REGX52.H>
#include "key.h"
#include "intrins.h"
#include "time.h"unsigned char move = 1; // 流水灯方向决定值int main()
{unsigned char key_ret = 0;P2 = 0xFE; // 流水灯初始化Timer0_Init(); // 初始化定时器模式while(1){key_ret = key();if(key_ret){if(key_ret == 1){move = 1;}if(key_ret == 2){move = 2;}}}return 0;
}
void Timer0_Routine() interrupt 1
{static int c = 0;TH0 = 64535/256; // 由于每次中断 计算器会从新开始计算TL0 =64535%256; // 为了保持间隔为1毫秒 在中断中再次赋值为64535c++;if(c>500){c = 0;if(move==1){P2 =_cror_(P2,1);}if(move==2){P2 =_crol_(P2,1);}}
}
2.闹钟主函数文件代码:
#include <REGX52.H>
#include "LCD1602.H"
#include "time.h"unsigned char S,M,H = 0; // 秒 分钟 小时
int main()
{LCD_Init();Timer();LCD_ShowString(1,1,"colck");LCD_ShowString(2,1," : :");while(1){LCD_ShowNum(2,1,H,2);LCD_ShowNum(2,4,M,2);LCD_ShowNum(2,7,S,2);}return 0;
}void Timer0() interrupt 1
{static int i = 0 ;TL0 = 0x66; TH0 = 0xFC;i++;if(i>1000){i = 0 ;S++;if(S>=60){S = 0;M++;if(M>=60){M = 0;H++;if(H>=24)H = 0;}}}
}