(基于江协科技)51单片机入门:5.定时器

目录

定时器

中断系统

按键控制LED流水灯模式

[可位寻址 和 不可位寻地址](#可位寻址 和 不可位寻地址)

利用定时器计时2秒闪烁一次LED代码:

[利用STC - ISP获取定时器](#利用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代码:

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;
			}
		}
	}
}
相关推荐
what&&why4 小时前
STM32控制继电器
单片机·嵌入式硬件
JYGD6868687 小时前
《膜厚检测仪CHT - C200:基于干涉原理的精密测量技术探秘》
科技·膜厚测量仪·景颐光电·膜厚检测仪·膜厚测试仪
芯片智造7 小时前
半导体制造常见分析仪器之高分辨率 3D X 射线显微镜
经验分享·科技·芯片·知识分享·半导体产业
我不吃西红柿k8 小时前
蓝桥杯嵌入式——基础模块的使用(初始化+调度器+LED+按键+LCD)
单片机·嵌入式硬件
XCOSnTh8 小时前
XCOSnTh单片机的串口
c语言·单片机·嵌入式硬件·算法·xcosnth
XINVRY-FPGA10 小时前
XA7A75T-1FGG484Q 赛灵思 Xilinx AMD Artix-7 XA 系列 FPGA
嵌入式硬件·fpga开发·车载系统·云计算·硬件架构·硬件工程·fpga
驱动起爆大师x_x11 小时前
CAN总线的数据采样和同步问题
stm32·嵌入式硬件·学习·can
XINVRY-FPGA11 小时前
XC7A100T-2CSG324I 赛灵思 Xilinx AMD Artix-7 FPGA
arm开发·嵌入式硬件·fpga开发·硬件工程·信号处理·dsp开发·fpga
2301_14725836913 小时前
STM32 单片机开发 - FreeRTOS 实时操作系统
stm32·单片机·嵌入式硬件