C语言按键矩阵实现

    技术2022-07-11  80

    51单片机_按键矩阵扫描demo代码

    工作以来一直在搞单片机方面的软件,决定把工作以来写的一些模块写成文章,总结一下。

    按键矩阵扫描的原理

    1.当只有几个按键时,单片机GPIO口充足够用,可以直接使用一个按键对应一个gpio口,但是当项目中要用到的按键很多时,单纯的一个gpio口对应一个按键肯定是不够的,例如有16个按键,mcu使用51单片机,gpio口就显得不够用了,这时候就需要使用到按键矩阵。所谓的按键矩阵,是指几个io口组成一个交叉的矩阵,例如用4个io口组成行,4个io口组成列,这样一个组合能够识别的按键就可以达到(44=16个),而我们只用到了4+4=8个gpio口,这样就能大大提高gpio口的利用效率,达到有限的io口识别多个按键的目的。 2.按键矩阵一般的接线方式为下图所示,作为矩阵的列和行的几个io口的交叉点接按键,按键的一端接在对应列上,另一端接在对应行上,这样一个44的矩阵,就能承载16个按键。 在gpio口有输入输出方向的单片机上(例如stm32),需要在gpio口初始化时,将列(或者行)设置为输出,将行(或者列)设置成输入。在51单片机上,gpio口不分方向,所以此步可以省略。假如将作为行的gpio口设置成了输出,作为列的gpio口设置成了输入。假设作为输入的gpio口在默认情况下是低电平(可以通过上拉或者下拉电阻实现设置gpio口的默认电平),则在进行矩阵按键扫描时,先依次将几个作为行的gpio口设置成高电平,然后再去挨个读每个列的gpio口的电平状态,如果某个作为列的gpio口读到的电平也是高电平,那就说明这个行和这个列所对应的交叉点处的按键是按下了,因为按键按下以后行和列所在的gpio就是导通状态,它们电平应该是一样的。 举个例子,假设将上图的4个作为行的gpio口中的P17脚设置成高电平,P16,P15,P14三个脚设置成低电平。然后再依次去读P13,P12,P11,P10四个脚的电平,如果读到P11脚电平是高电平,则说明P11和P17所在的交叉位置的按键S3按下了。就这样一个行一个行,按照上面的步骤搞过去,扫描哪个按键按下去了。

    函数实现

    1.定义一个枚举类型,枚举每个按键

    //枚举类型 typedef enum { S1=0, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15, S16, NUM_KEY }EN_KEY; 2.定义一个行*列的EN_KEY类型的二维数组,用来配置每个按键对应的行号和列号 #define NUM_VERTI 4 //列数量 #define NUM_HORIZ 4 //行数量 //每列对应的io口 sbit gbit_VertiMap[NUM_VERTI]={P1.3,P1.2,P1.1,P1.0}; //每行对应的io口 sbit gbit_HorizMap[NUM_HORIZ]={P1.7,P1.6,P1.5,P1.4}; //行+列组合io口对应一个按键 ,例如 行0列0对应于S1 //二维数组 EN_KEY gen_KeyMap[NUM_HORIZ][NUM_VERTI ]={{S1,S2,S3,S4}, {S5,S6,S7,S8}, {S9,S10,S11,S12}, {S13,S14,S15,S16},}

    3.主要依靠如下3个函数实现矩阵按键扫描

    3.1.

    //选择一行,此行io置0,其它行置1,在矩阵扫描时,依次调用此函数将行io置为高,其他行io置为低, //然后再去挨个读列io void Key_SelOneHori(u8 horiIndex) { u8 i=0; for(i=0;i<NUM_HORIZ;i++) { if(i==horiIndex) { gbit_HorizMap[i]=1; } else { gbit_HorizMap[i]=0; } } }

    3.2

    .//按键矩阵扫描,通过两个嵌套的for循环,实现行,列的依次循环扫描。如果扫描到了按下的按键,就返回行号列号 void Key_Scan(u8*verti,u8* horiz) { u8 i=0,j=0; for(i=0;i<NUM_HORIZ;i++) { Key_SelOneHori(i);//将此时的行置高,其它行置低 for(j=0;j<NUM_VERTI;j++) { //如果列值io对应的电平等于0,则认为该行列组合对应的按键按下 if(gbit_VertiMap[j] == 1) { *verti=j; //返回按键的列号和行号 *horiz=i; return; } } } }

    3.3.

    //得到按下的按键的代号 S1 ,S2 ,S3等 EN_KEY Key_GetKey(u8 horiz,u8 verti) { return (gen_KeyMap[horiz][verti]); }

    4.在main函数中每隔10ms调用一次Key_Scan()函数

    void main() { u8 verti = 0XFF; u8 horiz=0XFF; EN_KEY key; while(1) { //按键扫描 Key_Scan(&verti, &horiz); //如果得到的行号列号有效,就获取按键号 if((verti != 0XFF) && (horiz != 0XFF)) { key = Key_GetKey(verti,horiz); } verti = 0XFF; horiz = 0XFF; }

    }

    其它需要补充的

    关于枚举类型和typedef的组合用法: typedef enum { …, …, … }ENUM_xxxxx; 表示对一个枚举类型重新定义一个别名为ENUM_XXXX;然后如果想定义一个此种类型的变量, 直接写 ENUM_XXXX val; val就是一个ENUM_XXXX类型的变量,取值只能取枚举类型中枚举的某个值。另外默认情况下,编译器会对枚举类型的第一个变量设置成0.后面依次加1.

    源代码的话,其实上面基本贴的差不多了。。。如果有需要可以加qq技术交流群 869630744 谢谢大家,有什么问题的地方尽管指出来。这份代码没有具体跑过,但是问题不大。

    Processed: 0.018, SQL: 9