STM32-4*4矩阵键盘原理及实现HAL库+标准库版

项目器件

STM32F103C8T6开发板

0.96英寸4针脚OLED

4*4矩阵键盘

程序现象

如图 1,按下按键后回在OLED屏幕上显示按键的行列号(如13代表第1行第3列按键)

1

项目接线

矩阵键盘:行线接开发板PA0-PA3,列线接开发版PA4-PA7

OLED:时钟线(SCK)接PB8,数据线(SDA)接PB9

实现原理

        4*4矩阵键盘本质上是在线与线的交叉处放置按键(如图 2,图中线号为错误,请勿使用!),本代码使用动态扫描的方式实现按键检测。

2

        首先按键的四个行配置为通用推挽输出,四个列配置为上拉输入。当扫描某一行时,需要将该行输出低电平,当该行有按键按下时线路导通,线路电平被拉低,对应列的输入口电平将被拉低。此时可以检测列电平,读取列口的电平情况,读取到低电平的口则则证明时该行该列的按键被按下。重复4次以上步骤则可实现对4*4矩阵键盘的整体扫描。

实现代码

HAL库

CubeMX配置

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "OLED.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define KEY_ROW1_OUT			GPIO_PIN_0
#define KEY_ROW2_OUT			GPIO_PIN_1
#define KEY_ROW3_OUT			GPIO_PIN_2
#define KEY_ROW4_OUT			GPIO_PIN_3

#define KEY_COL1_IN				GPIO_PIN_4
#define KEY_COL2_IN				GPIO_PIN_5
#define KEY_COL3_IN				GPIO_PIN_6
#define KEY_COL4_IN				GPIO_PIN_7


/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint8_t Key_Scan(void);

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint8_t KeyNum=0;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
	OLED_Init();
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
	OLED_ShowString(1,1,"KeyNum:");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		KeyNum = Key_Scan();
		OLED_ShowNum(1,8,KeyNum,2);		
		
		//LEDStream_Right();
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
uint8_t Key_Scan(void)
{
	uint8_t KEY_ROWs[4] = {KEY_ROW1_OUT, KEY_ROW2_OUT, KEY_ROW3_OUT, KEY_ROW4_OUT};
	uint8_t KEY_COLs[4] = {KEY_COL1_IN, KEY_COL2_IN, KEY_COL3_IN, KEY_COL4_IN};
	
	uint8_t key[4][4] = {0};
	uint8_t Answer=0;
	
	int i, j;
	for(i=0;i<4;i++)
	{
		//行扫描 拉低该行电平
		HAL_GPIO_WritePin(GPIOA,KEY_ROWs[i],GPIO_PIN_RESET);
		for(j=0;j<4;j++)
		{
			if(HAL_GPIO_ReadPin(GPIOA,KEY_COLs[j]) == GPIO_PIN_RESET)
			{
				key[i][j] = 1;
			}
			else
			{
				key[i][j] = 0;
			}
		}
		//扫描结束 拉高行电平
		HAL_GPIO_WritePin(GPIOA,KEY_ROWs[i],GPIO_PIN_SET);
	}
	
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(key[i][j] == 1)
				Answer = ((i+1)*10)+(j+1);
		}
	}
	return Answer;
}


/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#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 CODE BEGIN 6 */
  /* 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) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

标准库

main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Button4_4.h"
#include "Delay.h"


uint8_t KeyNum;

int main(void)
{
	Button4_4_Init();
	OLED_Init();
	
	OLED_ShowString(1,1,"KeyNum:");
	
	while (1)
	{
		KeyNum = Key_Scan();
		Light_LEDs(KeyNum);
		if(KeyNum!=0)
		{
			OLED_ShowNum(1,8,KeyNum,2);
			Delay_ms(100);
		}
		else
		{
			OLED_ShowString(1,8,"    ");
		}
	}
}



button4_4.c

#include "stm32f10x.h"                  // Device header

#define ROW_1_KEYOUT		GPIO_Pin_0		
#define ROW_2_KEYOUT		GPIO_Pin_1		
#define ROW_3_KEYOUT		GPIO_Pin_2		
#define ROW_4_KEYOUT		GPIO_Pin_3		

#define COL_1_KEYIN			GPIO_Pin_4		
#define COL_2_KEYIN			GPIO_Pin_5		
#define COL_3_KEYIN			GPIO_Pin_6		
#define COL_4_KEYIN			GPIO_Pin_7	


uint16_t Rows[4] = {ROW_1_KEYOUT,ROW_2_KEYOUT,ROW_3_KEYOUT,ROW_4_KEYOUT};
uint16_t Cols[4] = {COL_1_KEYIN,COL_2_KEYIN,COL_3_KEYIN,COL_4_KEYIN};

void Button4_4_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//初始化行 输出低电平
	GPIO_InitTypeDef GPIO_Row_InitStructure;
	GPIO_Row_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Row_InitStructure.GPIO_Pin = Rows[0] | Rows[1] | Rows[2] | Rows[3];
	GPIO_Row_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_Row_InitStructure);
	
	//初始化列 默认高电平
	GPIO_InitTypeDef GPIO_Col_InitStructure;
	GPIO_Col_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Col_InitStructure.GPIO_Pin = Cols[0] | Cols[1] | Cols[2] | Cols[3];
	GPIO_Col_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_Col_InitStructure);
	
	//默认设置行为高电平
	GPIO_SetBits(GPIOA,Rows[0] | Rows[1] | Rows[2] | Rows[3]);
}


uint8_t Key_Scan(void)
{
	uint16_t key[4][4] = {0};
	
	int i, j;
	for(i=0;i<4;i++)
	{
		GPIO_ResetBits(GPIOA,Rows[i]);
		for(j=0;j<4;j++)
		{
			if(GPIO_ReadInputDataBit(GPIOA,Cols[j]) == 0)
			{
				key[i][j] = 1;
			}
			else
			{
				key[i][j] = 0;
			}
		}
		GPIO_SetBits(GPIOA,Rows[i]);
	}
	
	if(key[0][0]==1)return 11;
  else if(key[0][1]==1)return 12;
  else if(key[0][2]==1)return 13;
  else if(key[0][3]==1)return 14;
  else if(key[1][0]==1)return 21;
  else if(key[1][1]==1)return 22;
  else if(key[1][2]==1)return 23;
  else if(key[1][3]==1)return 24;
  else if(key[2][0]==1)return 31;
  else if(key[2][1]==1)return 32;
  else if(key[2][2]==1)return 33;
  else if(key[2][3]==1)return 34;
  else if(key[3][0]==1)return 41;
  else if(key[3][1]==1)return 42;
  else if(key[3][2]==1)return 43;
  else if(key[3][3]==1)return 44;
	else return 0;
}



button4_4.h

#ifndef __BUTTON4_4_H
#define __BUTTON4_4_H

	
void Button4_4_Init(void);
uint8_t Key_Scan(void);





#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值