4*4矩阵按键(STM32)超!超!超!详细分析

07-21 1399阅读

矩阵键盘的基本结构

  • 定义:

  • 矩阵键盘,又称为行列式键盘,是用4条I/O线作为行线,4条I/O线作为列线组成的键盘。在行线和列线的每一个交叉点上设置一个按键,因此键盘中按键的个数是4×4个。

  • 优势:

  • 这种行列式键盘结构能够有效地提高单片机系统中I/O口的利用率,节约单片机的资源。(8引脚控制16按键)

  • 4*4矩阵按键(STM32)超!超!超!详细分析

    硬件连接

    • 行线与列线:在硬件上,4条行线连接到微控制器的输出引脚,而4条列线连接到微控制器的输入引脚。
    • 按键位置:每个按键位于行线和列线的交叉点上。
    • 4*4矩阵按键(STM32)超!超!超!详细分析

      GPIO初始化

      行线

      通常设置为输出模式,用于向列线发送扫描信号。

      扫描信号

      行线的主要功能是向列线发送扫描信号。当某一行线被设置为低电平时,与之相交的列线会被检测(若相对应的按键被按下,列线被检测为低电平)以判断是否有按键被按下。

      确定按键行

      通过依次将每一行线设置为低电平(可以逐行扫描键盘)。当检测到列线上有低电平时,可以确定被按下的按键位于当前选中的行上。若列线上无低电平,那么将此行线设置为高电平,下一行设置为低电平,进行新一轮按键检测。

      列线

      设置为输入模式,用于读取按键状态。

      读取按键状态

      列线的主要功能是读取按键状态。(STM32就靠列线来确认按键状态)当某一行线被选中(设置为低电平)时,微控制器会读取所有列线的电平状态,以判断是否有按键被按下。

      注意事项

      在实际应用中,还需要考虑去抖处理、中断处理等因素,以确保按键检测的准确性和稳定性。

      代码详解

      初始化

      void KEY_4x4_Init(void)     //键盘IO口配置及初始化
      {
       	GPIO_InitTypeDef GPIO_InitStructure;       	
       	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);      
      	//使用GPIOA的0,1,2,3引脚为行 R1~R4对应矩形按钮的5,6,7,8引脚
      	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;  
         //行  0123
      	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //使用推挽输出
          GPIO_Init(GPIOA, &GPIO_InitStructure);
      	GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);   
      //令GPIO的0,1,2,3引脚输出为1
      	/********************************************************************/
      	/********************************************************************/
      	/********************************************************************/
      	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;         
      //使用GPIOA的4,5,6,7引脚为列  
      	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;        //使用上拉输入
      	GPIO_Init(GPIOA, &GPIO_InitStructure);
      	GPIO_SetBits(GPIOA, GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);	
      //令GPIO的4,5,6,7引脚输出为1	
      }

      这里比较重要的是,我们需要将0-7的I/O口全部拉成高电平!我们后面检测的是低电平,所以你不设置,全部都是低电平。直接让自己的按键默认全部按下(成为一个导线(手动滑稽一下))。

      按键检测

      首先讲一讲我们都部分参数,方便接下来的理解。

      u8 anxian = 0;  //anxian这个变量为1时,证明有按钮正在被按下
      *key = 1;   
      //输出key的值为1传递到主函数,当值为x时,则说明按钮Sx被按下,在变量前增加*,为c语言中的指针操作,使用指针进行地址传递,在子函数中修改的值在主函数中可以利用地址读取,而不需要利用子函数返回值,然后主函数在增加一个变量进行接收。

      开始吧!

      void KEY_Scan(u8 *key) 
      {	 	
      	GPIO_Write(GPIOA,0x00fe); 
      	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(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)   
      		{
      			anxian = 1;    
      			*key = 1;   
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4));   
      				//当按钮处于被按下的状态的时候,程序一直卡在循环读取按钮的状态,避免多按钮同时按下时读取错误
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==0)
      		{
      			anxian = 1;
      			*key = 2;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==0)
      		{
      			anxian = 1;
      			*key = 3;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)==0)
      		{
      	    	anxian = 1;
      			*key = 4;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7));
      		}
      		else  //如果第一行四列中没有按钮被按下
      		{
      			anxian = 0;
      			GPIO_Write(GPIOA,0x00ff);
      		}
      	}//第一行判断完成,这是我们判断第二行
      	GPIO_Write(GPIOA,0x00fd);    //第二行         
      	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(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)
      		{
      			anxian = 1;
      			*key = 5;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==0)
      		{
      	     	anxian = 1;
      			*key = 6;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==0)
      		{
      	    	anxian = 1;
      			*key = 7;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)==0)
      		{	 
      	    	anxian = 1;
      			*key = 8;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7));
      		}
      		else 
      		{
      			anxian = 0;
      			GPIO_Write(GPIOA,0x00ff);
      		}
      	}
      	GPIO_Write(GPIOA,0x00fb);//第三行
      	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(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)
      		{
      			anxian = 1;
      			*key = 9;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==0)
      		{
      	     	anxian = 1;
      			*key = 10;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==0)
      		{
      	    	anxian = 1;
      			*key = 11;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)==0)
      		{
      	    	anxian = 1;
      			*key = 12;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7));
      		}
      		else 
      		{
      			anxian = 0;
      			GPIO_Write(GPIOA,0x00ff);
      		}
      	}
      	GPIO_Write(GPIOA,0x00f7);//第四行
      	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(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)
      		{
      			anxian = 1;
      			*key = 13;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==0)
      		{
      	     	anxian = 1;
      			*key = 14;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==0)
      		{
      	    	anxian = 1;
      			*key = 15;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6));
      		}
      		else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)==0)
      		{
      	    	anxian = 1;
      			*key = 16;
      			while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7));
      		}
      		else 
      		{
      			anxian = 0;
      			GPIO_Write(GPIOA,0x00ff);
      		}
      	}
      	
      }
      

      我们拉出来讲一个(第一行)。

      GPIO_Write(GPIOA,0x00fe);(0000 0000 1111 1110)

      根据我们打开串口的位置可知——>低四位为行,所以1110可得将第一行的电位设置为0,即低电平,高四位为列,所以11111可得前四列全部保持高电平,等待我们按下按键。

      这里属于炫技了,其实你可以手都复位GPIO_Pin_0使其为低电平。其他不改变!

      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(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)  

      //如果第一列处于低电平则表示第一列被按下

              {

                  anxian = 1;   

                  *key = 1;  

                  while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4));   
      当按钮处于被按下的状态的时候,程序一直卡在循环读取按钮的状态,避免多按钮同时按下时读取错误(必须有这个否则代码运行顺序会混乱)

              }

      else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==0)

              {

                  anxian = 1;

                  *key = 2;

                  while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5));

              }

              else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==0)

              {

                  anxian = 1;

                  *key = 3;

                  while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6));

              }

              else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)==0)

              {

                  anxian = 1;

                  *key = 4;

                  while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7));

              }

      到这里都是一列一列的判断,掌握一个就行

              else  //如果第一行四列中没有按钮被按下

              {

                  anxian = 0;

                  GPIO_Write(GPIOA,0x00ff);

      (0000 0000 1111 1111)

      再次全部拉高,按键程序中断!

              }

          }

      第一行判断完成,判断第二行

      主函数使用

      KEY_Scan (&key);
      		if(FLAG == 1)    
      		{
      			FLAG = 0;
      			if(key==1)  
      			{
      				//运行我需要的代码。
      			}	

      在主函数时因为Key是一个指针所以在调用时使用&key

      当按键被按下时:FLAG置1——判断按键FLAG为1进入if函数内部——再将FLAG置零用于下次判断——如果我们接受到key指针的返回值为1——运行我们的程序!!!!

       

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]