外部中断EXTI、复用功能、重映射、嵌套向量中断控制器NVIC、外部中断按键控制LED灯
SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的。
文章目录
外部中断EXTI
1. 外部中断基础知识
1.1 STM32外部中断框架
中断的概念:在主程序运行过程中,出现了特定的中断触发条件,使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完后又返回原来被暂停的位置继续运行。
- 有20条外部中断线
- 有十六条是GPIO的中断线(0~15),同一个引脚号参与同一个line
- GPIO的中断线要启用必须先要配置AFIO的寄存器EXTICRx

1.2 STM32外部中断机制框架

- 边沿检测(中断发生条件):上升沿 (低电平往高电平的变化),下降沿 或者 双边沿 (只要电平发生变化就触发)
- 软件配置中断或者事件寄存器
- 屏蔽中断寄存器或事件寄存器
- 请求挂起寄存器 (不工作)
- 中断发送给NVIC中断控制器 (管理先做哪一个中断)
- 事件则产生一个脉冲响应
2. 复用功能
处理器的引脚本身默认就是一个普通的GPIO,但是它还可以被复用成其他功能,我们称之为一个引脚的复用功能

3. 重映射
重映射属于复用功能的另外一个功能,可以把具有特殊功能的引脚,分配到其他引脚上去
如果某个功能被重映射了,那么这个功能将不再遵循其默认的默认分配。
比如STM32F1xx手册中第120页中:
串口1 发送和接收默认引脚 PA9 和 PA10,当 USART1_REMAP = 1 时,发送和接收引脚为 PB6 和 PB7。
(比如 PA9 和 PA10 已经当做普通引脚使用了,这个时候需要USART1的功能,可以配置PB6和PB7成为USART1 的功能来使用)
4. 嵌套向量中断控制器NVIC
4.1 中断向量表
Cortex-M3内核支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断(异常)和68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个)。
优先级号越小,优先级越高。(优先级负数为内核中断)
STM32F1xx手册中第130页中:
4.2 中断优先级分组
这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别称为组0~4,同时,对每个中断设置一个抢占优先级和响应优先级。分组设置是由SCB->AIRCR寄存器的bit10~8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的。

CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit 抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高,介绍一下抢占优先级、响应优先级的区别:
- 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
- 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
- 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
- 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
除此之外还有两点需要注意:
- 打断的情况只会与抢占优先级有关,和响应优先级无关!(中断嵌套)
- 一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断优先级配置混乱,影响系统稳定性。
4.3 中断优先级控制函数结构体
- 中断优先级控制函数
- NVIC_SetPriorityGrouping()。
- 中断优先级控制结构体参数
- NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,例如UART1_IRQn
- NVIC_IRQChannelPreemptionPriority:定义此中断的 抢占优先级级别。
- NVIC_IRQChannelSubPriority:定义此中断的 响应优先级级别。
- NVIC_IRQChannelCmd:该中断是否使能。
- NVIN_Init()函数初始化NVIC寄存器
4.4 中断优先级设置步骤
1、系统运行后先设置中断优先级分组,调用函数。
2、针对每个中断,设置对应的抢占优先级和响应优先级。
3、如果需要挂起或恢复某个中断,应首先查询该中断当前的激活状态,然后根据状态分别调用对应的挂起(suspend) 或 恢复(resume)函数。
5. 外部中断按键控制LED灯
按键控制LED的开发流程:
第一步:使能功能复用时钟
第二步,配置复用寄存器
第三步,配置中断屏蔽寄存器
固件库按键控制LED灯
外部中断EXTI结构体:
typedef struct
{
uint32_t EXTI_Line; // 外部中断线(比如 GPIOA 0~15)
EXTIMode_TypeDef EXTI_Mode; // 外部中断模式(中断, 事件)
EXTITrigger_TypeDef EXTI_Trigger; // 边沿检测(触发条件:上升沿,下降沿,双边沿)
FunctionalState EXTI_LineCmd; // 表示外部中断线的使能或禁用状态
}EXTI_InitTypeDef;
外部中断EXTI相关库函数:
void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
软件流程设计
- 初始化系统
- 初始化GPIO、EXTI外设时钟
- 初始化按键和LED引脚
- 初始化EXTI外部中断条件
- 初始化NVIC嵌套中断控制器
- 编写外部中断函数
- 判断中断发生控制LED灯
WEAK: 弱定义。
如果用户没有自己定义的中断函数,则系统默认执行系统自定义的中断函数
如果用户定义了中断函数,则优先执行用户的中断函数
// exti.h 头文件
void Exti_Init(void);
// exti.h 源文件
#include "stm32f10x.h"
#include "exti.h"
// 初始化GPIO按键 EXTI外部中断 NVIC中断控制器 函数
void Exti_Init(void)
{
GPIO_InitTypeDef GPIO_initstruct; // 定义GPIO结构体变量
EXTI_InitTypeDef EXTI_initstruct; // 定义外部中断EXTI结构体变量
NVIC_InitTypeDef NVIC_initstruct; // 定义NVIC嵌套中断控制器结构体变量
// 初始化时钟, GPIOA 和 AFIO外部中断时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// 优先级分组配置 2位配置抢占优先级 2位配置响应优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
GPIO_initstruct.GPIO_Pin = GPIO_Pin_0; // 初始化A0引脚
GPIO_initstruct.GPIO_Mode = GPIO_Mode_IPU; // 初始化A0为上拉输入模式
GPIO_Init(GPIOA, &GPIO_initstruct); // 初始化A0结构体
// GPIOA0变成外部中断功能
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_initstruct.EXTI_Line = EXTI_Line0; // 初始化外部中断线0
EXTI_initstruct.EXTI_Mode = EXTI_Mode_Interrupt; // 初始化外部中断模式
EXTI_initstruct.EXTI_Trigger = EXTI_Trigger_Falling; // 初始化下降沿触发条件
EXTI_initstruct.EXTI_LineCmd = ENABLE; // 外部中断线使能
EXTI_Init(&EXTI_initstruct); // 初始化EXTI结构体
NVIC_initstruct.NVIC_IRQChannel = EXTI0_IRQn; // 初始化中断线0
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority = 0; // 初始化抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority = 0; // 初始化响应优先级
NVIC_initstruct.NVIC_IRQChannelCmd = ENABLE; // 该中断使能
NVIC_Init(&NVIC_initstruct); // 初始化 NVIC寄存器
}
// main.c
#include "stm32f10x.h"
#include "main.h"
#include "LED.h"
#include "exti.h"
void delay(uint16_t time)
{
uint16_t i=0;
while(time--)
{
i = 12000;
while(i--);
}
}
int main()
{
LED_Init();
Exti_Init();
GPIO_SetBits(GPIOA, GPIO_Pin_1);
while(1)
{
}
}
// 外部中断函数, 主要满足完整的中断触发条件, 函数就会由硬件自动执行
// "中断配置条件" 必须同时包含 EXTI配置 + NVIC使能
void EXTI0_IRQHandler()
{
// 如果检查到外部中断线(EXTI0)发生了中断请求
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
delay(1000);
GPIO_SetBits(GPIOA, GPIO_Pin_1);
delay(1000);
// 清除外部中断线(EXTI0)的中断挂起标志位, 方便下次还能运行函数
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
更多推荐



所有评论(0)