一 .介绍
端口PC分配给矩阵键盘,行线连接PC4至PC7,列线连接PC0至PC3。 还有一些 节省GPIO的方法,比如采用“并入串出”类芯片进行电路调整,把键盘行列情况变成串行数字脉冲信号,再由GPI0取回串行数据经过移位后得到并行数据,或者采用专用的键盘扩展芯片扩
展按键资源.
二. 线反转式键盘扫描(也有其他办法)
1.配置PC高4位为推挽输出,低4位为上拉输入,让4条行线全部输出低电平且4条列线由于上拉作用都保持高电平。
2.假设这时候没有按键按下,那么读取PC端口的值肯定应该是(0x0F) B,若读回的值与预想值不相等,那么可以肯定是有按键被按下了。
3.当按键按下时4条列线上就不再是高电平了(应该有1个位变为低电平),若端口值为(0x0E) H则第0列按下,若为(0x0D) H则第1列按下,若为(0x0B) H则第2列按下,若为(0x07) H则第3列按下,这样一来我们就先得到 了按键按下时的列值。文章来源:https://www.toymoban.com/news/detail-541501.html
三.代码案例
下面的代码是我查的不具备可行性,但是有思想其实可以看看的,也有一些小错误(可以忽略)文章来源地址https://www.toymoban.com/news/detail-541501.html
#include "main.h"
#include "Keypad.h"
/**
* @brief KeyPad Init port Group of ports
* @param pin Group of pins
*/
KeyPad_Init(GPIO_TypeDef* Port,unsigned long int Pin)
{
GPIO_InitTypeDef GPIO_InitStruct;
uint16_t i;
RCC_AHB1PeriphClockCmd(RCC_GPIOE, ENABLE);//使能GPIOE时钟
GPIO_InitStruct.GPIO GPIO_Mode_IN; //设置为输入端口
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//设置为推挽
GPIO_GPIO_Speed = GPIO_Speed_100MHz; //设置100M时钟
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //拉
/*初始化第一排*/
for(i = 4; i < 8; i++)
GPIO_InitStruct.GPIO_Pin = Pin;
GPIO_Init(Port, &GPIO_InitStruct);
Pin = Pin <<1;
}
/*CC_AHB1PeriphClockCmd(RCC_AHB1Per, 0;*/
for(i = 4; i < 8; i++){
GPIO_InitStruct.GPIO_Pin = Pin;
GPIO_Init(Port, &GPIO_InitStruct);
Pin = Pin << 1
}
/**
* @brief Get KeyPad Num
* @param none
* @retval key num
*/
uint8_t Get_KeyPad_Num(void)
{
uint8_t i;
uint8_t KeyVal=0;
GPIOE->ODR&=~0xf << 4; //PE4-PE7 输出低
GPIOD->ODR|=0xf << 4; //PD4-PD7 输出高
while(1)
{
for(int i=4;i<8; i++ )
{
if(GPIO_ReadInputDataBit(GPIOD,GPIO_4<<i)==RESET)
{
while(ReadInputDataBit(GPIOD,GPIO_Pin_4<<i)ET);
KeyVal=(4<<4)|i;
}
if (GPIO_ReadInputGPIOE,GPIO_Pin_4<<i)==RESET) //键盘低
{
while(GPIO_ReadInputDataBit(GP,GPIO_Pin_4<<i)==RESET);
KeyVal=i;
}
if(KeyVal!=0) return KeyVal; //有按键按下时返回按键值
}
}
}
1. 首先定义键值:
```c
const Key_Type Key_Value[ROW][COL] = { { '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', },
{ '*', '0', '#', 'D' } };
```
2. 用查询表的思想来实现扫描,同时用4个数组变量来接收列值,两个状态变量(一个列状态,一个行状态):
```c
u8 key_num = 0; //按键数量变量
u8 key_row = 0; //存放该按键的行号
u8 key_status = 0;//按键的状态
u8 key_data[COL]; //存放按键的查询表
```
3. 扫描过程:
```c
void Scan_Key(void)
{
u8 key_time = 0; //判断按下按键时间
//列输出,放低对应行
GPIO_ResetBits(GPIOE, GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5);
GPIO_SetBits(GPIOG, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);
//获取矩阵键盘状态,存放到key_data[4]中
for(u8 i=0; i<4; i++)
{
key_data[i]=((~GPIOE->IDR)>>2 | (0xf0)); //引脚PE2-PE5/PG1-PG4读取状态
GPIO_ResetBits(GPIOG, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);
GPIO_SetBits(GPIOE, GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5);
delay_us(2);
}
//检查4行4列查询表中的键值根据查询表附加状态
for(u8 i=0; i<ROW; i++)
{
for(u8 j=0; j<COL; j++)
{
if(key_data[j] & (0x01<<i))
{
if(!key_status) //如果没有按键按下,判断按键状态
{
key_status = 1; //记录按键状态
key_num = Key_Value[i][j]; //记录按键值
key_row = i; //记录按下的按键所属行数
key_time = 0; //按下按键时间
}
else if(i == key_row) //检查是否是按下的同一按键
{
key_time++; //如果是按键时间加1
if(key_time > 20) //如果按键时间大于20,按键时间清0_time = 0;
Key_Handler(key_num); //按键处理函数
}
break;
}
}
}
if(!key_data[j] | (0x01<<i))
{
key_status = 0; //没有按键按下,记录按键状态为没有按键按下
}
}
}
```
4. 消抖函数:
```c
void Key_Handler(u8 Key_Value)
{
static unsigned char key_buf=0; //用来进行消抖的缓存区
if( Key_Value != key_buf ) //如果两次输入按键不一样,把新输入的值存入缓存区
{
key_buf=Key_Value;
if(Key_Value != 0XFF) //如果按键不是 非法数值,执行逻
switch(Key_Value)
{
//执行用户定义的操作
}
}
}
}
```
uint8_t key_scan(void)
{
uint8_t keys,keyVal=0xFF;
uint8_t keyrow,keycol;
GPIO_InitTypeDef gpio;
gpio.GPIO_Mode = GPIO_Mode_OUT;
/*************** COL ******************/
gpio.GP_12IO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;//推挽输出键列
GPIO_Init(GPIOD, &gpio);
GPIO_SetBits(GPIOD,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //把列线高电平
/*************** ROW ******************/
gpio.GPIO_Mode = GPIO_Mode_IN;
gpio.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;//浮空输入IOE, &gpio);
/****************start Key scan******************/
for(keyrow=0;keyrow<4;keyrow++){
switch (keyrow){
case 0:GPIO_ResetBits(GPIOD, GPIO_Pin_12); break; //拉低行线
case 1:GPIO_ResetBits(GPIOD, GPIO_Pin_13); break;
case 2:GPIO_ResetBits(GPIOD, GPIO_Pin_14); break;
case 3:GPIO_ResetBits(GPIOD, GPIO_Pin_15);ResetBits(GPIOD, GPIO_Pin_12); break;
}
/***************** colum Scan *********************/
for(keycol=0;keycol<4;keycol++){
switch (keycol){
case 0:keys=GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_0); break; //获取列口
case 1:keys=GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_1); break;
case 2:keys=GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_2); break;
case 3:keys=GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3); break;
default:keys=GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_0); break;
}
if(!keys) keyVal = keyrow*4+keycol ;
}
GPIO_Set12|GPIO_Pin//拉高 } **************** 这里错的 少了
return keys
}
void Matrix_ssKey_Pin_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
int Matrix_Key_Scan(void)
{
u8 temp = 0;
int key_val = -1;
GPIO_ResetBits(GPIOB,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11); //拉低行线
delay_us(10);
temp=(GPIO_ReadInputData(GPIOB) >> 8)&0xff;
//没有按键按下时扫描
if (temp == 0xf0)
{
delay_ms(50);
GPIO_ResetBits(GPIOB,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11); //拉低行线
delay_us(10);
temp=(GPIO_ReadInputData(GPIOB) >> 8)&0xff;
if (temp != 0xf0) //按键按下时,对键值进行赋值
{
//第一行
GPIO_Write(GPIOB,0);
delay_ms(5);
GPIO_Write(GPIOB,(uint16_t)(0xFE << 8));
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0) != 0XF0)
{
delay_ms(20);//消抖
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOB) >> 8) & 0XFE); //对列进行扫描
switch(temp)
{
case 0xEE: key_val = 1; break;
case 0xDE: key_val = 2; break;
case 0xBE: key_val = 3; break;
case 0x7E: key_val = 4; break;
default: key_val = -1; break;
}
}
}
//第二行
GPIO_Write(GPIOB,0);
delay_ms(5);
GPIO_Write(GPIOB,(uint16_t)(0xFD << 8));
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0)!= 0XF0)
{
delay_ms(20);
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOB) >> 8) & 0XFD);
switch(temp)
{
case 0xED: key_val = 5; break;
case 0xDD: key_val = 6; break;
case 0xBD: key_val = 7; break;
case 0x7D: key_val = 8; break;
default: key_val = -1; break;
}
}
}
//第三行
GPIO_Write(GPIOB,0);
delay_ms(5);
GPIO_Write(GPIOB,(uint16_t)(0xFB << 8));
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0) != 0XF0)
{
delay_ms(20);
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOB) >> 8) & 0XFB);
switch(temp)
{
case 0xEB: key_val = 9; break;
case 0xDB: key_val = 10; break;
case 0xBB: key_val = 11; break;
case 0x7B: key_val = 12; break;
default: key_val = -1; break;
}
}
}
//第四行
GPIO_Write(GPIOB,0);
delay_ms(5);
GPIO_Write(GPIOB,(uint16_t)(0xF7 << 8));
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0) !=0XF0)
{
delay_ms(20);
if(((GPIO_ReadInputData(GPIOB) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOB) >> 8) & 0XF7);
switch(temp)
{
case 0xE7: key_val = 13; break;
case 0xD7: key_val = 14; break;
case 0xB7: key_val = 15; break;
case 0x77: key_val = 16; break;
default: key_val = -1; break;
}
}
}
}
}
return key_val;
}
int main(void)
{
int key_val = 0;
Sys_Delay_Init();
Matrix_ssKey_Pin_Init();
Usart1_Pin_Init(115200);
printf("初始化成功\r\n");
while(1)
{
key_val = Matrix_Key_Scan();
if (key_val > 0 && key_val < 17)
printf("This is S%d key\r\n",key_val);
}
}
#define keyboard P1 //四条行线三条列线所连接的IO口
unsigned char Check_Keyboard()
{
unsigned char row_scan_code=0x01; //行扫描码
unsigned char col_scan_code=0xEF;//列扫描码
unsigned char keycode; //按键键值
unsigned char i,x,j;
//可以做一个消抖处理
for(i=0;i<3;i++)//逐列扫描,将列线逐列拉低
{
keycode=i+1;
keyboard=col_scan_code;
x=keyboard; //读取行线状态
for(j=0;j<4;j++)//逐行扫描
{
if(!(x&row_scan_code))//说明对应行的按键按下,使行线被拉低
{
keycode+=3*j;
//如果按键还未释放,则仍有行线被拉至低电平
while((keyboard&0x0f)!=0x0f);//等待按键释放
P1=0x0F; //恢复原状态,为下次按键做准备
return keycode; //已检测到按键键码,返回
}
else
row_scan_code=_crol_(row_scan_code,1);
}
col_scan_code=_crol_(col_scan_code,1);//左移一位,将下一列线拉低
row_scan_code=0x01;//重置行扫描码,为下一行扫描作准备
}
keycode=0;//没有按键按下,键值记为0
return keycode;
}
//@brief:判断4*4矩阵键盘是否有键可靠按下,高4位口接行线,低四位口接列线
//@retval:当有键可靠按下时返回1-16的键值,否则返回0
#define keyboard P1
unsigned char Check_Keydown()
{
unsigned char KeyValue=0;
keyboard=0x0f;
if(keyboard!=0x0f)//如果按键按下
{
delay_ms(10);//延时10ms消抖
if(keyboard!=0x0f)//按键确实按下
{
//判断按键所在列,以所在列的第一行的按键键值赋给KeyValue
keyboard=0X0F;
switch(keyboard)
{
case(0X07): KeyValue=1;break; //第一列按下
case(0X0b): KeyValue=2;break; //第二列按下
case(0X0d): KeyValue=3;break; //第三列按下
case(0X0e): KeyValue=4;break; //第四列按下
}
//判断按键所在行
keyboard=0XF0;
switch(keyboard)
{
case(0X70): KeyValue=KeyValue;break; //第一行按下
case(0Xb0): KeyValue=KeyValue+4;break; //第二行按下
case(0Xd0): KeyValue=KeyValue+8;break; //第三行按下
case(0Xe0): KeyValue=KeyValue+12;break; //第四行按下
}
while(keyboard!=0xf0); //按键松手后退出
return KeyValue;
}
else //否则认为是信号干扰导致
{
return 0; //认为没有按键按下
}
}
return 0; //如果没有按键按下返回零
}
到了这里,关于矩阵键盘原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!