蓝桥杯模块学习5——独立按键(深夜学习——单片机)

目录

一、硬件部分:

1、原理图:

 二、独立按键:

1、功能一参考代码:

(1)宏定义:

(2)主函数:(感觉现在迷上位操作了,不会的可以自已定义IO口)

1、功能一参考代码(模块化):

2、功能二参考代码: 

(1)宏定义:(同上)

(2)主函数:(如果跟着我之前几篇文章一起优化过自己代码的人,应该看得懂我这次写的代码,如果看不懂建议回去巩固一下)

(3)主函数:(优化版但可能比较难懂)

3、功能一参考代码: (直观化)

4、优化代码:(最好学过定时器后再来学习以下代码)

(1)宏定义:

(2)主函数:


一、硬件部分:

1、原理图:

 (1)J5:当1和2相接,电路就变成一个4*4的矩阵键盘电路;当2和3相接时,电路变成

了一个S4-S7的独立按键

(2)对于独立按键的分析:

独立按键,按键检测_佛科院深夜学习的博客-CSDN博客https://blog.csdn.net/weixin_63568691/article/details/128374461

 二、独立按键:

实现功能:

(1)按下S7-S4分别点亮L1-L4,同时要求松手熄灭和消抖

(2)S7、S6为选择键,S5和S4为开关键:

        (A)当按下S7,指示灯L1亮(无松手检测),同时表明当前选择L3、L4,分别由S5和S4控制(有松手检测)

        (B)当按下S6,指示灯L2亮(无松手检测),同时表明当前选择L5、L6,分别由S5和S4控制(有松手检测)

        (C)同时要求未进行选择之前,开关键不起作用

1、功能一参考代码:

(1)宏定义:

#ifndef _PUBLIC_H
#define _PUBLIC_H

#include <reg52.h>

#define u8 unsigned char
#define u16 unsigned int

void Delay_1ms(u16 num);
void Close_All(void);
void Delay_10us(u16 num);
#endif
#include "Public.h"
// 延时函数(最小约1ms@12MHz)
void Delay_1ms(u16 num)
{
  unsigned int i;
  while(num--)
    for(i=0; i<628; i++);
}


/*
	输入变量:无
	输出变量:无
	功能:关闭蜂鸣器和继电器
*/
void Close_All(void)
{
	//关闭蜂鸣器和继电器
	P0 = 0x00;
	P2 = (P2 & 0x1f) | 0xA0;
	P2 &= 0x1f;
	//关闭LED灯
	P0 = 0xff;
	P2 = (P2 & 0x1F) | 0x80;
	P2 &= 0x1f;	
}

void Delay_10us(u16 num)
{
  u16 i;
  while(num--)
    for(i=0; i<3; i++);
}

(2)主函数:(感觉现在迷上位操作了,不会的可以自已定义IO口)

// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "reg52.h"
#include "Public.h"

void LED_ON(u8 L_X);

// 主函数
void main(void)
{
	u8 i;
	Close_All();
  while(1)
  { 
		for(i=0;i<4;i++)
		{
			if(!(P3 & (0x01<<i)))
			{
				Delay_1ms(20);
				if(!(P3 & (0x01<<i)))
				{
					LED_ON(~(0x01<<i));
					while(!(P3 & (0x01<<i)));
					LED_ON(0xff);
				}
			}
		}
  }
}

/*
	输入变量:P0口的对应的16进制数
	输出变量:无
	功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
	P0 = L_X;
	P2 = (P2 & 0x1f) | 0x80;
	P2 &= 0x1f;
}

1、功能一参考代码(模块化):

// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "reg52.h"
#include "Public.h"

void LED_ON(u8 L_X);
u8 Key_check();

// 主函数
void main(void)
{
	u8 temp;
	Close_All();
  while(1)
  { 
		if(temp=Key_check())
		{
			LED_ON(~(0x01<<(7-temp)));
			while(!(P3 & 0x01<<(7-temp)));
			LED_ON(0xff);
		}
  }
}

/*
	输入变量:P0口的对应的16进制数
	输出变量:无
	功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
	P0 = L_X;
	P2 = (P2 & 0x1f) | 0x80;
	P2 &= 0x1f;
}

/*
	输入变量:无
	输出变量:7-4代表第几个按键被按下;0代表没有按键按下
	功能:检测哪个独立按键被按下
	注意:只有消抖,没有松手检测
*/
u8 Key_check()
{
	u8 i;
	for(i=0;i<4;i++)
	{
		if(!(P3 & (0x01<<i)))
		{
			Delay_1ms(20);
			if(!(P3 & (0x01<<i)))
			{
				return 7-i;
			}
		}
	}
	return 0;
}

2、功能二参考代码: 

(1)宏定义:(同上)

(2)主函数:(如果跟着我之前几篇文章一起优化过自己代码的人,应该看得懂我这次写的代码,如果看不懂建议回去巩固一下)

// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "reg52.h"
#include "Public.h"

void LED_ON(u8 L_X);
u8 Key_check();

// 主函数
void main(void)
{
	u8 temp,last_led;
	Close_All();
  while(1)
  { 
		if(temp=Key_check())
		{
			//按下S7
			if(temp==7)
			{
				//L1亮,并且保存好L1亮的信息
				LED_ON(~(last_led=0x01<<(7-temp)));
				//松手后再进行其他操作,但不关灯
				while(!(P3 & 0x01<<(7-temp)));
				while(1)
				{
					//再对S5、S4进行判断
					temp=Key_check();
					if(temp==5||temp==4)
					{
						//L1和其中之一的灯一起亮
						LED_ON(~((0x01<<(7-temp))|last_led));
						while(!(P3 & 0x01<<(7-temp)));
						//保持为L1亮的状态
						LED_ON(~last_led);
					}
					if(temp==7)
					{
						//关闭所有灯
						LED_ON(0xff);
						while(!(P3 & 0x01<<(7-temp)));
						break;
					}
			  }
			}
			//按下S6
			if(temp==6)
			{
				//L2亮
				LED_ON(~(last_led=0x01<<(7-temp)));
				//松手后再进行其他操作,但不关灯
				while(!(P3 & 0x01<<(7-temp)));
				while(1)
				{
					//再对S5、S4进行判断
					temp=Key_check();
					if(temp==5||temp==4)
					{
						//如果是按下S6,表明选择L5、L6
						LED_ON(~((0x01<<(7-temp+2))|last_led));
						while(!(P3 & 0x01<<(7-temp)));
						LED_ON(~last_led);
					}
					if(temp==6)
					{
						LED_ON(0xff);
						while(!(P3 & 0x01<<(7-temp)));
						break;
					}
			  }
			}
		}
  }
}

/*
	输入变量:P0口的对应的16进制数
	输出变量:无
	功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
	P0 = L_X;
	P2 = (P2 & 0x1f) | 0x80;
	P2 &= 0x1f;
}

/*
	输入变量:无
	输出变量:7-4代表第几个按键被按下;0代表没有按键按下
	功能:检测哪个独立按键被按下
	注意:只有消抖,没有松手检测
*/
u8 Key_check()
{
	u8 i;
	for(i=0;i<4;i++)
	{
		if(!(P3 & (0x01<<i)))
		{
			Delay_1ms(20);
			if(!(P3 & (0x01<<i)))
			{
				return 7-i;
			}
		}
	}
	return 0;
}

(3)主函数:(优化版但可能比较难懂)

// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "reg52.h"
#include "Public.h"

void LED_ON(u8 L_X);
u8 Key_check();

// 主函数
void main(void)
{
	u8 temp,last_led,c_sign;
	Close_All();
  while(1)
  { 
		if(temp=Key_check())
		{
			if(temp==7||temp==6)
			{
				//按下S7,控制L3、L4
				if(temp==7)
					c_sign=0;
				//按下S6,控制L5、L6
				if(temp==6)
					c_sign=2;
				//L1/L2亮,并且保存好L1/L2亮的信息
				LED_ON(~(last_led=0x01<<(7-temp)));
				//松手后再进行其他操作,但不关灯
				while(!(P3 & 0x01<<(7-temp)));
				while(1)
				{
					//再对S5、S4进行判断
					temp=Key_check();
					if(temp==5||temp==4)
					{
						//L1和其中之一的灯一起亮
						LED_ON(~((0x01<<(7-temp+c_sign))|last_led));
						while(!(P3 & 0x01<<(7-temp)));
						//保持为L1亮的状态
						LED_ON(~last_led);
					}
					//检测到控制键按下且标志位对应
					if((temp==7&&c_sign==0)||(temp==6&&c_sign==2))
					{
						//关闭所有灯
						LED_ON(0xff);
						while(!(P3 & 0x01<<(7-temp)));
						break;
					}
			   }
			  }
			}
		}
}

/*
	输入变量:P0口的对应的16进制数
	输出变量:无
	功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
	P0 = L_X;
	P2 = (P2 & 0x1f) | 0x80;
	P2 &= 0x1f;
}

/*
	输入变量:无
	输出变量:7-4代表第几个按键被按下;0代表没有按键按下
	功能:检测哪个独立按键被按下
	注意:只有消抖,没有松手检测
*/
u8 Key_check()
{
	u8 i;
	for(i=0;i<4;i++)
	{
		if(!(P3 & (0x01<<i)))
		{
			Delay_1ms(20);
			if(!(P3 & (0x01<<i)))
			{
				return 7-i;
			}
		}
	}
	return 0;
}

由于不太清楚是模块化思想编程好,还是减少模块和位运算尽量直观化的方法好,我决定都尝试一下

3、功能一参考代码: (直观化)

// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "reg52.h"
#include "Public.h"

void LED_ON(u8 L_X);

sbit S7 = P3^0;
sbit S6 = P3^1;
sbit S5 = P3^2;
sbit S4 = P3^3;
// 主函数
void main(void)
{
	u8 i;
	Close_All();
  while(1)
  { 
		if(!S7)
		{
			Delay_1ms(20);
			if(!S7)
			{
				LED_ON(~(0x01<<i));
				while(!S7);
				LED_ON(0xff);
			}
		}
		if(!S6)
		{
			Delay_1ms(20);
			if(!S6)
			{
				LED_ON(~(0x01<<i));
				while(!S6);
				LED_ON(0xff);
			}
		}
		if(!S5)
		{
			Delay_1ms(20);
			if(!S5)
			{
				LED_ON(~(0x01<<i));
				while(!S5);
				LED_ON(0xff);
			}
		}
		if(!S4)
		{
			Delay_1ms(20);
			if(!S4)
			{
				LED_ON(~(0x01<<i));
				while(!S4);
				LED_ON(0xff);
			}
		}			
  }
}

/*
	输入变量:P0口的对应的16进制数
	输出变量:无
	功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
	P0 = L_X;
	P2 = (P2 & 0x1f) | 0x80;
	P2 &= 0x1f;
}

4、功能二参考代码: (直观化)

// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "reg52.h"
#include "Public.h"

void LED_ON(u8 L_X);
u8 Key_check();

sbit S7 = P3^0;
sbit S6 = P3^1;
sbit S5 = P3^2;
sbit S4 = P3^3;

// 主函数
void main(void)
{
	Close_All();
  while(1)
  { 
		if(!S7)
		{
			Delay_1ms(20);
			if(!S7)
			{
				LED_ON(~(0x01<<0));
				while(!S7);
				while(1)
				{
					if(!S5)
					{
						Delay_1ms(20);
						if(!S5)
						{
							LED_ON(~((0x01<<2)|(0x01<<0)));
							while(!S5);
							LED_ON(~(0x01<<0));
						}
					}
					if(!S4)
					{
						Delay_1ms(20);
						if(!S4)
						{
							LED_ON(~((0x01<<3)|(0x01<<0)));
							while(!S4);
							LED_ON(~(0x01<<0));
						}
					}
					if(!S7)
					{
						Delay_1ms(20);
						if(!S7)
						{
							while(!S7);
							LED_ON(0xff);
							break;
						}
					}
			  }
			}
		}
		if(!S6)
		{
			Delay_1ms(20);
			if(!S6)
			{
				LED_ON(~(0x01<<1));
				while(!S6);
					while(1)
					{
						if(!S5)
						{
							Delay_1ms(20);
							if(!S5)
							{
								LED_ON(~((0x01<<4)|(0x01<<1)));
								while(!S5);
								LED_ON(~(0x01<<1));
							}
						}
						if(!S4)
						{
							Delay_1ms(20);
							if(!S4)
							{
								LED_ON(~((0x01<<5)|(0x01<<1)));
								while(!S4);
								LED_ON(~(0x01<<1));
							}
						}
						if(!S6)
						{
							Delay_1ms(20);
							if(!S6)
							{
								while(!S6);
								LED_ON(0xff);
								break;
							}
						}
					 }
		      }
				}
	}
		
}

/*
	输入变量:P0口的对应的16进制数
	输出变量:无
	功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
	P0 = L_X;
	P2 = (P2 & 0x1f) | 0x80;
	P2 &= 0x1f;
}

写完的个人感受:

(1)优点:直观化的可读性强,在局部阅读属于一看就明白,如果是一小部分写错,几乎是一下子就能看出错误;缺点:代码整体冗杂,如果是整体有错误,全局阅读性很差,特别是存在过多大括号的情况下‘;重复性强,如果存在一处错误,基本上需要改多处;可移植性差,基本不存在一码多用的情况

(2)优点:模块化的可移植性强,有时候写着写着发现都是用已经写好的函数;整体可读性强,基本上都是不同的模块组成,可以分开一部分一部分看;修改简单,由于程序大量使用同一函数,基本上只需要改某个的函数就行;缺点:局部可读性极差,极其考虑逻辑思维能力;函数与主函数联系起来较为麻烦;

(3)总结:建议还是以模块化思维编程会更有逻辑性,但是不要过度追求模块化,实在想不明白的地方直接用最笨的方法也行,在平时可以进行优化训练,但在比赛场上就不要硬求优化,能写出来就行

更新:学习其他代码后而且经过一个实际工程代码的编写,深感之前代码的鲁棒性太差了,也通过定时器和位操作优化了消抖和松手检测

4、优化代码:(最好学过定时器后再来学习以下代码)

蓝桥杯模块学习8——定时器中断(深夜学习——单片机)_佛科院深夜学习的博客-CSDN博客icon-default.png?t=MBR7https://blog.csdn.net/weixin_63568691/article/details/128508355

(1)宏定义:

#ifndef _PUBLIC_H
#define _PUBLIC_H

#include "STC15F2K60S2.H" 

#define u8 unsigned char
#define u16 unsigned int

void Delay_1ms(u16 num);
void Close_All(void);
void Delay_10us(u16 num);
#endif
#include "Public.h"
// 延时函数(最小约1ms@12MHz)
void Delay_1ms(u16 num)
{
  unsigned int i;
  while(num--)
    for(i=0; i<628; i++);
}


/*
	输入变量:无
	输出变量:无
	功能:关闭蜂鸣器和继电器
*/
void Close_All(void)
{
	//关闭蜂鸣器和继电器
	P0 = 0x00;
	P2 = (P2 & 0x1f) | 0xA0;
	P2 &= 0x1f;
	//关闭LED灯
	P0 = 0xff;
	P2 = (P2 & 0x1F) | 0x80;
	P2 &= 0x1f;	
}

void Delay_10us(u16 num)
{
  u16 i;
  while(num--)
    for(i=0; i<3; i++);
}

(2)主函数:

// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "Public.h"

u8 key_old,key_1ms = 1;
void LED_ON(u8 L_X);
void Key_Proc();
void Timer_0_Init(u16 time);
// 主函数
void main(void)
{
	Close_All();
	Timer_0_Init(1000);//1ms
  while(1)
  { 
		Key_Proc();
  }
}
/*****************LED*******************/
/*
	输入变量:P0口的对应的16进制数
	输出变量:无
	功能:选择相应的锁存器后,通过P0口控制LED灯亮
*/
void LED_ON(u8 L_X)
{
	P0 = L_X;
	P2 = (P2 & 0x1f) | 0x80;
	P2 &= 0x1f;
}

/*****************按键*******************/

/*
	输入变量:无
	输出变量:7-4代表第几个按键被按下;0代表没有按键按下
	功能:检测哪个独立按键被按下
	注意:只有消抖,没有松手检测
*/
u8 Key_read()
{
	u8 i;
	for(i=0;i<4;i++)
	{
			if(!(P3 & (0x01<<i)))
			{
				return 7-i;
			}
	}
	return 0;
}

void Key_Proc()
{
	u8 key_now,key_down,key_up;
	//消抖,同时节省CPU资源
	if(key_1ms < 20) return;
	key_1ms = 1;
   //读取按键按下的编号
	key_now = Key_read();
	//按键按下:通过判断按键状态是否发生变化,并且变化后状态是否为按下
	key_down = key_now & (key_old ^ key_now);
  //按键抬起
	key_up = ~key_now & (key_old ^ key_now);
	//记录当前状态为下一次检测做准备
	key_old = key_now;
	if(key_down)//按键按下
		LED_ON(~(0x01<<(7-key_now)));
	else if(key_up)//按键抬起
		LED_ON(0xff); 
}


/**************定时器**************************/

/*
	输入变量:定时时长___us
	输出变量:无
	功能:配置并开启定时器0
*/
void Timer_0_Init(u16 time)
{
	//12T模式
	AUXR &= 0x7f;
	//定时器0 模式0
	TMOD &= 0xf0;
	//设置初值
	TH0 = (65536-time)/256;
	TL0 = (65536-time)%256;
	//打开中断
	ET0 = 1;
	EA = 1;
	//开始计数
	TR0 = 1;
}

void Timer_0_IT(void) interrupt 1
{
	key_1ms++;
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数码管是一种常见的数字显示器件,可以用于显示各种数字、字母等字符。在单片机应用中,数码管通常用于显示计数器、计时器、温度、湿度等实时数据。 数码管的种类有很多,包括共阳数码管、共阴数码管、共阳共阴混合数码管等。其中,共阳数码管是最常见的一种,也是本文所涉及的数码管类型。 单片机控制数码管的原理是通过对数码管的各个管脚进行控制,使其显示相应的数字或字符。数码管的控制方式有两种,即静态显示和动态显示。 静态显示是指将要显示的数字或字符的每一位分别输出到数码管的每个管脚上,然后使其保持不变,从而实现显示效果。静态显示的缺点是需要使用大量的I/O口,且不能灵活地改变显示内容。 动态显示是指将要显示的数字或字符的每一位依次输出到数码管的每个管脚上,并在短时间内快速切换下一个数字或字符,从而形成连续的显示效果。动态显示的优点是可以使用较少的I/O口,且可以灵活地改变显示内容。 以下是一个简单的动态显示数码管的实现示例: 1. 定义数码管的引脚 ```c #define DIG_PORT P2 // 数码管位选端口 #define DIG_COM 0x00 // 数码管位选端口初始值 #define LED_PORT P0 // 数码管段选端口 ``` 2. 定义数码管显示的数字或字符 ```c unsigned char code ledChar[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f }; ``` 3. 实现数码管动态显示函数 ```c void display(unsigned char i) { unsigned char j, k; for (j = 0; j < 8; j++) { DIG_PORT = DIG_COM | (1 << j); // 选择数码管位(从左到右) for (k = 0; k < 100; k++); // 延时,视情况可调整 LED_PORT = ledChar[i]; // 显示数码管上的数字或字符 } } ``` 4. 调用数码管动态显示函数 ```c int main() { unsigned char i = 0; while (1) { display(i % 10); // 显示数字 i 的个位数 i++; } return 0; } ``` 以上就是一个简单的数码管动态显示的实现示例。需要注意的是,数码管的控制方式和具体实现方法可能因不同的硬件平台和编程语言而有所不同。因此,在具体应用中需要根据实际情况进行适当的调整和修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值