1.什么是EXTI?
EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
先看EXTI框图:
1.1产生中断线路流程
①输入线,EXTI 控制器有 20 个中断/事件输入线,可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。
②边沿检测电路,EXTI 可以对触发方式进行选择,通过上升沿触发选择寄存器和下降沿触发选择寄存器对应位的设置来控制信号触发。
③或门电路,一端输入信号线由边沿检测电路提供,一端由软件中断事件寄存器(可以使用软件来启动中断/事件线)提供,只要有一个为有效信号 1,则输出有效信号 1。输出的信号会被保存到挂起寄存器内,电路输出为 1 就会把挂起寄存器对应位置 1。
④与门电路,一端输入信号线由③电路输出提供,一端由中断屏蔽寄存器提供,只有当两者都为有效信号 1,才会输出有效信号 1。
⑤将挂起寄存器内容输入到 NVIC 内,从而实现系统中断事件的控制。
则上面整个流程是产生中断的过程①②③④⑤
2.2产生事件线路流程
①输入线,EXTI 控制器有 20 个中断/事件输入线,可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。
②边沿检测电路,EXTI 可以对触发方式进行选择,通过上升沿触发选择寄存器和下降沿触发选择寄存器对应位的设置来控制信号触发。
③或门电路,一端输入信号线由边沿检测电路提供,一端由软件中断事件寄存器(可以使用软件来启动中断/事件线)提供,只要有一个为有效信号 1,则输出有效信号 1。输出的信号会被保存到挂起寄存器内,电路输出为 1 就会把挂起寄存器对应位置 1。
⑥与门电路,一端来至标号 ③的输出信号,一端来至事件屏蔽寄存器(可以简单的控制事件屏蔽寄存器来实现是否产生事件),只有两者都为有效电平 1,才会输出有效高电平。
⑦脉冲发生器电路,⑥有效,脉冲发生器会输出一个脉冲信号。
⑧脉冲信号:脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC 等等,这样的脉冲信号一般用来触发TIM或者ADC开始转换。
2.中断/事件线
EXTI有20个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0-EXTI15,还有另外7根用于特定的外设事件。
EXTIO至 EXTI15用于GPIO,通过编程控制可以实现任意一个GPIO作为EXTI的输入源。由表18-1可知,在stm32F10x中,EXTIO可以通过AFIO的外部中断配置寄存器1(AFIO_EXTICR1)的EXTIO[3:0]位选择配置为PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0或者PI0,见参考手册。
在stm32F40xx中, 通过配置外部中断配置寄存器SYSCFG_EXTICR1的EXTIO[3:0]位选择配置为PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0或者PI0.
关于NVIC
1.什么是NVIC?
NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。用于为中断分组,从而分配抢占优先级和响应优先级;
NVIC是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M3内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说STM32的NVIC是Cortex-M3的NVIC的一个子集。
NVIC结构体定义:
typedef struct
{
__IO uint32_t ISER[8]; /*!《 Offset: 0x000 Interrupt Set Enable Register */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!《 Offset: 0x080 Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!《 Offset: 0x100 Interrupt Set Pending Register */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!《 Offset: 0x180 Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!《 Offset: 0x200 Interrupt Active bit Register */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!《 Offset: 0x300 Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!《 Offset: 0xE00 Software Trigger Interrupt Register */
} NVIC_Type;
在配置中断的时候我们一般只用ISER、ICER 和IP这三个寄存器,ISER用来使能中断,ICER用来失能中断,IP用来设置中断优先级。
2.中断优先级
2.1优先级定义
在NVIC有一个专门的寄存器:中断优先级寄存器NVIC_IPRx,用来配置外部中断的优先级,IPR 宽度为8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。但是绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数减少,在F103中,只使用了高4bit,如下所示:
用于表达优先级的这4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。
2.2优先级分组
优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCR的PRIGROUP[10:8]位决定,F103分为了5组,具体如下:主优先级=抢占优先级
3.中断编程
NVIC初始化结构体
typedef struct
{
uint8_t NVIC_IRQChannel; //中断源
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //子优先级
FunctionalState NVIC_IRQChannelCmd; //中断使能或者失能
} NVIC_InitTypeDef;
1.NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样.日不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考stm32f10x.h头文件里面的IRQn Type结构体定义,这个结构体包含了所有的中断源。
2.NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定,具体参考表格17-5优先级分组真值表。
3.NVIC_IRQChannelSubPriority:子优先级,具体的值要根据优先级分组来确定,具体参考表格17-5优先级分组真值表。
4.NVIC_IRQChannelCmd:中断使能(ENABLE)或者失能(DISABLE)。操作的是NVIC_ISER和NVIC_ICER这两个寄存器。
编写中断服务函数
在启动文件 startup_stm32f10x_hd.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在stm32f10x it.c这个库文件中。
关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
EXTI和NVIC的关系
编程实例:
通过EXTI配置按键中断,点亮led灯
#include “key.h”
//按键初始化
void key_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;//定义GPIO初始化结构体
EXTI_InitTypeDef EXTI_InitStruct;//定义EXTI初始化结构体
NVIC_InitTypeDef NVIC_InitStruct;//定义NVIC初始化结构体
//开启GPIOE时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
//开启系统配置时钟,为EXTI配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
//-----------------GPIO初始化--------------------------
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;//选择输入模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;//配置GPIO引脚
//GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//输入模式没有推挽或者开漏
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;//选择上拉电阻
GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;//选择高速
GPIO_Init(GPIOE,&GPIO_InitStruct);//¸初始化
//-----------------EXTI初始化----------------------
//多路分选器选择PE3
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
EXTI_InitStruct.EXTI_Line = EXTI_Line3;//选择3号线
EXTI_InitStruct.EXTI_LineCmd = ENABLE;//´使能
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择中断
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStruct);//初始化
//------------------NVIC配置¯-------------------------
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//´使能
NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;//选择EXTI3_IRQHandler
NVIC_Init(&NVIC_InitStruct);//初始化
//-----------------EXTI初始化¯-------------------------
//多路分选器选择PE4
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
EXTI_InitStruct.EXTI_Line = EXTI_Line4;//选择4号线
EXTI_InitStruct.EXTI_LineCmd = ENABLE;//使能
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择中断
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStruct);//初始化
//------------------NVIC配置¯-------------------------
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;//选择EXTI4_IRQHandler
NVIC_Init(&NVIC_InitStruct);//初始化
}
#include “stm32f4xx.h”
#include “led.h”
#include “key.h”
//´ÖÑÓʱ
void delay(int x)
{
int i,j;
for(i=0;i《x;i++)
for(j=0;j《10000;j++);
}
int main()
{
led_init();
key_init();
while(1)
{
if(PEin(3)==0)
{
PAout(6)=0;//设置PA6为低电平,点亮LED灯
}
else if(PEin(3)==1)
{
PAout(6)=1;//设置PA6为高电平,熄灭LED灯
}
}
}
1.什么是EXTI?
EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
先看EXTI框图:
1.1产生中断线路流程
①输入线,EXTI 控制器有 20 个中断/事件输入线,可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。
②边沿检测电路,EXTI 可以对触发方式进行选择,通过上升沿触发选择寄存器和下降沿触发选择寄存器对应位的设置来控制信号触发。
③或门电路,一端输入信号线由边沿检测电路提供,一端由软件中断事件寄存器(可以使用软件来启动中断/事件线)提供,只要有一个为有效信号 1,则输出有效信号 1。输出的信号会被保存到挂起寄存器内,电路输出为 1 就会把挂起寄存器对应位置 1。
④与门电路,一端输入信号线由③电路输出提供,一端由中断屏蔽寄存器提供,只有当两者都为有效信号 1,才会输出有效信号 1。
⑤将挂起寄存器内容输入到 NVIC 内,从而实现系统中断事件的控制。
则上面整个流程是产生中断的过程①②③④⑤
2.2产生事件线路流程
①输入线,EXTI 控制器有 20 个中断/事件输入线,可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。
②边沿检测电路,EXTI 可以对触发方式进行选择,通过上升沿触发选择寄存器和下降沿触发选择寄存器对应位的设置来控制信号触发。
③或门电路,一端输入信号线由边沿检测电路提供,一端由软件中断事件寄存器(可以使用软件来启动中断/事件线)提供,只要有一个为有效信号 1,则输出有效信号 1。输出的信号会被保存到挂起寄存器内,电路输出为 1 就会把挂起寄存器对应位置 1。
⑥与门电路,一端来至标号 ③的输出信号,一端来至事件屏蔽寄存器(可以简单的控制事件屏蔽寄存器来实现是否产生事件),只有两者都为有效电平 1,才会输出有效高电平。
⑦脉冲发生器电路,⑥有效,脉冲发生器会输出一个脉冲信号。
⑧脉冲信号:脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC 等等,这样的脉冲信号一般用来触发TIM或者ADC开始转换。
2.中断/事件线
EXTI有20个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0-EXTI15,还有另外7根用于特定的外设事件。
EXTIO至 EXTI15用于GPIO,通过编程控制可以实现任意一个GPIO作为EXTI的输入源。由表18-1可知,在stm32F10x中,EXTIO可以通过AFIO的外部中断配置寄存器1(AFIO_EXTICR1)的EXTIO[3:0]位选择配置为PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0或者PI0,见参考手册。
在stm32F40xx中, 通过配置外部中断配置寄存器SYSCFG_EXTICR1的EXTIO[3:0]位选择配置为PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0或者PI0.
关于NVIC
1.什么是NVIC?
NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。用于为中断分组,从而分配抢占优先级和响应优先级;
NVIC是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M3内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说STM32的NVIC是Cortex-M3的NVIC的一个子集。
NVIC结构体定义:
typedef struct
{
__IO uint32_t ISER[8]; /*!《 Offset: 0x000 Interrupt Set Enable Register */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!《 Offset: 0x080 Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!《 Offset: 0x100 Interrupt Set Pending Register */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!《 Offset: 0x180 Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!《 Offset: 0x200 Interrupt Active bit Register */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!《 Offset: 0x300 Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!《 Offset: 0xE00 Software Trigger Interrupt Register */
} NVIC_Type;
在配置中断的时候我们一般只用ISER、ICER 和IP这三个寄存器,ISER用来使能中断,ICER用来失能中断,IP用来设置中断优先级。
2.中断优先级
2.1优先级定义
在NVIC有一个专门的寄存器:中断优先级寄存器NVIC_IPRx,用来配置外部中断的优先级,IPR 宽度为8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。但是绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数减少,在F103中,只使用了高4bit,如下所示:
用于表达优先级的这4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。
2.2优先级分组
优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCR的PRIGROUP[10:8]位决定,F103分为了5组,具体如下:主优先级=抢占优先级
3.中断编程
NVIC初始化结构体
typedef struct
{
uint8_t NVIC_IRQChannel; //中断源
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //子优先级
FunctionalState NVIC_IRQChannelCmd; //中断使能或者失能
} NVIC_InitTypeDef;
1.NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样.日不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考stm32f10x.h头文件里面的IRQn Type结构体定义,这个结构体包含了所有的中断源。
2.NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定,具体参考表格17-5优先级分组真值表。
3.NVIC_IRQChannelSubPriority:子优先级,具体的值要根据优先级分组来确定,具体参考表格17-5优先级分组真值表。
4.NVIC_IRQChannelCmd:中断使能(ENABLE)或者失能(DISABLE)。操作的是NVIC_ISER和NVIC_ICER这两个寄存器。
编写中断服务函数
在启动文件 startup_stm32f10x_hd.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在stm32f10x it.c这个库文件中。
关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
EXTI和NVIC的关系
编程实例:
通过EXTI配置按键中断,点亮led灯
#include “key.h”
//按键初始化
void key_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;//定义GPIO初始化结构体
EXTI_InitTypeDef EXTI_InitStruct;//定义EXTI初始化结构体
NVIC_InitTypeDef NVIC_InitStruct;//定义NVIC初始化结构体
//开启GPIOE时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
//开启系统配置时钟,为EXTI配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
//-----------------GPIO初始化--------------------------
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;//选择输入模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;//配置GPIO引脚
//GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//输入模式没有推挽或者开漏
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;//选择上拉电阻
GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;//选择高速
GPIO_Init(GPIOE,&GPIO_InitStruct);//¸初始化
//-----------------EXTI初始化----------------------
//多路分选器选择PE3
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
EXTI_InitStruct.EXTI_Line = EXTI_Line3;//选择3号线
EXTI_InitStruct.EXTI_LineCmd = ENABLE;//´使能
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择中断
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStruct);//初始化
//------------------NVIC配置¯-------------------------
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//´使能
NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;//选择EXTI3_IRQHandler
NVIC_Init(&NVIC_InitStruct);//初始化
//-----------------EXTI初始化¯-------------------------
//多路分选器选择PE4
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
EXTI_InitStruct.EXTI_Line = EXTI_Line4;//选择4号线
EXTI_InitStruct.EXTI_LineCmd = ENABLE;//使能
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择中断
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStruct);//初始化
//------------------NVIC配置¯-------------------------
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;//选择EXTI4_IRQHandler
NVIC_Init(&NVIC_InitStruct);//初始化
}
#include “stm32f4xx.h”
#include “led.h”
#include “key.h”
//´ÖÑÓʱ
void delay(int x)
{
int i,j;
for(i=0;i《x;i++)
for(j=0;j《10000;j++);
}
int main()
{
led_init();
key_init();
while(1)
{
if(PEin(3)==0)
{
PAout(6)=0;//设置PA6为低电平,点亮LED灯
}
else if(PEin(3)==1)
{
PAout(6)=1;//设置PA6为高电平,熄灭LED灯
}
}
}
举报