蓝桥杯单片机07——矩阵键盘的扫描原理与基本应用

注:本文所涉及的图片均来自于B站小蜜蜂老师的视频。

一、题目要求

二、题目分析

1.矩阵键盘的扫描

(1)独立键盘:

将J5处的跳帽接到2~3引脚,使按键S4~S7四个按键的另一端接地从而成为4个独立键盘,此时按下独立按键后,按键与地相连,为低电平,即按下按键为低电平,松开按键为高电平。

(2)矩阵键盘:

将J5处的跳帽接到1~2引脚,对4*4的矩阵键盘进行操作,按下按键后,左右两侧I/O同状态

在上面的矩阵键盘,我们逐行扫描,然后读取列的状态信号。如果R2行输出低电平,那么黄色按键如果有按下动作的话,那读取C3列信号也应该为低电平,而该行上其他没有按下动作的按键的列信号则为高电平。

因此我们可以得到矩阵键盘扫描的基本步骤:使R1(R2/R3/R4)行输出低电平,逐个读取判断列信号,如果都为高电平,则R1(R2/R3/R4)行没有按键按下;如果发现C1(C2/C3/C4)列信号为低电平,那么可以判断R1行的C1(C2/C3/C4)列的按键有按下动作。

2.点亮数码管

数码管的引脚可以划分为段码和公共端两个部分,由图可知段码引脚(a1~dp1)的输入与Y7C有关,公共端引脚(com1~com4)与Y6C有关

三、重要代码详解

1.定义P4内存地址

#include "reg52.h"

sfr P4=0xc0;

该型号单片机无P4 ,需自行定义P4的内存地址

2.定义变量

unsigned char key_num;

定义变量key_num,便于后续从数组中取出相应的要显示的数字

3.矩阵键盘扫描

    R1=0;
	R2=R3=R4=1;
	C1=C2=C3=C4=1;
	if(C1==0)
	{
		while(C1==0);
		key_num=0;
		DisplayKeyNum(SMG_duanma[key_num]);//如果C1没有松开,定义key_num为0
	}
	else if(C2==0)
	{
		while(C2==0);
		key_num=1;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C3==0)
	{
		while(C3==0);
		key_num=2;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C4==0)
	{
		while(C4==0);
		key_num=3;
		DisplayKeyNum(SMG_duanma[key_num]);
	}

对R1行进行逐列扫描,此时R1为低电平,R2~R4为高电平,C1~C4初始化为高电平,如果C1为低电平,即按下R1行C1列对应的按键时,数码管显示数组中的第一位的值,在该题即为显示0,其余同理。

四、总体代码展示

#include "reg52.h"

sfr P4=0xc0;//该型号单片机无P4,需自行定义P4的内存地址

sbit R1=P3^0;
sbit R2=P3^1;
sbit R3=P3^2;
sbit R4=P3^3;

sbit C4=P3^4;
sbit C3=P3^5;
sbit C2=P4^2;
sbit C1=P4^4;

unsigned char code SMG_duanma[18]=
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
	0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,
0xbf,0x7f};

void SelectHC573(unsigned char channel)
{
	switch(channel)
	{
		case 4:
			P2=(P2&0x1f)|0x80;
		break;
		case 5:
			P2=(P2&0x1f)|0xa0;
		break;
		case 6:
			P2=(P2&0x1f)|0xc0;
		break;
		case 7:
			P2=(P2&0x1f)|0xe0;
		break;
	}
}

void DisplayKeyNum(unsigned char value)
{
	SelectHC573(6);
	P0=0x01;
	SelectHC573(7);
	P0=value;
}

unsigned char key_num;

void ScanKeysMulti()
{
	R1=0;
	R2=R3=R4=1;
	C1=C2=C3=C4=1;
	if(C1==0)
	{
		while(C1==0);
		key_num=0;
		DisplayKeyNum(SMG_duanma[key_num]);//如果C1没有松开,定义key_num为0
	}
	else if(C2==0)
	{
		while(C2==0);
		key_num=1;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C3==0)
	{
		while(C3==0);
		key_num=2;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C4==0)
	{
		while(C4==0);
		key_num=3;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	
	R2=0;
	R1=R3=R4=1;
	C1=C2=C3=C4=1;
	if(C1==0)
	{
		while(C1==0);
		key_num=4;
		DisplayKeyNum(SMG_duanma[key_num]);//如果C1没有松开,定义key_num为0
	}
	else if(C2==0)
	{
		while(C2==0);
		key_num=5;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C3==0)
	{
		while(C3==0);
		key_num=6;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C4==0)
	{
		while(C4==0);
		key_num=7;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	
	R3=0;
	R1=R2=R4=1;
	C1=C2=C3=C4=1;
	if(C1==0)
	{
		while(C1==0);
		key_num=8;
		DisplayKeyNum(SMG_duanma[key_num]);//如果C1没有松开,定义key_num为0
	}
	else if(C2==0)
	{
		while(C2==0);
		key_num=9;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C3==0)
	{
		while(C3==0);
		key_num=10;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C4==0)
	{
		while(C4==0);
		key_num=11;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	
	R4=0;
	R1=R2=R3=1;
	C1=C2=C3=C4=1;
	if(C1==0)
	{
		while(C1==0);
		key_num=12;
		DisplayKeyNum(SMG_duanma[key_num]);//如果C1没有松开,定义key_num为0
	}
	else if(C2==0)
	{
		while(C2==0);
		key_num=13;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C3==0)
	{
		while(C3==0);
		key_num=14;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
	else if(C4==0)
	{
		while(C4==0);
		key_num=15;
		DisplayKeyNum(SMG_duanma[key_num]);
	}
}	

void main()
{
	while(1)
	{
		ScanKeysMulti();
	}
}

<think>好的,用户想了解蓝桥杯单片机比赛中键盘相关的开发教程或示例代码。首先,我需要回忆一下蓝桥杯常用的单片机型号,比如CT117E,它使用的是STM32G431芯片,HAL库开发。用户可能已经看过一些资料,但发现很多教程的按键处理部分存在问题,所以需要提供可靠的编程思路。 接下来,我应该考虑按键处理的基本流程,包括硬件连接、消抖处理、状态检测和事件触发。硬件方面,通常使用矩阵键盘来节省IO口,所以可能需要解释矩阵键盘原理。然后,消抖处理是关键,软件延时或者定时器扫描都是常见方法,但HAL库开发可能更推荐定时器中断方式,以避免阻塞主程序。 状态检测方面,需要区分按下、释放、长按等状态,使用状态机来处理会比较可靠。示例代码部分需要展示如何初始化GPIO和定时器,如何编写中断服务函数,以及状态机的实现。同时,用户可能需要了解如何避免常见的错误,比如直接在中断中处理复杂逻辑,或者忽略消抖导致的误触发。 还要考虑引用用户提供的参考内容,特别是引用[1]中提到的按键编程注意事项,强调正确的编程思路。可能还需要提到一些优化方法,比如使用回调函数或者事件驱动的方式,提高代码的可维护性。 最后,生成的相关问题需要围绕用户可能遇到的进一步疑问,比如优化按键响应、矩阵键盘的实现细节、长按和短按的区分方法,以及调试技巧。这些问题可以帮助用户更深入地理解单片机键盘开发的关键点。</think>### 蓝桥杯单片机键盘开发指南 #### 一、硬件基础 蓝桥杯单片机开发板(如CT117E)通常采用**矩阵键盘设计**,通过$4 \times 4$或$3 \times 4$的按键矩阵节省IO口资源。以$4 \times 4$矩阵键盘为例: - 行线(ROW0-ROW3)配置为**推挽输出** - 列线(COL0-COL3)配置为**上拉输入** #### 二、核心开发步骤 1. **GPIO初始化** ```c void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // 行线配置(输出模式) __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 列线配置(输入模式) __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` 2. **按键扫描实现** ```c #define KEY_DEBOUNCE_TIME 20 // 消抖时间20ms uint8_t Key_Scan(void) { static uint8_t last_key = 0xFF; uint8_t current_key = 0xFF; for(uint8_t row=0; row<4; row++) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0<<row, GPIO_PIN_RESET); HAL_Delay(1); // 稳定信号 for(uint8_t col=0; col<4; col++) { if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0<<col) == GPIO_PIN_RESET) { current_key = row*4 + col; } } HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0<<row, GPIO_PIN_SET); } if(current_key != last_key) { last_key = current_key; HAL_Delay(KEY_DEBOUNCE_TIME); return current_key; } return 0xFF; } ``` #### 三、高级优化技巧 1. **定时器扫描方案** 建议使用定时器中断实现周期扫描(推荐1ms周期),避免阻塞主程序: ```c void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM6) { // 假设使用TIM6 static uint8_t count = 0; if(++count >= 20) { // 20ms扫描一次 count = 0; Key_Scan(); } } } ``` 2. **状态机设计** 实现长按/短按检测: ```c typedef enum { KEY_IDLE, KEY_DOWN, KEY_HOLD } KeyState; void Key_Handler(void) { static KeyState state = KEY_IDLE; static uint32_t press_time = 0; uint8_t key = Key_Scan(); switch(state) { case KEY_IDLE: if(key != 0xFF) { state = KEY_DOWN; press_time = HAL_GetTick(); } break; case KEY_DOWN: if((HAL_GetTick() - press_time) > 1000) { // 长按处理 state = KEY_HOLD; } else if(key == 0xFF) { // 短按处理 state = KEY_IDLE; } break; case KEY_HOLD: if(key == 0xFF) { state = KEY_IDLE; } break; } } ``` #### 四、注意事项 1. 避免在主循环中使用阻塞式延时 2. 矩阵键盘扫描时应**逐行扫描**,每次只激活一行 3. 按键消抖时间建议在$10ms \sim 50ms$之间[^1] 4. 使用状态机处理复杂按键逻辑时,注意状态转移的完整性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值