本次实验采用4*4矩阵键盘进行应用,矩阵键盘为了节约引脚的占用,提供了多行多列的排列组合为矩形的按键,所以为矩阵按键或矩阵键盘。如图所示:
矩阵键盘
本实验通过stm32和矩阵按键实现点亮LED,以二进制的方式显示。如二进制的0000,对应四颗LED都熄灭;二进制的0001,对应第四颗LED点亮,其余三颗熄灭,以此类推;即1亮0灭。0.96寸OLED显示反馈的键值(以两个单位),如key1—01,key5----05,key11—11…;以按键响应对应的键值和二进制显示的LED。矩阵按键实现方法:1.响应对应中断的方法。2.行列扫描的方法。本实验主要是采用第二种方法,更容易理解。
原理图
行列扫描法:
矩阵按键扫描方式理解图如下:
解释说明:左边的填充以横向理解,右边的填充以竖向理解。
首先可以看到0x00ff,表示行列的按键均未接触,0x00fe 竖向1110表示第一行接触,那么等待第一行的第几列进行下一步接触,当第一行的第一列同时响应接触,那么表示第一行第一列对应的LED以二进制0001的方式进行显示,即点亮第四颗灯。
0x00fd 竖向1101表示第二行接触,那么等待第二行的第几列进行下一步接触,当第二行的第三列同时响应接触,那么表示第二行第三列对应的LED以二进制1101的方式进行显示,即点亮第一、二、四颗灯。
0x00fb 竖向1011表示第三行接触,那么等待第三行的第几列进行下一步接触,当第三行的第一、三列同时响应接触,那么表示第三行第一、三列对应的LED以二进制0101的方式进行显示,即点亮第二、四颗灯。
第四行同理可得。
keys.c
#include "stm32f10x.h"
#include "keys.h"
#include "delay.h"
//
//********************************************************************************
u8 FLAG = 0;
void KEY_4x4_Init(void) //IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = KEY_HANG; //行 0123
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(KEY_PROT, &GPIO_InitStructure);
GPIO_SetBits(KEY_PROT,KEY_HANG);
//init GPIOA 上拉输入
GPIO_InitStructure.GPIO_Pin = KEY1|KEY2|KEY3|KEY4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY_PROT, &GPIO_InitStructure);
GPIO_SetBits(KEY_PROT,KEY1|KEY2|KEY3|KEY4);
}
void KEY_Scan(u8 *key)
{
GPIO_Write(KEY_PROT,0x00fe);//第一行
if((KEY1_Input==RESET)||(KEY2_Input==RESET)||(KEY3_Input==RESET)||(KEY4_Input==RESET))
{
delay_ms(10);//去抖动
if(KEY1_Input==RESET)
{
FLAG = 1;
*key = 1;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY1));
}
else if(KEY2_Input==RESET)
{
FLAG = 1;
*key = 2;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY2));
}
else if(KEY3_Input==RESET)
{
FLAG = 1;
*key = 3;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY3));
}
else if(KEY4_Input==RESET)
{
FLAG = 1;
*key = 4;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY4));
}else
{
FLAG = 0;
GPIO_Write(KEY_PROT,0x00ff);
}
}
GPIO_Write(KEY_PROT,0x00fd);//第二行
if((KEY1_Input==RESET)||(KEY2_Input==RESET)||(KEY3_Input==RESET)||(KEY4_Input==RESET))
{
delay_ms(10);//去抖动
if(KEY1_Input==RESET)
{
FLAG = 1;
*key = 5;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY1));
}
else if(KEY2_Input==RESET)
{
FLAG = 1;
*key = 6;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY2));
}
else if(KEY3_Input==RESET)
{
FLAG = 1;
*key = 7;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY3));
}
else if(KEY4_Input==RESET)
{
FLAG = 1;
*key = 8;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY4));
}else
{
FLAG = 0;
GPIO_Write(KEY_PROT,0x00ff);
}
}
GPIO_Write(KEY_PROT,0x00fb);//第三行
if((KEY1_Input==RESET)||(KEY2_Input==RESET)||(KEY3_Input==RESET)||(KEY4_Input==RESET))
{
delay_ms(10);//去抖动
if(KEY1_Input==RESET)
{
FLAG = 1;
*key = 9;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY1));
}
else if(KEY2_Input==RESET)
{
FLAG = 1;
*key = 10;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY2));
}
else if(KEY3_Input==RESET)
{
FLAG = 1;
*key = 11;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY3));
}
else if(KEY4_Input==RESET)
{
FLAG = 1;
*key = 12;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY4));
}else
{
FLAG = 0;
GPIO_Write(KEY_PROT,0x00ff);
}
}
GPIO_Write(KEY_PROT,0x00f7);//第四行
if((KEY1_Input==RESET)||(KEY2_Input==RESET)||(KEY3_Input==RESET)||(KEY4_Input==RESET))
{
delay_ms(10);//去抖动
if(KEY1_Input==RESET)
{
FLAG = 1;
*key = 13;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY1));
}
else if(KEY2_Input==RESET)
{
FLAG = 1;
*key = 14;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY2));
}
else if(KEY3_Input==RESET)
{
FLAG = 1;
*key = 15;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY3));
}
else if(KEY4_Input==RESET)
{
FLAG = 1;
*key = 16;
while(!GPIO_ReadInputDataBit(KEY_PROT,KEY4));
}else
{
FLAG = 0;
GPIO_Write(KEY_PROT,0x00ff);
}
}
}
keys.h
#ifndef __KEYS_H
#define __KEYS_H
//
//********************************************************************************
//V1.1修改说明
//修改按键扫描函数,使整个代码可以支持SWD下载。
//
#include "stm32f10x.h"
#define KEY_HANG 0x000f //0123
#define KEY1 GPIO_Pin_4
#define KEY2 GPIO_Pin_5
#define KEY3 GPIO_Pin_6
#define KEY4 GPIO_Pin_7
#define KEY1_Input GPIO_ReadInputDataBit(KEY_PROT,KEY1)
#define KEY2_Input GPIO_ReadInputDataBit(KEY_PROT,KEY2)
#define KEY3_Input GPIO_ReadInputDataBit(KEY_PROT,KEY3)
#define KEY4_Input GPIO_ReadInputDataBit(KEY_PROT,KEY4)
#define KEY_PROT GPIOA
extern u8 FLAG;
void KEY_4x4_Init(void);//IO初始化
//u8 KEY_Scan(void);
void KEY_Scan(u8 *key); //按键扫描函数
#endif
main.c
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "keys.h"
//#include "usart.h"
#include "OLED.h"
/*接线说明
PA0~PA3-------R4~R1 //行
PA4~PA7-------C4~C1 //列
*/
u8 key = 0;
u8 i;
int main(void)
{
LED_Init();
delay_init();
KEY_4x4_Init();
//uart_init(115200);
OLED_Init();
/*OLED初始化界面*/
OLED_ShowChar(1, 1, 'A');
OLED_ShowString(1, 3, "HelloWorld!");
OLED_ShowNum(2, 1, 12345, 5);
OLED_ShowSignedNum(2, 7, -66, 2);
OLED_ShowHexNum(3, 1, 0xAA55, 4);
OLED_ShowBinNum(4, 1, 0xAA55, 16);
while(1)
{
KEY_Scan(&key); //获取键值key
if(FLAG == 1) //按键按下
{
FLAG = 0;
OLED_Clear();
OLED_ShowNum(2, 1, key, 2); //显示键值key
//Printf("KEY = %d\r\n",key);
if(key==1) //0001
{
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14);
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
if(key==2) //0010
{
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_14);
}
if(key==3) //0011
{
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13);
GPIO_ResetBits(GPIOB,GPIO_Pin_14|GPIO_Pin_15);
}
if(key==4) //0100
{
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_14|GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_13);
}
if(key==5) //0101
{
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_14);
GPIO_ResetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_15);
}
if(key==6) //0110
{
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14);
}
if(key==7) //0111
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
GPIO_ResetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
}
if(key==8) //1000
{
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
if(key==9) //1001
{
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14);
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_15);
}
if(key==10) //1010
{
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_14);
}
if(key==11) //1011
{
GPIO_SetBits(GPIOB,GPIO_Pin_13);
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_14|GPIO_Pin_15);
}
if(key==12) //1100
{
GPIO_SetBits(GPIOB,GPIO_Pin_14|GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13);
}
if(key==13) //1101
{
GPIO_SetBits(GPIOB,GPIO_Pin_14);
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_15);
}
if(key==14) //1110
{
GPIO_SetBits(GPIOB,GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14);
}
if(key==15) //1111
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
}
if(key==16) //来回闪烁4次
{
for(i=0;i<4;i++)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13);
GPIO_SetBits(GPIOB,GPIO_Pin_14|GPIO_Pin_15);
delay_ms(1000);
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13);
GPIO_ResetBits(GPIOB,GPIO_Pin_14|GPIO_Pin_15);
delay_ms(1000);
}
}
//LCD12864_Write_Number(0x8d,key);
}
}
}
矩阵按键实验效果:
矩阵按键
总结:
根据以上内容,学习矩阵按键,跟轻触按键类似还是容易理解的,给输入信号给stm32以驱动LED相应点亮或熄灭。矩阵按键主要是应用需求多数按键解决引脚少的情况。
总结调试代码或硬件中,针对可能出现的问题,说明一下:
(1)出现按键响应慢的情况:因为矩阵按键模块的触发所用的为行列扫描,比中断触发响应的速度较慢;代码部分按键扫描延迟过高,或者去抖动(10ms~20ms)。
(2)出现按键硬件无响应的情况:可能是由于键盘模块引脚与stm32引脚接线错误或接反;也可能是对应引脚冲突占用,需要调用AFIO端口复用转换为普通I/O,也许即可解决;还可能是硬件坏了的问题,或者铜暴露出来导致的短路。
扩展: 可通过矩阵键盘模块可以做简易计算器、密码锁、功能键等实验。那么就分享到这,大胆尝试实践探索吧,欢迎讨论。
今日就分享到此了,要有信心一步步积累理论和实践经验,一起学习加油!
链接: 参考文献
链接: 参考视频
链接: 矩阵按键视频分享
链接: 源码例程
提取码:3232