目录
鉴于最近实训,需要写一份矩阵键盘的代码,看到网上大部分矩阵键盘不是很方便修改引脚,遂自己手敲了一份,可以随便修改引脚的代码方便移植。
不过我是直接把函数写在了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