目录
[可位寻址 和 不可位寻地址](#可位寻址 和 不可位寻地址)
[利用STC - ISP获取定时器](#利用STC - ISP获取定时器)
定时器
定时器是外设模块,当程序执行后,时间计算的 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代码:
cpp
#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毫秒一次,中断中再次赋值为64535
c++;
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(独立函数) 的返回值对中断函数的左右移操作进行判断
cpp
#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)
头文件
cpp
#ifndef __TIME_H_
#define __TIME_H_
void Timer(void);
#endif
源文件
cpp
#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.流水灯主函数文件代码:
cpp
#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毫秒 在中断中再次赋值为64535
c++;
if(c>500)
{
c = 0;
if(move==1)
{
P2 =_cror_(P2,1);
}
if(move==2)
{
P2 =_crol_(P2,1);
}
}
}
2.闹钟主函数文件代码:
cpp
#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;
}
}
}
}