目录
一、简介
1.1 中断
中断指的是单片机运行过程中,当出现某些意外情况需要主机干预时,计算机能够自动停止正在运行的程序并转入处理新情况的程序。处理完毕后,计算机能够返回原被暂停的程序继续运行。打个比方来讲就比如你此刻正在看这篇文章,你妈妈突然喊你下楼去吃早饭,文章随时可以看,但不吃早饭可能会被妈妈修理,因此你只能停下看文章下去吃早饭,吃完早饭后你继续回来看文章。
1.2 中断优先级
单片机系统中有多个中断源,有可能出现两个或两个以上中断源同时发出中断请求的情况。多个中断源同时请求中断时,CPU必须先确定为哪一个中断源服务,要能辨别优先级最高的中断源并进行响应。CPU在处理中断时也要能响应更高级别的中断申请,而屏蔽掉同级或较低级的中断申请,这就是中断优先级问题。
中断系统中,CPU一般根据各中断请求的轻重缓急分别处理,即给每个中断源确定一个中断优先级别,系统自动对它们进行排队判优,保证首先处理优先级别高的中断请求,待级别高的中断请求处理完毕后,再响应级别较低的中断请求。对多个中断源进行识别和优先级排队的目的就是要确定出最高级别的中断源,并形成该中断源的中断服务程序入口地址,以便CPU将控制转移到该中断服务程序去。
一般指以下两层含义:
①若有2个或2个以上的中断源同时提出中断请求,微处理器先响应哪个中断源,后响应哪个中断源;
②若一个中断源提出中断请求,微处理器给予响应并正在执行其中断服务程序时,又有1个中断源提出中断请求,后来的中断源能否中断前一个中断源的中断服务程序。
打个比方来说你此刻正在看这篇文章,你妈妈突然喊你下楼去吃早饭,文章随时可以看,但不吃早饭可能会被妈妈修理,因此你只能停下看文章下去吃早饭,此时你女朋友打来一个电话,你只能停下吃饭这个动作先接电话,电话接完继续吃饭,吃完早饭后再回来看文章。这里看文章是正在处理的任务,吃早饭是一个中断打断了看文章这个任务,而后续到来的电话是比吃早饭优先级更高的中断打断了吃早饭这个中断任务,这就是中断的优先级。
1.3 MM32G0001的EXTI 中断和事件
嵌套向量中断控制器(NVIC)连接处理器核,管理低延迟的异常和中断处理。NVIC 内部包含 2 位的 中断优先级配置位,从而可提供 4 个中断优先级等级。EXTI 模块包括边沿检测电路,能够产生中断请求 或者唤醒事件,边沿检测支持上升沿、下降沿、任意边沿配置。每一个边沿检测电路支持独立的使能与屏 蔽。
1.4 主要特征
• 独立触发与屏蔽每个中断
• 软件配置中断/事件输出
• 产生唤醒事件唤醒低功耗模式
• 挂起寄存器保存对应每条中断线的状态
• 所有 GPIO 支持配置为 EXTI 的触发源
• 支持上升沿触发,下降沿触发和任意边沿触发
二、功能描述
2.1 功能框图
2.2 中断和异常向量
在 Handler 模式下,Cortex-M0 处理器与内嵌中断向量控制(NVIC)对所有的异常进行优先级区分处 理。当异常发生时,系统会将当前处理的工作压栈,执行完中断服务程序后出栈。取向量与当前工作的压 栈并行进行的,提高了中断的效率,下表分别列出了异常类型与中断向量。
位置 | 优先级 | 优先级类型 | 名称 | 说明 | 地址 |
保留 | 0x0000 0000 | ||||
-3 | 固定 | Reset | 复位 | 0x0000 0004 | |
-2 | 固定 | NMI | 不可屏蔽中断 RCC时钟安全系统(CSS)连接 到NMI | ox0000 0008 | |
-1 | 固定 | 硬件失效(HardFault) | 所有类型失效 | 0x0000 000C |
位置 | 优先级 | 优先级类型 | 名称 | 说明 | 地址 |
3 | 可设置 | SVCall | 通过 SWI 指令的系统服务调用 | 0x0000 002C | |
- | 保留 | 0x0000 0030 | |||
保留 | 0x0000 0034 | ||||
5 | 可设置 | PendSV | 可挂起的系统服务 | 0x0000 0038 | |
6 | 可设置 | SysTick | 系统嘀嗒定时器 | 0x0000 003C | |
0 | 7 | 可设置 | IWDG | 连到 EXTI17 的独立看门狗中断 | 0x0000 0040 |
1 | 8 | 可设置 | PVD | 连到 EXTI16 的电源电压检测 (PVD)中断 | 0x0000 0044 |
2 | 9 | 可设置 | 保留 | 保留 | 0x0000 0048 |
3 | 10 | 可设置 | Flash | 闪存全局中断 | 0x0000 004C |
4 | 11 | 可设置 | RCC | RCC 全局中断 | 0x0000 0050 |
5 | 12 | 可设置 | EXTI0_1 | EXTI 线[1:0]中断 | 0x0000 0054 |
6 | 13 | 可设置 | EXTI2_3 | EXTI 线[3:2]中断 | 0x0000 0058 |
7 | 14 | 可设置 | EXTI4_15 | EXTI 线[15:4]中断 | 0x0000 005C |
8 | 15 | 可设置 | 保留 | 保留 | 0x0000 0060 |
9 | 16 | 可设置 | 保留 | 保留 | 0x0000 0064 |
10 | 17 | 可设置 | 保留 | 保留 | 0x0000 0068 |
11 | 18 | 可设置 | 保留 | 保留 | 0x0000 006C |
12 | 19 | 可设置 | ADC | ADC 全局中断 | 0x0000 0070 |
13 | 20 | 可设置 | TIM1_BRK_UP_TRG _COM | TIM1 刹车、更新、触发、COM 中断 | 0x0000 0074 |
14 | 21 | 可设置 | TIM1_CC | TIM1 捕捉比较中断 | 0x0000 0078 |
15 | 22 | 可设置 | 保留 | 保留 | 0x0000 007C |
16 | 23 | 可设置 | TIM3 | TIM3 全局中断 | 0x0000 0080 |
17 | 24 | 可设置 | 保留 | 保留 | 0x0000 0084 |
18 | 25 | 可设置 | 保留 | 保留 | 0x0000 0088 |
19 | 26 | 可设置 | TIM14 | TIM14 全局中断 | 0x0000 008C |
20 | 27 | 可设置 | 保留 | 保留 | 0x0000 0090 |
21 | 28 | 可设置 | 保留 | 保留 | 0x0000 0094 |
22 | 29 | 可设置 | 保留 | 保留 | 0x0000 0098 |
23 | 30 | 可设置 | I2C1 | I2C1 全局中断 | 0x0000 009C |
24 | 31 | 可设置 | 保留 | 保留 | 0x0000 00A0 |
25 | 32 | 可设置 | SPI1 | SPI1 全局中断 | 0x0000 00A4 |
26 | 33 | 可设置 | 保留 | 保留 | 0x0000 00A8 |
27 | 34 | 可设置 | USART1 | USART1 全局中断 | 0x0000 00AC |
28 | 35 | 可设置 | USART2 | USART2 全局中断 | 0x0000 00B0 |
29 | 36 | 可设置 | 保留 | 保留 | 0x0000 00B4 |
30 | 37 | 可设置 | 保留 | 保留 | 0x0000 00B8 |
31 | 38 | 可设置 | 保留 | 保留 | 0x0000 00BC |
2.3 唤醒事件管理
EXTI 模块支持产生中断或者事件用于将系统从低功耗模式下唤醒,用户执行 WFE 指令进入相应的低 功耗模式后,可以通过配置 EXTI 线产生事件输出唤醒系统,用户执行 WFI 进入低功耗模式后,可以通过 配置 EXTI 线产生中断输出唤醒系统。
2.4 中断功能描述
要使能中断功能,产生中断,首先配置边沿检测触发寄存器为需要的触发类型,打开相应的中断屏蔽 寄存器的对应位允许中断请求。在对应的外部中断线检测到配置的触发条件时,产生一个中断请求,挂起 寄存器对应位置 1,通过对挂起寄存器对应位写 1,将清除中断。
配置产生事件,首先配置边沿检测触发寄存器为需要的触发类型,打开相应的事件屏蔽寄存器的对应 位允许事件请求。在对应的外部中断线检测到配置的触发条件时,产生一个事件请求。
使能软件中断/事件寄存器的对应位,也能够产生中断/事件请求。
2.5 硬件中断输出
配置硬件中断源的具体步骤如下:
• 打开对应中断线的屏蔽位(EXTI_IMR),使能中断。
• 配置对应中断线的触发寄存器位(EXTI_RTSR/EXTI_FTSR)。
• 打开对应连接到 NVIC 的中断通道,使得中断请求能够传递到 CPU,被正确的响应。
当配置 EXTIx(x=31~0)线产生中断输出后,EXTI_PR 寄存器的对应位会置 1,需要清除 EXTI_PR 寄存器的对应挂起位才能再次检测 EXTIx(x=31~0)线的翻转并产生中断。
清除 EXTI_PR 寄存器挂起位有以下三种方式:
• EXTI_PR 寄存器的挂起位写 1。
• 如果配置了上升沿触发选择寄存器(EXTI_RTSR),对应位写 0 会清除挂起位。如果配置了下降 沿触发选择寄存器(EXTI_FTSR),对应位写 0 会清除挂起位。
• 通过改变 EXTI 线的边沿检测极性清除。
2.6 硬件事件输出
配置硬件事件源的具体步骤如下:
• 打开对应事件线的屏蔽位(EXTI_EMR)。
• 配置对应事件线的触发寄存器位(EXTI_RTSR/EXTI_FTSR)。
2.7 软件中断与事件输出
支持通过软件的方式配置产生中断与事件,具体步骤如下:
• 使能事件或中断使能位(EXTI_IMR,EXTI_EMR)。
• 配置软件中断事件寄存器对应位为 1(EXTI_SWIER)。
2.8 外部中断映射
所有的 GPIO 均可用做 EXTI 的触发源用于产生中断或事件请求,通过配置 SYSCFG 章节的 SYSCFG_EXTICRx 寄存器,同时支持内部模块(包括 PVD 和 IWDG)触发。具体存在的连接关系如下 表所示。
其他的外部中断/事件控制器的连接如下:
EXTI 线 16 连接到 PVD 输出
EXTI 线 17 连接到 IWDG 输出
三、寄存器
3.1 寄存器总览
3.2 中断屏蔽寄存器 (EXTI_lMR)
偏移地址:0x0
复位值:0x0000 0000
3.3 事件屏蔽寄存器 (EXTI_EMR)
偏移地址:0x04
复位值:0x0000 0000
3.4 上升沿触发选择寄存器 (EXTI_RTSR)
偏移地址:0x08
复位值:0x0000 0000
3.5 下降沿触发选择寄存器 (EXTI_FTSR)
偏移地址:0x0C
复位值:0x0000 0000
3.6 软件中断事件寄存器 (EXTI_SWlER)
偏移地址:0x10
复位值:0x0000 0000
3.7 软件中断事件挂起寄存器 (EXTI_PR)
偏移地址:0x14
复位值:0x0000 0000
四、按键中断控制LED灯
4.1 硬件电路设计
略,参考GPIO_LED灯与按键设计。
4.2 软件开发
4.2.1 初始化LED_GPIO
void PLATFORM_LED_Enable(LEDn_TypeDef LEDn, FunctionalState State)
{
switch (LEDn)
{
case LED1:
GPIO_WriteBit(GPIOA, GPIO_Pin_15, (ENABLE == State) ? Bit_RESET : Bit_SET);
break;
case LED2:
GPIO_WriteBit(GPIOA, GPIO_Pin_10, (ENABLE == State) ? Bit_RESET : Bit_SET);
break;
case LED3:
GPIO_WriteBit(GPIOA, GPIO_Pin_6, (ENABLE == State) ? Bit_RESET : Bit_SET);
break;
case LED4:
GPIO_WriteBit(GPIOA, GPIO_Pin_5, (ENABLE == State) ? Bit_RESET : Bit_SET);
break;
default:
break;
}
}
void PLATFORM_InitLED(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
PLATFORM_LED_Enable(LED1, DISABLE);
PLATFORM_LED_Enable(LED2, DISABLE);
PLATFORM_LED_Enable(LED3, DISABLE);
PLATFORM_LED_Enable(LED4, DISABLE);
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_10 | GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
}
4.2.2 配置按键中断
void EXTI_Configure(void)
{
EXTI_InitTypeDef EXTI_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_SYSCFG, ENABLE);
/* K1->PA2->EXTI_Line2 */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource2);
EXTI_StructInit(&EXTI_InitStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line2;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/* K2->PA9->EXTI_Line9 */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource9);
EXTI_StructInit(&EXTI_InitStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line9;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/* K3->PA8->EXTI_Line8 */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource8);
EXTI_StructInit(&EXTI_InitStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line8;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/* K4->PA3->EXTI_Line3 */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource3);
EXTI_StructInit(&EXTI_InitStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line3;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/* EXTI Interrupt */
NVIC_InitStruct.NVIC_IRQChannel = EXTI2_3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority = 0x01;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI4_15_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority = 0x01;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
4.3.3 main主函数
int main(void)
{
PLATFORM_InitLED();
EXTI_Configure();
while (1)
{
PLATFORM_LED_Toggle(LED1);
PLATFORM_DelayMS(100);
}
}
4.3.4 运行效果
LED1红灯按间隔100ms闪烁,当任意按键按下时,LED2绿灯闪烁一次。
中断演示