前言:这一节以及后面的定时器部分都是学习所有的单片机时的最重要的部分,我也花了很多时间去理解手册和程序,我争取尽量全面并且细致的记录我的体会。
一、中断的概念
这里就省略了,相信你学过单片机就会懂。
二、stm32里面的NVIC是什么
NVIC的中文意思是嵌套向量中断控制器,控制着中断的相关功能(其中包括中断源、抢占优先级、响应优先级、中断的使能与失能),具体的配置方法后面会提到。
三、stm32里面的优先级
在stm32中,一个中断的优先级由两部分决定,一个是抢占式优先级,还有一个是响应优先级。两个优先级组成了一个4位的控制字。
如图:
那么我们就会问,这有四位,那两个优先级各占多少位。就像图中所展示的,抢占式优先级(黄色)可以沾满四位,也可以一位都不占,如果占满四位,则抢占式优先级共有0-15这些等级,占满2位,就有0-3这些等级。同理响应优先级也是这样。分配他们各占多少位的函数为:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);后面这个数字代表的是抢占式优先级的位数
抢占式优先级和响应优先级分析
这两个优先级中,抢占式优先级占主导地位,抢占式优先级高的中断会优先打断主程序或者另外一个中断程序。
1.如果两个中断的抢占式优先级一样高,当两个中断同时发生时,响应优先级高的先发生,完了再发生另一个。但是要注意,响应优先级高的中断不会打断低的中断,也就是当一个高响应优先级的中断来时,如果正在执行一个低的中断,他不会打断他,而是等待低的执行完在执行。也就是说只有高抢占式优先级可以阻断。
四,中断运用过程
1.中断的相关配置(其中包括很多很多步骤)
2.设置优先级分组,也就是前面提到的两个优先级的位数
3.书写中断服务函数
注意:第一个步骤对应一个总的函数my_exti_init();第二步骤单独写在主函数中,第三个步骤的中断服务函数直接写在头文件中不写在第一个函数中,不用写在主函数中。总的来说这三个步骤是指主函数中的步骤。
这三个步骤都在后面详细分析
五、配置过程详细分析
首先需要知道,stm32f4中有23个外部中断,也就是有23个外部中断线路,但是只有0-15线是用来连到io口的(平时没有连上,需要用库函数来连上)
到这里你一定会发现,0-15这有16条线,而stm32刚好每个端口有16个针脚,于是你自然也会想到阵脚的编号肯定是与中断线路编号刚好对应上的,事实上你很聪明,就是这样。至于是哪一个端口的针脚,就需要库函数配置。
配置步骤一:因为用到io口,所以首先要配置io口,就是打开时钟,设为输入模式等操作,至于为上啦还是下拉就看外设了,因为这里外设为按键,就好判断。
配置步骤二:像前面说的,要将中断线路和io口连接起来,用到的函数是:
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);//这里就是将A端口的0管脚接在0线路上,用到几个线路就写多少个这个函数
1
但是SYSCFG是接在APB2总线上的,所以在SYSCFG_EXTILineConfig之前需要用RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);使能SYSCFG。
配置步骤三:配置NVIC(用到多少中断就写多少组)
NVIC_InitTypeDef NVIC_InitStructure;//声明一个结构体
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//中断通道,根据线路修改数字
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//设置抢占式优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;//设置响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//通道使能
NVIC_Init(&NVIC_InitStructure);//最后在申明下
配置步骤四:初始化外部中断EXTI(用到多少中断就写多少组)
EXTI_InitTypeDef EXTI_InitStructure;//声明结构体
EXTI_InitStructure.EXTI_Line=EXTI_Line0;//选择线路
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//选模式,这里为中断模式
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//上升沿触发,下降沿为Falling
EXTI_InitStructure.EXTI_LineCmd=ENABLE;//使能
EXTI_Init(&EXTI_InitStructure);
配置步骤五:编写中断服务函数(写在头文件中)
格式:
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==1)//看是否确实触发了中断,值为1就为触发了
{
delay_ms(10);//消抖
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)//再一次确定,只不过这一次是确定按钮是否按下
{
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0));
GPIO_ResetBits(GPIOF,GPIO_Pin_10);//open the led
}
}
EXTI_ClearITPendingBit(EXTI_Line0);//注意,这一步非常重要,经常容易忘记,写在中断服务函数的最后。用于清空中断的状态,以便于下次再次使用
注意:中断函数名不是自己取得,是有规定的
线路一用第一个,线路二用第二个,但是线路5到9用的是一样的倒数第二个,线路10-15用的最后一个。
完整程序:
exti.c
#include "exti.h"
void my_exti_init()
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊÇ°ÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊÇ°ÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊÇ°ÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊÇ°ÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
//Line0
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//ģʽÅäÖÃΪÖжÏģʽ
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
//Line2
EXTI_InitStructure.EXTI_Line=EXTI_Line2;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//ģʽÅäÖÃΪÖжÏģʽ
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
//Line3
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//ģʽÅäÖÃΪÖжÏģʽ
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
//Line4
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//ģʽÅäÖÃΪÖжÏģʽ
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
void EXTI0_IRQHandler(void)//ÖжÏÏß·0
{
if(EXTI_GetITStatus(EXTI_Line0)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0));
GPIO_ResetBits(GPIOF,GPIO_Pin_10);//open the led
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
void EXTI2_IRQHandler(void)//ÖжÏÏß·¶þ
{
if(EXTI_GetITStatus(EXTI_Line2)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)));
GPIO_SetBits(GPIOF,GPIO_Pin_8);
}
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
void EXTI3_IRQHandler(void)//ÖжÏÏß·Èý
{
if(EXTI_GetITStatus(EXTI_Line3)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)));
GPIO_SetBits(GPIOF,GPIO_Pin_10);//turn off the led
}
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
void EXTI4_IRQHandler(void)//ÖжÏÏß·ËÄ
{
if(EXTI_GetITStatus(EXTI_Line4)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)));
GPIO_ResetBits(GPIOF,GPIO_Pin_8);
}
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
main.c
int main()
{
RCC_HSE_Config(8,336,2,7);
Beep_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
SysTick_Init(168);
key_init();
LED_Init();
my_exti_init();
while(1)
{
}
}
说明:去前面一节的按钮实验现象虽然相同,但是原理不同,前面的按钮实验是通过不断检测按钮的状态,而这里是用的中断,满足触发条件自动执行。
上一篇:STM32F0xx_EXTI中断配置详细过程-按键检测
下一篇:STM32的EXTI小实验
推荐阅读最新更新时间:2024-03-16 16:22