1. 程式人生 > >STM32CubeMX學習教程之三:GPIO輸入之利用SysTick中斷給按鍵去抖

STM32CubeMX學習教程之三:GPIO輸入之利用SysTick中斷給按鍵去抖

完整原始碼下載:https://github.com/simonliu009/STM32CubeMX-GPIO-Debounce

上一篇博文講述瞭如何使用GPIO的外部中斷檢測按鍵控制LED。但是實際情況是,物理按鍵通常會有抖動,導致中斷多次被觸發。較好的設計,應該是在硬體上做去抖設計,比如設計RC電路,或者單端穩態電路、施密特觸發器(比如NEC555)等來實現。硬體去抖的好處是可以避免抖動產生的負壓對MCU的GPIO造成的可能損壞。壞處是增加成本。而軟體實現的去抖則無法避免負壓帶來的可能損害。

本文主要討論軟體去抖。實現方法是通過SysTick中斷每1ms對按鍵進行掃描,當檢測到連續的穩定無抖動電平訊號(長度可設定)之後,才進行相應的邏輯操作。

軟體版本:
STM32CubeMX V4.25.0  
System Workbench V2.4

硬體:OneNet 麒麟座V2.3

在STM32CubeMX中新建專案,選擇正確的MCU型號

然後設定RCC和SYS,然後根據板子實際情況設定時鐘(麒麟座外部晶振是12M,STM32F103x的最高主頻是72M



然後設定GPIO_Output (連線到LED) 和GPIO_Input(連線到按鍵)。注意上一篇文章裡面按鍵連線的引腳設定為外部中斷模式,這裡只需要設定為GPIO_Input就可以了。


GPIO_Output的具體設定如下:


GPIO_Input設定如下


這裡按鍵我用了SW1/2/3/4。

同樣修改

Project - setting ,ToolChain/IDE選擇 SW4STM32

還要勾選這裡

然後生成程式碼,開啟專案。

編輯stm32f1xx_it.h,函式宣告那裡增加一行 :

void Key_Scan(void);

然後編輯stm32f1xx_it.c 增加如下程式碼:

/* USER CODE BEGIN 0 */
uint8_t sw1Count,sw2Count,sw3Count,sw4Count;
uint8_t pushFlag1,pushFlag2,pushFlag3,pushFlag4;
extern uint8_t swState1,swState2,swState3,swState4;
 void Key_Scan(void)
{
         /*檢測SW1是否按下 */
         if(   HAL_GPIO_ReadPin(SW1_GPIO_Port,SW1_Pin) == GPIO_PIN_RESET )
         {
                 sw1Count++;                         //SW1鍵按下,計數sw1Count加1
                 if(sw1Count>=32)                    //1MS中斷一次,sw1Count大於等於32,即按鍵已按下32ms
                 {
                         if(pushFlag1==0)                //判斷有沒有重按鍵,1為有,0為沒有
                        {
                        swState1=1;                       //設定按鍵標誌
                        sw1Count=0;
                        pushFlag1=1;                     //設定重按鍵標誌
                        }
                        else
                        sw1Count=0;
                 }
                 else
                	    swState1=0;

         }
         else                                            //無按鍵按下
         {
                 sw1Count=0;                           //清零sw1Count
                 swState1=0;                            //清除按鍵標誌
                 pushFlag1=0;                          //清除重按鍵標誌
         }

         /*檢測SW2是否按下 */
         if(   HAL_GPIO_ReadPin(SW2_GPIO_Port,SW2_Pin) == GPIO_PIN_RESET )
         {
                 sw2Count++;                         //SW2鍵按下,計數sw2Count加1
                 if(sw2Count>=32)                    //1MS中斷一次,sw2Count大於等於32,即按鍵已按下32ms
                 {
                         if(pushFlag2==0)                //判斷有沒有重按鍵,1為有,0為沒有
                        {
                        swState2=1;                       //設定按鍵標誌
                        sw2Count=0;
                        pushFlag2=1;                     //設定重按鍵標誌
                        }
                        else
                        sw2Count=0;
                 }
                 else
                	    swState2=0;

         }
         else                                            //無按鍵按下
         {
                 sw2Count=0;                           //清零sw2Count
                 swState2=0;                            //清除按鍵標誌
                 pushFlag2=0;                          //清除重按鍵標誌
         }

         /*檢測SW3是否按下 */
         if(   HAL_GPIO_ReadPin(SW3_GPIO_Port,SW3_Pin) == GPIO_PIN_RESET )
         {
                 sw3Count++;                         //SW3鍵按下,計數sw3Count加1
                 if(sw3Count>=32)                    //1MS中斷一次,sw3Count大於等於32,即按鍵已按下32ms
                 {
                         if(pushFlag3==0)                //判斷有沒有重按鍵,1為有,0為沒有
                        {
                        swState3=1;                       //設定按鍵標誌
                        sw3Count=0;
                        pushFlag3=1;                     //設定重按鍵標誌
                        }
                        else
                        sw3Count=0;
                 }
                 else
                	    swState3=0;

         }
         else                                            //無按鍵按下
         {
                 sw3Count=0;                           //清零sw3Count
                 swState3=0;                            //清除按鍵標誌
                 pushFlag3=0;                          //清除重按鍵標誌
         }

         /*檢測SW4是否按下 */
         if(   HAL_GPIO_ReadPin(SW4_GPIO_Port,SW4_Pin) == GPIO_PIN_RESET )
         {
                 sw4Count++;                         //SW4鍵按下,計數sw4Count加1
                 if(sw4Count>=32)                    //1MS中斷一次,sw4Count大於等於32,即按鍵已按下32ms
                 {
                         if(pushFlag4==0)                //判斷有沒有重按鍵,1為有,0為沒有
                        {
                        swState4=1;                       //設定按鍵標誌
                        sw4Count=0;
                        pushFlag4=1;                     //設定重按鍵標誌
                        }
                        else
                        sw4Count=0;
                 }
                 else
                	    swState4=0;

         }
         else                                            //無按鍵按下
         {
                 sw4Count=0;                           //清零sw4Count
                 swState4=0;                            //清除按鍵標誌
                 pushFlag4=0;                          //清除重按鍵標誌
         }

}
/* USER CODE END 0 */

然後在SysTick中斷處理函式增加一行 void Key_Scan(void);, 程式碼如下:

/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
	Key_Scan();
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  HAL_SYSTICK_IRQHandler();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

 在gpio.c 中增加如下兩處程式碼:

/* USER CODE BEGIN 1 */
GPIO_TypeDef* GPIO_PORT[] = {LED1_GPIO_Port,
                             LED2_GPIO_Port,
			     LED3_GPIO_Port,
			     LED4_GPIO_Port};

const uint16_t GPIO_PIN[] = {LED1_Pin,
			     LED2_Pin,
                             LED3_Pin,
			     LED4_Pin};

/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
void LED_Toggle(Led_TypeDef Led)
{
HAL_GPIO_TogglePin(GPIO_PORT[Led], GPIO_PIN[Led]);
}

/* USER CODE END 2 */

然後編輯main.c,增加如下兩處程式碼:

/* USER CODE BEGIN 0 */
uint8_t swState1,swState2,swState3,swState4;
/* USER CODE END 0 */
 /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if  ( swState1 == 1 )
	  {
		  LED_Toggle(LED1);
//		  HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
		  HAL_Delay(1);
	  }
	  if  ( swState2 == 1 )
	  {
		  LED_Toggle(LED2);
//		  HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
		  HAL_Delay(1);
	  }
	  if  ( swState3 == 1 )
	  {
		  LED_Toggle(LED3);
//		  HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
		  HAL_Delay(1);
	  }
	  if  ( swState4 == 1 )
	  {
		  LED_Toggle(LED4);
//		  HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin);
		  HAL_Delay(1);
	  }
  /* USER CODE END WHILE */
注意 如下兩個語句是等效的,我註釋掉了HAL_GPIO_TogglePin()。因為我們使用列舉重新定義了LED狀態切換的函式LED_Toggle()。
1.		  LED_Toggle(LED1);  
2.		  HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);

然後右鍵點選專案,選擇Properties, Run-Debug Settings, 點選右側的New,在彈出對話方塊中選擇Ac6 STM32 Debugging。


然後工作列上點選Run圖,當然會報錯的,原因請檢視另一篇我的部落格(https://blog.csdn.net/toopoo/article/details/79680323),所以需要右鍵點選  專案名Run.cfg ,給它改個名字,


然後右鍵點選專案樹裡面的專案名稱,選擇“Propeties”,然後在Run/Debug Settings-選擇專案名-Edit-Main-C/C++Application那裡點選“Search Project”,然後選擇出現的預設的elf檔案:


然後在Debugger-User Defined-Browse 那裡選擇你自己改名的配置檔案:


然後右鍵點選那個新的cfg檔案,選擇"Open With - Text Editor", 進行如下更改:

source [find interface/stlink.cfg] 更改為 source [find interface/stlink-v2.cfg]

reset_config srst_only srst_nogate connect_assert_srst 這一行改為 reset_config none 

然後再Run一下,就可以了。


就實現四個按鍵分別控制LED的開關切換並實現了軟體去抖,不會產生誤動作了。

本文參考瞭如下文章:

http://www.waveshare.net/study/article-630-1.html

https://blog.csdn.net/yongyooh/article/details/21877227