裸機--按鍵采集方式:
- 掃描方式:while(1)中不斷掃描引腳的高低電平,實現掃描按鍵的功能,效率低
- 外部中斷:把中斷源和引腳連接起來.再設置外部中斷觸發方式.實現按鍵觸發外部中斷,效率高
- 定時器中斷:每隔一段時間檢測引腳電平,然后消抖.實現按鍵檢測.常用於實時系統等復雜場景.裸機也可以用.
- 按鍵FIFO:
- 狀態機+定時器
- 隊列

/* 掃描方式,代碼取自野火13(按鍵檢測) */ /* 主函數 */ int main(void) { /* LED端口初始化 */ LED_GPIO_Config(); LED1_ON; /* 按鍵端口初始化 */ Key_GPIO_Config(); /* 輪詢按鍵狀態,若按鍵按下則反轉LED */ while(1) { if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) { /*LED1反轉*/ LED1_TOGGLE; } if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) { /*LED2反轉*/ LED2_TOGGLE; } } } /* 初始化按鍵 */ void Key_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*開啟按鍵端口的時鍾*/ RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE); //選擇按鍵的引腳 GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN; // 設置按鍵的引腳為浮空輸入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //使用結構體初始化按鍵 GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure); //選擇按鍵的引腳 GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN; //設置按鍵的引腳為浮空輸入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //使用結構體初始化按鍵 GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure); } /* 按鍵檢測函數 */ uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin) { /*檢測是否有按鍵按下 */ if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON ) { /*等待按鍵釋放 */ while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON); return KEY_ON; } else return KEY_OFF; }

1 /* 外部中斷方式,代碼取自野火18(EXIT-外部中斷) */ 2 /* 中斷優先級配置 */ 3 static void NVIC_Configuration( void ) 4 { 5 NVIC_InitTypeDef NVIC_InitStructure; 6 7 //將優先級組配置成第1組 8 NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1 ); 9 10 /***************************************************************************** 11 NVIC_IRQChannel指需要配置的中斷向量。因使用PE4口的按鍵,所以配置在4通道 12 如果使用GPIO_PIN_5~GPIO_PIN9的任意一個, 則配置通道為EXTI9_5_IRQn 13 如果使用GPIO_PIN_10~GPIO_PIN15的任意一個,則配置通道為EXTI15_10_IRQn 14 ******************************************************************************/ 15 NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; 16 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 17 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 18 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 19 20 NVIC_Init( &NVIC_InitStructure ); 21 22 } 23 /* 外部中斷初始化 */ 24 void EXTI_PE4_Init(void) 25 { 26 GPIO_InitTypeDef GPIO_InitStructure; 27 EXTI_InitTypeDef EXTI_InitStructure; 28 29 //打開GPIOE時鍾和AFIO時鍾 30 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE ); 31 32 NVIC_Configuration(); 33 34 // PE4外部中斷配置 35 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; 36 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉輸入 37 GPIO_Init( GPIOE, &GPIO_InitStructure ); 38 39 GPIO_EXTILineConfig( GPIO_PortSourceGPIOE, GPIO_PinSource4 ); 40 EXTI_InitStructure.EXTI_Line = EXTI_Line4; 41 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 42 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿中斷 43 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 44 45 EXTI_Init( &EXTI_InitStructure ); 46 } 47 /* 中斷服務函數 */ 48 void EXTI4_IRQHandler( void ) 49 { 50 //確保產生了 EXTI Line 中斷 51 if( EXTI_GetITStatus( EXTI_Line4 ) != RESET ) 52 { 53 LED2_REV; 54 55 //清除中斷標志位 56 EXTI_ClearITPendingBit( EXTI_Line4 ); 57 } 58 }
定時器按鍵掃描
- 原理
- 定時器用於記錄時間和狀態,相比delay延時函數減少cpu資源占用,省時間.
- 在主循環中檢測按鍵狀態,按下則掃描定時器,
- 檢測時間和狀態來響應按鍵事件
- 優化
- 軟件層次
- 定時器中斷服務函數
- 只記錄時間和轉台
- 定時器中斷事件
- 不斷掃描按鍵(IO)狀態,然后根據狀態和按下時間,響應按鍵事件
- 按鍵事件
- 響應事件
- 數據結構
- 軟件標志位,FIFO機制
- 進階
- 更多的按鍵事件...
- 待定
- 示例

1 //基本定時器頭文件 2 /********************基本定時器TIM參數定義,只限TIM6、7************/ 3 #define BASIC_TIM6 // 如果使用TIM7,注釋掉這個宏即可 4 5 6 #define TIM_KEY 1 //定時器按鍵 7 8 9 #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) 10 #define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) 11 #define KEY3 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//讀取按鍵3(WK_UP) 12 13 14 #ifdef BASIC_TIM6// 使用基本定時器TIM6 15 #define BASIC_TIM TIM6 16 #define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd 17 #define BASIC_TIM_CLK RCC_APB1Periph_TIM6 18 #define BASIC_TIM_Period (1000-1) 19 #define BASIC_TIM_Prescaler 71 20 #define BASIC_TIM_IRQ TIM6_IRQn 21 #define BASIC_TIM_IRQHandler TIM6_IRQHandler 22 23 24 #else // 使用基本定時器TIM7 25 #define BASIC_TIM TIM7 26 #define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd 27 #define BASIC_TIM_CLK RCC_APB1Periph_TIM7 28 #define BASIC_TIM_Period 1000-1 29 #define BASIC_TIM_Prescaler 71 30 #define BASIC_TIM_IRQ TIM7_IRQn 31 #define BASIC_TIM_IRQHandler TIM7_IRQHandler 32 33 34 #endif 35 /**************************函數聲明********************************/ 36 void BASIC_TIM_Init(void); 37 38 39 /***************************************************************** 40 * 基本定時器配置 41 * 自動重裝載值 1000 42 * 時鍾 72/72=1M 43 * 計數一次時間 1/1M=1us 44 * 產生一次中斷時間 1us*1000次 = 1ms' 45 ******************************************************************** 46 */ 47 static void BASIC_TIM_Mode_Config(void) 48 { 49 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 50 51 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); // 開啟定時器時鍾,即內部時鍾CK_INT=72M 52 TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period; //自動重裝載值為1000 53 TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM_Prescaler; // 時鍾預分頻數為72M 54 55 TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure); // 初始化定時器 56 TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update); // 清除計數器中斷標志位 57 TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE); // 開啟計數器中斷 58 TIM_Cmd(BASIC_TIM, ENABLE); // 使能計數器 59 } 60 61 62 void BASIC_TIM_Init(void) 63 { 64 BASIC_TIM_Mode_Config(); 65 } 66 /***************************************************************** 67 * 基本定時器中斷優先級配置 68 ****************************************************************** 69 */ 定時器中斷優先級配置 */ 70 #if TIM_KEY //定時器按鍵 71 /* 配置中斷源 基本定時器 */ 72 NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn; 73 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 74 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 75 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 76 NVIC_Init(&NVIC_InitStructure); 77 #endif 78 79 80 /***************************************************************** 81 * 基本定時器中斷服務函數 82 ****************************************************************** 83 */ 84 #if TIM_KEY //定時器按鍵 85 void BASIC_TIM_IRQHandler() 86 { 87 88 if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) 89 { 90 if(BASIC_TIM_flg == 1) //按下標志,開始計時 91 TIM_Cnt++; 92 TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update); 93 } 94 } 95 #endif 96 97 98 #if TIM_KEY //定時器按鍵 99 /******************************************************************* 100 * 按鍵函數--短按 101 ******************************************************************** 102 */ 103 void KEY_Short_Event() 104 { 105 LED1_TOGGLE; 106 printf("-----短按%d\n",TIM_Cnt);//按鍵觸發事件 107 printf("-----LED1燈亮"); 108 } 109 /***************************************************************** 110 * 按鍵函數--長按 111 *************************************************************** 112 */ 113 void KEY_Long_Event() 114 { 115 LED2_TOGGLE; 116 printf("-----長按%d\n",TIM_Cnt);//按鍵觸發事件 117 printf("-----LED2燈亮"); 118 } 119 120 121 /***************************************************************** 122 * 定時器掃描 123 ***************************************************************** 124 */ 125 void TIM_Event() 126 { 127 if (GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)//檢測按鍵按下,開始計時 128 { 129 BASIC_TIM_flg = 1;//計時 130 } 131 if(TIM_Cnt>100)//去抖100ms 132 { 133 if((TIM_Cnt<800)&&(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==1)) //短按:按鍵按下100ms然后松開 134 { 135 TIM_Cnt = 0; //每次按完重置 136 BASIC_TIM_flg = 0; 137 KEY_Short_Event(); 138 } 139 else if((TIM_Cnt>800)&&(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==1)) //長按:按鍵安下800ms然后松開 140 { 141 TIM_Cnt = 0; //每次按完重置 142 BASIC_TIM_flg = 0; 143 KEY_Long_Event(); 144 } 145 } 146 } 147 #endif
待補充.......