已经详细介绍了 NVIC,对 STM32F10x 系列中断管理系统有个全局的了解,这章的内容是 NVIC 的实例应用,也是 STM32F10x 控制器非常重要的一个资源。
STM32的通用I/O引脚可以直接映射为外部中断通道或事件输出,用于产生中断/事件请求。这就需要微控制器上的另一个外设——EXTI。
外部中断/事件控制器, 管理了控制器的 20个中断/事件线(EXTI19只适用于以太网唤醒事件(只适用互联型)微控制器)。
每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性
EXTI 可分为两大部分功能:
一个是产生中断(红色虚线),红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到 NVIC 控制器内。
另一个是产生事件(绿色虚线),这两个功能从硬件上就有所不同。
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。
另外, EXTI 是在 APB2 总线上的,在编程时候需要注意到这点。
EXTI 有 20 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至EXTI15,还有另外七根用于特定的外设事件,见表:
根特定外设中断/事件线由外设触发,具体用法参考《STM32F10X-中文参考手册》中对外设的具体说明
EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。
EXTI可以通过 AFIO 的外部中断配置寄存(AFIO_EXTICR)的
EXTI0[3:0]位选择配置为 PA0、 PB0、 PC0、 PD0、 PE0、 PF0、 PG0。这样的寄存器有4个,每一个寄存器控制4条事件线。GPIO有16个端口,就需要4个寄存器来控制。
初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。
typedef struct {
uint32_t EXTI_Line; // 中断/事件线选择,可选 EXTI0 至 EXTI19
EXTIMode_TypeDef EXTI_Mode; // EXTI 模式可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。
EXTITrigger_TypeDef EXTI_Trigger; // 触发类型可选上升沿触发(EXTI_Trigger_Rising)、下降 沿 触 发 ( EXTI_Trigger_Falling) 或 者 上 升 沿 和 下 降 沿 都 触 发( EXTI_Trigger_Rising_Falling)。
FunctionalState EXTI_LineCmd; // EXTI 使能控制是否使能 EXTI 线,可选使能 EXTI 线(ENABLE)或禁用(DISABLE)。
} EXTI_InitTypeDef;
PA0接按键
PB0接led灯
1-初始化要连接到EXTI的GPIO
2-初始化EXTI用于产生中断/事件
3-初始化NVIC,用于处理中断
4-编写中断服务函数
5-main函数
具体代码看下面,都有详细的注释
bsp_exti.c内容:
#include "bsp_exti.h"
/*
1-初始化要连接到EXTI的GPIO
2-初始化EXTI用于产生中断/事件
3-初始化NVIC,用于处理中断
*/
static void GPIO_Configuration(void)//使用static,此函数只能在 bsp_exti.c 文件中调用
{
GPIO_InitTypeDef GPIO_InitStructure;
//打开GPIO口的时钟
RCC_APB2PeriphClockCmd(KEY_INT_GPIO_CLK, ENABLE);
/* 选择按键用到的 GPIO */
GPIO_InitStructure.GPIO_Pin = KEY_INT_GPIO_PIN;
/* 配置为浮空输入 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY_INT_GPIO_PORT, &GPIO_InitStructure);
}
//初始化EXTI
static void EXTI_Configuration(void)//使用static,此函数只能在 bsp_exti.c 文件中调用
{
EXTI_InitTypeDef EXTI_InitStructure; //定义一个结构体变量
//选择信号源
GPIO_EXTILineConfig(KEY_INT_EXTI_PORTSOURCE, \
KEY_INT_EXTI_PINSOURCE);
// 结构体初始化,在stm32f10x_exti.h中,那个线 0
EXTI_InitStructure.EXTI_Line = KEY_INT_EXTI_LINE;
/* EXTI 为中断模式0x00 ,还有一种事件模式0x04*/
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿中断 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中断 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
//初始化NVIC
static void NVIC_Configuration(void)//使用static,此函数只能在 bsp_exti.c 文件中调用
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置 NVIC 为优先级组 1 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//函数在misc.c中
/* 配置中断源:按键 ,四个结构体成员初始化,在misc.h中 */
NVIC_InitStructure.NVIC_IRQChannel = KEY_INT_EXTI_IRQ;
/* 配置抢占优先级: 1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级: 1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI_Config(void)
{
/* 配置GPIO */
GPIO_Configuration();
/* 配置 NVIC 中断*/
NVIC_Configuration();
/* 配置 EXTI */
EXTI_Configuration();
}
bsp_exti.h内容:
#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H
#include "stm32f10x.h"
#define KEY_INT_GPIO_PIN GPIO_Pin_0
#define KEY_INT_GPIO_PORT GPIOA
#define KEY_INT_GPIO_CLK (RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO)
#define KEY_INT_EXTI_IRQ EXTI0_IRQn //中断源,在stm32f10x.h中
#define KEY_INT_EXTI_PORTSOURCE GPIO_PortSourceGPIOA //中断源在哪个口 A
#define KEY_INT_EXTI_PINSOURCE GPIO_PinSource0 //中断源在那个线 0
#define KEY_INT_EXTI_LINE EXTI_Line0 //线0
static void GPIO_Configuration(void); /* 配置GPIO */
static void EXTI_Configuration(void); /* 配置 NVIC 中断*/
static void NVIC_Configuration(void); /* 配置 EXTI */
void EXTI_Config(void);
#endif /* __BSP_EXTI_H */
stm32f10x_it.c内容:
#include "stm32f10x_it.h"
#include "bsp_led.h"
#include "bsp_exti.h"
void EXTI0_IRQHandler (void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
LED_G_TOGGLE;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
main.c内容:
#include "stm32f10x.h" // 相当于51单片机中的 #include
#include "bsp_led.h"
#include "bsp_exti.h"
int main(void)
{
// 来到这里的时候,系统的时钟已经被配置成72M。
LED_GPIO_Config();
EXTI_Config();
while(1)
{
/*无限循环是main程序的主体部分。当系统初始化完成后,*/
/*即始终陷于该无限循环中运行。与之前的按键控制灯亮灭*/
/*不同,此循环不做任何事情。因为所有工作都由中断服务程序完成*/
}
}
前/后台架构,是由后台程序和前台程序两部分构成的。后台又被称为任务级程序。主要负责处理日常事务;前台通过中断及其服务函数实现,又被称为中断级程序。
位于main.c中,主体为主函数main,通常由几个初始化函数和一个无限循环组成。其中,初始化完成中断源和NVIC的配置。
中断服务程序,位于stm32f10x_it.c中,由中断服务函数组成。