蓝桥杯单片机基础学习部分(4)——按键控制

一、前言

在蓝桥的比赛里,按键控制数码管切换界面,按键控制LED灯,按键控制各种外设,每一次的比赛,按键控制数码管切换界面是必考的!而按键的逻辑是比较难的,是蓝桥的一个难点。本文通过独立按键和矩阵键盘两种模式进行讨论。

第十三届题目(部分)
第十四届题目(部分)

二、原理图

在开发板上面,按键都是放在一起的,通过J5键帽控制,独立键盘还是矩阵键盘

1、原理图

按键

不管是独立按键还是矩阵键盘,我们判断按键按下的方法都是一样的,都是去检测引脚是不是相同电平。

2、独立按键

内部结构图

独立按键内部结构图

从图中看出来,按键右端连接了GND,左端连接了上拉电阻。

通过检测KeyIn1,KeyIn2,KeyIn3,KeyIn4是高电平还是低电平判断按键按下。

没有按键按下时,K1右端连接GND,所以此时的KeyIn1检测就是低电平0,当按键K1按下后,电路被接通,在上拉电阻的作用下,K1此时的电压被拉高,此时KeyIn1的电压测量就是高电平1。

当J5用键帽接1 2时,键盘状态为独立按键

独立按键

从图里可以看出来,当键盘为独立按键时,S4,S5,S6,S7按键的左端接GND,就是默认左端为低电平0,所以我们要判断按键是否按下,只需要判断S4,S5,S6,S7按键的右端引脚状态即可。S4,S5,S6,S7按键的引脚分别为P33~P30。判断这四个引脚状态即可。

3、矩阵键盘

J5用键帽接2 3,键盘状态为矩阵键盘

矩阵键盘

当J5接2 3时,S4,S5,S6,S7按键的纵行就是通过引脚P44控制。原理和独立键盘一样都是判断引脚状态为高电平还是低电平。

矩阵键盘是通过按行扫描或者按列扫描来判断按键按下的,所以可以先按行或者列定义引脚的初始化,再通过判断引脚是等于1或者0,判断按键按下。

以按行扫描为例

比如:我们要判断S10按键按下,如果S10按键按下,就是P42和P31两个引脚接通,所以他们的电平是相同的。先定义P44 = 1,P42 = 1;P35 = 1;P34 = 1;P33 = 1;P32=0;P31 = 1;P30 = 1;这样做的目的是让按键相互不影响。然后判断P42和P31两个引脚是不是相同,如果相同,我们就可以认为S10按键按下。

三、代码实现功能

1、独立按键控制LED灯亮灭

(1)按下S4,LED灯的L1亮起。

(2)按下S5,LED灯的L8亮起。

(3)按下S6,LED灯全部亮起。

(4)按下S7,LED灯全部熄灭。

要求按下按键后LED灯立刻电亮/熄灭,放手后LED灯不熄灭,长按不影响LED灯。

#include <STC15F2K60S2.H>

sbit K5 = P3^3;
sbit K6 = P3^2;
sbit K7 = P3^1;
sbit K8 = P3^0;

void Delay20ms()	//@11.0592MHz
{
	unsigned char data i, j;

	i = 216;
	j = 37;
	do
	{
		while (--j);
	} while (--i);
}

void Scan_Key()
{
	if(K5 == 0||K6 == 0||K7 == 0||K8 == 0)
	{
		Delay20ms();					//按键去抖
		if(K5 == 0)						//S4按键按下
		{
			P0 = 0xfe;
		}
		if(K6 == 0)						//S5按键按下
		{
			P0 = 0x7f;
		}
		if(K7 == 0)						//S6按键按下
		{
			P0 = 0x00;
		}
		if(K8 == 0)						//S7按键按下
		{
			P0 = 0xff;
		}
	}
}

void main()
{
	P2 = (P2 & 0x1f) | 0x80;			//打开锁存器通道指向LED灯
	P0 = 0xff;
	while(1)
	{
		Scan_Key();
	}
}

2、矩阵键盘控制数码管显示数字

(1)矩阵键盘每一个按键控制一个数字,如图

按键对应数字

(2)数码管第一位显示,其他位置不显示,不出现频闪,过亮,过暗现象。

要求按下按键后数码管立刻电亮,放手后数码管不熄灭,长按不影响数码管显示效果。

#include <STC15F2K60S2.H>

sbit K1 = P4^4;
sbit K2 = P4^2;
sbit K3 = P3^5;
sbit K4 = P3^4;
sbit K5 = P3^3;
sbit K6 = P3^2;
sbit K7 = P3^1;
sbit K8 = P3^0;

code unsigned char Seg_Table[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};

void Delay20ms()						 //@11.0592MHz
{
	unsigned char data i, j;
	i = 216;
	j = 37;
	do
	{
		while (--j);
	} while (--i);
}

void Nixie_pos_num(unsigned char dat)
{
	P2 = (P2 & 0x1f) | 0xc0;			//打开锁存器通道指向数码管位选
	P0 = 0x01;
	P2 = (P2 & 0x1f) | 0xe0;			//打开锁存器通道指向数码管段选
	P0 = Seg_Table[dat];
}

void Scan_Key()
{
	//按行扫描
	//第一行扫描
	K1 = K2 = K3 = K4 = K5 = K6 = K7 = 1;K8 = 0;
	if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
	{
		Delay20ms();					//去抖
		if(K1 == 0)						//s7按键按下
		{
			Nixie_pos_num(0);			//显示数字0
		}
		if(K2 == 0)						//s11按键按下
		{
			Nixie_pos_num(1);			//显示数字1
		}
		if(K3 == 0)						//s15按键按下
		{
			Nixie_pos_num(2);			//显示数字2
		}
		if(K4 == 0)						//s19按键按下
		{
			Nixie_pos_num(3);			//显示数字3
		}
	}
	//第二行扫描
	K1 = K2 = K3 = K4 = K5 = K6 = K8 = 1;K7 = 0;
	if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
	{
		Delay20ms();					//去抖
		if(K1 == 0)						//s6按键按下
		{
			Nixie_pos_num(4);			//显示数字4
		}
		if(K2 == 0)						//s10按键按下
		{
			Nixie_pos_num(5);			//显示数字5
		}
		if(K3 == 0)						//s14按键按下
		{
			Nixie_pos_num(6);			//显示数字6
		}
		if(K4 == 0)						//s18按键按下
		{
			Nixie_pos_num(7);			//显示数字7
		}
	}
	//第三行扫描
	K1 = K2 = K3 = K4 = K5 = K7 = K8 = 1;K6 = 0;
	if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
	{
		Delay20ms();					//去抖
		if(K1 == 0)						//s5按键按下
		{
			Nixie_pos_num(8);			//显示数字8
		}
		if(K2 == 0)						//s9按键按下
		{
			Nixie_pos_num(9);			//显示数字9
		}
		if(K3 == 0)						//s13按键按下
		{
			Nixie_pos_num(10);			//显示数字10
		}
		if(K4 == 0)						//s17按键按下
		{
			Nixie_pos_num(11);			//显示数字11
		}
	}
	//第四行扫描
	K1 = K2 = K3 = K4 = K6 = K7 = K8 = 1;K5 = 0;
	if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
	{
		Delay20ms();					//去抖
		if(K1 == 0)						//s4按键按下						
		{
			Nixie_pos_num(12);			//显示数字12
		}
		if(K2 == 0)						//s8按键按下
		{
			Nixie_pos_num(13);			//显示数字13
		}
		if(K3 == 0)						//s12按键按下
		{
			Nixie_pos_num(14);			//显示数字14
		}
		if(K4 == 0)						//s16按键按下
		{
			Nixie_pos_num(15);			//显示数字15
		}
	}
}

void main()
{
	P2 = (P2 & 0x1f) | 0x80;
	P0 = 0xff;
	while(1)
	{
		Scan_Key();
	}
}

四、补充

按键去抖

在按键按下的时候,有时候并不是直接按下电路接通,有时候按下的力度,时间都会影响按键的接触,如果每一次的按键接触,CPU都需要产生反应,会大大降低CPU处理的速度。所以我们需要判断按是不是真的按下了,排除按键抖动的干扰,这就需要在按键按下时,要先延时一段时间,确定按键是一直接触着的,才让CPU做出反应。Delay20ms(); 的作用就是去抖,排除抖动的干扰。

### 蓝桥杯按键长按题目解析 在嵌入式开发领域,按键长按是一种常见的输入处理方式。通常情况下,这种类型的题目会涉及到定时器、中断服务程序以及状态机的设计[^1]。 #### 定时器的应用 为了检测按键是否被长时间按下,可以利用硬件定时器来记录时间间隔。当检测到按键按下事件时启动计时,在达到预设的时间阈值之前持续监测按键的状态变化。如果在此期间按键释放,则重置计数;反之则触发相应的长按操作逻辑。 ```c volatile uint8_t key_press_time = 0; // 记录按键已按下的毫秒数 void TIM2_IRQHandler(void){ if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){ key_press_time++; TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } ``` 上述代码片段展示了如何通过STM32微控制器上的定时器中断服务例程增加全局变量`key_press_time`的数值以追踪按键按压时长。 #### 中断机制配合GPIO口监听 除了单纯依赖轮询的方式外,还可以借助外部中断源更高效地捕捉按键动作的发生时刻。配置好相应端口作为输入模式之后,每当有电平跳变发生就会自动进入指定好的ISR(Interrupt Service Routine),从而减少CPU占用率并提高响应速度。 ```c // 假定PA0连接了一个上拉电阻开关按钮 EXTI_InitTypeDef EXTI_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA , ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;// 下降沿触发 GPIO_Init(GPIOA,&GPIO_InitStructure); EXTI_InitStruct.EXTI_Line = EXTI_Line0 ; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling ; EXTI_InitStruct.EXTI_LineCmd = ENABLE ; EXTI_Init(&EXTI_InitStruct ); NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn ; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x0F ; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x0F ; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ; NVIC_Init(&NVIC_InitStruct ); ``` 以上C语言代码初始化了基于STM32系列MCU的一个简单下降沿触发外部中断设置过程。 #### 使用有限状态机管理不同阶段行为 对于较为复杂的交互需求来说,采用FSM(Finite State Machine)模型能够清晰表达各个可能场景之间的转换关系。比如针对短按与长按区分的情况就可以设计如下几个典型节点:“等待按键”、“初步判定为短按”、“进一步确认可能是长按”,最后再分别执行对应的任务流控制路径。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值