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

1 篇文章 0 订阅

这里采用的八个端口为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手册截图吧:

  • 9
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值