IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 详解如何在STM32上使用4x4矩阵键盘 -> 正文阅读

[嵌入式]详解如何在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;

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-11 12:52:08  更:2021-11-11 12:54:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:40:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码