一、外部中断
外部中断:外部中断的中断是相对于外部中断控制器(EXTI)来说,如下图所示:
EXTI掌管着23根中断线,具体分布图下:
16根连接GPIO口,如下图:
所有的0口连接到中断线0,所有的1口连接到中断线1,以此类推。
具体的哪个IO口连接到相应的中断线,通过寄存器:SYSCFG_EXTICRx决定(x = 1~4)。
其余7根呢??
另外七根 EXTI 线连接方式如下:
● EXTI 线 16 连接到 PVD 输出
● EXTI 线 17 连接到 RTC 闹钟事件
● EXTI 线 18 连接到 USB OTG FS 唤醒事件
● EXTI 线 19 连接到以太网唤醒事件
● EXTI 线 20 连接到 USB OTG HS(在 FS 中配置)唤醒事件
● EXTI 线 21 连接到 RTC 入侵和时间戳事件
● EXTI 线 22 连接到 RTC 唤醒事件
上面7根来自片内外设
外部中断框图分析
下图为外部中断控制器框架结构,上面讲的外部中断线上的每个中断源都会经过这样的一个结构。
框图分成了两个部分,红色代表产生的是中断,绿色代表产生的是事件。
补充:
中断:当一定的条件满足后(比如串口接收到数据)一定会进入中断服务函数。
事件:当一定的条件满足后(比如串口接收到数据)自动触发硬件工作。例如:将某一个寄存器的某一个位置位1或将某一个位清成0,接收数据标志位置1等。
相同点:中断与事件的产生条件一致。
不同点:中断过程需要CPU参与,事件不需要CPU参与。
(1)中断(重点掌握)
编号①代表外部中断线(外部中断的信号来源),外部中断线一共有23根,其中16根来源为GPIO口,另外7根来源为片上外设。需要从这23根中选择其中一个作为输入线。
编号②是边沿检测电路,这个边沿检测电路的信号来源来自外部中断线,通过对上升沿触发选择寄存器和下降沿触发选择寄存器的配置来决定这个边沿检测电路检测哪种边沿信号(上升沿、下降沿、双边沿)。当编号①输入了对应的边沿信号时,边沿电测电路就会输出有效信号‘1’,否则输出‘0’。
编号③是一个或门电路,这个或门电路有两个信号源,一个来自边沿检测电路的输出端,一个来自软件中断事件寄存器。当边沿检测电路输出有效信号‘1’或者软件中断事件寄存器对应位为1时,或门电路就输出有效信号‘1’。其实,这里可以直接通过设置软件中断事件寄存器来产生中断信号而不需要检测到对应边沿,这个就是所谓的软件中断。
编号④是一个与门电路,这个与门电路有两个信号源,一个来自或门电路的输出端,一个来自中断屏蔽寄存器。当或门电路输出有效信号‘1’同时中断屏蔽寄存器也输出有效信号‘1’时,这个与门电路才输出有效信号‘1’。其实就是可以通过对中断屏蔽寄存器的配置来决定是否响应该中断。(模块级中断使能)
编号⑤:当与门电路输出了有效信号‘1’到挂起请求寄存器时,挂起请求寄存器就会告诉NVIC当前产生了外部中断信号,请求NVIC响应。
注意:产生中断线路目的是把输入信号输入到NVIC,进一步会运行中断服务函数.
产生外部中断原理框图
外部中断配置流程:
SYSCFGEN时钟打开
选择对应的外部中断线SYSCFG_EXTICRx
配置边沿触发器
使能中断屏蔽寄存器(模块级中断使能)
配置NVIC控制器(核心级中断使能)
编写中断服务函数
事件
绿色部分是产生了事件
编号①②③与前面的分析一样。
编号⑥与编号④的原理一样,就是可以通过对事件屏蔽寄存器的配置决定是否可以产生事件。
编号⑦是脉冲发生器,当编号⑥电路输出有效信号‘1’时,可以触发脉冲发生器产生一个脉冲信号触发其他模块工作,不需要CPU参与。
注意:
当脉冲发生器输入端是一个有效信号1时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。
脉冲信号,事件的最终的结果,这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC等等。
相关寄存器
中断屏蔽寄存器
位 22:0 MRx:x 线上的中断屏蔽 (Interrupt mask on line x)
0:屏蔽来自 x 线的中断请求
1:开放来自 x 线的中断请求
事件屏蔽寄存器
位 22:0 MRx:x 线上的事件屏蔽 (Event mask on line x)
0:屏蔽来自 x 线的事件请求
1:开放来自 x 线的事件请求
上升沿触发选择寄存器
位 22:0 TRx:线 x 的上升沿触发事件配置位 (Rising trigger event configuration bit of line x)
0:禁止输入线上升沿触发(事件和中断)
1:允许输入线上升沿触发(事件和中断)
下降沿触发选择寄存器
位 22:0 TRx:线 x 的下降沿触发事件配置位 (Falling trigger event configuration bit of line x)
0:禁止输入线下降沿触发(事件和中断)
1:允许输入线下降沿触发(事件和中断)
软件中断事件寄存器
位 22:0 SWIERx:线 x 上的软件中断 (Software Interrupt on line x) (关闭软件中断通过写0操作)
当该位为“0”时,写“1”将设置 EXTI_PR 中相应的挂起位。
如果在 EXTI_IMR 和 EXTI_EMR 中允许产生该中断,则产生中断请求。
通过清除 EXTI_PR 的对应位(写入“1”),可以清除该位为“0”。
挂起寄存器
位 22:0 PRx:挂起位 (Pending bit) (产生中断的标志位,清中断标志位)
0:没有发生触发请求
1:发生了选择的触发请求
当在外部中断线上发生了选择的边沿事件,该位被置“1”。
在此位中写入“1”可以清除它, 也可以通过改变边沿检测的极性清除。
外部中断线中断源选择相关寄存器
SYSCFG 外部中断配置寄存器 1 (SYSCFG_EXTICR1)
位 15:0 EXTIx[3:0]:EXTI x 配置(x = 0 到 3)(EXTI x configuration (x = 0 to 3))
这些位通过软件写入,以选择 EXTIx 外部中断的源输入。
0000:PA[x] 引脚
0001:PB[x] 引脚
0010:PC[x] 引脚
0011:PD[x] 引脚
0100:PE[x] 引脚
0101:PF[C] 引脚
0110:PG[x] 引脚
0111:PH[x] 引脚
1000:PI[x] 引脚
注意高16位是保留位
例:需要将PB7作为外部中断的信号源。
将SYSCFG_EXTICR2的EXTI7位写入0001
SYSCFG 外部中断配置寄存器 2 (SYSCFG_EXTICR2)
SYSCFG 外部中断配置寄存器 3 (SYSCFG_EXTICR3)
SYSCFG 外部中断配置寄存器 4 (SYSCFG_EXTICR4)
RCC APB2 外设时钟使能寄存器 (RCC_APB2ENR)
位 14 SYSCFGEN:系统配置控制器时钟使能 (System configuration controller clock enable)
由软件置 1 和清零。
0:禁止系统配置控制器时钟
1:使能系统配置控制器时钟
软件设计
配置流程:
- 按键能正常工作(PA0配置成浮空输入)
- 输入线选择(把PA0映射到EXTI0中)
- 设置边沿检测电路(上升沿检测)
④屏蔽软件中断,屏蔽事件
⑤模块级中断使能(配置中断屏蔽寄存器)
⑥配置NVIC(设置优先级、系统级中断使能)
⑦编写中断服务函数(清标志)
- 按键初始化
- 开启GPIO的时钟
- 配置GPIO的模式(普通输入)
- 配置GPIO的无上下拉
- 初始化外部中断
- 开启syscfg的时钟
- 配置syscfg-exticr1寄存器对应的外部中断线。
- 配置上升沿检测,关闭下降沿检测
- 关闭软件中断事件
- 关闭事件屏蔽
- 开启中断屏蔽
- 开启NVIC控制器
- 清除一次标志位
- 编写中断服务函数
- 判断是哪一个中断
- 清中断标志
- 功能代码
二、软件中断
软件中断简介
软件中断:完全不需要硬件的参与,通过代码(软件)就可以产生中断的方式。
软件中断框图
软件中断--相关寄存器
中断屏蔽寄存器 (EXTI_IMR)
软件中断事件寄存器 (EXTI_SWIER)
软件设计:
- 初始化外部中断
- 开启中断屏蔽(模块级中断使能)
- 开启NVIC控制器(核心级中断使能)
- 初始化先关闭软件中断功能(在需要的是开启,标志位就会置一)
- 编写中断服务函数
- 清中断标志
- 具体代码
#include "exti.h"
//PE3->EXTI3
void EXTI_Init(void)
{
/*1IO配置*/
RCC->AHB1ENR |= 1<< 4;
GPIOE->MODER &=~(0X3<< 6);//输入模式
GPIOE->PUPDR &=~(0X3<< 6);//没有上下拉
/*2选择外部中断线的输入*/
RCC->APB2ENR |=1 << 14;//打开系统配置控制器的时钟(APB2)
SYSCFG->EXTICR[0]|=4<< 12;//选择外部中断线3的中断来源是PE3
/*3选择触发边沿*/
EXTI->FTSR |=1<< 3;//外部中断线3设置为下降沿触发中断
/*4允许中断产生*/
EXTI->IMR |=1<< 3;//开放来自 x 线的中断请求
/*5配置EXTI3 的NVIC*/
NVIC_SetPriority(EXTI3_IRQn, NVIC_EncodePriority (7-2, 0, 2));
NVIC_EnableIRQ (EXTI3_IRQn);//核心级中断使能
}
#include "stdio.h"
/*编写中断服务函数*/
void EXTI3_IRQHandler (void)
{
//请标志是写入一清除标志
EXTI->PR |=1<< 3;//清除一次标志
/*功能代码*/
printf("w外部中断线3的中断来了\r\n");
}
void EXTI8_Init(void)
{
/*4允许中断产生*/
EXTI->IMR |=1<< 8;//开放来自 x 线的中断请求
/*5配置EXTI8 的NVIC*/
NVIC_SetPriority(EXTI9_5_IRQn, NVIC_EncodePriority (7-2, 3, 2));
NVIC_EnableIRQ (EXTI9_5_IRQn);//核心级中断使能
}
/*编写中断服务函数*/
void EXTI9_5_IRQHandler (void)
{
//请标志是写入一清除标志
EXTI->PR |=1<< 8;//清除一次标志
/*功能代码*/
printf("外部中断线8的中断来了\r\n");
}
#ifndef EXTI_H
#define EXTI_H
#include "stm32f4xx.h"
#include "io_bit.h"
void EXTI_Init(void);
void EXTI8_Init(void);
#endif
/************************************
* @brief :主函数
* @param :none
* @return:none
* @note 程序入口
*************************************/
int main(void)
{
NVIC_SetPriorityGrouping (7-2);//抢占2响应4-2
u8 key = 0;
//硬件初始化配置
KEY_Init();
EXTI_Init();
EXTI8_Init();
while(1)//让CPU卡主,单片机的程序架构
{
if(KeyScan())
{
EXTI->SWIER|=1<< 8;//软件产生一个中断
}
}
}