中断与定时器一直是stm32中非常重要的两部分,今天先简单介绍一下有关中断中外部中断的使用。下面直接开始代码部分,而原理会在代码部分一起讲解。
中断的使用需要初始化,不妨写一个初始化函数名为void CountSensor_Init(void),用于存放外部中断的初始化。
以启用GPIOB,Pin14为外部中断引脚为例。
事实上,启用外部中断的顺序就是从启动GPIO、AFIO、EXTI、NVIC顺序导通,依次配置即可。
配置GPIO初始化如下:
void RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//配置时钟使能
GPIO_InitTypeDef GPIO_InitStructure;//定义名为GPIO_InitStructure的结构体变量
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//配置成上拉输入模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;//由原理图配置PB14口
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//速度一般设置成50MHZ即可
GPIO_Init(GPIOB, &GPIO_InitStructure);//对GPIO进行初始化
通过以上配置,GPIOB Pin14口便彻底完成了初始化,通过以上的流程图,我们还需要再进行配置以下AFIO寄存器,AFIO配置较为简单,打开时钟即可。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
接下来我们进行EXTI外部中断的初始化。而EXTI与NVIC属于系统的基本功能模块,与系统时钟(HSI、HSE等)一同启动并在运行时自动工作,因此无需配置时钟。
选用函数GPIO_EXTILineConfig();用于配置EXTI外部中断通道,由于我们选择的是GPIOB Pin14,所以配置函数为:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
下面操作与GPIO结构体类似,在此不作赘述
EXTI_InitTypeDef EXTI_InitStructrue;//定义名为EXTI_InitStructrue的结构体变量
EXTI_InitStructrue.EXTI_Line = EXTI_Line14;//通道14
EXTI_InitStructrue.EXTI_LineCmd = ENABLE;//使能
EXTI_InitStructrue.EXTI_Mode = EXTI_Mode_Interrupt;//中断而非事件
EXTI_InitStructrue.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStructrue);//EXTI初始化
下面开始配置NVIC
先进行配置NVIC优先组,因为本次代码较为简单,没有过多的中断冲突,因此分组可以比较随意,这里设置为级别2,采用函数NVIC_PriorityGroupConfig();配置为:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
从stm32官方库中我们可以得知组2的优先级分组为
即两位用于抢占优先级,两位用于子优先级。
接下来也是配置NVIC的结构体,配置如下
NVIC_InitTypeDef NVIC_InitStructrue;//定义名为NVIC_InitStructrue的结构体变量
NVIC_InitStructrue.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructrue.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_InitStructrue.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级与从优先级级别相同时不会发生中断嵌套,这里我们不需要中断嵌套,设置级别相同即可
NVIC_InitStructrue.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructrue);//NVIC初始化
以上,整个外部中断的初始化配置就算完成了,完整代码如下:
void CountSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*EXIT与NVIC不需要开启时钟*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
EXTI_InitTypeDef EXTI_InitStructrue;
EXTI_InitStructrue.EXTI_Line = EXTI_Line14;
EXTI_InitStructrue.EXTI_LineCmd = ENABLE;
EXTI_InitStructrue.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructrue.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructrue);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructrue;
NVIC_InitStructrue.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructrue.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructrue.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructrue.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructrue);
}
初始化完成后,就可以编写中断服务函数了。而中断函数不可以随意起名,这时我们就可以打开启动文件,我的文件名是startup_stm32f10x_md.s,打开后找到EXTI15_10_IRQHandler函数,而这里选择EXTI15是因为通道10~15都被放在了这个中断函数之中,而我们的通道14自然也是用这个函数。
找到中断函数后,我们首先需要判断是否程序进入了中断,通过调用EXTI_GetITStatus()中断标志位函数是否置1来判断是否进入了中断
if(EXTI_GetITStatus(EXTI_Line14) == SET)
{
}
随后,务必要记得进入一次中断服务程序后要清除标志位,否则程序会一直卡死在中断程序里面无法出去。实现这一操作调用的是EXTI_ClearITPendingBit()函数。完整中断服务函数程序如下:
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) == SET)
{
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
同时,中断服务函数不需要在.h文件中进行声明。