一种可移植的不连续IO口矩阵键盘

52次阅读

[toc]

前言

博主最近参加电赛,花了一天时间研究STM32矩阵按键检测程序,使用的是STM32F103C8T6核心板(在此吐槽一句,C8T6怎么越来越贵了)。由于STM32IO口寄存器数量多,实际硬件IO口不连续,所以操作寄存器对每一位组合起来进行判断十分麻烦,所以在一天调试未果后,选择上网查找,终于找到了一个很有效的通用代码,只需要简单修改就可以适用到自己的开发板上,在此分享出来。

原文在此(点我)

程序主体


// 这是key.h头文件
#ifndef __KEY4_4_H
#define __KEY4_4_H   

#include <stm32f10x.h>
#include "usart.h"    //这两个头文件是正点原子提供的,文末会打包整个工程
#include "sys.h"

#define uint unsigned int 
#define uchar unsigned char

// 按键IO初始化
void Key_Init(void);
// 按键扫描
int Key_Scan(void);

//测试,这个函数实际应用中如果不需要向串口发送就可以去掉
void Key_Test(void) ;

#endif

// 这是程序主体key.c文件
#include "key4_4.h"
#include "delay.h"
#include "sys.h"
//8个引脚 4个为行 4个为列
//行输出端口定义
#define X1_GPIO_PORT GPIOB           // 只需要修改这里的端口GPIO为你使用的IO即可
#define X2_GPIO_PORT GPIOB   
#define X3_GPIO_PORT GPIOB           
#define X4_GPIO_PORT GPIOB 
//列输入端口定义
#define Y1_GPIO_PORT GPIOB           
#define Y2_GPIO_PORT GPIOB   
#define Y3_GPIO_PORT GPIOB           
#define Y4_GPIO_PORT GPIOA 

//行输出引脚定义
#define X1_GPIO_PIN GPIO_Pin_9
#define X2_GPIO_PIN GPIO_Pin_8
#define X3_GPIO_PIN GPIO_Pin_7
#define X4_GPIO_PIN GPIO_Pin_6

//列输入引脚定义
#define Y1_GPIO_PIN GPIO_Pin_5
#define Y2_GPIO_PIN GPIO_Pin_4
#define Y3_GPIO_PIN GPIO_Pin_3
#define Y4_GPIO_PIN GPIO_Pin_15

//行输出时钟定义
#define X1_RCC RCC_APB2Periph_GPIOB
#define X2_RCC RCC_APB2Periph_GPIOB
#define X3_RCC RCC_APB2Periph_GPIOB
#define X4_RCC RCC_APB2Periph_GPIOB

//列输入时钟定义
#define Y1_RCC RCC_APB2Periph_GPIOB
#define Y2_RCC RCC_APB2Periph_GPIOB
#define Y3_RCC RCC_APB2Periph_GPIOB
#define Y4_RCC RCC_APB2Periph_GPIOA

// 按键顺序可以按照你的数字矩阵键盘来从左到右或者从右向左来依次修改到上方文件里
// 具体顺序可以实际测试一下
//移植代码只需要修改上面的端口和引脚和时钟即可,下面的代码不用修改。
//矩阵键盘所用的8个引脚可连续可不连续,看实际需要和个人爱好自己定义。

unsigned char Y1,Y2,Y3,Y4;
void Key_Init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;   
   RCC_APB2PeriphClockCmd(X1_RCC|X2_RCC|X3_RCC|X4_RCC|Y1_RCC|Y2_RCC|Y3_RCC|Y4_RCC|RCC_APB2Periph_AFIO, ENABLE);

   GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);     // 一定要先禁用JTAG接口,否则会干扰到按键电平识别

/*****************************4行输出*********************************************/
   GPIO_InitStructure.GPIO_Pin =  X1_GPIO_PIN ;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(X1_GPIO_PORT, &GPIO_InitStructure);

   GPIO_InitStructure.GPIO_Pin =  X2_GPIO_PIN ;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(X2_GPIO_PORT, &GPIO_InitStructure);

   GPIO_InitStructure.GPIO_Pin =  X3_GPIO_PIN ;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(X3_GPIO_PORT, &GPIO_InitStructure);

   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Pin = X4_GPIO_PIN ;    
   GPIO_Init(X4_GPIO_PORT, &GPIO_InitStructure);

/**************************************4列输入*************************************/
   GPIO_InitStructure.GPIO_Pin =  Y1_GPIO_PIN ;   
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;     
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(Y1_GPIO_PORT, &GPIO_InitStructure);    

   GPIO_InitStructure.GPIO_Pin =  Y2_GPIO_PIN ;   
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;     
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(Y2_GPIO_PORT, &GPIO_InitStructure);    

   GPIO_InitStructure.GPIO_Pin =  Y3_GPIO_PIN ;   
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;     
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(Y3_GPIO_PORT, &GPIO_InitStructure);    

   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;     
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Pin = Y4_GPIO_PIN;      
   GPIO_Init(Y4_GPIO_PORT, &GPIO_InitStructure);    
}
// 基本思想就是行扫描,这里通过大量的GPIO_SetBits,GPIO_ResetBits,GPIO_ReadInputDataBit函数读取IO状态,写起来挺费手的
int Key_Scan(void)
{
   uchar KeyVal;
   GPIO_SetBits(X1_GPIO_PORT,X1_GPIO_PIN);  //先让X1输出高
   GPIO_SetBits(X2_GPIO_PORT,X2_GPIO_PIN);  //先让X2输出高
   GPIO_SetBits(X3_GPIO_PORT,X3_GPIO_PIN);  //先让X3输出高
   GPIO_SetBits(X4_GPIO_PORT,X4_GPIO_PIN);  //先让X4输出高

    if((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN)|GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN)|GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN)|GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))==0x0000)  
        return -1; //如果X1到X4全为零则没有按键按下  
     else
     {  
        delay_ms(5);    //延时5ms去抖动
         if((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN)|GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN)|GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN)|GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))==0x0000)
        return -1;
     }

     GPIO_ResetBits(X1_GPIO_PORT,X1_GPIO_PIN);
     GPIO_ResetBits(X2_GPIO_PORT,X2_GPIO_PIN);
     GPIO_ResetBits(X3_GPIO_PORT,X3_GPIO_PIN);
     GPIO_SetBits(X4_GPIO_PORT,X4_GPIO_PIN); 

    Y1=GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN);Y2=GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN);
    Y3=GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN);Y4=GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN);
     if(Y1==1&&Y2==0&&Y3==0&&Y4==0)
            KeyVal='*';
     if(Y1==0&&Y2==1&&Y3==0&&Y4==0)
            KeyVal=0;
     if(Y1==0&&Y2==0&&Y3==0&&Y4==1)
            KeyVal='D';
     if(Y1==0&&Y2==0&&Y3==1&&Y4==0)
            KeyVal='#';

     while(((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN))|(GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN))|(GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN))|(GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))) > 0);
    //等待按键释放
     GPIO_SetBits(X1_GPIO_PORT,X1_GPIO_PIN);
     GPIO_ResetBits(X2_GPIO_PORT,X2_GPIO_PIN);
     GPIO_ResetBits(X3_GPIO_PORT,X3_GPIO_PIN);
     GPIO_ResetBits(X4_GPIO_PORT,X4_GPIO_PIN);

    Y1=GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN);Y2=GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN);
    Y3=GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN);Y4=GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN);
     if(Y1==1&&Y2==0&&Y3==0&&Y4==0)
            KeyVal=1;
     if(Y1==0&&Y2==1&&Y3==0&&Y4==0)
            KeyVal=2;
     if(Y1==0&&Y2==0&&Y3==1&&Y4==0)
            KeyVal=3;
     if(Y1==0&&Y2==0&&Y3==0&&Y4==1)
            KeyVal='A';

      while(((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN))|(GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN))|(GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN))|(GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))) > 0);

     GPIO_ResetBits(X1_GPIO_PORT,X1_GPIO_PIN);
     GPIO_SetBits(X2_GPIO_PORT,X2_GPIO_PIN);
     GPIO_ResetBits(X3_GPIO_PORT,X3_GPIO_PIN);
     GPIO_ResetBits(X4_GPIO_PORT,X4_GPIO_PIN);

     Y1=GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN);Y2=GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN);
     Y3=GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN);Y4=GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN);
     if(Y1==1&&Y2==0&&Y3==0&&Y4==0)
            KeyVal=4;
     if(Y1==0&&Y2==1&&Y3==0&&Y4==0)
            KeyVal=5;
     if(Y1==0&&Y2==0&&Y3==1&&Y4==0)
            KeyVal=6;
     if(Y1==0&&Y2==0&&Y3==0&&Y4==1)
            KeyVal='B';

      while(((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN))|(GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN))|(GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN))|(GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))) > 0);

     GPIO_ResetBits(X1_GPIO_PORT,X1_GPIO_PIN);
     GPIO_ResetBits(X2_GPIO_PORT,X2_GPIO_PIN);
     GPIO_SetBits(X3_GPIO_PORT,X3_GPIO_PIN);
     GPIO_ResetBits(X4_GPIO_PORT,X4_GPIO_PIN);   

     Y1=GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN);Y2=GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN);
     Y3=GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN);Y4=GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN);
     if(Y1==1&&Y2==0&&Y3==0&&Y4==0)
            KeyVal=7;
     if(Y1==0&&Y2==1&&Y3==0&&Y4==0)
            KeyVal=8;
     if(Y1==0&&Y2==0&&Y3==1&&Y4==0)
            KeyVal=9;
     if(Y1==0&&Y2==0&&Y3==0&&Y4==1)
            KeyVal='C';

       while(((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN))|(GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN))|(GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN))|(GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))) > 0);

        return KeyVal;
}

/************************************
        按键表盘为:      1  2  3  A 
                            4  5  6  B
                            7  8  9  C
                            *  0  #  D 
************************************/

// 测试程序,读取到哪个按键按下就向串口发送按键对应数字

void Key_Test(void) 
{
    int num;
      num = Key_Scan();
      switch(num)
      { 
                case 0: printf("0\n"); break;                                       
                case 1: printf("1\n"); break;                                        
                case 2: printf("2\n"); break;                                      
                case 3: printf("3\n"); break;                                      
                case 4: printf("4\n"); break;                        
                case 5: printf("5\n"); break;                                       
                case 6: printf("6\n"); break;                                       
                case 7: printf("7\n"); break;                                    
                case 8: printf("8\n"); break;                                            
                case 9: printf("9\n"); break;                                                 
                case 'A': printf("A\n"); break;                                                 
                case 'B': printf("B\n"); break;                                       
                case 'C': printf("C\n"); break;                                                 
                case 'D': printf("D\n"); break;                                                 
                case '#': printf("#\n"); break;                                       
                case '*': printf("*\n"); break;                                   
      }
}

最后,在main文件中提供调用,在此就不提供测试代码,只需要

uart_init(9600);             
Key_Init();                 
delay_init();               

即可。

这样,一个模块就完成了,通过STM32进行认为控制就方便多了,而且可控制方式也增加了。

按需自取

网盘链接:https://pan.baidu.com/s/1K4KRjzN34yALMoYY7AuNqw
提取码:1111

liubobo
版权声明:本站原创文章,由 liubobo 2021-11-10发表,共计7813字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。