stm32 F103C8T6 4x4矩阵键盘使用

首先感谢 江科大 的stm32入门课程 受益匪浅。推荐有兴趣的朋友去看看。

先看看我用的矩阵键盘是啥样的(很常见的一种)

接线如图(其他型号根据自己需求接上GPIO口)

代码基于stm大善人的代码修改而来,讲的很详细,非常感谢。

直接上代码:

头文件Key4x4.h

复制代码
#ifndef __KEY4x4_H
#define __KEY4x4_H

void KEY_4x4_Init(void); 
void KEY_Scan(void);
u16 Key_Read(void);

#endif

主体文件Key4x4.c

复制代码
#include "stm32f10x.h"
#include "Delay.h"

u8 anxia = 0;
u8 key = 1;
u16 line[4] = {0x00fe , 0x00fd , 0x00fb ,0x00f7};
u16 off = 0x00ff; // 全部引脚置为1
u16 keys[16] = {
    49, 50, 51, 65,
    52, 53, 54, 66,
    55, 56, 57, 67,
    42, 48, 35, 68,
};

void KEY_4x4_Init(void){
    
    GPIO_InitTypeDef GPIO_InitStructre;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); // 使能 GPIOA 的时钟
    
    // 第一组
    GPIO_InitStructre.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
    GPIO_InitStructre.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
    GPIO_InitStructre.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA , &GPIO_InitStructre);
    GPIO_SetBits(GPIOA , GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
    // 第二组数据
    GPIO_InitStructre.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructre.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA , &GPIO_InitStructre);
    GPIO_SetBits(GPIOA , GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
    
}

void Do_Click(uint16_t gpio_pin_x , u8 num){
    anxia = 1;
    key = num;
    while(!GPIO_ReadInputDataBit(GPIOA , gpio_pin_x));
}

void KEY_Click_Listener(u8 num){
    if((GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)==0)){
        Delay_ms(10);
        if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)){
            Do_Click(GPIO_Pin_4 , num+0);
        }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)){
            Do_Click(GPIO_Pin_5 , num+1);
        }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)){
            Do_Click(GPIO_Pin_6 , num+2);
        }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)){
            Do_Click(GPIO_Pin_7 , num+3);
        }else {
            anxia = 0;
            GPIO_Write(GPIOA , off);
        }
    }
}

void KEY_Scan(){
    // 第一行 1111 1110
    GPIO_Write(GPIOA , line[0]);
    KEY_Click_Listener(1);
    // 第二行
    GPIO_Write(GPIOA , line[1]);
    KEY_Click_Listener(5);
    // 第三行
    GPIO_Write(GPIOA , line[2]);
    KEY_Click_Listener(9);
    // 第四行
    GPIO_Write(GPIOA , line[3]);
    KEY_Click_Listener(13);
}

u16 Key_Read(){
    return keys[key-1];
}

主要代码说明:

初始化配置 (KEY_4x4_Init):

  • 使能GPIOA模块的时钟。
  • 配置GPIOA的前四个引脚(GPIO_Pin_0至GPIO_Pin_3)为推挽输出模式,用于键盘行线的扫描。
  • 设置GPIOA的后四个引脚(GPIO_Pin_4至GPIO_Pin_7)为上拉输入模式,用于检测键盘列线的状态。
复制代码
// 第一组
    GPIO_InitStructre.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
    GPIO_InitStructre.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
    GPIO_InitStructre.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA , &GPIO_InitStructre);
    GPIO_SetBits(GPIOA , GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
    // 第二组数据
    GPIO_InitStructre.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructre.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA , &GPIO_InitStructre);
    GPIO_SetBits(GPIOA , GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

按键检测 (KEY_Click_Listener):

  • 检测列线状态,如果有键按下,则调用Do_Click函数记录按键信息并等待按键释放。
  • 使用延时函数Delay_ms来消除抖动。
复制代码
void KEY_Click_Listener(u8 num){
    if((GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)==0)
||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)==0)){
        Delay_ms(10);
        if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)){
            Do_Click(GPIO_Pin_4 , num+0);
        }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)){
            Do_Click(GPIO_Pin_5 , num+1);
        }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)){
            Do_Click(GPIO_Pin_6 , num+2);
        }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)){
            Do_Click(GPIO_Pin_7 , num+3);
        }else {
            anxia = 0;
            GPIO_Write(GPIOA , off);
        }
    }
}
复制代码
void Do_Click(uint16_t gpio_pin_x , u8 num){
    anxia = 1;
    key = num;
    while(!GPIO_ReadInputDataBit(GPIOA , gpio_pin_x));
}

扫描过程 (KEY_Scan):

  • 循环扫描每一行,通过改变行线的状态来检测是否有键按下。
  • 调用KEY_Click_Listener函数来处理每一行的按键检测。

读取按键值 (Key_Read):

  • 返回当前按下的键对应的数值。

补充说明:

复制代码
u16 line[4] = {0x00fe , 0x00fd , 0x00fb ,0x00f7};
u16 off = 0x00ff; // 全部引脚置为1
u16 keys[16] = {
    49, 50, 51, 65,
    52, 53, 54, 66,
    55, 56, 57, 67,
    42, 48, 35, 68,
};

line 定义 了4个16进制的数值分别转成二进制

0000 0000 1111 1110 0x00fe

0000 0000 1111 1101 0x00fd

0000 0000 1111 1011 0x00fb

0000 0000 1111 0111 0x00f7

低4位为行 高四位为列

设置0就是给对应行设置低电平

这样我们在scan的代码就能看出来 扫描的做法就是先给传入的行linex 设置低电平

所有列都是高电平当扫描到某一列为低电平时就说明这一列被点击了。

循环从第一列到第四列设置低电平直到检测到某一列也变成低电平

假设1被点击 则1这一列也是低电平

就变成第一行 第一列低电平

这样就能确定1被点击。给对应参数赋值1即可

再根据定义的keys (askII 码表对应数值 )

主要代码就是上面这些,其他代码只要复制 江科大OLED的课件源码即可

使用方式 man.c

复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key4x4.h"

int main(void)
{
    
    /*模块初始化*/
    OLED_Init();        //OLED初始化
    KEY_4x4_Init();
    
    /*OLED显示*/
    OLED_ShowString(1, 1, "in put:");                //1行1列显示字符A
    u8 num = 0;
    while (1)
    {
        KEY_Scan();
        num = Key_Read();
        OLED_ShowChar(1 ,8 ,num);
    }
}
相关推荐
✎ ﹏梦醒͜ღ҉繁华落℘3 天前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
牛根生同志3 天前
SPI数据收发的时候 TXE与RXNE标志位置位的时机
stm32·spi·transfer
goldenrolan3 天前
学习型红外控制系统稳定性挂测工装专项总结
软件测试·python·stm32·嵌入式·红外
CC城子3 天前
STM32H7_FDCAN 驱动笔记
stm32·can·canfd
意法半导体STM323 天前
【官方原创】如何为STM32CubeMX2配置Visual Studio Code配置方案
vscode·stm32·单片机·嵌入式硬件·策略模式·stm32cubemx·嵌入式开发
雾削木4 天前
B语言经典教程现代化重构
java·前端·stm32·单片机·嵌入式硬件
Digitally4 天前
如何快速将文件从电脑传输到平板电脑
stm32·嵌入式硬件·电脑
项目題供诗4 天前
STM32-USART串口协议(二十二)
stm32·单片机·嵌入式硬件
欢乐熊嵌入式编程4 天前
选型避坑:ESP32 vs STM32+模组 vs NB-IoT,不同场景怎么选
stm32·单片机·嵌入式硬件·物联网·esp32·嵌入式iot
振南的单片机世界4 天前
ARM中断比51快在哪?硬件压栈+NVIC集中管理
arm开发·stm32·单片机·嵌入式硬件