? 矩阵键盘的使用在日常生活中非常常见,驱动矩阵键盘其实就是单纯的使用GPIO去获取电平的状态,但如何获取其实有很多方法,常见的方法有轮询和中断,本次介绍一个比较常用的方法,通过定时器轮询来进行读取(采用STM32F103)。
首先,设置好定时器,每隔30ms去轮询一次按键,因为人手随手按一下的时间肯定不止30ms,因此每隔30ms轮询一次,是肯定能读到按下的值的。那么定时器的设置方法如下:
定时器头文件tim7.h
#ifndef _TIM7_H_
#define _TIM7_H_
#include "stm32f10x.h"
extern uint8_t KEY_VALUE;
void KEY_4x4_SCAN_TIM_Init(u16 Period,u16 Prescaler);
#endif
定时器源码tim7.c
#include "tim7.h"
uint8_t KEY_VALUE;
static void KEY_4x4_SCAN_TIM_NVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=TIM7_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=3;
NVIC_Init(&NVIC_InitStruct);
}
void KEY_4x4_SCAN_TIM_Init(u16 Period,u16 Prescaler)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);
//TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Down;
TIM_TimeBaseInitStruct.TIM_Period=Period;
TIM_TimeBaseInitStruct.TIM_Prescaler=Prescaler;
//TIM_TimeBaseInitStruct.TIM_RepetitionCounter=
TIM_TimeBaseInit(TIM7,&TIM_TimeBaseInitStruct);
KEY_4x4_SCAN_TIM_NVIC_Init();
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM7,ENABLE);
}
? 对于定时器相关内容这里就不做深入讲解,设置好定时器定时时间,以及编写好中断服务函数,定时器就能正常工作,这里设置定时器中断时间为30ms中断一次,溢出中断的方式进行中断。
设置好中断分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
初始化好中断周期
KEY_4x4_SCAN_TIM_Init(99,7199);
中断服务函数
void TIM7_IRQHandler(void)
{
if(TIM_GetITStatus(TIM7,TIM_IT_Update) == SET)
{
KEY_VALUE=KEY_4x4_Scan();
}
TIM_ClearITPendingBit(TIM7,TIM_IT_Update);
}
? 那么定时器部分已经完成了,接下来就是矩阵按键的具体实现。
4x4_key.h
#ifndef __4X4_KEY_H
#define __4X4_KEY_H
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
#define HANG0 GPIO_Pin_0
#define HANG1 GPIO_Pin_1
#define HANG2 GPIO_Pin_2
#define HANG3 GPIO_Pin_3
#define LIE0 GPIO_Pin_11
#define LIE1 GPIO_Pin_12
#define LIE2 GPIO_Pin_13
#define LIE3 GPIO_Pin_14
#define HANG0_GPIO_PORT GPIOF
#define HANG1_GPIO_PORT GPIOF
#define HANG2_GPIO_PORT GPIOF
#define HANG3_GPIO_PORT GPIOF
#define LIE0_GPIO_PORT GPIOF
#define LIE1_GPIO_PORT GPIOF
#define LIE2_GPIO_PORT GPIOF
#define LIE3_GPIO_PORT GPIOF
#define HANG0_GPIO_CLK RCC_APB2Periph_GPIOF
#define HANG1_GPIO_CLK RCC_APB2Periph_GPIOF
#define HANG2_GPIO_CLK RCC_APB2Periph_GPIOF
#define HANG3_GPIO_CLK RCC_APB2Periph_GPIOF
#define LIE0_GPIO_CLK RCC_APB2Periph_GPIOF
#define LIE1_GPIO_CLK RCC_APB2Periph_GPIOF
#define LIE2_GPIO_CLK RCC_APB2Periph_GPIOF
#define LIE3_GPIO_CLK RCC_APB2Periph_GPIOF
#define HANG0_LOW GPIO_ResetBits(HANG0_GPIO_PORT,HANG0)
#define HANG1_LOW GPIO_ResetBits(HANG1_GPIO_PORT,HANG1)
#define HANG2_LOW GPIO_ResetBits(HANG2_GPIO_PORT,HANG2)
#define HANG3_LOW GPIO_ResetBits(HANG3_GPIO_PORT,HANG3)
#define HANG0_HIGH GPIO_SetBits(HANG0_GPIO_PORT,HANG0)
#define HANG1_HIGH GPIO_SetBits(HANG1_GPIO_PORT,HANG1)
#define HANG2_HIGH GPIO_SetBits(HANG2_GPIO_PORT,HANG2)
#define HANG3_HIGH GPIO_SetBits(HANG3_GPIO_PORT,HANG3)
#define LIE0_INPUT GPIO_ReadInputDataBit(LIE0_GPIO_PORT,LIE0)
#define LIE1_INPUT GPIO_ReadInputDataBit(LIE1_GPIO_PORT,LIE1)
#define LIE2_INPUT GPIO_ReadInputDataBit(LIE2_GPIO_PORT,LIE2)
#define LIE3_INPUT GPIO_ReadInputDataBit(LIE3_GPIO_PORT,LIE3)
void KEY_4x4_Init(void);
uint8_t KEY_4x4_Scan(void);
#endif
4x4_key.c
#include "4x4_key.h"
extern u8 flag_key_read;
void KEY_4x4_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//开启GPIO时钟
RCC_APB2PeriphClockCmd(HANG0_GPIO_CLK,ENABLE); //由于HANG0、HANG1、HANG2、HANG3时钟相同故只开启一次
RCC_APB2PeriphClockCmd(LIE0_GPIO_CLK,ENABLE);
//将行设置为推挽输出
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=HANG0|HANG1|HANG2|HANG3;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(HANG0_GPIO_PORT,&GPIO_InitStruct); //全部行都是GPIOF,故一起设置
//将列设置位浮空输入
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=LIE0|LIE1|LIE2|LIE3;
GPIO_Init(LIE0_GPIO_PORT,&GPIO_InitStruct); //全部LIE都是GPIOF,故一起设置
}
uint8_t KEY_4x4_Scan(void)
{
static uint8_t KEY_4x4_VALUE = 0;
HANG0_LOW;
HANG1_HIGH;
HANG2_HIGH;
HANG3_HIGH;
if( !LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT )
{
delay_ms(10);
if(!LIE0_INPUT) KEY_4x4_VALUE=16;
if(!LIE1_INPUT) KEY_4x4_VALUE=15;
if(!LIE2_INPUT) KEY_4x4_VALUE=14;
if(!LIE3_INPUT) KEY_4x4_VALUE=13;
while(!LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT);
}
HANG0_HIGH;
HANG1_LOW;
HANG2_HIGH;
HANG3_HIGH;
if( !LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT )
{
delay_ms(10);
if(!LIE0_INPUT) KEY_4x4_VALUE=12;
if(!LIE1_INPUT) KEY_4x4_VALUE=11;
if(!LIE2_INPUT) KEY_4x4_VALUE=10;
if(!LIE3_INPUT) KEY_4x4_VALUE=9;
while(!LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT);
}
HANG0_HIGH;
HANG1_HIGH;
HANG2_LOW;
HANG3_HIGH;
if( !LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT )
{
delay_ms(10);
if(!LIE0_INPUT) KEY_4x4_VALUE=8;
if(!LIE1_INPUT) KEY_4x4_VALUE=7;
if(!LIE2_INPUT) KEY_4x4_VALUE=6;
if(!LIE3_INPUT) KEY_4x4_VALUE=5;
while(!LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT);
}
HANG0_HIGH;
HANG1_HIGH;
HANG2_HIGH;
HANG3_LOW;
if( !LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT )
{
delay_ms(10);
if(!LIE0_INPUT) KEY_4x4_VALUE=4;
if(!LIE1_INPUT) KEY_4x4_VALUE=3;
if(!LIE2_INPUT) KEY_4x4_VALUE=2;
if(!LIE3_INPUT) KEY_4x4_VALUE=1;
while(!LIE0_INPUT || !LIE1_INPUT || !LIE2_INPUT || !LIE3_INPUT);
}
return KEY_4x4_VALUE;
}
? 通过代码可以了解到,轮询的方法和51单片机一样,每次只要拉低一行或者一列,读取列或者行就可以知道是哪个按键被按下了,按下了返回相应的值即可实现矩阵按键的读取过程。
??重点:因为在uint8_t KEY_4x4_Scan(void)函数中定义了一个静态变量,因此每次进入这个函数,
读取按键后,按键的值是不会清零的,如果你不是定义为
static uint8_t KEY_4x4_VALUE = 0;的话,每次按键完之后,KEY_4x4_VALUE的值就会被清零,
因为定时器不断的读取KEY_4x4_Scan()的值,这样可能会导致你按下了按键却没有读取到相应的值,
因为定时器第二次进入,KEY_4x4_VALUE被清零了,你按下的按键值只短短的存在了30ms,程序未能及时
读取到。
??所以加入static的目的在于此,这样只要你按下了,那么会一直保持对应的按键值,即使定时器
再一次进入,只要你没按其它值就还是上一次的值。
??那么问题又来了,有时候我用switch进行选择分支,一直保持同一个值也不能满足我的要求,
那怎么处理呢?
??可以添加一个全局变量,在main函数上方定义一个全局变量,每当我用完之后,
通过全局变量再将KEY_4x4_VALUE的值清楚。
uint8_t KEY_4x4_Scan(void)
{
? ? ? ? static uint8_t KEY_4x4_VALUE = 0;
? ? ? ? if(flag_key_read==0)KEY_4x4_VALUE = 0;//加上这两句话
? ? ? ? flag_key_read=1; //加上这两句话
? ? ? ? HANG0_LOW;
? ? ? ? HANG1_HIGH;
? ? ? ? HANG2_HIGH;
通过这个全局变量就可以实现,不想让值保留就重置!!!
if(flag_key_read==0)KEY_4x4_VALUE = 0;
flag_key_read=1;
|