STM32_按键_外部中断_定时器扫描_循环扫描_FIFO机制

裸机--按键采集方式:
  • 扫描方式:while(1)中不断扫描引脚的高低电平,实现扫描按键的功能,效率低
  • 外部中断:把中断源和引脚连接起来.再设置外部中断触发方式.实现按键触发外部中断,效率高
  • 定时器中断:每隔一段时间检测引脚电平,然后消抖.实现按键检测.常用于实时系统等复杂场景.裸机也可以用.
  • 按键状态机,队列,,待补充
/*    扫描方式,代码取自野火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
定时器扫描_按键

待补充.......

猜你喜欢

转载自www.cnblogs.com/RcBlog/p/11937639.html