目录
一 前言
大家好呀,好久没更新博客了,最近因为买的一些硬件没有到就一直迟迟没有完成博客的攥写,接下来这段时间会给大家讲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 工作原理
每个按键连接在一行一列的交点上。
控制器(如stm32)先设置某一行输出低电平,其它行为高电平,按下的某个按键为低电平。
轮流扫描每一行,并读取每一列输入状态。
如果某一列被检测到为低电平,说明该行该列的按键被按下。
我们也会在写代码当中将行(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 即可获取」