江协科技51单片机学习- p17 定时器

🚀write in front🚀  
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​ 

💬本系列哔哩哔哩江科大51单片机的视频为主以及自己的总结梳理📚 

 前言:

本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记,在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。

引用:

51单片机入门教程-2020版 程序全程纯手打 从零开始入门_哔哩哔哩_bilibili

​​​​​​c51语言变量语句意思,C51中循环语句-CSDN博客

定时器引用:

51单片机学习笔记_c51.h-CSDN博客

江科大51单片机入门学习笔记合集_51单片机江科大教程下载-CSDN博客

https://xiajun.blog.csdn.net/article/details/139968278

正文:

0. 🌿概述

在淘宝上购买了江协科技51单片机开发板套件(普中科技STC51单片机A2型号),就上在上一篇博文里说的自己计划学习下江协科技51单片机开发教程,通过STC51单片机这种MCU这种贴近于裸机的开发来增加对于系统硬件层面知识的了解和掌握。

1. 🚀C51单片机定时器

🐳定时器是51单片机的内部资源,其电路的连接和运转均在单片机内部完成。

🐳定时器的作用

  • 💘用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作。
  • 💘代替长时间的Delay,提高CPU的运行效率和处理速度。

  •  STC89C52系列单片机内部设置的两个16位定时器/计数器T0和T1,都具有计数和定时两种重做方式。
  • 对于每个定时器/计数器(T0/T1),在特殊功能计数器TMOD中都有一控制位- C/T 来选择T0或T1为定时器还是计数器。
  • 定时器/计数器0 有4中工作方式
    💘 模式0 (13位定时器/计数器)
    💘 模式1 (16位定时器/计数器)
    💘 模式2 (8位自动装载模式)
    💘 模式3 (两个8为定时器/计数器)
  • 定时器/计数器1除模式3外,其它工作模式与定时器0相同,T1在模式3无效,停止计数。

💖 定时器的个数是和单片机的型号联系在一起的,不同型号的单片机定时器的个数可能不同,但一般来说T0和T1的操作方式和所有51单片机所共有的和传统的51单片机兼容。

STC52RC单片机有3个定时器 T0, T1, T2,T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源。

2. 🚀C51单片机定时器中断

定时器就像一个小闹钟,一样根据时钟输出信号,每隔“几个脉冲”,计时器单元的值就增加1,当计数器单元数值增加到“设定的闹钟提醒时间”时,计数器就会向中断系统发出中断申请,产生“响铃提醒”,是程序跳转到中断服务函数中执行😊。

😛 中断也是一种单片机资源,查看STC89C52的数据手册。

  • 中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的😘。
  • 当CPU正在处理某事件的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完成以后,在回到被中断的地方,继续原来的工作,这样的过程称为中断😘。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。
  • 微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断请求源的问题。通常是根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是优先响应最级别最高的中断请求。🤗

🔍️ 当CPU正在处理一个中断源的时候(执行相应的中断服务程序),发生了另外一个中断优先级比他还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完成后,在回到原低优先级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统📘,没有中断嵌套功能的中断系统称为单级中断系统📙。

STC89C52系列单片机提供了 8个中断请求源 , 它们分别是:

  1. 🔖外部中断0 (INT0
  2. 🔖定时器0中断
  3. 🔖外部中断1 (INT1
  4. 🔖定时器1中断
  5. 🔖串口中断(UART)
  6. 🔖定时器2中断
  7. 🔖外部中断2(INT2
  8. 🔖外部中断3(INT3

🔍️所有的中断都具有 4个优先级

  • 🔍️用户可以用关中断允许位(EA/IE.7)或相应中断的允许位来屏蔽所有的中断请求,也可以打开相应的中断允许为来使PCU响应相应的中断申请;
  • 🔍️每一个中断源可以用软件独立的控制为开中断或者关中断状态;
  • 🔍️每一个中断的优先级别均可以用软件设置。
  • 🔍️高优先级的中断可以打断低优先级的中断,反之,低优先级的中断请求不可以打断高优先级的中断。
  • 🔍️当两个相同优先级的中断同时产生时,将由查询次序来决定先响应哪个中断。

STC89C52系列单片机的各个中断查询次序如下表所示:

51单片机的计时器T0/T1 模式1 (16为定时器/计数器)功能框图如下,主要分为3部分,时钟部分,计数器部分,中断部分。

定时器计数部分:

  • 🦜TL0 16为位计数器的低8位,TLH0 16位计数器的高8位,最大可以存数值为65535。
  • 🦜C/T 选择T0/T1计数器/定时器是工作在定时器模式,还是工作在计数器模式。T 字符上面的横线表示‘低电平’有效选择定时器,C 表示‘高电平’有效选择计数器。

 定时器时钟部分:

  • 🦜定时器时钟部分来自于系统时钟 SYSclk  或者 T0Pin (选择T0Pin时外部每输入一个脉冲,计数器加1)。
  • 🦜本实验我使用的普中科技C51单片机开发板的晶振是 11.0952Mhz,即系统时钟 SYSclk 是 11.0952MHz

定时器的系统使用 SYSclk 通过 12或者6 分频,进入定时器,我使用的普中单片机开发板上面的晶振是 11.0952Mhz/12=921,600Hz,也就是说计时器每加1 时间经过了 1/921,600 s 

定时器和中断的整体关系,定时器的时钟部分产生脉冲,

3. 🚀定时器相关寄存器

  • 寄存器是连接软硬件的媒介。
  • 在单片机中寄存器就是一段特殊的RAM存储器,一方面寄存器可以存储和读取数据,另一方面,寄存器背后都连着一根导线,控制着电路的链接方式。
  • 寄存器相当于一个复杂机器的“操作按钮”。

定时器 TCON 寄存器

定时器 TMODE 寄存器

 4. 🚀C51单片机中断相关寄存器

51单片机中断相关寄存器如下

5. 单片机寄存器可位寻址不可位寻址

😎单片机寄存器分为可位寻址不可位寻址,可位寻址的寄存器可以单独对寄存器的某一位赋值,不可位寻址的寄存器只能对寄存器整体赋值。

6. 单片机C 中断处理函数

单片机C 中断处理函数需要增加关键字 "interupt x",x就是中断号(中断查询次序号)。

7. 定时器中断源码编写

7.1 定时器中断源码编写。

STC-ISP中硬件定时器计算工具

  • 根据开发板实际的晶振频率选择主频为11.0592MHz
  • 定时器模式,因为STC89C52RC的定时器/计数器模式为0/1/2/3 这四种模式,此时实验我们选用的是定时器/计数器T0的16位模式。(STC89C52不支持16为自动装载模式)
  • 定时器时钟的分频值选择,本次实验选择12分频。

#include <REGX52.H>
#include <INTRINS.H>

/**
  * @brief  定时器0初始化函数
  * @param  无
  * @retval 无
  */
void Timer0_Init()
{
	//TMOD = 0x01;		//TMODE寄存器,定时器T0配置为模式1,16为定时器/计数器
	TMOD &= 0xF0;
	TMOD |= 0x01;
	
	TF0 = 0; 			//TCON寄存器,
	TR0 = 1;			//T0定时器开始计时
	
	
	//每隔1ms产生一次中断,然后重新开始计数,重新计数1000次就是1秒
	//从 64535 开始计数,计数到 
	TH0 = (65535 - 1000) / 256;					//12MHz晶振,12分频
	TL0 = (65535 - 1000) % 256 + 1; 			//
	
	
	//中断部分寄存器
	ET0 = 1;			//允许定时器T0中断
	EA = 1;				//允许中断
	PT0 = 0;			//定时器T0中断优先级
}

//void Timer0Init(void)		//1毫秒@11.0592MHz
//{
//	AUXR &= 0x7F;		//定时器时钟12T模式
//	TMOD &= 0xF0;		//设置定时器模式
//	TL0 = 0x66;			//设置定时初值
//	TH0 = 0xFC;			//设置定时初值
//	TF0 = 0;			//清除TF0标志
//	TR0 = 1;			//定时器0开始计时
//}


void main()
{
	Timer0_Init();		//定时器T0初始化
	
	P2_0 = 0;
	while(1)
	{
		
	}
}

/**
  * @brief  定时器0中断处理函数
  * @param  无
  * @retval 无
  */
void Timer_Routine(void) interrupt 1
{
	static unsigned int count = 0;
	
	count++;
	//P2_0 = 0;
	if(count >= 500)		//定时器T0每1ms中断一次,进入1000次经过了1s
	{
		P2_0 = ~P2_0;
		count = 0;
	}
	
	//定时器溢出之后需要重新装载
	TH0 = (65535 - 1000) / 256;					//12MHz晶振,12分频
	TL0 = (65535 - 1000) % 256 + 1; 			//
}

7.2 定时器和按键实现LED流水灯左移和右移

定时器和按键实现LED流水灯左移和右移,程序设计思路:

  • 在main.c 主循环里循环检测独立按键 KEY_1/2/3/4 是否被按下。
  • 在单片机定时器中断里每隔 1 秒控制LED流水灯左移或者右移,每次按下独立按键KEY_1翻转LED流水灯的移动方向。
  • 本实验使用 Keil5 里自带的 _cror_() 循环右移和_crol_()循环左移函数。

 

💘 循环左移,循环右移和普通移位运算的区别是,循环左移每次移位的时候最高位会被移到最低位,而普通的向左移位运算最高位会被丢弃,而最低位补零。使用循环移位运算来控制LED流水灯的亮灭非常的简单。

在单片机定时器中断里使用循环移位运算来控制LED流水灯的向左移位和向右移位的源码如下,每此按下独立按键KEY_1的时候LED流水灯的方向Toggle翻转一次。

#include <REGX52.H>
#include <REGX52.H>
#include <INTRINS.H>
#include "timer0.h"
#include "key.h"

unsigned char LEDMode = 0;

void main()
{
	unsigned char keyNum;
	
	Timer0_Init();		//定时器T0初始化
	
	P2 = 0xFE;
	while(1)
	{
		keyNum = Key();
		if(keyNum)
		{
			if(keyNum == 1){
				LEDMode++;
				if(LEDMode >= 2)
					LEDMode = 0;
			}
			
			if(keyNum == 2) P2_2 = ~P2_2;
			if(keyNum == 3) P2_3 = ~P2_3;
			if(keyNum == 4) P2_4 = ~P2_4;
		}
	}
}

/**
  * @brief  定时器0中断处理函数模版
  * @param  无
  * @retval 无
  */
void Timer_Routine(void) interrupt 1
{
	static unsigned int count = 0;
	
	count++;
	//P2_0 = 0;
	if(count >= 500)		//定时器T0每1ms中断一次,进入1000次经过了1s
	{
		if(LEDMode == 0){
			P2 = _cror_(P2, 1);		//循环右移
		}
		else{
			P2 = _crol_(P2, 1);		//循环左移
		}
		count = 0;
	}
	
	//定时器溢出之后需要重新装载
	TH0 = (65535 - 1000) / 256;					//12MHz晶振,12分频
	TL0 = (65535 - 1000) % 256 + 1; 			//
}

7.3 单片机定时器中断实现LCD1602时钟

单片机定时器中断实现LCD1602时钟,设计如下:

#include <REGX52.H>
#include <INTRINS.H>
#include "timer0.h"
#include "lcd1602.h"

static char hour = 23;
static char minute = 59;
static char second = 23;
	
void main()
{
	Timer0_Init();
	LCD_Init();
	
	LCD_ShowString(1,1,"Clock:");
	
	while(1)
	{
		LCD_ShowNum(1, 7, hour, 2);
		LCD_ShowChar(1, 9, ':');
		LCD_ShowNum(1, 10, minute, 2);
		LCD_ShowChar(1, 12, ':');
		LCD_ShowNum(1, 13, second, 2);
		
	}
}


/**
  * @brief  定时器0中断处理函数模版
  * @param  无
  * @retval 无
  */
void Timer_Routine(void) interrupt 1
{
	static unsigned int count = 0;

	
	
	count++;
	//P2_0 = 0;
	if(count >= 1000)		//定时器T0每1ms中断一次,进入1000次经过了1s
	{
		second++;
		if(second >= 60){ 
			minute++; 
			second = 0;
		}
		if(minute >= 60){
			hour++; 
			minute = 0;
		}
		if(hour >= 24){
			hour = 0;
		}
		
		count = 0;
	}
	
	//定时器溢出之后需要重新装载
	TH0 = (65535 - 1000) / 256;					//12MHz晶振,12分频
	TL0 = (65535 - 1000) % 256 + 1; 			//
}

烧录到单片机开发板之后实际效果:

8. 总结

8.1 定时器每次溢出后需要重新装载计数器的计数值

😎😎😎定时器每次溢出后需要重新装载计数器的值。😎😎😎

因为每次定时器计数值都溢出为了0,如果不重新装载定时器计数器的值下一次计数值计数的时候就从0开始计数直到 65535 溢出,下一次中断时经过的时间就不是我们期望的时间了🤓。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值