STM32之中断

1、NVIC

Nested vectored interrupt controller :可嵌套向量中断控制器 (NVIC)

  • NVIC 特性

    1. 82个可屏蔽中断 ##不包括内核的16个中断
    2. 16个可编程优先级 ##适用于全部中断
    3. 低延迟异常和中断处理
    4. 电源管理控制
    5. 系统控制寄存器的实现

NVIC与处理器内核接口紧密耦合, 实现了高效快速的中断响应。所有的中断,包括内核异常都被 NVIC 所管理.

2、中断向量表

其实中断向量表在**STM32F4XX**启动文件里面就可以看出来,详情可看 :[STM32F4XX启动文件分析](http://blog.csdn.net/u013904227/article/details/51168546)

3、EXTI(External interrupt/event controller) 外部中断/事件控制器

主要特性

  1. 在每个中断/事件线都有独立的触发和屏蔽功能
  2. 每个中断线有专用的状态标志位
  3. 可产生高达 23 个软件事件/中断请求
  4. 以比APB2时钟周期更短的脉冲宽度检测外部信号

中断与事件配置

  • 硬件中断选择

    配置23线为中断源可参考以下配置步骤 :

    1. 配置 23 中断线的屏蔽位 (EXTI_IMR)
    2. 配置中断线的触发选择位 (EXTI_RTSR and EXTI_FTSR)
    3. 配置控制 NVIC IRQ 通道的使能与屏蔽位来使来自 23 线的一个中断可以被正确的应答,NVIC IRQ 被映射到外部中断控制器(EXTI)
  • 硬件事件选择

    配置23线为事件源可参考以下配置步骤 :

    1. 配置 23 事件线的屏蔽位 (EXTI_EMR)
    2. 配置事件线的触发选择位 (EXTI_RTSR and EXTI_FTSR)
  • 软件 中断/事件 选择

    23 线可以被配置为软件 中断/事件 线。下面的操作步骤可以产生一个软件中断.

    1. 配置 23 事件/中断 线的屏蔽位 (EXTI_IMR, EXTI_EMR)
    2. 设置软件中断寄存器的应答位 (EXTI_SWIER)

外部 中断/事件 映射

STM32F407ZG 的 140 个 GPIO 引脚都与一个外部中断线相连,具体如图所示:

上述一共用到了16根 EXTI 线,其余 7 根 EXTI 线的连接使用如下:

  • EXTI 16 连接到 PVD 输出(PVD:掉电检测)
  • EXTI 17 连接到 RTC Alarm 事件
  • EXTI 18 连接到 USB OTG FS Wakeup 事件
  • EXTI 19 连接到 Ethernet Wakeup 事件
  • EXTI 20 连接到 USB OTG HS (configured in FS) Wakeup 事件
  • EXTI 21 连接到 RTC Tamper and TimeStamp 事件
  • EXTI 22 连接到 RTC Wakeup 事件

中断与事件的区别

一个硬件中断/事件的产生:

  • input line 输入外部信号
  • 边缘检测电路检测电平变化(电平变化检测可以人为配置,并且上升沿检测与下降沿检测是独立的)
  • 经过一个或门,此或门连接电平检测电路的输出与软件事件/中断寄存器。也因此任意一条线的值为真,那么输出就为真,所以可以产生软触发中断或者事件
  • 或门输入信号分别经过两个与门,另个与门分别再与中断屏蔽寄存器与事件屏蔽寄存器连接,控制中断或者事件的产生。这两个也是独立的,所以可以同时产生中断以及事件
  • 如果是中断的话,输出信号会再经过中断挂起请求寄存器,如果此时芯片正处于不可被中断打断的时候,可以配置中断挂起寄存器来暂时挂起一个中断。需要软件参与
  • 如果是事件的话,输出信号直接输出到一个脉冲发生器里面,脉冲发生器可以产生一个脉冲,调动相应的硬件完成此次事件响应。无需软件参与

DMA传输的例子:

  • 如果配置为中断的话,需要在中断产生之后,进入中断处理函数,在中断处理函数中触发DMA操作,然后进行DMA。
  • 如果配置为事件的话,直接由事件最终输出脉冲来触发DMA操作,不需要经过中断处理函数进行DMA的触发。

事件可以降低CPU的负荷,提高了响应速度

4、内核最重要的两个模块(SCB:System controller block NVIC:Nested vectored interrupt controller)

内核的外设

SCB

地址 寄存器名 读写权限 特权 复位值 作用描述
0xE000E008 ACTLR RW Privileged 0x00000000 辅助控制寄存器
0xE000ED00 CPUID RO Privileged 0x410FC240 CPU的ID号码
0xE000ED04 ICSR RW Privileged 0x00000000 中断控制与状态寄存器
0xE000ED08 VTOR RW Privileged 0x00000000 中断向量表偏移,一般只取两个值,第29位为1表示SRAM区,为0表示code区
0xE000ED0C AIRCR RW Privileged 0xFA050000 应用程序中断以及复位
0xE000ED10 SCR RW Privileged 0x00000000 系统控制
0xE000ED14 CCR RW Privileged 0x00000200 配置与控制
0xE000ED18 SHPR1 RW Privileged 0x00000000 系统中断处理函数优先级寄存器1
0xE000ED1C SHPR2 RW Privileged 0x00000000 系统中断处理函数优先级寄存器2
0xE000ED20 SHPR3 RW Privileged 0x00000000 系统中断处理函数优先级寄存器3
0xE000ED24 SHCRS RW Privileged 0x00000000 系统中断处理函数控制与状态
0xE000ED28 CFSR RW Privileged 0x00000000 配置异常状态寄存器
0xE000ED28 MMSRb RW Privileged 0x00 内存管理异常状态寄存器
0xE000ED29 BFSRb RW Privileged 0x00 总线异常状态寄存器
0xE000ED2A UFSRb RW Privileged 0x0000 使用异常状态寄存器
0xE000ED2C HFSR RW Privileged 0x00000000 硬件异常状态寄存器
0xE000ED34 MMAR RW Privileged Unknown 内存管理异常地址寄存器
0xE000ED38 BFAR RW Privileged Unknown 总线异常地址寄存器
0xE000ED3C AFSR RW Privileged 0x00000000 辅助异常状态寄存器

NVIC

地址 寄存器名 读写权限 特权 复位值 作用描述
0xE000E100-0xE000E11C NVIC_ISER0-NVIC_ISER7 RW Privileged 0x00000000 中断使能
0XE000E180-0xE000E19C NVIC_ICER0-NVIC_ICER7 RW Privileged 0x00000000 中断禁止
0XE000E200-0xE000E21C NVIC_ISPR0-NVIC_ISPR7 RW Privileged 0x00000000 中断挂起
0XE000E280-0xE000E29C NVIC_ICPR0-NVIC_ICPR7 RW Privileged 0x00000000 中断恢复
0xE000E300-0xE000E31C NVIC_IABR0-NVIC_IABR7 RW Privileged 0x00000000 中断激活
0xE000E400-0xE000E4EF NVIC_IPR0-NVIC_IPR59 RW Privileged 0x00000000 中断优先级
0xE000EF00 STIR WO Configurable 0x00000000 软件触发中断

5、优先级分组的概念

Cortex M4 的优先级分组如下图所示

内核优先级的分组:

要注意的是,在 STM32F407ZG 只使用了 4bits 的位(高4位),也就是说分组情况如下

STM32组编号 PRIGROUP Binary point Group priority bits Subpriority bits Group priorities subpriorities
0 0b111 b.yyyyyyyy none [7:4] 1 16
1 0b110 bx.yyyyyyy [7] [6:4] 2 8
2 0b101 bxx.yyyyyy [7:6] [5:4] 4 4
3 0b100 bxxx.yyyyy [7:5] [4] 8 2
4 0b011 bxxxx.yyyy [7:4] none 16 1

在STM32中组编号恰好与内核手册中的是反的,这样设计的原因是为了兼容性,也就是说如果程序移植到了只支持3位优先级设置的系统中也能够运行。另外有三种设计方式分别是:使用高 4bits,组编号不反转;使用低 4bits,组编号不反转;使用低 4bits,组编号反转。这三种方法如果按照内核分组写出来之后会发现会有优先级完全一样的情况出现,所以不可取。

  • 要了解优先级分组,就要明确两个概念:抢占优先级(组优先级)、响应优先级(子优先级)
  1. 抢占优先级:可以被中断嵌套。也就是在一个中断发生的时候,另一个抢占优先级比此中断级别高的中断可以打断正在进行的中断,直到更高优先级的中断执行完毕之后,才会返回来继续执行这个被打断的中断
  2. 响应优先级:不可以被中断嵌套。也就是说在多个中断同时发生的时候,只能够优先相应较高优先级的中断,并且如果在中断过程中有更高优先级中断发生的时候,正在进行的中断也不能够被打断。

    抢占优先级与响应优先级的关系有点像 TCP/IP 协议中的网络号与子网号的区别,两个中断也是先比较抢占优先级然后才是比较响应优先级

6、程序编写

#define SUM_NVIC_PRIOTITY_BITS 4    //一共用了4个位

/* 中断优先级分组
 * group_num : 分组号,上面有列出各个分组对应的优先级
 */
static void set_priority_group(u8 group_num)
{
    u32 temp = 0, temp1 = 0;

    group_num = group_num % (SUM_NVIC_PRIOTITY_BITS + 1);   //因为只有5个组,所以限制数量
    temp1 = (((~group_num) & 0x07) << 8);  //取反区低三位
    temp = SCB->AIRCR;
    temp &= 0x0000F8FF; //清除8-10位
    temp |= 0x05FA0000; //必须写5FA位,是作为钥匙的作用,不写的话写入的分组是无效的
    temp |= temp1;

    SCB->AIRCR = temp;
}

/* 设置优先级分组
 * g_priority :抢占优先级    sub_priority :响应优先级
 * irq_num :中断号         prioritygroup :优先级组
 */
void NVIC_set_priority(u8 g_priority, u8 sub_priority, IRQn_Type irq_num, u8 prioritygroup)
{
    int32_t sub_priority_bits = 0;

    sub_priority_bits = SUM_NVIC_PRIOTITY_BITS - prioritygroup;
    ASSERT(sub_priority_bits >= 0); //断言,如果小于0就报错
    /* 原型
    #define ASSERT(x)   while(!(x)){                             printf("Assert failed!!! File:%s Function:%s Line:%d\r\n", __FILE__, __FUNCTION__, __LINE__);                             delay_ms(1000);                             }
    */
    set_priority_group(prioritygroup);  //设置优先级分组

    /* 参考内核头文件写的 */
    if(irq_num < 0) //要判断是否小于0原因是:内核头文件中把内核的中断设置为小于0的枚举类型,而其他的都是大于0的,参照内核头文件
    {
        /* 根据内核头文件中的 SCB 结构体对应内核手册部分推算 */
        SCB->SHP[((uint32_t)(irq_num) & 0xF)-4] = ((g_priority << sub_priority_bits) | sub_priority) << (8 - SUM_NVIC_PRIOTITY_BITS);
    }
    else
    {
        NVIC->IP[(uint32_t)(irq_num)] = ((g_priority << sub_priority_bits) | sub_priority) << (8 - SUM_NVIC_PRIOTITY_BITS);
    }
}
/* 使能相应的中断,为必须的 */
void NVIC_enable_irq(IRQn_Type irq)
{
    NVIC->ISER[(uint32_t)((int32_t)irq) >> 5] |= (uint32_t)(1 << ((uint32_t)((int32_t)irq) % 32));
}

void EX_irq_config(GPIOx_SELECT gpiox,GPIOx_pn_SELECT gpiox_n,GPIO_IRQ_TRIGGER trigger)
{
    RCC->APB2ENR |= (1 << 14);  //使能SYSCFG模块,只有使能之后对SYSCFG寄存器的设置才会有效
    gpio_init(gpiox, gpiox_n, BYM_PULL_UP, BYM_GPI, BYM_HIGH_LEVEL, BYM_PUSH_PULL); //初始化GPIO为输入,内部上拉

    SYSCFG->EXTICR[gpiox_n/4] &= ~(0xF << ((gpiox_n % 4) * 4));    //
    SYSCFG->EXTICR[gpiox_n/4] |= (gpiox << ((gpiox_n % 4) * 4));//映射 Px_n 到 EXTIn中断线

    EXTI->IMR |= (1 << gpiox_n);    //解除屏蔽
    EXTI->RTSR |= ((trigger & 0x01) << gpiox_n);    //设置触发沿
    EXTI->FTSR |= ((trigger >> 1) << gpiox_n);
}

/* 只支持外部中断的中断标志清除 */
void EX_irq_clear(u8 irq)
{
    EXTI->PR |= (1 << irq);
}

7、测试

测试抢占优先级不同的情况

主程序

NVIC_set_priority(1, 2, EXTI3_IRQn, 2);
NVIC_set_priority(2, 1, EXTI2_IRQn, 2);
EX_irq_config(BYM_GPIOE, BYM_Px3, IRQ_BOTHEDGE);
EX_irq_config(BYM_GPIOE, BYM_Px2, IRQ_BOTHEDGE);
NVIC_enable_irq(EXTI3_IRQn);
NVIC_enable_irq(EXTI2_IRQn);

中断服务程序

void EXTI3_IRQHandler(void)
{
    if(0 == gpio_get(BYM_GPIOE, BYM_Px3))
    {
        printf("Key 1 down \r\n");
        delay_ms(1000);
        printf("Key 1 end \r\n");
    }
    EX_irq_clear(3);
}

void EXTI2_IRQHandler(void)
{
    if(0 == gpio_get(BYM_GPIOE, BYM_Px2))
    {
        printf("Key 2 down \r\n");
        delay_ms(1000);
        printf("Key 2 end \r\n");
    }
    EX_irq_clear(2);
}

先按下 GPE3(对应EXTI3),立马按下 GPE2(对应EXTI2),有下面的输出结果(不会被打断)

Key 1 down
Key 1 end

先按下 GPE2(对应EXTI2),立马按下 GPE3(对应EXTI3),有下面的输出结果(EXTI2的中断被EXTI3打断了)

Key 2 down
Key 1 down
Key 2 end
Key 1 end

测试抢占优先级相同而响应优先级不同的情况

主程序改变如下

NVIC_set_priority(1, 2, EXTI3_IRQn, 2);
NVIC_set_priority(1, 1, EXTI2_IRQn, 2);
EX_irq_config(BYM_GPIOE, BYM_Px3, IRQ_BOTHEDGE);
EX_irq_config(BYM_GPIOE, BYM_Px2, IRQ_BOTHEDGE);
NVIC_enable_irq(EXTI3_IRQn);
NVIC_enable_irq(EXTI2_IRQn);

先按下 GPE3(对应EXTI3),立马按下 GPE2(对应EXTI2),有下面的输出结果(不会被打断)

Key 1 down
Key 1 end

先按下 GPE2(对应EXTI2),立马按下 GPE3(对应EXTI3),有下面的输出结果(不会被打断)

Key 2 down
Key 2 end

8、程序编写思路

  1. 设置优先级分组,使用 SCB 的 AIRCR 寄存器(重要的是写入钥匙,5FA)
  2. 具体规划分组内部抢占与响应优先级,如果中断是内核的,使用 SCB 3. 控制模块,如果是外部的,使用 NVIC 模块
  3. 使能 SYSCFG 模块时钟,映射相应的中断线,如果是IO口中断还要设置跳变沿以及初始化IO管脚
  4. 解除中断或者事件屏蔽(EXTI寄存器)
  5. NVIC使能相应的中断
  6. 编写中断服务程序
时间: 2024-10-12 21:22:19

STM32之中断的相关文章

stm32 NVIC中断管理实现[直接操作寄存器]

本文转自:http://www.ichanging.org/stm32_NVIC.html cortex-m3支持256个中端,其中包含了16个内核中断,240个外部中断.stm32只有84个中断,包括16个内核中断和68个可屏蔽中断.stm32f103上只有60个中断,f107上才有68个中断. 中断是stm32很基础的一个功能,学会使用中断,才可以更好的使用其他的外设.理解stm32的中断,必须要先从stm32的中断优先级分组是怎么回事.要理解优先级分组,就要先理解什么是先占优先级,和次占优

STM32之中断与事件---中断与事件的区别

STM32之中断与事件---中断与事件的区别  http://blog.csdn.net/flydream0/article/details/8208463 这张图是一条外部中断线或外部事件线的示意图,图中信号线上划有一条斜线,旁边标志19字样的注释,表示这样的线路共有19套.图中的蓝色虚线箭头,标出了外部中断信号的传输路径,首先外部信号从编号1的芯片管脚进入,经过编号2的边沿检测电路,通过编号3的或门进入中断挂起请求寄存器,最后经过编号4的与门输出到NVIC中断检测电路,这个边沿检测电路受上升

Stm32的中断在测试FREERTOS的时候的问题

在测试FreeRTOS在stm32上运行的时候,需要用到timer的中断,则打开了中断,但是如下的代码则让系统死机: /* 选择优先级分组0 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); /* 开启 TIM2 中断, 0级先占优先级,0级后占优先级 */ NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPr

STM32 外部中断

1)STM32一般有19 个外部中断为: 线 0~15:对应外部 IO 口的输入中断.线 16:连接到 PVD 输出. 线 17:连接到 RTC 闹钟事件. 线 18:连接到 USB 唤醒事件. 2)配置 GPIO 与中断线的映射关系的函数 GPIO_EXTILineConfig()来实现的: void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource) ; 中断线上中断的初始化是通过函数 EXTI_Init(

STM32串口中断的一些资料

在研究STM32串口接收发送中断的时候找到不少不错的资料,现在备份在这里.以供自己查阅,以及方便其他人. TC ====TXE 顺便预告下最近会写个有关串口处理数据的帖子,从查询和中断方面以及数据处理的方式,从队列以及FIFO方面写起. SECTION 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 /* 调试STM32串口过程中发现一个奇怪的问题,

STM32 外部中断简介

文章来源:http://blog.chinaunix.net/uid-24219701-id-4088004.html STM32  IO 口中断的一些基础概念.STM32 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32 的强大之处.STM32F103 的中断控制器支持 19 个外部中断/事件请求.每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置.STM32F103 的19 个外部中断为: 线 0~15:对应外部 IO 口的输入中断. 线 16:连接到 PVD 输出

STM32的中断和事件

上图框图中对应的寄存器可以在技术手册中找到相关的意义. 上图19个中断源的意义也可以在技术手册中找到, 可以这样简单的理解: 详细点的理解: 在STM32中,中断与事件不是等价的,一个中断肯定对应一个事件,但一个事件不一定对应一个中断. 事件只是一个触发信号(脉冲),而中断则是一个固定的电平信号. 事件:是表示检测到某一动作(电平边沿)触发事件发生了. 中断:有某个事件发生并产生中断,并跳转到对应的中断处理程序中. 事件可以触发中断,也可以不触发. 中断有可能被更优先的中断屏蔽,事件不会. 中断

STM32串口中断卡死主循环问题分析

在一项目中,使用STM32作为主控,程序运行一段时间后概率出现主循环卡死现象. 问题分析如下: 1.程序USART2不停接收并处理串口数据,波特率115200: 2.主循环卡死: 3.USART1中断及TIM2中断响应函数运行正常:(USART1及TIM2中断优先级均比USART2高) 4.出现现象后,拔掉USART2的接收数据线,现象不能回复正常: 5.出现现象后,拔掉后再插入USART2的接收数据线,现象不能回复正常: 6.并未出现HardFault现象: 基于以上4点,可能原因如下: 1.

STM32串口中断实例二

int main(void) { uint8_t a=0;//LED高低电压控制 /* System Clocks Configuration */ RCC_Configuration(); //系统时钟设置 /*嵌套向量中断控制器 说明了USART1抢占优先级级别0(最多1位) ,和子优先级级别0(最多7位) */ NVIC_Configuration(); //中断源配置 /*对控制LED指示灯的IO口进行了初始化,将端口配置为推挽上拉输出,口线速度为50Mhz.PA9,PA10端口复用为串