STM32F1 外部中断
STM32F1中断系统(这部分是直接拷贝原子哥的书上的)
CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。但 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。STM32 有 84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。而我们常用的就是这 68 个可屏蔽中断, 但是 STM32 的 68 个可屏蔽中断,在 STM32F103 系列上面,又只有 60 个(在 107 系列才有 68 个)。因为我们开发板选择的芯片是 STM32F103 系列的所以我们就只针对 STM32F103 系列这 60 个可屏蔽中断进行介绍。 这里简单介绍一下 STM32 的中断分组: STM32 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如表 4.5.1 所示:
组 AIRCR[10: 8] bit[7: 4]分配情况 分配结果
0 111 0: 4 0 位抢占优先级, 4 位响应优先级
1 110 1: 3 1 位抢占优先级, 3 位响应优先级
2 101 2: 2 2 位抢占优先级, 2 位响应优先级
3 100 3: 1 3 位抢占优先级, 1 位响应优先级
4 011 4: 0 4 位抢占优先级, 0 位响应优先级
表 4.5.1 AIRCR 中断分组设置表
通过这个表,我们就可以清楚的看到组 0~4 对应的配置关系,例如组设置为 3,那么此时所有的 60 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是响应优先级。每个中断,你可以设置抢占优先级为 0~7,响应优先级为 1 或 0。抢占优先级的级别于响应优先级。而数值越小所代表的优先级就越高。这里需要注意两点:
第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应先级不可以打断低响应优先级的中断。结合实例说明一下:假定设置中断优先级组为 2,然后设置中断 3(RTC 中断)的抢占优先级为 2,响应优先级为 1。中断 6(外部中断 0)的抢占优先级为 3,响应优先级为 0。中断 7(外部中断 1) 的抢占优先级为 2,响应优先级为 0。那么这 3 个中断的优先级顺序为:中断 7>中断 3>中断 6。上面例子中的中断 3 和中断 7 都可以打断中断 6 的中断。而中断 7 和中断 3 却不可以相互打断!
通过以上介绍,我们熟悉了 STM32 中断设置的大致过程。接下来我们介绍如何使用库函数实现以上中断分组设置以及中断优先级管理,使得我们以后的中断设置简单化。 NVIC 中断管理函数主要在 misc.c 文件里面。
中断优先级分组函数 NVIC_PriorityGroupConfig,其函数申明如下:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改。
STM32F1外部中断线
这里我们首先 STM32 IO 口中断的一些基础概念。 STM32 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32 的强大之处。 STM32F103 的中断控制器支持 19 个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。 STM32F103 的19 个外部中断为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件。
从上面可以看出, STM32 供 IO 口使用的中断线只有 16 个,但是 STM32 的 IO 口却远远不止 16 个,那么 STM32 是怎么把 16 个中断线和 IO 口一一对应起来的呢? 于是 STM32 就这样设计, GPIO 的管教 GPIOx.0~GPIOx.15(x=A,B,C,D,E, F,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口,以线 0 为例:它对应了 GPIOA.0、 GPIOB.0、 GPIOC.0、 GPIOD.0、GPIOE.0、 GPIOF.0、 GPIOG.0。而中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。 下面我们看看 GPIO 跟中断线的映射关系图:
1.通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。对于小容量、中容量和大容量的产品,参见6.3.7137/754节;对于互联型产品,参见7.3.7节。
另外四个EXTI线的连接方式如下:
● EXTI线16连接到PVD输出
● EXTI线17连接到RTC闹钟事件
● EXTI线18连接到USB唤醒事件
● EXTI线19连接到以太网唤醒事件(只适用于互联型产品)
我们配置完中断优先级之后,接着我们要做的就是编写中断服务函数。中断服务函数的名字是在 MDK 中事先有定义的。 这里需要说明一下, STM32 的 IO 口外部中断函数只有 6 个,分别为:
EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPORT EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler
中断线 0-4 每个中断线对应一个中断函数, 中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。 在编写中断服务函数的时候会经常使用到两个函数, 第一个函数是判断某个中断线上的中断是否发生(标志位是否置位):
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
这个函数一般使用在中断服务函数的开头判断中断是否发生。 另一个函数是清除某个中断线上的中断标志位:
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
这个函数一般应用在中断服务函数结束之前, 清除中断标志位。
配置外部中断的步骤
1,IO配置,打开相应的IO时钟,把IO设置为输入,根据外部电路确定是上拉还是下拉
2,打开AFIO复用时钟,一定要打开这个时钟,本人就跳过这个坑
3,在主函数中对优先级进行分组
4,配置外部中断线的触发方式等EXTI_Init()
5,配置中断通道NVIC_Init()
6,一定要把IO口映射到相应的中断线上,GPIO_EXTILineConfig()
7,中断初始化完成,等待中断
8,中断处理函数中要先确认是不是相应的中断发生,这个中断是要手动清零中断标志位的
代码如下;
#include "sys.h"
#include "delay.h"
/************************************************************
功能,按一次按键,LED状态翻转一次
这个按键中断只是示范外部中断功能,项目中不能这样搞
其中LED接在PB5上,低电平有效
按键接在PE4上,低电平有效,中断设置为下降沿触发
****************************************************************/
void init_led()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//打开GPIOB时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//翻转速度=10MHZ
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_WriteBit(GPIOB,GPIO_Pin_5,1);//初始化输出1吧
}
void init_key_interrupt()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
EXTI_InitTypeDef EXTI_InitStruct;//中断线设置
NVIC_InitTypeDef NVIC_InitStruct;//中断优先级配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//打开GPIOE时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//低电平有效,所以设置为上拉输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4;//PE4
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//输入是不需要设置翻转速度的
GPIO_Init(GPIOE, &GPIO_InitStruct);
EXTI_InitStruct.EXTI_Line=EXTI_Line4;//外部中断线4
EXTI_InitStruct.EXTI_LineCmd=ENABLE;//使能
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//选择中断而不是事件
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel=EXTI4_IRQn;//中断通道连接到外部中断线4
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//中断通道使能
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级为2
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;//子优先级为2
NVIC_Init(&NVIC_InitStruct);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);//一定要把相应的IO口连接到外部中断线
}
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组,2个抢占优先级,2个子优先级
init_led(); //LED初始化
init_key_interrupt();
while(1)
{}//等待中断
}
//中断处理函数,这个函数在starup_stm32f10x_hd.s文件里有
void EXTI4_IRQHandler(void)
{
delay_ms(30); //延时消抖
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)//确认是否为按键按下
{
//中断处理函数
if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5))//读取ledIO状态,如果状态为1,就输出0
GPIO_WriteBit(GPIOB,GPIO_Pin_5,0);
else
GPIO_WriteBit(GPIOB,GPIO_Pin_5,1);//,如果状态为0,就输出1
}
EXTI_ClearITPendingBit(EXTI_Line4);//手动清零中断标志位
}
STM32F1 外部中断
STM32F1中断系统(这部分是直接拷贝原子哥的书上的)
CM3