前言
接着上次的流水灯今天来学习中断操作。
一、如何配置中断及其详解
1.中断是什么?
中断就是在程序正常运行时,CPU突然收到更急紧急的事情的时候,这时CPU就需要暂停正在处理的事立马去处理这件更加紧急的事,处理完后再回到原来的事情。中断的过程如下图所示:
2.中断优先级
这是需要思考一个问题,当多个中断触发时,CPU应该先处理哪一个中断呢?这时我们就需要配置中断优先级,既然都是紧急的事情当然会分哪一个更加紧急。m4中最多支持16个优先级。其中优先级又分为抢占优先级和响应优先级。
2.1 抢占优先级
不管中断产生的先后顺序,只比较抢占优先级的高低,优先级高的立即执行,例如:如果一个中断执行过程中,又有一个更高抢占优先级中断产生,CPU则会暂停当前中断跑去执行更高抢占优先级中断,形成中断嵌套。
2.2 相应优先级
如果两个相同抢占优先级的中断产生,那么两个中断会按照响应优先级顺序进行排队,优先级高的先执行
3.如何配置中断?
我们查看stm32f4的中文参考手册,如下图:
从图中可以看出我们可以同时配置16个中断。结合开发文档总结出配置的大概思路(今天使用的时PE3和PE4配置两个中断):
1.初始化PE3和PE4引脚。
2.配置外部中断。
3.其中中断不是直接告诉CPU需要处理中断的,而是由中断向NVIC再向CPU申请中断的,所以还要配置NVIC.
二、详细代码
#include "stm32f4xx.h" // Device header
#include "sys.h"
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
void delay(void)
{
uint32_t i=0x2000000;
while(i--);
}
void exti3_init(void)
{
//是能端口E硬件时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
//使能系统配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//配置PE3为输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
//将PE3和exti连接在一起
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);
//外部中断配置
EXTI_InitStructure.EXTI_Line = EXTI_Line3;//中断引脚
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//触发方式
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//打开外部中断3的通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能该通道
NVIC_Init(&NVIC_InitStructure);
}
void exti4_init(void)
{
//是能端口E硬件时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
//使能系统配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//配置PE3为输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
//将PE3和exti连接在一起
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
//外部中断配置
EXTI_InitStructure.EXTI_Line = EXTI_Line4;//中断引脚
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//触发方式
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//打开外部中断3的通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;//响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能该通道
NVIC_Init(&NVIC_InitStructure);
}
int main(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOE时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
GPIO_SetBits(GPIOF,GPIO_Pin_9 |GPIO_Pin_10);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
exti3_init();//exti3的初始化
exti4_init();
while(1)
{
}
}
void EXTI3_IRQHandler(void)
{
//判断是否有中断请求
if(EXTI_GetITStatus(EXTI_Line3) == SET)
{
PFout(9)=0;
delay();
//清空标志位
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
void EXTI4_IRQHandler(void)
{
//判断是否有中断请求
if(EXTI_GetITStatus(EXTI_Line4) == SET)
{
PFout(10)=0;
delay();
delay();
delay();
PFout(10)=1;
//清空标志位
EXTI_ClearITPendingBit(EXTI_Line4);
}
}
总结
刚开始不是很懂中断,对优先级理解不了,多学习即便多动手反复实验慢慢就懂了。持续学习可以学到更多东西,反复学习可以把所学的的东西学得更有深度,学习真是一个持续且循序渐进的过程。