基于STM32F407最小系统板三种矩阵键盘实现方法

2019-08-16来源: eefocus关键字:STM32F407  最小系统  矩阵键盘

这里采用的八个端口为PA0-PA7。


此处先给出矩阵键盘的原理图:


一、八个端口采用开漏输出,配置上拉电阻,实现同51一样的双向IO口功能。


//按键初始化函数

void KEY_Init(void){

  GPIO_InitTypeDef  GPIO_InitStructure;

 

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA

 

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

  GPIO_InitStructure.GPIO_Pin|=  GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;

  //这里这么写是因为复制代码块里,太长了,所以分成两部分。

  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;//普通输出模式

  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//开漏输出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;//上拉

  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

  

以上初始化配置完成之后,精华部分如下:


/*mode =1代表连按,mode = 0代表单按*/

u8 matrixkey(int mode){

u8 row = 0;

u8 column =0;

static u8 key = 1;

GPIO_Write(GPIOA,0x0F);

if(key&&(GPIO_ReadInputData(GPIOA)&0XFF)!=0X0F){

delay_ms(10);//按下消抖 

column = GPIO_ReadInputData(GPIOA)&0X0F;

GPIO_Write(GPIOA,0xF0);

delay_ms(1);//这个语句超级重要!!!

row = GPIO_ReadInputData(GPIOA)&0XF0;

LCD_ShowNum(100,20,row+column,4,12);

key = 0;

}

if(mode)    key = 1;

if( (GPIO_ReadInputData(GPIOA)&0XFF) == 0X0F){

delay_ms(10); //松手消抖

key = 1;

}

        return row+column;

}

因为配置成端口开漏,上拉电阻模式,


判断第几行的时候,矩阵键盘的1-4行输出逻辑1与5-8行输出的逻辑0,线与为0;


判断第几列的时候,矩阵键盘的5-8行输出逻辑1与1-4行输出的逻辑0,线与为0;


最后得到的行与列相加返回。


区别同51的代码,这里判断按键按下后,有个delay_ms(1)的语句极为重要,不一定需要1ms可以更小,让CPU有一定的反应时间后,再次读取端口电平状态。(也只是推测,具体的原因,以后要深究。)

在主函数里即


int main(void){ 

u8 count =1;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2

delay_init(168);      //初始化延时函数

uart_init(115200); //初始化串口波特率为115200

  LCD_Init();           //初始化LCD FSMC接口

KEY_Init();

LCD_Clear(BLUE);

  while(1) {  

        switch(matrixkey(0)){

                 /*第一行*/

case 0xee:count=count+1; break; case 0xde:count=count+2;break;

case 0xbe:count=count+3; break; case 0x7e:count=count+4;break; 

 

/*第二行*/

case 0xed:count=count+5; break; case 0xdd:count=count+6;break;

case 0xbd:count=count+7; break; case 0x7d:count=count+8;break;  

 

/*第三行*/

case 0xeb:count=count+9; break; case 0xdb:count=count+10;break;

case 0xbb:count=count+11;break; case 0x7b:count=count+12;break;  

                /*第四行*/

case 0xe7:count=count+13;break; case 0xd7:count=count+14;break;

case 0xb7:count=count+15;break; case 0x77:count=count+16;break;  

       }

LCD_ShowNum(100,100,count,4,12);

}

二、动态配置成4行输出,4行输入。


/*四行输出,四列输入*/

void R_Out_C_Input(void){ 

    GPIO_InitTypeDef  GPIO_InitStructure;

 

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA

 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//开漏输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

}

 

/*四列输出,四行输入*/

void C_Out_R_Input(void){  

GPIO_InitTypeDef  GPIO_InitStructure;

 

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA

 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//开漏输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

}

动态配置输入输出的矩阵键盘实现代码如下:


u8 matrixkey(int mode){

u8 row = 0;

u8 column = 0;

static u8 key =1;

GPIO_Write(GPIOA,0); //这里主函数里初始化后,PA0-3输出,PA4-7输入,让PA0-3输出0

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

  delay_ms(10);

  column =  GPIO_ReadInputData(GPIOA)&0XF0; //获取列值

  C_Out_R_Input();//PA0-3输入,PA4-7输出

  delay_ms(1);

  GPIO_Write(GPIOA,0);

  row = GPIO_ReadInputData(GPIOA)&0X0F;//获取行值

  LCD_ShowNum(100,20,row+column,4,12);

  key = 0;

  R_Out_C_Input();//重新配置成低四位输出,高四位输入。

}

if(mode)key = 1;

if( (GPIO_ReadInputData(GPIOA)&0XF0) == 0XF0){

delay_ms(10);//松手消抖

key = 1;

}

        return row+column;

 

}

主函数同上,只不过将KEY_Init();这条语句改为R_Out_C_Input();


三、低四位输出,高四位输入模式固定,逐行扫描,获取1-16按钮。


 主函数同二、,端口初始化配置即void R_Out_C_Input(void);函数


矩阵键盘扫描实现略长,看起来复杂,不过是依葫芦画瓢。


但最重要的是逐行扫描,必须每一行扫描,如果有按键按下,要进行松手检测,否则实现不了。


具体的心得体会,以后回来再改。


while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0); //松手检测


紧接着delay_ms(10); 松手消除抖动。


u8 matrixkey(int mode){

static u8 key =1;

         GPIO_Write(GPIOA,0x0e); //第一行输出到第五-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xee;

case 0xd0:return 0xde;

case 0xb0:return 0xbe;

case 0x70:return 0x7e;

}

}

while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0); //松手检测

delay_ms(10);

 

GPIO_Write(GPIOA,0x0d);//第二行输出到第五-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xed;

case 0xd0:return 0xdd;

case 0xb0:return 0xbd;

case 0x70:return 0x7d;

}

}

while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0);

delay_ms(10);

 

GPIO_Write(GPIOA,0x0b);//第三行输出到第四-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xeb;

case 0xd0:return 0xdb;

case 0xb0:return 0xbb;

case 0x70:return 0x7b;

}

}

while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0);

delay_ms(10);

 

GPIO_Write(GPIOA,0x07);//第四行输出到第五-八行

if(key&&((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)){

delay_ms(10);

key = 0;

switch(GPIO_ReadInputData(GPIOA)&0xF0){

case 0xe0:return 0xe7;

case 0xd0:return 0xd7;

case 0xb0:return 0xb7;

case 0x70:return 0x77;

}

}

if(mode) key =1;

if((GPIO_ReadInputData(GPIOA)&0xF0)==0xF0)

key =1;

return 0;

}

比较:


三种方案其实,第二种方案比较通用,第一种比较简单易懂,前两种都比较好,因为按键按下不松开,并不会影响CPU一直停留在while()循环里啥都不干。


最后的实验效果:


之前用的板子是STM32F107,数据手册中的GPIO口一些输出输入方式的配置与STM32F407有较大不同,这里以STM32F407的为基准。32的单片机跟51不同,51的端口不需要配置成这个端口做输出还是做输入用,双向IO意味着我端口输出XXH(八位),照样可以直接读取到,而32以及PIC系列的单片机,要想读取端口的状态,需要配置成输入模式。


但是32系列的单片机有点好处,即只要设置成开漏输出模式下,再加上拉电阻,采用GPIO_ReadInputData();


即能实现双向IO功能。


针对F407而言,内部的上拉电阻开关,无论是输入还是输出模式都可以打开,所以用第一种方案好。我看了看手册,这里配置成输出模式,并没指明开漏模式下才能端口读取电平状态,应该是两者都可以(通用或者推挽模式)。


而F103系列的,上拉电阻开关只针对输入模式下,(输出模式下,内部上、下拉电阻功能被禁止)如果要实现第一种方案,还需要额外加上拉电阻,麻烦许多,所以用二、三方案比较好。同时读取电平必须要开漏输出。


在此附上F407的输出GPIO手册截图吧:


关键字:STM32F407  最小系统  矩阵键盘

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic471354.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32笔记(二)(寄存器)——矩阵键盘
下一篇:基于 STM32F407 使用 4*4 矩阵键盘

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

基于STM32F103ZET6主控平台实现定时器TIM3的驱动

_Clock_Init(9);   //系统时钟设置 delay_init(72);     //延时初始化 uart_init(72,115200); //串口初始化为115200 LED_Init(); BEEP_Init(); TIM3_Init(4999,7199); while(1) { if(Wl_stat==0)//代表进入WHILE循环 { printf("Input While(1)-->rn"); Wl_stat=1; } LED1=!LED1; delay_ms(200); }} 第四步:编译通过后,烧录进STM32F103ZET6开发板,实现
发表于 2019-08-22

STM32f103ZET6定时器TIM3使用通道1输出PWM程序

总结:TIM3_CH1输出口为PA6(重映射为PB4),PB5位DS0接口。1.使用重映射之后,原始IO口不再有输出;本例子中,若使用部分重映射,则 PA6映射到PB4中,所以仅仅PB4有波形输出;飞线连接PB4与PB5即可观察PWM呼吸灯效果。2.使用IO口前务必初始化,本例子中开始仅仅初始化了PB5口,而没有初始化PA6导致一直验证失败;正点原子给出程序中是由通道2直接映射到PB5因此只需要初始化PB5口即可,不存在上述问题。3.除了正常输出输入信号之外,输出PWM以及其他功能均属于IO口的复用功能。#include "stm32f10x.h"#include "led.h"
发表于 2019-08-22

stm32f0设置tim2单脉冲模式-tim1内部触发

/***************************************************************************************************************以下TIM1和TIM2的设置实现TIM1输出40hz,占空比7%即高电平输出180us,TIM2输出单脉冲,周期360us,占空比50%,TIM2的触发是TIM1的OC1REF信号。TIM2设置单脉冲需设置的参数:  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;    TIM_SelectOnePulseMode(TIM2,TIM_OPMode_Single);//单脉冲  TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);//定时器1内部触发 
发表于 2019-08-22
stm32f0设置tim2单脉冲模式-tim1内部触发

STM32F103 使用TIM3产生四路PWM

为PC6、PC7、PC8、PC9*******************************************************************************/#include"stm32f10x.h"void RCC_Cfg(void);void GPIO_Cfg(void);void TIM_Cfg(void);void NVIC_Cfg(void);void delay_ms(u32 i);void PWM_Cfg(float dutyfactor1,float dutyfactor2,float dutyfactor3,float dutyfactor4); int
发表于 2019-08-22
STM32F103 使用TIM3产生四路PWM

STM32F10 OLED液晶显示(IIC)

2019年7月27日做小车的第六天。今天我实现了一个新的功能 OLED显示汉字。刚开始学习这个显示汉字的时候也是一头污水,代码啊大部分看不懂,看OLED讲解的那个视频反复看了好几遍。还是没什么思路。但是那自己慢慢的开始修改自带的源代码,尝试的去做,从显示点、线、字符、字符串一步步的开始进行,随着这个一步步的学会,显示汉字那,哈哈,原理也是差不多。大致思路也就是调用.c中的函数,设置里面的变量,去调出自己想要的字符,汉字的代码(这些代码都是在一个数组里)先说说重要的,怎么显示出汉字:比如我们想让OLED显示屏显示一个字符串:“CODE”,主函数是这么两条语句:OLED_ShowString(63,48,“CODE:”,16
发表于 2019-08-22
STM32F10 OLED液晶显示(IIC)

STM32F103_SD卡FatFs文件系统移植

;       status = RES_OK;            break;至此diskio.c移植完成三、ffconfig.h需要修改的参数如下:  //准许使用文件字符串函数: f_gets(), f_putc(), f_puts() and/  f_printf()#define    _USE_STRFUNC    1    //支持格式化函数f_mkfs()#define    _USE_MKFS   
发表于 2019-08-22
STM32F103_SD卡FatFs文件系统移植

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved