STM32_5(中断)

Posted 每天学习之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32_5(中断)相关的知识,希望对你有一定的参考价值。

中断系统

  • 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
  • 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
  • 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

 

中断执行流程

           

 

NVIC基本结构

CPU相当于一个医生,NVIC相当于叫号系统,EXTI、TIM、ADC、USART相当于病人,看这里面谁的更紧急谁就优先级越小。

 

NVIC优先级分组

响应优先级:上个病人在看病,这个病人看病完了之后,紧急的病人即使后来的,也会最先进去看病,插队的优先级。

抢占优先级:如果这个病人更加紧急,并且此时已经有人在看病,那么就会把看病的人放在一边,先让更紧急的病人看病,看完之后再看放在一边的病人。

 

EXTI简介

  • EXTI(Extern Interrupt)外部中断
  • EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
  • 支持的触发方式:上升沿/下降沿/双边沿/软件触发
  • 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
  • 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
  • 触发响应方式:中断响应/事件响应

 

AFIO复用IO口

  • AFIO主要用于引脚复用功能的选择和重定义
  • 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择

 

EXTI框图

 

旋转编码器介绍

  • 旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
  • 类型:机械触点式/霍尔传感器式/光栅式

 

建议

  尽量别在中断函数里调用复杂函数,可以执行一个标志位,在主函数或其他函数里执行其他代码。

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
",TIM_Cnt);//按键触发事件
107     printf("-----LED1灯亮");
108 }
109 /*****************************************************************
110 *    按键函数--长按
111 ***************************************************************
112 */
113 void KEY_Long_Event()
114 {
115     LED2_TOGGLE;
116     printf("-----长按%d
",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
定时器扫描_按键

待补充.......

以上是关于STM32_5(中断)的主要内容,如果未能解决你的问题,请参考以下文章

STM32中断函数

STM32_中断

求教stm32f030 HAL库,怎么关闭和打开所有中断

STM32F10xxx_异常与中断

stm32串口接收完整的数据包

stm32不同中断可以有不同分组吗