详解如何在STM32上使用4x4矩阵键盘

  矩阵键盘的使用在日常生活中非常常见,驱动矩阵键盘其实就是单纯的使用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;

  • 3
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值