STM32_FreeRTOS_按鍵處理_狀態機+定時器_FIFO機制


裸機--按鍵采集方式:
  • 掃描方式: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
定時器掃描_按鍵

待補充.......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM