stm32矩阵按键状态机(可快速移植)


首先声明,所写内容是协会成员集体成果。其中过程艰难坎坷,寻找过大量资料,失败过无数次,最终成功运行。其中仍有不足,欢迎大佬批评指正!


目录

一、扫描按键思路

二、状态机思路

三、代码部分

四、小结

 


一、扫描按键思路

1.原理图

 2.使用引脚的选择

IO口的选择很重要,一定要选没有被复用的空闲端口,以及最好是连在一起的GPIO口,首先被复用的话就直接无法正常输入输出,也就无法编程;

连在一起是因为4*4矩阵键盘需要8个引脚,用库函数操作太麻烦了,所以我们直接采用寄存器操作,连在一起的话寄存器比较容易控制(其实我们也尝试过不连在一起,但由于寄存器方面知识学的不太好,怎么也实现不了,希望有大佬指点)

3.原理讲解

 

我们使用的方法是逐行扫描,读取列的电平来判断键值

  1. 首先,将PB8到PB11全部输出为高电平,如果,PB12到PB15不为0,则,有键按下。
  2. 然后,进行逐行检测,即将P8到P11依次置高,读取PB12到PB15的值,判断具体为哪个键被按下

其中PB12到PB15一直为下拉输入模式,用于读取按键变化

二、状态机思路

状态机我们先配置一个定时器,如TIM2,用于每10ms进入中断扫描一次按键,正好跳过抖动

然后状态设为3个,分别是:

 状态一:KEY_Up ,空闲状态检测是否有按键按下

 状态二:KEY_Rowscan,逐行检测判断具体哪个键按下

 状态三:KEY_Wait,等待松手返回状态一

 如果对状态机还有疑问,可参考这位大佬的:

https://blog.csdn.net/xdedmbb/article/details/129738797?app_version=5.15.0&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129738797%22%2C%22source%22%3A%22xdedmbb%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app

三、代码部分


定时器部分需自己配,这里只展示按键部分和main.c


1.Matrix_Key.h

/**
  ******************************************************************************
  * @file    Matrix_Key.h
  * @author  22级电子协会全体成员
  * @version V1.0
  * @date    18-March-2023
  * @brief   This file contains all the functions prototypes for the KEY 
  *          firmware library.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, XuQQ SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT
  * OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
  * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION
  * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2022 XuQQ</center></h2>
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MATRIX_KEY_H
#define __MATRIX_KEY_H

#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "sys.h"



/** @defgroup KEY_Exported_Macros 宏定义
  * @{
  */
      #define KEY_Up         1  
      #define KEY_Rowscan    2
      #define KEY_Wait       3	 
      
      #define key_allput_High      GPIO_Write(GPIOB,(GPIOB_Out | 0x0f00))  //先让PB8到PB11全部输出高。 
      #define key_judge_have       (GPIOB_In) != 0x0000                      //如果,PB12到PB15不为0,则,有键按下
      
      #define First_rowscan        GPIO_Write(GPIOB,(GPIOB_Out | 0x0100))	//让PB11到PB8输出二进制的0001.
      #define Second_rowscan       GPIO_Write(GPIOB,(GPIOB_Out | 0x0200))	//让PB11到PB8输出二进制的0010.
      #define Third_rowscan        GPIO_Write(GPIOB,(GPIOB_Out | 0x0400))	//让PB11到PB8输出二进制的0100.
      #define Fourth_rowscan       GPIO_Write(GPIOB,(GPIOB_Out | 0x0800))	//让PB11到PB8输出二进制的1000.
      
      #define key_row_down         (GPIOB_In) > 0x0000                   //单行 有按键按下
      #define key_row_up           (GPIOB_In) == 0x0000                   //松手
      
      #define GPIOB_Out            GPIOB->ODR & 0xf0ff
      #define GPIOB_In             GPIOB->IDR & 0xf000

      #define FALSE 0
      #define TRUE 1 
/**
  * @}
  */
 

void Key_GPIO_Init(void);//IO初始化

void KEY_Scan(void);  //按键扫描函数		


#ifdef __cplusplus
}
#endif

#endif /* __KEY_H */

/******************* (C) COPYRIGHT 2022 XuQQ *****END OF FILE****/

2.Matrix_Key.c

/**
  ******************************************************************************
  * @file    Matrix_Key.c
  * @author  22级电子协会全体成员
  * @version V1.0
  * @date    18-March-2023
  * @brief   This file provides all the KEY firmware functions.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, XuQQ SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT
  * OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
  * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION
  * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2022 XuQQ/center></h2>
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "Matrix_Key.h"
#include "delay.h"

/**
  * @brief  void KEY_Init(void);  // 初始化程序
	* @note   初始化程序 
  * @param  None
  * @retval None
  */


//4*4矩阵键盘 已经在STM32F103测试通过。
//2013-08-02 BY hjlmgc
///
//GPIO的硬件分配:
//第0行:PB8  ----|------|------|------|
//第1行:PB9  ----|------|------|------|
//第2行:PB10 ----|------|------|------|
//第3行:PB11 ----|------|------|------|
//              PB12   PB13   PB14   PB15
//             第0列   第1列  第2列  第3列  
/
//键值:
//1----2----3----C
//4----5----6----D
//7----8----9----E
//A----0----B----F 
/
#define KEY_WAY_1 //第一种方法
//
//KEY_WAY_1的GPIO初始化
//
void Key_GPIO_Init(void)
{
#ifdef KEY_WAY_1
	GPIO_InitTypeDef  GPIO_InitStructure;
 	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB端口时钟
		
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //PB8-PB11推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB
	
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;              
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;	//PB12-15 下拉输入
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
#endif	
}

//初始状态4列全部置0,逐行检测
///

u8 key_return=10;

void KEY_Scan(void)
{
#ifdef KEY_WAY_1
  
  static u8 key_state=KEY_Up;
  static u8 KEY_flag=FALSE;
  int KeyVal=-1;	 //keyVal为按键数值 
  
  switch(key_state)
  {
/********************状态1:KEY_Up*********************/ 
	  case KEY_Up:
    { 
      key_allput_High;//先让PB8到PB11全部输出高。
      if(key_judge_have)//如果,PB12到PB15不为0,则,有键按下
        key_state = KEY_Rowscan;//判断是否抖动
    }
    break;
    
/********************状态2:KEY_Rowscan*********************/
    case KEY_Rowscan:
    {
      if(KEY_flag==FALSE)
      {
        //扫描第一行
        First_rowscan;
        if(key_row_down)
        {
          switch(GPIOB_In)	 //对PB15到PB12的值进行判断,以输出不同的键值。
          {
            case 0x1000: KeyVal=1;KEY_flag=TRUE;	break;
            case 0x2000: KeyVal=2;KEY_flag=TRUE;	break;
            case 0x4000: KeyVal=3;KEY_flag=TRUE;	break;
            case 0x8000: KeyVal='C';KEY_flag=TRUE;	break;
            default: break;
          }
//          key_state = KEY_Wait;
          
        }
      } 
      
      if(KEY_flag==FALSE)
      {
        //扫描第二行
        Second_rowscan;
        if(key_row_down)
        {
          switch(GPIOB_In)	 //对PB15到PB12的值进行判断,以输出不同的键值。
          {
            case 0x1000: KeyVal=4;KEY_flag=TRUE;	break;
            case 0x2000: KeyVal=5;KEY_flag=TRUE;	break;
            case 0x4000: KeyVal=6;KEY_flag=TRUE;	break;
            case 0x8000: KeyVal='D';KEY_flag=TRUE;	break;
            default: break;
          }
//          key_state = KEY_Wait;
          
        } 
      }
      
      if(KEY_flag==FALSE)
      {  
        //扫描第三行
        Third_rowscan;
        if(key_row_down)
        {
          switch(GPIOB->IDR & 0xf000)	  //对PB15到PB12的值进行判断,以输出不同的键值。
          {
            case 0x1000: KeyVal=7;KEY_flag=TRUE;	break;
            case 0x2000: KeyVal=8;KEY_flag=TRUE;	break;
            case 0x4000: KeyVal=9;KEY_flag=TRUE;	break;
            case 0x8000: KeyVal='E';KEY_flag=TRUE;	break;
            default: break;
          }
//          key_state = KEY_Wait;
          
        } 
      }
      
      if(KEY_flag==FALSE)
      {
        //扫描第四行
        Fourth_rowscan;
        if(key_row_down)
        {
          switch(GPIOB->IDR & 0xf000)	  //对PB15到PB12的值进行判断,以输出不同的键值。
          {
            case 0x1000: KeyVal='A';KEY_flag=TRUE;	break;
            case 0x2000: KeyVal=0;KEY_flag=TRUE;	break;
            case 0x4000: KeyVal='B';KEY_flag=TRUE;	break;
            case 0x8000: KeyVal='F';KEY_flag=TRUE;	break;
            default: break;
          }
//          key_state = KEY_Wait;
          
        } 
      }
      
      if(KEY_flag==TRUE)
        key_state = KEY_Wait;
    }
	  break;
  
/********************状态3:KEY_Wait*********************/
    case KEY_Wait:
    {
      if(key_row_up)
      {
        key_state = KEY_Up;//完成一次按键动作,切换到状态1
      }
 
    }
    break;
    
  }
  
  if(KEY_flag == TRUE)
  {
     key_return = KeyVal;
     
     KEY_flag = FALSE;
  }

#endif
}


/******************* (C) COPYRIGHT 2022 XUQQ *****END OF FILE****/

3.main.c

/**
  ******************************************************************************
  * @file    main.c 
  * @author  22级电子协会全体成员
  * @version V3.5.0
  * @date    18-March-2023
  * @brief   Main program body.
  *              (01)初始化按键;
  *              (02)按下按键控制相应的LED灯亮
  *              
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 XuQQ</center></h2>
  ******************************************************************************
  */ 

/* Includes ----------------------------------库文件--------------------------*/
#include "stm32f10x.h"
#include "bmp.h"

/** @addtogroup STM32F10x_StdPeriph_Examples
  * @{
  */


/* Private typedef --------------------------变量类型重新定义-----------------*/
/* Private define ---------------------------私有定义-------------------------*/
/* Private macro ----------------------------私有宏定义-----------------------*/
/* Private variables ------------------------私有变量-------------------------*/
/* Private function prototypes --------------私有函数属性---------------------*/
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/* Private functions ------------------------私有函数-------------------------*/

/**
  * @brief  Main program.
  * @note   
  * @param  None
  * @retval None
  */
int main(void)
{
  LED_Init();
  Key_GPIO_Init();
  KEY_Scan();
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  TIM2_Int_Init(19, 7199);//定时2ms进一次中断
	while(1)
	{
    if(key_return==1)
    {
      PAout(0)=!PAout(0);//LED0取反
      key_return=10;
    }
    if(key_return==2)
    {
      PAout(1)=!PAout(1);//LED1取反
      key_return=10;
    }
    if(key_return==4)
    {
      PAout(0)=!PAout(0);//LED0取反
      key_return=10;
    }
    if(key_return==7)
    {
      PAout(1)=!PAout(1);//LED1取反
      key_return=10;
    }
    if(key_return==0)
    {
      PAout(0)=!PAout(0);//LED0取反
      key_return=10;
    }
	}

}

#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
		
  }
}


#endif

/**
  * @}
  */

/**
  * @}
  */


/******************* (C) COPYRIGHT 2011 XuQQ *****END OF FILE****/

四、小结

 写完了,还是有一些瑕疵,但还是希望能对你有所帮助,欢迎大佬批评指正!

视频效果:stm32矩阵按键状态机效果_哔哩哔哩_bilibili

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
STM32 4*4矩阵键盘原理是指使用STM32微控制器和4行4列的矩阵键盘组合起来实现按键输入的一种方法。 矩阵键盘是一种常见的输入设备,由多个按键按照矩阵排列组成,每个按键连接到矩阵的一个交叉点上。4*4矩阵键盘共有4行和4列,总共16个按键。 在STM32微控制器中,通过使用GPIO(通用输入输出)引脚作为键盘的控制线和数据线,实现对矩阵键盘的扫描。具体工作步骤如下: 1. 初始化GPIO引脚:选择4个GPIO引脚作为行线,设置为输出模式,并把它们输出高电平。选择4个GPIO引脚作为列线,设置为输入模式,开启上拉电阻。 2. 扫描行:将其中一个行线设置为低电平,其余行线保持高电平。然后读取列线的电平状态,判断是否有按键按下。如果有按键按下,说明该列线和行线交叉点有连接。 3. 判断按键:通过检测哪个列线有电平变化,可以确定按下的按键所在的列。再结合之前设置的低电平行线,就可以确定按键所在的行。通过这样的扫描方式,可以检测到按键的位置。 4. 键值获取:根据按键所在的行和列,可以确定按键的唯一编号或者键值。可以通过查找键值对应的字符、数字或其他功能来实现具体的按键响应。 5. 不断循环扫描:通过不断循环这些步骤,可以不断检测键盘状态,实现对按键的准确响应。 通过上述原理,STM32 4*4矩阵键盘可以实现对16个按键的检测和响应,广泛应用于各种嵌入式系统和电子产品中,以实现人机交互。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东湖西泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值