stm32 HAL库 4X4 矩阵键盘 附原理图和C语言代码

目录

原理图:


鉴于最近实训,需要写一份矩阵键盘的代码,看到网上大部分矩阵键盘不是很方便修改引脚,遂自己手敲了一份,可以随便修改引脚的代码方便移植。

不过我是直接把函数写在了main函数的while循环里,没有消抖,有需求的同志可以自行添加,或者可以进行一点修改放到定时器里也可以。这里只把代码放出来供大家参考学习,有错误也请各位大佬指出,一起学习。

原理图:

使用的是立创EDA专业版,单片机使用的是stm32C8T6,OLED和HX177可以忽略,主要还是看矩阵键盘。左下角哪个8P的排针是留给矩阵键盘模块用的,我为了以防万一预留的,他跟键盘主体其实是一个东西,可以不用管。

这里简述一下原理,矩阵键盘原理就是动态扫描,Key0-3配置为输出,Key4-7配置成上拉输入,一定要是上拉输入。

首先Key3输出低电平(扫描哪一行就把那一行拉低),Key0-2输出高电平,记为0x07(行);然后检测Key4-7的输入,举个栗子,假设SW1按下,因为Key3行我们输出低电平,同时1脚和3脚导通,所以Key4就被拉低了,读引脚就会读出低电平,而其他没有被按下的并没有导通,所以他们还是高电平,就可以记为0x07(列);同理,SW5、9、13按下时分别为0x0b(列)、0x0d(列)和0x0e(列),就可以判断出第一行是哪个按键被按下,要是都没被按下就是0x0f(列)。

然后同理扫描Key2、Key1、Key0行,分别是0x0b(行)、0x0d(行)和0x0e(行),再次重复判断哪一列被按下后就可以得到16个按键的情况了。

代码

key.c

#include "key.h"

uchar key_scan;		//键盘扫描的值

/*********
*
*函数:按键GPIO输出重写
*
*传参:key(0-3):按键号;		lev(0/1);电平高低
*
*例:Key_GpioOut(0, 1)	Key_0置高
*********/
void Key_GpioOut(uchar key, uchar lev)
{
	switch(key)
	{
		case 0:
			if(lev)	HAL_GPIO_WritePin(KEY_0_GPIO_Port, KEY_0_Pin, GPIO_PIN_SET);
			else		HAL_GPIO_WritePin(KEY_0_GPIO_Port, KEY_0_Pin, GPIO_PIN_RESET);
		break;
		
		case 1:
			if(lev)	HAL_GPIO_WritePin(KEY_1_GPIO_Port, KEY_1_Pin, GPIO_PIN_SET);
			else		HAL_GPIO_WritePin(KEY_1_GPIO_Port, KEY_1_Pin, GPIO_PIN_RESET);
		break;
		
		case 2:
			if(lev)	HAL_GPIO_WritePin(KEY_2_GPIO_Port, KEY_2_Pin, GPIO_PIN_SET);
			else		HAL_GPIO_WritePin(KEY_2_GPIO_Port, KEY_2_Pin, GPIO_PIN_RESET);
		break;
		
		case 3:
			if(lev)	HAL_GPIO_WritePin(KEY_3_GPIO_Port, KEY_3_Pin, GPIO_PIN_SET);
			else		HAL_GPIO_WritePin(KEY_3_GPIO_Port, KEY_3_Pin, GPIO_PIN_RESET);
		break;
	}
}

/*********
*
*函数:按键GPIO输出重写
*
*传参:key(0-3):按键号;
*
*传回参数:lev(0/1): 0:低		1:高
*
*例:Key_GpioIn(4)		读取Key_4的值
*********/
uchar Key_GpioIn(uchar key)
{
	uchar lev;		//防止warning
	switch(key)
	{
		case 4:
			if(HAL_GPIO_ReadPin(KEY_4_GPIO_Port, KEY_4_Pin) == GPIO_PIN_SET)	lev = 1;
			else		lev = 0;
		break;
		
		case 5:
			if(HAL_GPIO_ReadPin(KEY_5_GPIO_Port, KEY_5_Pin) == GPIO_PIN_SET)	lev = 1;
			else		lev = 0;
		break;
		
		case 6:
			if(HAL_GPIO_ReadPin(KEY_6_GPIO_Port, KEY_6_Pin) == GPIO_PIN_SET)	lev = 1;
			else		lev = 0;
		break;
		
		case 7:
			if(HAL_GPIO_ReadPin(KEY_7_GPIO_Port, KEY_7_Pin) == GPIO_PIN_SET)	lev = 1;
			else		lev = 0;
		break;
	}
	
	if(lev)	return 1;
	else		return 0;
}

/*********
*
*函数:按键输出
*
*传参:out(0x0a):方便按键扫描
*
*例:Key_Output(0x07)		Key_3置低,Key_0-2置高
*********/
void Key_Output(uchar out)
{
	uchar temp;
	for(uchar i=0; i<4; i++)
	{
		temp = out & (0x01<<i);
		temp = temp >> i;
		Key_GpioOut(i, temp);
	}
}

/*********
*
*函数:按键读取
*
*传参:out(0x0a):方便按键扫描
*
*例:Key_Output(0x07)		Key_3置低,Key_0-2置高
*********/
uchar Key_Input()
{
	uchar back = 0x00;
	
	back |= Key_GpioIn(4);
	
	for(uchar i=5; i<8; i++)
	{
		back = back << 1;
		back |= Key_GpioIn(i);
	}
	
	return back;
}

/*********
*
*函数:按键扫描
*
*传参:无
*********/
void Key_Scan()
{
	uchar temp;					//临时储存
	
	//扫描第一行---------------------------------
	Key_Output(0x07);		//Key_3置低
	
	if(Key_Input() != 0x0f)
	{
		temp = Key_Input();
		
		switch(temp)
		{
			case 0x07:	key_scan = 1;	break;
			case 0x0b:	key_scan = 2;	break;
			case 0x0d:	key_scan = 3;	break;
			case 0x0e:	key_scan = 4;	break;
		}
	}
	
	//扫描第二行---------------------------------
	Key_Output(0x0b);		//Key_2置低
	
	if(Key_Input() != 0x0f)
	{
		temp = Key_Input();
		
		switch(temp)
		{
			case 0x07:	key_scan = 5;	break;
			case 0x0b:	key_scan = 6;	break;
			case 0x0d:	key_scan = 7;	break;
			case 0x0e:	key_scan = 8;	break;
		}
	}
	
	//扫描第三行---------------------------------
	Key_Output(0x0d);		//Key_1置低
	
	if(Key_Input() != 0x0f)
	{
		temp = Key_Input();
		
		switch(temp)
		{
			case 0x07:	key_scan = 9;	break;
			case 0x0b:	key_scan = 10;	break;
			case 0x0d:	key_scan = 11;	break;
			case 0x0e:	key_scan = 12;	break;
		}
	}
	
	//扫描第四行---------------------------------
	Key_Output(0x0e);		//Key_1置低
	
	if(Key_Input() != 0x0f)
	{
		temp = Key_Input();
		
		switch(temp)
		{
			case 0x07:	key_scan = 13;	break;
			case 0x0b:	key_scan = 14;	break;
			case 0x0d:	key_scan = 15;	break;
			case 0x0e:	key_scan = 16;	break;
		}
	}
}

key.h

#ifndef _KEY_H
#define _KEY_H

#include "main.h"

#define KEY_0_Pin 				GPIO_PIN_15
#define KEY_0_GPIO_Port 	GPIOA

#define KEY_1_Pin 				GPIO_PIN_3
#define KEY_1_GPIO_Port 	GPIOB

#define KEY_2_Pin 				GPIO_PIN_4
#define KEY_2_GPIO_Port 	GPIOB

#define KEY_3_Pin 				GPIO_PIN_5
#define KEY_3_GPIO_Port 	GPIOB

#define KEY_4_Pin 				GPIO_PIN_6
#define KEY_4_GPIO_Port 	GPIOB

#define KEY_5_Pin 				GPIO_PIN_7
#define KEY_5_GPIO_Port 	GPIOB

#define KEY_6_Pin 				GPIO_PIN_8
#define KEY_6_GPIO_Port 	GPIOB

#define KEY_7_Pin 				GPIO_PIN_9
#define KEY_7_GPIO_Port 	GPIOB

void Key_GpioOut(uchar key, uchar lev);
uchar Key_GpioIn(uchar key);
void Key_Output(uchar out);
uchar Key_Input(void);
void Key_Scan(void);

#endif

同志们可以在key.h中修改管脚定义,但各位最好对照原理图来修改,行和列的顺序还是要看好的。

main.c

//记得添加头文件
#include "key.h" 

while (1)
  {
		Key_Scan();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

只需要在while(1)中添加扫描函数即可,键盘读取的值就是key.c前面的 key_scan 这个参数,用的时候记得加extern,还有记得添加key.h的头文件。

main.h

#define uint 	unsigned int
#define uchar unsigned char

我在main.h里面添加了两个定义,方便我自己敲代码。

演示视频

VID_20240614_160059

<think>嗯,用户想要查找4x4矩阵键盘原理图。首先,我需要回忆一下矩阵键盘的基本原理。根据引用[1]的内容,矩阵键盘通过行列的排列来减少IO口的使用,4x4的键盘需要8个GPIO,对吧?所以原理图应该涉及到行线列线的连接方式。 用户可能想了解如何具体连接这些行列到微控制器,比如STM32。这时候应该解释扫描方法,比如行扫描或者列扫描。比如,将行设置为输出,列设置为输入,然后逐行拉低,检测列的电平变化,从而确定按键位置。 另外,原理图可能包括上拉电阻,确保列线在空闲时保持高电平。还有二极管的使用,防止鬼影现象。这些都是原理图中的关键部分。 用户可能需要示意图,但这里只能文字描述。需要详细说明行列的交叉点放置按键,以及如何通过扫描确定按键。同时要提到消抖处理,硬件或软件的。可能还需要区分原理图实际PCB布局的不同,比如薄膜键盘机械键盘的区别。 最后,生成相关问题时要围绕原理、扫描方法、硬件设计、消抖应用场景,这样用户能进一步深入。</think>### 4x4矩阵键盘原理图解析 #### 1. **硬件连接原理** 4x4矩阵键盘通过8个GPIO(4行+4列)实现16个按键的检测。原理图的核心是**行线与列线交叉点布置按键**: - **行线(Row)**:连接到MCU的GPIO输出模式,初始设置为高电平(或低电平,具体取决于电路设计)。 - **列线(Column)**:连接到GPIO输入模式,通常通过上拉电阻保持高电平[^1]。 $$ \begin{array}{cccc} R1 & \rightarrow & (Key1) & \leftrightarrow & C1 \\ R2 & \rightarrow & (Key5) & \leftrightarrow & C1 \\ R3 & \rightarrow & (Key9) & \leftrightarrow & C1 \\ R4 & \rightarrow & (Key13) & \leftrightarrow & C1 \\ \end{array} $$ #### 2. **扫描方法** - **逐行扫描**: 1. 将某一行设为低电平,其余行保持高电平。 2. 读取列线电平:若某列为低电平,则对应交叉点的按键被按下。 3. 通过行列组合确定具体按键坐标。 #### 3. **典型原理图特性** - **上拉电阻**:列线通过上拉电阻连接至VCC,确保未按下时保持高电平。 - **防抖设计**:部分电路会加入电容或软件消抖逻辑。 - **二极管防串扰**:在按键两端串联二极管,避免多键同时按压时的信号冲突。 #### 4. **示例电路** ``` R1 R2 R3 R4 | | | | | | | | C1 --Key1-Key2-Key3-Key4 | | | | C2 --Key5-Key6-Key7-Key8 | | | | C3 --Key9-Key10Key11Key12 | | | | C4 --Key13Key14Key15Key16 ``` #### 5. **软件实现伪代码** ```python def scan_keypad(): for row in rows: set_row_low(row) for col in cols: if read_column(col) == LOW: return (row, col) set_row_high(row) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值