异常类型
1.中断简介
程序从下到上执行,当发生中断时,程序转而执行中断程序,执行完毕后,继续执行主函数。中断中可以接中断嵌套。中断同时发生时,优先执行优先级高的。中断优先级越小,优先级越高。
2.中断类型
系统异常:体现在内核水平
外部中断:体现在外设水平
3.NVIC(嵌套向量中断控制器)
属于内核外设,管理着包括内核和片上所有外设的中断相关的功能。
主要在两个库文件上:core_cm3.h和misc.h
CM3内核一共支持256个中断。
4.NVIC寄存器简介
中断使能寄存器-------------ISER
8个32位寄存器来控制,每个位控制一个中断。对于STM32F103的可屏蔽中断最多只有60个。所以只需要ISER[0]和ISER[1]就可以全部表示。ISER[0]的bit0~31分别对应中断0 ~ 31,ISER[1]的0 ~ 27对应32 ~ 59.
中断清除寄存器-------------ICER
用来清除中断使能的,写1有效。其余和ISER等效。
中断使能悬起寄存器 ----- ISPR
可以将正在执行的中断挂起,而去执行同级或者更高级别的中断
中断清除悬起寄存器 - ----ICPR
与ISPR相反,解除中断挂起的状态
中断有效位寄存器 ---------IABR
一个只读寄存器,用来判断目前是哪个中断正在被执行
中断优先级寄存器----------IP
IP寄存器是由240个八位寄存器组成,每个可屏蔽中断占八位,这样就可以表示240可屏蔽中断,IP[59]~IP[0]分别对应中断59 ~0.每个可屏蔽中断占用的8bit并没有全部使用,而是只用了高四位。这四位有分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。这两个优先级的占位由SCB->AIRCR中的中断分组设置来决定。子优先级又叫响应优先级。
正在执行的优先级由抢占优先级-相应优先级-自然优先级的方式来决定正在执行中断。
优先级分组 | 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 位抢占优先级, 4 位响应优先级 |
中断优先级设定NVIC->IPRx
寄存器高四位用来表达优先级,低四位未使用,读回为0。
优先级分组:SCB->AIRCR:PRIGROUP[10:8]
判断优先级:分组>主优先级>子优先级.如果都相同,则看硬件中断编号(中断向量表)。
中断编程顺序
1——使能中断请求
使能中断请求一共需要使能两个。外设和NVIC中断使能寄存器。
2——配置中断优先级分组
配置SCB->AIRCR的8到10位确定优先级分组
固件库函数为
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//设置中断优先级的分组
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority,uint32_t SubPriority);//设置中断优先级
一共有0到4五个组。
3——配置NVIC寄存器,初始化NVIC_InitTypeDef;
(1)指定中断源
uint8_t NVIC_IRQChannel; //中断源为中断向量表中的中断
(2)设置抢占优先级,分组不同,抢占优先级的位数不同。
uint8_t NVIC_IRQChannelPreemptionPriority;
(3)设置子优先级
uint8_t NVIC_IRQChannelSubPriority;
(4)使能或者失能
FunctionalState NVIC_IRQChannelCmd;
4——编写中断服务函数
(1)中断服务函数名
中断服务函数在启动文件里面有弱定义(stm32f10x.h中的62行),当我们写相应的中断服务函数时,要保持与启动文件的中断服务函数名一致,若命名错误,则程序会执行启动文件中的中断服务函数。
(2)中断服务函数位置
为了编程方便管理,一般把中断服务函数放在stm32f10x_it.c中。
GPIO中断示例
EXTI(外部中断/事件控制器)简介
EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
EXTI控制器简介
EXTI 控制器有 20 个中断/事件输入线,编号为EXTI0到EXTI19,EXTI0到EXTI15的每条线控制GPIO的组数(即GPIOA到GPIOI),线的编号(0—15)对应着GPIO的16个端口。所以可以输入任意GPIO口。16线为PVD输出,17线为RTC闹钟事件,18线为USB唤醒事件,19线为互联网唤醒事件(只适用于互联型,即105或107)
AFIO_EXTICR——外部中断配置寄存器
AFIO_EXTICR外部中断配置寄存器共四组,高16位保留,低十六位有效。每4bit决定一个GPIO和中断线的映射关系。
EXYI_RTSP——上升沿触发选择寄存器
0—19位有效,x位为1时,允许输入线x上的上升沿触发(中断和事件),为0时禁止上升沿触发。
EXIT_SWIER 软件中断事件寄存器
0—19位有效,x位为1时,允许输入线x上的上升沿触发(中断和事件),为0时禁止上升沿触发。
EXIT_PR——挂起寄存器
0—19位有效,为1表示接受到了中断请求。挂起是指接收到了中断请求,但是响不响应需要下一步判断
EXIT_IMR——中断屏蔽寄存器
0—19位有效,为1表示开放来自线x上的中断请求,
EXIT_EMR——事件屏蔽寄存器
初始化结构体函数EXTI_InitTypeDef;
uint32_t EXTI_Line;//选择中断/事件线,控制的是EXIT_IMR——中断屏蔽寄存器,置一为开放相应线的中断请求
EXTIMode_TypeDef EXTI_Mode;//选择是中断还是事件两种模式
EXTITrigger_TypeDef EXTI_Trigger;//选择触发方式(上升沿、下降沿、上升沿和下降沿)
FunctionalState EXTI_LineCmd; //对应EXIT_IMR——中断屏蔽寄存器或者EXIT_EMR——事件屏蔽寄存器,
EXTI外部中断的配置步骤
1.使能对应的GPIO时钟
2.设置GPIO的工作模式,触发条件,开启AFIO时钟,设置IO口与中断线的映射关系。
3.配置好中断优先级(NVIC),并使能中断
4.编写中断服务函数
5.编写中断处理回调函数HAL_GPIO_EXTI_Callback
在main函数中
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
beep_init(); /* 初始化蜂鸣器 */
extix_init(); /* 初始化外部中断输入 */
LED0(0); /* 先点亮红灯 */
while (1)
{
printf("OK\r\n");
delay_ms(1000);
}
}
exti.c
/**
****************************************************************************************************
* @file exti.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2020-04-19
* @brief 外部中断 驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 STM32F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20200420
* 第一次发布
*
****************************************************************************************************
*/
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"
/**
* @brief KEY0 外部中断服务程序
* @param 无
* @retval 无
*/
void KEY0_INT_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
__HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}
/**
* @brief KEY1 外部中断服务程序
* @param 无
* @retval 无
*/
void KEY1_INT_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY1_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY1所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
__HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}
/**
* @brief WK_UP 外部中断服务程序
* @param 无
* @retval 无
*/
void WKUP_INT_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(WKUP_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY_UP所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
__HAL_GPIO_EXTI_CLEAR_IT(WKUP_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}
/**
* @brief 中断服务程序中需要做的事情
在HAL库中所有的外部中断服务函数都会调用此函数
* @param GPIO_Pin:中断引脚号
* @retval 无
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20); /* 消抖 */
switch(GPIO_Pin)
{
case KEY0_INT_GPIO_PIN:
if (KEY0 == 0)
{
LED0_TOGGLE(); /* LED0 状态取反 */
LED1_TOGGLE(); /* LED1 状态取反 */
}
break;
case KEY1_INT_GPIO_PIN:
if (KEY1 == 0)
{
LED0_TOGGLE(); /* LED0 状态取反 */
}
break;
case WKUP_INT_GPIO_PIN:
if (WK_UP == 1)
{
BEEP_TOGGLE(); /* 蜂鸣器状态取反 */
}
break;
}
}
/**
* @brief 外部中断初始化程序
* @param 无
* @retval 无
*/
void extix_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */
KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */
WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */
gpio_init_struct.Pin = KEY0_INT_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下升沿触发 */
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct); /* KEY0配置为下降沿触发中断 */
gpio_init_struct.Pin = KEY1_INT_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下升沿触发 */
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &gpio_init_struct); /* KEY1配置为下降沿触发中断 */
gpio_init_struct.Pin = WKUP_INT_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_IT_RISING; /* 上升沿触发 */
gpio_init_struct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP配置为下降沿触发中断 */
HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2); /* 抢占0,子优先级2 */
HAL_NVIC_EnableIRQ(KEY0_INT_IRQn); /* 使能中断线4 */
HAL_NVIC_SetPriority(KEY1_INT_IRQn, 1, 2); /* 抢占1,子优先级2 */
HAL_NVIC_EnableIRQ(KEY1_INT_IRQn); /* 使能中断线3 */
HAL_NVIC_SetPriority(WKUP_INT_IRQn, 2, 2); /* 抢占2,子优先级2 */
HAL_NVIC_EnableIRQ(WKUP_INT_IRQn); /* 使能中断线0 */
}
exti.h
/**
****************************************************************************************************
* @file exti.h
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2020-04-20
* @brief 外部中断 驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 STM32F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20200419
* 第一次发布
*
****************************************************************************************************
*/
#ifndef __EXTI_H
#define __EXTI_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 引脚 和 中断编号 & 中断服务函数 定义 */
#define KEY0_INT_GPIO_PORT GPIOE
#define KEY0_INT_GPIO_PIN GPIO_PIN_4
#define KEY0_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY0_INT_IRQn EXTI4_IRQn
#define KEY0_INT_IRQHandler EXTI4_IRQHandler
#define KEY1_INT_GPIO_PORT GPIOE
#define KEY1_INT_GPIO_PIN GPIO_PIN_3
#define KEY1_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY1_INT_IRQn EXTI3_IRQn
#define KEY1_INT_IRQHandler EXTI3_IRQHandler
#define WKUP_INT_GPIO_PORT GPIOA
#define WKUP_INT_GPIO_PIN GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define WKUP_INT_IRQn EXTI0_IRQn
#define WKUP_INT_IRQHandler EXTI0_IRQHandler
/******************************************************************************************/
void extix_init(void); /* 外部中断初始化 */
#endif
EXTI外部中断函数的执行过程
当中断发生时执行对应的EXTIX_IRQHandler函数,该函数为弱函数,可重写。