首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

stm32 F103C8T6 4x4矩阵键盘使用

编程知识
2024年08月05日 15:08

首先感谢 江科大 的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的代码就能看出来 扫描的做法就是先给传入的行line[x] 设置低电平

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

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

 假设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);
    }
}

 

From:https://www.cnblogs.com/wobeinianqing/p/18343335
本文地址: http://www.shuzixingkong.net/article/813
0评论
提交 加载更多评论
其他文章 如何对MIL-STD-1553B进行选型
MIL-STD-1553B产品选型是一个复杂而细致的过程,‌需要综合考虑多个因素以确保所选产品能够满足特定应用场景的需求。 一、‌引言 MIL-STD-1553B作为一种广泛应用于航空航天领域的数据总线标准,‌其产品的选型对于确保系统的高效、‌可靠运行至关重要。‌选型过程中,‌需要充分理解MIL-S
C#自定义快捷操作键的实现 - 开源研究系列文章
这次想到应用程序的快捷方式使用的问题。 Windows已经提供了API函数能够对窗体的热键进行注册,然后就能够在窗体中使用这些注册的热键进行操作了。于是笔者就对这个操作进行了整理,将注册热键操作写成了帮助类,并且用此博文来记录这个使用DEMO,便于其他读者进行复用代码。 1、 项目目录; 2、 源码
C#自定义快捷操作键的实现 - 开源研究系列文章 C#自定义快捷操作键的实现 - 开源研究系列文章 C#自定义快捷操作键的实现 - 开源研究系列文章
Kotlin 布尔值教程:深入理解与应用示例
Kotlin中的布尔值是一种数据类型,仅能存储`true`或`false`两种状态,适用于表示二选一的情况,如开关或真假判断。布尔类型可通过`Boolean`关键字声明,并直接赋值为`true`或`false`。此外,Kotlin支持使用比较运算符创建布尔表达式,用于条件判断。条件语句包括`if`、
数据库与我:一段关于学习与成长的深情回顾
然而,近来我觉察到国产数据库的发展十分活跃。连我这样一个普通的程序员都能接触到部分信息,这说明国产数据库的关键时刻已经到来。正如文中所述,国产数据库的发展既是机遇也是挑战。在这个竞争激烈的市场上,只有少数公司能够生存下来,取决于谁能够获得更多的市场支持和客户案例。
数据库与我:一段关于学习与成长的深情回顾 数据库与我:一段关于学习与成长的深情回顾 数据库与我:一段关于学习与成长的深情回顾
解锁GraphRag.Net的无限可能:手把手教你集成国产模型和本地模型
在上次的文章中,我们已经详细介绍了GraphRag的基本功能和使用方式。如果你还不熟悉,建议先阅读前面的文章 通过前两篇文章,相信你已经了解到GraphRag.Net目前只支持OpenAI规范的接口,但许多小伙伴在社区中提议,希望能增加对本地模型(例如:ollama等)的支持。所以这次,我们将探讨如
南京大学计算机基础(四)踩坑笔记
第六周 缓冲区溢出章节 bang问题: 每次输入的id将影响getbuf中的堆栈位置,用-u 12的ebp和-u 123的ebp位置就不一样。 注意汇编代码中不能出现a0(代表换行符),如果地址有a0就将它随便改改就行了(a0改为a8-0x8)。 注意gdb如果不能重定向,可能是因为你修改了gdbi
南京大学计算机基础(四)踩坑笔记 南京大学计算机基础(四)踩坑笔记 南京大学计算机基础(四)踩坑笔记
一些八股:1.fetch 的理解。2.let、const、var
一、 说说你对 Fetch 的理解,它有哪些优点和不足? Fetch API 是现代 JavaScript 中用于进行网络请求的接口,旨在替代传统的 XMLHttpRequest。它提供了一种更简单、更灵活的方法来获取资源和与服务器进行交互。下面,我将详细介绍 Fetch 的优点和不足。 Fetch
代码随想录Day5
242.有效的字母异位词 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。 示例 1: 输入: s = "anagram", t = "nagaram&