赞
踩
中断:正在进行的事务被突发事件打断,转而去处理这个突发事件,突发事件处理完成后回到被打断的事务继续执行,这一处理突发事件的过程叫中断
常规程序:main方法和被main方法调用的程序;由于main方法的执行而得到执行的程序,叫常规程序
用电脑给STM32串口发送程序,只要电脑发来了数据,STM32就必须马上处理这个数据,如果不及时处理,电脑发送新的数据,就会覆盖之前传送的数据,造成数据的丢失
为了实现,来了数据,立马处理这个数据的功能,我们写了一个方法(也就说写了一个函数,函数内部代码的功能为,先接收数据,再处理数据)
流程为:
我们将这样突发的事件,叫中断源
在main中程序叫常规程序
由于中断源触发而调用的程序叫中断响应函数
STM32中的中断概念:
由于中断源的触发,常规程序被打断,CPU转而运行中断响应函数,而后又回到常规程序的执行,这一过程叫做中断。
突发事件有:
突发性:随机的在任意时刻发生、无预兆
紧急性:需要马上被处理
保存现场:记录常规程序执行到哪了
清楚中断源:接电话,第一步按接听键挂断电话铃声
还原现场:还原导出常规程序执行的步骤,继续执行
中断优先级:
- 优先级的值小,优先级越高
- 抢占优先级高的中断会对抢占优先级低的发生中断嵌套
- 发生中断嵌套时,会发生排队,总优先级高的中断排在前面
例子1:
执行A中断的过程总发生了中断B和C.
中断A先发生,而后中断B发生, 但中断B的抢占优先级高,所以发生中断嵌套
例子2:
执行A中断的过程总发生了中断B和C.
中断A先发生,而后中断B和C同时发生, 且B和C的抢占优先级不比A的高,不发生中断嵌套
但需要判断B和C的中断优先级B为1100(12) C为0101(5) 根据优先级数低的优先级高,所以C先发生
例子3:
执行A中断的过程总发生了中断B和C.
中断A先发生,而后中断B和C同时发生, 且B和C的抢占优先级不比A的高,不发生中断嵌套
但需要判断B和C的中断优先级B为0101(5) C为0100(4) 根据优先级数低的优先级高,所以C先发生
“脉冲型”中断源(罕见):中断提示信号一闪而过
“电平型”中断源(常见):中断信号持续,直到手动关闭,因此必须在中断响应函数结束前清除中断
NVIC(Nested Vectored Interrupt Controller)嵌套中断向量控制器,是一种核心外设,位于Cortex-M3内核中,负责管理中断
中断源:由片上外设产生,同一片上外设可产生多个中断源
NVIC:管理中断,负责中断进入和中断退出,中断优先级设置等。
Flash:等于电脑里硬盘,存储写的中断响应函数程序
开关部分:负责中断屏蔽,将项目中使用不到中断源开关断开,进行屏蔽
中断优先级:由4个bit位进行存储,
中断仲裁:将存储的中断优先级拿出来进行比较,看看谁优先
中断优先级分组:我们可以根据需要,设置不同的分组方式
Flash存储器中,有各种代码,CPU在中断发生时,如何定位是执行这个中断的中断响应函数呢?
中断向量表:看成一个目录,Flash存储器内部从地址0开始的一段区域,按照中断号排列,每4个字节(STM32是32位处理器,1个字节8bit,4字节正好32位)存储一个中断响应函数地址
表在:中文参考手册9.1.2节
编程接口:
中断优先级分组:移动中间的虚线,具体分配几个bit位给抢占优先级,分几个bit给子优先级
NVIC初始化:对NVIC某一路中断源进行初始化
NVIC_PriorityGroupConfig()函数详解:
NVIC_Init()
根据中断编程模型,将中断编程分为三个部分:
问题1:如何让片上外设产生中断源?
- 不同的片上外设开启方式不同
- USART1外设开启某中断源的方法:USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
问题2:如何配置NVIC?
- 为啥NVIC不用开启时钟?因为NVIC在内核里,只要一上电就有时钟(大脑不能休眠)
- 1.设置中断优先级分组
- 2.填写初始化调查问卷(写名,填问题)
问题3:如何编写中断响应函数
- 弱方法:标注为weak的方法可以被同名的方法覆盖,一开始中断向量表指向的是弱方法进行占位,当我们编写同名函数后,就指向了我们所编写的函数代码
- 步骤:
- 在.s文件中找到中断响应函数
- 写一个同名函数覆盖它 void xxx_IRQHandler(void){}v
void USART1_IRQHandler(void) { //具体的中断响应代码//主函数外部 }
总结:
EXTI(External Interrupt/Event Controller)外部中断/事件控制器
EXTI是stm32外设的众多片上外设之一,能够检测外部输入信号的变化边沿,并由此产生中断
事件和中断区别:
事件:指突发事件本身,一般是一个脉冲信号,事件处理过程不需要CPU介入
实际项目中有多个外部输入信号,故将此电路复制多份。
EXTI01~19合起来才是一个完整的片上外设
EXTI这么多通道用来作什么呢?
- 让所有的IO口都具有触发中断的能力
使用PA0触发外部中断了 就不能使用PB0、PC0…了
- 其它功能
其中:
上升沿选择寄存器和下降沿选择寄存器:对应硬件电路的上升沿 下降沿 和双边沿的边沿选择软件触发寄存器:对应硬件图软件触发
中断屏蔽寄存器:对应开关
挂起寄存器:对应那个框
事件屏蔽寄存器:控制事件屏蔽的开关
EXTI_Init():
- 初始化EXTI20个通道中的某一个
- 参数结构体指针类型(填调查问卷方式)
例子:
EXTI_GenerateSWInterrupt()
- 产生软件中断
EXTI_GetFlagStatus()
- 获取中断标志位(硬件图中的4,获取中断挂起的bit位)
EXTI_ClearFlag()
- 清除标志位(硬件图中的4,清楚中断挂起的bit位)
EXTI_GetITStatus()
- 中断屏蔽寄存器与上中断挂起寄存器的值,中断屏蔽开关闭合同时中断被触发,中断挂起寄存器值也为1,函数才返回1
EXTI_ClearITPendingBit()
- 同EXTI_ClearFlag()
曾经我们通过while循环读读取上次和当前的值,变化代表按键发生了动作
现在我们用外部中断的方法,在上升沿到来时,EXTI外设产生中断,NVIC模块对中断进行管理,触发中断响应函数
GPIO片上外设:对按键使用的IO进行初始化
AFIO片上外设:EXTI有20个通道,拿出0-15个通道,对应GPIOx的0-15号引脚,引脚组x由AFIO选择,通过复用器选择编号
EXTI片上外设:使用外部中断,产生中断源
NVIC核内外设:配置中断参数
编写中断响应函数
- 找到.s文件
- 写同名函数覆盖
函数里写:- 清除中断源 EXTI_ClearITPendingBit(EXTI_Line1);
- 编写功能函数
#include "stm32f10x.h" #include "stm32f10x_pal.h" int main(void) { //4.1设置中断优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); PAL_Init(); //1.初始化IO引脚 //将PA0和PA1分别设置为输入上拉模式 //开启GPIOA的时钟 RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA,ENABLE); //PA0,PA1 GPIO_InitTypeDef GPIOInitStruct; GPIOInitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA,&GPIOInitStruct); //2.配置EXTI的引脚映射 //开启AFIO的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //PA0->EXTI0 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); //PA1->EXTI1 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1); //3.配置EXTI //3.1初始化EXTI0 EXTI_InitTypeDef EXTIInitStruct; EXTIInitStruct.EXTI_Line = EXTI_Line0; EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising; EXTIInitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTIInitStruct); //3.2初始化EXTI1 EXTIInitStruct.EXTI_Line = EXTI_Line1; EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising; EXTIInitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTIInitStruct); //4.初始化NVIC //4.2初始化某一路的中断 NVIC_InitTypeDef NVICInitStruct; //4.2.1EXTI0 NVICInitStruct.NVIC_IRQChannel = EXTI0_IRQn; NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVICInitStruct.NVIC_IRQChannelSubPriority = 2; NVICInitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVICInitStruct); //4.2.2EXTI1 NVICInitStruct.NVIC_IRQChannel = EXTI1_IRQn; NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVICInitStruct.NVIC_IRQChannelSubPriority = 2; NVICInitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVICInitStruct); //5.初始化PC13 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_OD; //open-drain开漏模式 GPIOInitStruct.GPIO_Pin = GPIO_Pin_13; GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOC,&GPIOInitStruct); while(1) { } } void EXTI0_IRQHandler(void) { //清除中断 EXTI_ClearITPendingBit(EXTI_Line0); //点亮LED-向PC13写0 GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET); } void EXTI1_IRQHandler(void) { //清除中断 EXTI_ClearITPendingBit(EXTI_Line1); //熄灭LED GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET); }
若是PA5和PA6产生中断,其中断处理函数为同一个,在调用中断处理函数时,怎么知道是哪个中断产生的呢?
- 只需要判断其中断挂起寄存器的值,若为1,则代表是这个中断源产生的中断
#include "stm32f10x.h" #include "stm32f10x_pal.h" int main(void) { //4.1设置中断优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); PAL_Init(); //1.初始化IO引脚 //将PA0和PA1分别设置为输入上拉模式 //开启GPIOA的时钟 RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA,ENABLE); //PA0,PA1 GPIO_InitTypeDef GPIOInitStruct; GPIOInitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6; GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA,&GPIOInitStruct); //2.配置EXTI的引脚映射 //开启AFIO的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //PA0->EXTI0 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5); //PA1->EXTI1 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6); //3.配置EXTI //3.1初始化EXTI0 EXTI_InitTypeDef EXTIInitStruct; EXTIInitStruct.EXTI_Line = EXTI_Line5; EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising; EXTIInitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTIInitStruct); //3.2初始化EXTI1 EXTIInitStruct.EXTI_Line = EXTI_Line6; EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising; EXTIInitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTIInitStruct); //4.初始化NVIC //4.2初始化某一路的中断 NVIC_InitTypeDef NVICInitStruct; //4.2.1EXTI0 NVICInitStruct.NVIC_IRQChannel = EXTI9_5_IRQn; NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVICInitStruct.NVIC_IRQChannelSubPriority = 2; NVICInitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVICInitStruct); //4.2.2EXTI1 //5.初始化PC13 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_OD; //open-drain开漏模式 GPIOInitStruct.GPIO_Pin = GPIO_Pin_13; GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOC,&GPIOInitStruct); while(1) { } } void EXTI9_5_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line5) == SET)//EXTI { EXTI_ClearITPendingBit(EXTI_Line5); //清除中断 GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET); } //不写if else原因:可能同时为1 两个都执行 if(EXTI_GetITStatus(EXTI_Line6) == SET) { EXTI_ClearITPendingBit(EXTI_Line6);//清除中断 GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。