stm32外部中断编程步骤(个人学习总结)
基础知识
外部中断控制器EXTI管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测
1、输入线
如何选择对应端口的IO?
通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1)的
EXTI0[3:0]位选择配置为 PA0、 PB0、 PC0、 PD0、 PE0、 PF0、 PG0、 PH0 。其他类似。
NVIC
是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设
NVIC初始化结构体
typedef struct
{
uint8_t NVIC_IRQChannel; //中断源
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占与优先级
uint8_t NVIC_IRQChannelSubPriority; //子优先级
FunctionalState NVIC_IRQChannelCmd; // 使能
} NVIC_InitTypeDef;
优先级分组
/*NVIC_PriorityGroup_X中的X表示用多少位抢占优先级,在F103系列中只用了四位来配置优先级,其中分为抢占优先级和子优先级如果抢占优先级用来X位,那么子优先级就只有4-X位。*/
/**
2 * 配置中断优先级分组:抢占优先级和子优先级
3 * 形参如下:
4 * @arg NVIC_PriorityGroup_0: 0bit for 抢占优先级
5 * 4 bits for 子优先级
6 * @arg NVIC_PriorityGroup_1: 1 bit for 抢占优先级
7 * 3 bits for 子优先级
8 * @arg NVIC_PriorityGroup_2: 2 bit for 抢占优先级零死角玩转 STM32
9 * 2 bits for 子优先级
10 * @arg NVIC_PriorityGroup_3: 3 bit for 抢占优先级
11 * 1 bits for 子优先级
12 * @arg NVIC_PriorityGroup_4: 4 bit for 抢占优先级
13 * 0 bits for 子优先级
14 * @注意 如果优先级分组为 0,则抢占优先级就不存在,优先级就全部由子优先级控制
15 */
16 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
17 {
18 // 设置优先级分组
19 SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
20 }
优先级概念
在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的
优先级, IPR 宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是绝大多数 CM3 芯片都会精简设计,以致实际上支持的优先级数减少,在F103 中,只使用了高 4bit。用于表达优先级的这 4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会 抢占 抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。
中断线、中断源、IO口的关系
编程步骤
- 时钟配置(GPIO时钟、AFIO时钟)
- GPIO配置(中断功能,映射)
- EXTI配置(触发方式,中断使能)
- NVIC配置(优先级分组,中断使能)
- 中断服务子程序(判断标志,清除标志)
EXTI初始化结构体
typedef struct
{
uint32_t EXTI_Line; //中断线
EXTIMode_TypeDef EXTI_Mode; //EXTI 模式
EXTITrigger_TypeDef EXTI_Trigger; //触发类型
FunctionalState EXTI_LineCmd; //使能
}EXTI_InitTypeDef;
一下两个文件是对火焰传感器的测试
.c文件
#include "flame.h"
/*NVIC初始化函数 用于处理中断(事件)*/
static void NVIC_init(void) //static 表示此函数为静态的 只能在这文件使用
{
NVIC_InitTypeDef NVIC_InitStruct;
/*配置中断优先级的分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/*NVIC_PriorityGroup_X中的X表示用多少位抢占优先级,在F103系列中只用了四位来配置优先级,其中分为抢占优先级和子优先级
如果抢占优先级用来X位,那么子优先级就只有4-X位。*/
/*配置NVIC的初始化结构体*/
NVIC_InitStruct.NVIC_IRQChannel=Flame_EXTI_IRQn;//中断源
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;//子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //使能中断请求
NVIC_Init(&NVIC_InitStruct);/*把配置好的结构体写到寄存器里*/
}
/*初始化EXTI 用于产生中断(事件)*/
void EXTI_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_init();
RCC_APB2PeriphClockCmd(Flame_EXTI_GPIO_CLK,ENABLE);
/*初始化要连接到EXTI的GPIO*/
GPIO_InitStruct.GPIO_Pin=Flame_EXTI_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(Flame_EXTI_GPIO_PORT,&GPIO_InitStruct);
/*初始化EXTI外设*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能EXTI外设时钟
GPIO_EXTILineConfig(Flame_EXTI_GPIO_PORTSOURCE,Flame_EXTI_GPIO_PinSourcex); //选择 GPIO 管脚用作外部中断线路
EXTI_InitStruct.EXTI_Line=Flame_EXTI_Line; //产生 中断或者事件
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; //EXTI模式 中断或者事件
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising; //触发方式 下降沿触发
EXTI_InitStruct.EXTI_LineCmd=ENABLE; //使能
EXTI_Init(&EXTI_InitStruct);
}
.h文件
#ifndef __exti_h_
#define __exti_h_
#include "stm32f10x.h"
#define Flame_EXTI_GPIO_CLK RCC_APB2Periph_GPIOA
#define Flame_EXTI_GPIO_PIN GPIO_Pin_0
#define Flame_EXTI_GPIO_PORT GPIOA
#define Flame_EXTI_GPIO_PORTSOURCE GPIO_PortSourceGPIOA
#define Flame_EXTI_GPIO_PinSourcex GPIO_PinSource0
#define Flame_EXTI_Line EXTI_Line0
#define Flame_EXTI_IRQn EXTI0_IRQn
#define Flame_EXTI_IRQHandler EXTI0_IRQHandler
void EXTI_init(void);
#endif /*__exti.h*/
中断服务函数
void Flame_EXTI_IRQHandler()
{
if(EXTI_GetITStatus(Flame_EXTI_Line)!=RESET)
{
LED1_ON;
printf("检测到火焰\r\n");
}
EXTI_ClearITPendingBit(Flame_EXTI_Line);
}