4×4矩阵键盘详解(stm32)

发布于:2025-07-22 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

一 前言

二 概述

2.1 硬件准备

2.2 硬件连接

2.3 实验目标

三 矩阵键盘

3.1 矩阵键盘介绍

3.2 工作原理

3.3 keyboard.c

3.4 keyboard.h

四 实现目标(main.c)

五 实现效果


一 前言

大家好呀,好久没更新博客了,最近因为买的一些硬件没有到就一直迟迟没有完成博客的攥写,接下来这段时间会给大家讲stm32的矩阵键盘、由用蓝牙实现舵机云台的转动、电机步进电机等等。一些关于stm32的拓展模块,也会列入在stm32专题的介绍。

我已为大家整理好之后讲的轮询法和中断法的代码,如下,大家自行下载运行即可


【超级会员V4】通过百度网盘分享的文件:轮询法.zip等2个文件
链接:https://pan.baidu.com/s/1Jp2InrAQOFNvuQ51qzYHag 
提取码:42z5
复制这段内容打开「百度网盘APP 即可获取」


二 概述

2.1 硬件准备

 

 我们需要准备一个4*4的矩阵键盘、OLED显示屏幕、stm32f103c8t6的最小开发板以及几根杜邦线

2.2 硬件连接

(1) 矩阵键盘

矩阵键盘引脚 stm32引脚
R1 PA0
R2 PA1
R3 PA2
R4 PA3
C1 PA4
C2 PA5
C3 PA6
C4 PA7

 (2)OLED屏幕

OLED stm32引脚
GND GND
VCC 5V
SCL PB6
SDA PB7

 请大家先将硬件的设备连接好,随后我们将会进行矩阵键盘的讲解,最后再落实到项目中

2.3 实验目标

我们本次将会完成一篇关于矩阵键盘的一个实验,我们要通过oled显示输入的密码,将密码设置成‘1234’

  • 如果输入密码正确,则在oled屏幕上显示“PassWord Right”;
  • 如果输入密码错误,则在oled屏幕上显示“PassWord Error”;
  • 如果输入密码不是四位,则在oled上显示“Input 4Num”

三 矩阵键盘

3.1 矩阵键盘介绍

矩阵键盘是一种常用的多按键输入设备,通常由多个按键按照行(Row)和列(Column)排布形成一个二维网格结构。最常见的是 4×4(16键)4×3(12键),这种行列式键盘结构能够有效地提高单片机中I/O口的利用率,节约单片机的资源。我们这里讲解16键的(8个引脚控制16个按键),12键和16键的大同小异,大家可以自行写12键的代码。

3.2 工作原理

  1. 每个按键连接在一行一列的交点上。

  2. 控制器(如stm32)先设置某一行输出低电平,其它行为高电平,按下的某个按键为低电平。

  3. 轮流扫描每一行,并读取每一列输入状态

  4. 如果某一列被检测到为低电平,说明该行该列的按键被按下。

我们也会在写代码当中将行(R1~4)设置为GPIO的输出模式,将列(C1~4)设置为GPIO的输入模式。

这里在延申一点,大家想一想,不按按键是高电平,按下去是低电平,所以我们在配置输出模式的时候,我们要选择推挽输出(GPIO_Mode_Out_PP),因为推挽输出既可以输出高电平也可以输出低电平。那每一列设置输入模式的时候,该选哪个模式呢?我们不按按键,则是高电平,用上拉输入主要是为了让输入端口有一个确定的高电平状态,避免悬空和干扰,提高可靠性,我们会默认将每一列输入模式设置为上拉输入模式(GPIO_Mode_IPU)。

 3.3使用方法

在写代码的时候,我们会有两种方式来写代码,分别为:轮询法、中断法。

实现方式 工作原理 优点 缺点 适用场景
轮询法 主动在主循环中不断扫描每一行,每列读取按键状态 代码简单,易于理解和调试,适合入门和学习 CPU占用高,效率较低,实时性一般 初学项目、对实时性要求不高的应用、教学演示
中断法 利用GPIO中断,当有按键按下时触发中断,进入中断服务函数内判断具体按键 响应快,节省CPU资源,适合多任务或省电场景 代码略复杂,需处理消抖和多键问题 对实时性有要求的项目、低功耗系统、正式产品开发

关于上述两种方法,我都会给大家总结好,大家进入网盘中自行领取即可~  因为这个矩阵键盘很多时候都会用到,所以大家可以直接拿走代码直接用,相关代码原理我也会给大家讲解。我们这篇文章主要是针对于初学者,会着重讲轮询法。接下来我会给大家分板块讲解矩阵键盘的代码部分,关于本项目的轮询法和中断法,大家直接进入百度网盘中直接调用源项目即可~

3.3 keyboard.c

(1)头文件部分

#include "stm32f10x.h"
#include "delay.h"  
#include "keyboard.h"


//4×4矩阵键盘
const char key_map[4][4] = {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'}
};

(2)键盘初始化

// 初始化 PA0~PA7
void Keyboard_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // PA0~PA3 行:推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // PA4~PA7 列:上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 默认将所有行拉高
    GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3);
}

 前面这两部分还是很好理解的 大家结合代码中的注释看一看就行,下面这个返回按键值部分,不太好理解,我给大家着重讲接下来这部分。

(3)返回按键值

// 扫描键盘,返回对应字符,无按键返回0
char Keyboard_Scan(void)
{
    uint8_t row, col;

    for (row = 0; row < 4; row++)
    {
        // 先将所有行拉高
        GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3);
        // 选定当前行拉低
        GPIO_ResetBits(GPIOA, (1 << row));

        delay_ms(5); // 消抖 + 等待稳定

        // 读取列状态
        for (col = 0; col < 4; col++)
        {
            if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4 << col) == 0)
            {
                while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4 << col) == 0);  // 等待松手
                return key_map[row][col];
            }
        }
    }

    return 0; // 无按键按下
}

 请大家跟紧接下来的讲解

char Keyboard_Scan(void)
{
    uint8_t row, col;

    for (row = 0; row < 4; row++)
    {
        for (col = 0; col < 4; col++)
        {
            //... ...
        }
    }
    return 0;
}

这是这个函数的外围框架,两个for 循环遍历每一行、每一列,row, col:分别用于行和列的索引。进行“逐行扫描”如果没检测到任何按键,最后返回 0(表示无按键按下)。

if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4 << col) == 0)
{
     while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4 << col) == 0);  // 等待松手
     return key_map[row][col];
}

关于上面这几行代码,大家可能就不懂了,GPIOA, GPIO_Pin_4 << col这个玩意又是什么呢?为啥要左移col(列)呢?接下来给大家讲一下:

在代码中的gpio.h中提供着gpio引脚如下的代码:

我们可以清晰的看到,PIN4被宏定义为了0x0010,PIN5被宏定义为了0x0020,PIN6被宏定义为了0x0040,PIN7被宏定义为了0x0080。GPIO_PIN_4在 STM32 标准库中是一个“位掩码”,值是 0x0010(也就是二进制 0001 0000),对应 PA4 这个引脚。

  • col取 0, 1, 2, 3,代表第几列。

  • GPIO_PIN_4 << col就会依次变成:

    • col = 0:GPIO_PIN_4 << 0 = GPIO_PIN_4 = 0x0010(PA4)

    • col = 1:GPIO_PIN_4 << 1 = 0x0020(PA5)

    • col = 2:0x0040(PA6)

    • col = 3:0x0080(PA7)

其中col = 0就是硬件上的C1这一列,col = 3就是硬件的上的C4这一列,只不过在最上面定义的二位数组,计算机识别是从0开始计数的。

 这个地方也是我认为轮询法很有意思的一个点,他告诉了我们GPIO_PIN_几只不过是一个位掩码。本质都不过是0和1这两个数构成的。


链接:https://pan.baidu.com/s/1Jp2InrAQOFNvuQ51qzYHag 
提取码:42z5


3.4 keyboard.h

#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_

#include "stm32f10x.h"

void Keyboard_Init(void);
char Keyboard_Scan(void);



#endif

在.h中,只需要将相应的头文件给加进来即可。

那么以上就已经完成了矩阵键盘的“轮询法”底层代码编写了,关于中断法请见我给大家提供的资料中。

四 实现目标(main.c)

通过oled显示输入的密码,将密码设置成‘1234’

如果输入密码正确,则在oled屏幕上显示“PassWord Right”;

如果输入密码错误,则在oled屏幕上显示“PassWord Error”;

如果输入密码不是四位,则在oled上显示“Input 4Num”

(1)头文件

#include "stm32f10x.h"
#include "oled.h"
#include "keyboard.h"
#include "usart.h"
#include "spi.h"
#include "delay.h"
#include "stdio.h"
#include "string.h"


//全局变量
char Key_Val;
char input_code[7] = {0};	//存放输入的密码
uint8_t input_index = 0;	//数组的下角标

(2)主函数

int main(void)
{
	oled_gpio_init();
	oled_init();
	Keyboard_Init();
	
	oled_fill(0xFF);
	delay_ms(1000);
	oled_fill(0x00);
	delay_ms(1000);	
	
	while (1)
	{
		//显示“Input Password”
		oled_show_string(0, 0, "Input Password:", 16);

		Key_Val = Keyboard_Scan();

		if (Key_Val)
		{
			//进行输入,四位密码
			if (Key_Val >= '0' && Key_Val <= '9')
			{
			    if (input_index < 6)
			    {
					input_code[input_index++] = Key_Val;
					oled_show_char(10 * input_index, 5, Key_Val, 16);  // 显示输入
				}
			}
						
        /*
        如果按下了#,要进行密码比对,从而清零
        */
		else if (Key_Val == '#')
		{
			input_code[input_index] = '\0';  // 补字符串结束符

			if (input_index == 4)
			{
				    if (strcmp(input_code, "1234") == 0)
			    	{
			    		oled_fill(0x00);
			    		oled_show_string(10, 3, "PassWord Right ", 16);
			    		delay_ms(1000);

			    	}
			    	else
			    	{
			    		oled_fill(0x00);
			    		oled_show_string(10, 3, "PassWord Error ", 16);
			    		delay_ms(1000);
			    	}
			    }
			    else
			    {
			    	oled_show_string(10, 3, "Input 4Num", 16);
		    		delay_ms(1000);

		    	}

			    input_index = 0;	//下角标也要重新赋值成0
		    	memset(input_code, 0, sizeof(input_code));
            //memset函数:把 input_code 这整个数组里的所有元素都设置为零,即清空了输入缓存
		        delay_ms(1000);
		       	oled_fill(0x00);
		    	delay_ms(500);
		    }
						
		    //如果按了*,则清零,在屏幕上显示Input Password Please Again!
	    	else if (Key_Val == '*')
		    {
			    input_index = 0;
			    memset(input_code, 0, sizeof(input_code));
			    oled_fill(0x00);
			    oled_show_string(0, 0, "Input Password", 16);
			    oled_show_string(3, 3, "Please Again!", 16);
							
		    }
	    }
    }
}

当然,我有个部分写的不是很好,我们可以把密码存放到ROM里面,也就是用到了原先我写的SPI应用W25Q64,可以把密码存放到W25Q64中。这次我们主要是学习关于矩阵键盘的内容,我尽量不给大家添加学习的负担,我会在未来我写单片机项目专栏中,进行多个知识点整合时,会讲解如何将密码或者数据存放到W25Q64当中。(狗头)

五 实现效果

最后实现的一些效果如下:

欢迎大家在评论区发表观点~


【超级会员V4】通过百度网盘分享的文件:轮询法.zip等2个文件
链接:https://pan.baidu.com/s/1Jp2InrAQOFNvuQ51qzYHag 
提取码:42z5
复制这段内容打开「百度网盘APP 即可获取」


网站公告

今日签到

点亮在社区的每一天
去签到