矩阵键盘简介
- 什么是矩阵键盘
矩阵键盘是单片机外部设备中所使用的排布类似于矩阵的键盘组,由于电路设计时需要更多的外部输入,单独的控制一个按键需要浪费很多的IO资源,所以就有了矩阵键盘,常用的矩阵键盘有4X4和8X8,其中用的最多的是4X4。 - 矩阵键盘的原理
矩阵键盘又称为行列式键盘,它是用4条I/O线作为行线,4条I/O线作为列线组成的键盘。
在行线和列线的每一个交叉点上,设置一个按键。这样键盘中按键的个数是4×4个。
这种行列式键盘结构能够有效地提高单片机系统中I/O口的利用率。由于单片机IO端口具有线与的功能,因此当任意一个按键按下时,行和列都有一根线被线与,通过运算就可以得出按键的坐标从而判断按键键值。
矩阵键盘扫描原理
- 行扫描的原理:因为如果有按键按下的话,某一个输入的引脚就会跟对应的输出引脚连接,因为输出为高电平,所以对应的输入引脚会被拉高,读取引脚的状态,判断哪个引脚被拉高就可以知道哪一行有按键按下了;总的来说是通过高四位输出高电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一行按键被按下。
- 再通过列操作模块:
#define KEY_CLO0_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET)
#define KEY_CLO1_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_RESET)
#define KEY_CLO2_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET)
#define KEY_CLO3_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET)
#define KEY_CLO0_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_SET)
#define KEY_CLO1_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_SET)
#define KEY_CLO2_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_SET)
#define KEY_CLO3_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_SET)
来进行列扫描。
端口的配置
介绍完矩阵键盘的扫描原理了我们该通过keli来实现我们想要的功能啦,首先我们要进行的就是对我们所需要的端口进行配置,如下:
//端口的配置
void key_init(){
GPIO_InitTypeDef GPIO_InitStruture;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //打开PB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); //打开PE时钟
//定义PB12、PB13、PB14、PB15为推挽输出
GPIO_InitStruture.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruture.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruture);
//定义PD8、PD9、PD10、PD11为上拉输入 分别定义为四行
GPIO_InitStruture.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruture.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11;
GPIO_Init(GPIOD,&GPIO_InitStruture);
}
相关函数的编写
配置完我们所需要的端口,接下来我们就应该编写相关函数啦。
- 行扫描函数:
//如果为1,代表没有按键被按下,如果为0,代表有按键被按下
char KEY_ROW_SCAN(void)
{
//读出行扫描状态
Key_row[0] = GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_8)<<3;
Key_row[0] = Key_row[0] | (GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_9)<<2);
Key_row[0] = Key_row[0] | (GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_10)<<1);
Key_row[0] = Key_row[0] | (GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_11));
if(Key_row[0] != 0x0f) //不是1111,代表肯定有一个0行
{
delay_ms(10); //消抖
if(Key_row[0] != 0x0f)
//0111 1011 1101 1110
{
//printf("Key_Row_DATA = 0x%x\r\n",Key_row[0]);
switch(Key_row[0])
{
case 0x07: //0111 判断为该列第1行的按键按下
return 1;
case 0x0b: //1011 判断为该列第2行的按键按下
return 2;
case 0x0d: //1101 判断为该列第3行的按键按下
return 3;
case 0x0e: //1110 判断为该列第4行的按键按下
return 4;
default :
return 0;
}
}
else return 0;
}
else return 0;
}
- 按键扫描函数:
char KEY_SCAN(void)
{
char Key_Num=0; //1-16对应的按键数
char key_row_num=0; //行扫描结果记录
KEY_CLO0_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0); //消抖
Key_Num = 0 + key_row_num;
}
KEY_CLO0_OUT_HIGH;
KEY_CLO1_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0);
Key_Num = 4 + key_row_num;
}
KEY_CLO1_OUT_HIGH;
KEY_CLO2_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0);
Key_Num = 8 + key_row_num;
}
KEY_CLO2_OUT_HIGH;
KEY_CLO3_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0);
Key_Num = 12 + key_row_num;
}
KEY_CLO3_OUT_HIGH;
return Key_Num;
}
主函数与其他
- 主函数:
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "key16.h"
#include "stdio.h"
#include "usart.h"
int main(void)
{
vu8 key=0;
char key_confirm;
led_init();
delay_init();
key_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200);
while(1)
{
key_confirm = KEY_SCAN();
if(key_confirm>0&&key_confirm<17){
printf("Key_NUM = %d \r\n",key_confirm); //按下1-16个按键的操作
printf("= = = = = = = = = = = \r\n");
}
}
}
- key.c:
#include "key16.h"
#include "delay.h"
uint8_t Key_row[1]={0xff}; //定义一个数组,存放行扫描状态
//端口的配置
void key_init(){
GPIO_InitTypeDef GPIO_InitStruture;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //打开PB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); //打开PE时钟
//定义PB12、PB13、PB14、PB15为推挽输出
GPIO_InitStruture.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruture.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruture);
//定义PD8、PD9、PD10、PD11为上拉输入 分别定义为四行
GPIO_InitStruture.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruture.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11;
GPIO_Init(GPIOD,&GPIO_InitStruture);
}
/***
*函数名:KEY_ROW_SCAN
*功 能:按键行扫描
*返回值:1~4,对应1~4行按键位置
*/
//如果为1,代表没有按键被按下,如果为0,代表有按键被按下
char KEY_ROW_SCAN(void)
{
//读出行扫描状态
Key_row[0] = GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_8)<<3;
Key_row[0] = Key_row[0] | (GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_9)<<2);
Key_row[0] = Key_row[0] | (GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_10)<<1);
Key_row[0] = Key_row[0] | (GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_11));
if(Key_row[0] != 0x0f) //不是1111,代表肯定有一个0行
{
delay_ms(10); //消抖
if(Key_row[0] != 0x0f)
//0111 1011 1101 1110
{
//printf("Key_Row_DATA = 0x%x\r\n",Key_row[0]);
switch(Key_row[0])
{
case 0x07: //0111 判断为该列第1行的按键按下
return 1;
case 0x0b: //1011 判断为该列第2行的按键按下
return 2;
case 0x0d: //1101 判断为该列第3行的按键按下
return 3;
case 0x0e: //1110 判断为该列第4行的按键按下
return 4;
default :
return 0;
}
}
else return 0;
}
else return 0;
}
/***
*函数名:KEY_SCAN
*功 能:4*4按键扫描
*返回值:0~16,对应16个按键
*/
char KEY_SCAN(void)
{
char Key_Num=0; //1-16对应的按键数
char key_row_num=0; //行扫描结果记录
KEY_CLO0_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0); //消抖
Key_Num = 0 + key_row_num;
}
KEY_CLO0_OUT_HIGH;
KEY_CLO1_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0);
Key_Num = 4 + key_row_num;
//printf("Key_Clo_2\r\n");
}
KEY_CLO1_OUT_HIGH;
KEY_CLO2_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0);
Key_Num = 8 + key_row_num;
//printf("Key_Clo_3\r\n");
}
KEY_CLO2_OUT_HIGH;
KEY_CLO3_OUT_LOW;
if( (key_row_num=KEY_ROW_SCAN()) != 0 )
{
while(KEY_ROW_SCAN() != 0);
Key_Num = 12 + key_row_num;
}
KEY_CLO3_OUT_HIGH;
return Key_Num;
}
- key.h:
#ifndef _KEY16_H
#define _KEY16_H
#include "sys.h"
#include "stm32f10x.h"
#include <string.h>
void key_init();
char KEY_SCAN(void);
char KEY_ROW_SCAN(void);
void HW_KEY_FUNCTION(void);
#define KEY_CLO0_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET)
#define KEY_CLO1_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_RESET)
#define KEY_CLO2_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET)
#define KEY_CLO3_OUT_LOW GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET)
#define KEY_CLO0_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_SET)
#define KEY_CLO1_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_SET)
#define KEY_CLO2_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_SET)
#define KEY_CLO3_OUT_HIGH GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_SET)
#endif
串口的接线与矩阵键盘的接线
- 首先我们先接串口,把串口的VCC和GND分别接到主板上的3.3V和GND上(注意要把串口上的插线帽调整到3.3V,不然容易把板子烧坏,板子烧坏了那可就成大xx了),然后再把串口上的TXD和RXD分别连到主板的RXD和TXD。
- 然后我们来接矩阵键盘,要用到八根线,R1 ~ R4分别连到主板上的PD8 ~ PD11,C1 ~ C4分别连到主板上的PB12 ~ PB15.
load与运行调试
好啦,所有准备工作都完成啦!!又到了我们激动人心的时刻啦!!
运行成功图如下:
over。