STM32--矩陣鍵盤的設計實現

MemsanGZmInG發表於2020-12-03

關於STM32的矩陣鍵盤實現

專案開發或者日常實驗中有時候需要用到多個按鍵,但微控制器IO口資源本身並不多,如果為每一個按鍵都分配一個IO的話未免太過浪費,所以使用矩陣鍵盤可以儘可能的節省微控制器IO口資源。

開發準備:

1.系統:Windows
2.軟體:Keil5
3.微控制器:STM32F103C8T6

矩陣鍵盤與普通的一個IO對應一個按鍵的普通鍵盤不同。如圖所示,16個按鍵通過圖中的電路連線,構成了行和列,而只僅僅引出了8條線路,所以只需要8個IO口就能識別16個按鍵。與之類似的還有4x2矩陣鍵盤、3x3矩陣鍵盤等。4x4矩陣鍵盤電路圖
掃描矩陣鍵盤的鍵值可以使用逐行(列)掃描法。在STM32中,4個行IO設定為上拉輸入,4個列IO設定為推輓輸出低電平。當其中一個按鍵按下時,該按鍵對應的行IO口會被置為低電平,這時,將4個列IO逐個拉高,每拉高一個的同時檢測行IO的電平,如果行IO的電平隨著變化的話,就能確定哪個按鍵按下。當然,設定列IO為輸入,或者下拉也是可以的,原理同上。

實驗裡使用2x4的矩陣鍵盤檢測(原理一樣,只是少了兩行掃描而已)。

/*****GPIO口配置*****/
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pins : Rowline1_Pin Rowline2_Pin */		
  GPIO_InitStruct.Pin = Rowline1_Pin|Rowline2_Pin;	//行IO下拉輸入
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : Column1_Pin Column2_Pin Column3_Pin Column4_Pin */
  GPIO_InitStruct.Pin = Column1_Pin|Column2_Pin|Column3_Pin|Column4_Pin;//列IO推輓輸出,無上下拉
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, Column1_Pin|Column2_Pin|Column3_Pin|Column4_Pin, GPIO_PIN_SET);
}
/*********列IO操作巨集定義*********/
#define Column1_OUT_Low  HAL_GPIO_WritePin(Column1_GPIO_Port, Column1_Pin, GPIO_PIN_RESET)
#define Column2_OUT_Low  HAL_GPIO_WritePin(Column2_GPIO_Port, Column2_Pin, GPIO_PIN_RESET)
#define Column3_OUT_Low  HAL_GPIO_WritePin(Column3_GPIO_Port, Column3_Pin, GPIO_PIN_RESET)
#define Column4_OUT_Low  HAL_GPIO_WritePin(Column4_GPIO_Port, Column4_Pin, GPIO_PIN_RESET)

#define Column1_OUT_High  HAL_GPIO_WritePin(Column1_GPIO_Port, Column1_Pin, GPIO_PIN_SET)
#define Column2_OUT_High  HAL_GPIO_WritePin(Column2_GPIO_Port, Column2_Pin, GPIO_PIN_SET)
#define Column3_OUT_High  HAL_GPIO_WritePin(Column3_GPIO_Port, Column3_Pin, GPIO_PIN_SET)
#define Column4_OUT_High  HAL_GPIO_WritePin(Column4_GPIO_Port, Column4_Pin, GPIO_PIN_SET)

/*********行IO操作巨集定義*********/
#define Rowline1_INPUT_Read  HAL_GPIO_ReadPin(Rowline1_GPIO_Port, Rowline1_Pin)
#define Rowline2_INPUT_Read  HAL_GPIO_ReadPin(Rowline2_GPIO_Port, Rowline2_Pin)



uint8_t key_scan(void){
	uint8_t i, keynum = 0;
	if (Rowline1_INPUT_Read == GPIO_PIN_SET){			//當有按鍵按下		
		for (i = 1; i <= 4; i++){
			if (i == 1) Column1_OUT_Low;		//逐個拉低每列的IO
			else if (i == 2) Column2_OUT_Low;
			else if (i == 3) Column3_OUT_Low;
			else if (i == 4) Column4_OUT_Low;
			if (Rowline1_INPUT_Read == GPIO_PIN_RESET){	//如果檢測到行IO電平跟著變化,則判斷為該按鍵按下			
				keynum = i;							
				if (i == 1) Column1_OUT_High;
				else if (i == 2) Column2_OUT_High;
				else if (i == 3) Column3_OUT_High;	//每次列IO電平操作之後記得復原
				else if (i == 4) Column4_OUT_High;
				break;				
			}
		}
	}else if (Rowline2_INPUT_Read == GPIO_PIN_SET){
		for (i = 1; i <= 4; i++){
			if (i == 1) Column1_OUT_Low;
			else if (i == 2) Column2_OUT_Low;
			else if (i == 3) Column3_OUT_Low;
			else if (i == 4) Column4_OUT_Low;
			if (Rowline2_INPUT_Read == GPIO_PIN_RESET){				
				KeyValue.keynum = i+4;			
				if (i == 1) Column1_OUT_High;
				else if (i == 2) Column2_OUT_High;
				else if (i == 3) Column3_OUT_High;
				else if (i == 4) Column4_OUT_High;
				break;
			}			
		}
	}
	return keynum;
}

上述程式碼可以檢測到哪個按鍵按下,但在實際專案開發途中,按下以及鬆開都需要進行一系列的消抖操作來判斷到底是真的按下跟真的鬆開。關於這個問題,下文會詳細的描述。

相關文章