速通51单片机————按键控制LED流水灯与定时器闹钟

前置知识

在我们执行一个程序时,比如LED的流水灯循环,此时如果我们再写一个代码让按键控制流水灯向左或者向右,那么此时会发生,例如:灯向左->按下向右按键->灯向左完->灯向右

这里就会发现,按键按下后会有很大的延迟。

这里就会引入一个中断的概念,我们引入中断之后,就会消除这种延迟。

具体过程可以查看下图:

这里打个比喻:CPU在处理完一次循环LED流水灯之后,才回去检测是否有按键按下改变方向,如果引入中断,就相当于引入了一个响铃,当响铃响起,CPU就知道要改变方向,此时直接改变方向即可。那么CPU就可以专心于流水灯的点亮即可。

这里的机制就是,响铃响起,中断产生,CPU优先处理中断指向的任务(改变方向),回去继续处理中断前的任务(流水线)

所以本节要讲的就是如何引发中断

初始化定时器

定时器与中断系统的基本原理:

具体原理说明请自行百度

这里说明下列代码:

1.赋值TMOD控制定时器1的值:此时为16位定时器,TH0为16位高8位,TL0位16位低8位。

2.赋值TF0:TF0是中断标志,当定时器溢出时,此时TF0=1,说明引发中断,所以一般初始位0。

3.赋值TR0:为1说明定时器0工作,此处没有赋值TR1说明定时器1不工作。

3.TH0与TL0赋值:此处为简单的数学原理,目的是用两个8位来表示16位,定时器最多到65535,我们这里取64535开始,也就是1ms,那么代码就需要取得64535的16位二进制表示m

4.ET0、EA、PT0为连通中断过程。

cpp 复制代码
void Timer0_Init()
{
	TMOD = 0x01; //控制定时器:0000 0001
	TF0 = 0; // 清空中断标志
	TR0 = 1; // 开始计数
	TH0 = 64535/256; // 16进制高位
	TL0 = 64535%256; // 16进制低位
	ET0 = 1;//连通IE中断过程
	EA = 1;//连通IE中断过程
	PT0 = 0;//连通IP中断过程
}

初始化定时器(优化版)

上述的代码只是基于对电路的理解来写的,实际上有更方便的方法,可以在STC-ISP软件里自动生成,但是,只能生成定时器的代码,连通中断等步骤还得自己添加:

这里简单说明一下为什么让TMOD进行与或操作,因为TMOD是一个不可寻址的变量,也就是我们不能单独控制一个位的值,只能同时控制整个TMOD的值,那么反过来,像之前控制单个LED灯闪烁的就是可寻址变量。

又因为是不可寻址,我们不能单独改变定时器1或者定时器2,所以我们采用与或的方式在不改变定时器1的条件下改变定时器0。

cpp 复制代码
void Timer0_Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;//连通IE中断过程
	EA = 1;//连通IE中断过程
	PT0 = 0;//连通IP中断过程
}

中断函数

在定时器触发时,会跳转到中断函数处理中断任务:

这里解释一下,我们设置的定时器是1ms触发一次中断,由于定时器溢出时,会将TL0和TH0清零,所以我们在进入中断函数内部需要将这两个变量重新赋值到我们想要的值

接下来T0Count时用来计算中断次数,1ms一次中断可能过于快,那么我们就令其中断1000次,也就是1000ms才让他进入操作,进入操作时,同样的需要将T0Count归0,如何再进行操作。

cpp 复制代码
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;//计算中断次数
	if(T0Count >= 1000)//1000次中断后
	{
		T0Count = 0;//重新计算
		P2_0 =~ P2_0;//灯泡闪烁
	}
}

独立按键控制LED流水灯

这里会复用到之前的Delay与key函数,用来捕捉按下的按键,请读者自行复制粘贴

这里解释一下,左移和右移我们调用_crol_与_cror_函数,这样就不用判断移动溢出的情况,会自动从最左边跳到最右边。

我们利用中断的特性,在主函数中不断地捕捉独立按键的情况并根据按下的条件不断更改LEDMode,然后通过中断的特性让cpu不断地根据LEDMode的值来处理LED灯移动。

所以,原本我们以为主函数是在点亮灯,而中断函数是在调整方向;

实际上,是主函数在调整方向,而中断函数在点亮灯。

cpp 复制代码
#include <REGX52.H>
#include <INTRINS.H>
#include "Timer0.h"
#include "Key.h"

unsigned char KeyNum,LEDMode;

void main()
{
	P2 = 0xFE;
	Timer0_Init();
	while(1)
	{
		KeyNum = Key();
		if(KeyNum)
		{
			if(KeyNum = 1)
			{
				LEDMode++;
				if(LEDMode >= 2) LEDMode = 0;
			}
		}
	}
}



void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;//计算中断次数
	if(T0Count >= 500)//1000次中断后
	{
		T0Count = 0;//重新计算
		if(LEDMode == 0)
		{
			P2 = _crol_(P2,1);//左移1位
		}
		if(LEDMode == 1)
		{
			P2 = _cror_(P2,1);//右移一位
		}
	}
}

定时器闹钟

这里需要复用LCD1602等函数。

接下来只给出主模块的实现方式,其他解耦的模块请自行添加。

先解释一下:

与刚刚不同的是,我们这里主函数是用来LCD显示,而中断函数则用来进行计时。

所以我们只需要定义秒,分,时,然后把一下要显示的冒号给显示就好了,非常简单。

而在开头的全局变量赋值是为了更快的检测是否会跳转归零的情况,方便测试。

cpp 复制代码
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Timer0.h"

unsigned char Sec=50,Min=59,Hour=59;

void main()
{
	LCD_Init();
	Timer0_Init();
	
    //常驻显示一下定量
	LCD_ShowString(1,1,"Clock:");
	LCD_ShowString(2,1,":");
	LCD_ShowString(2,3,":");
	LCD_ShowString(2,6,":");

    //循环显示变量
	while(1)
	{
		LCD_ShowNum(2,1,Hour,2);
		LCD_ShowNum(2,4,Min,2);
		LCD_ShowNum(2,7,Sec,2);
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;
	TH0 = 0xFC;
	T0Count++;

    //每隔1s则计数,每60秒则清0算1分钟,每60分钟清0算1小时,每24小时清0重新计时
	if(T0Count >= 1000)
	{
		T0Count = 0;
		Sec++;
		if(Sec >= 60)
		{
			Sec = 0;
			Min++;
			if(Min >= 60)
			{
				Min = 0;
				Hour++;
			}
			if(Hour >= 24)
			{
				Hour = 0;
			}
		}
	}
}
相关推荐
西城微科方案开发2 小时前
智能打气泵方案开发设计
单片机·嵌入式硬件·方案公司推荐
硅农深芯3 小时前
ARM Cortex-M系列的内核型号有什么区别,该怎么选择
arm开发·单片机·芯片
Y unes4 小时前
《uboot基础命令记录①》
linux·驱动开发·嵌入式硬件·mcu·ubuntu·uboot
jencepan5 小时前
【3C拓展坞】40V20A同步四开关升降压控制器 太矽TX9575
单片机·嵌入式硬件
qq_411262428 小时前
用MCU一个GPIO自锁PMOS就能实现按键开关机
单片机·嵌入式硬件
q_30238195568 小时前
RK3588 + YOLOv8 田块分割实战指南:从环境搭建到部署落地全流程
人工智能·单片机·深度学习·神经网络·物联网·yolo
创界工坊工作室8 小时前
DPJ-148 基于Arduino六自由度机械手设计(源代码+proteus仿真)
stm32·单片机·嵌入式硬件·51单片机·proteus
金色光环8 小时前
裸机stm32移植双串口modbus从机(附源码)
stm32·单片机·嵌入式硬件
一路往蓝-Anbo8 小时前
C语言从句柄到对象 (五) —— 虚函数表 (V-Table) 与 RAM 的救赎
c语言·开发语言·stm32·单片机·物联网