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(中断)的主要内容,如果未能解决你的问题,请参考以下文章