在STM32(Cortex-M3)中没有显示的代码拷贝,只有启动代码进行了向量的初始化,一直以为是编译器在程序影像中自己完成了相关向量的拷贝,即,拷贝到固定的NVIC区,事实上并不是这样,cortex-m3并没有一块专门用于存放NVIC向量表的地方,这张表实际是存放在代码(程序映像)的开始,下面引用cortex-M3权威指南进行解释:

当发生了异常并且要响应它时,CM3需要定位其服务例程的入口地址。这些入口地址存储在所谓的“(异常)向量表”中。缺省情况下,CM3认为该表位于零地址处,且各向量占用4字节。因此每个表项占用4字节,如表7.6所示。

      

因为地址0处应该存储引导代码,所以它通常映射到Flash或者是ROM器件,并且它们的值不得在运行时改变。然而,为了支持动态重分发中断,CM3允许向量表重定位——从其它地址处开始定位各异常向量。这些地址对应的区域可以是代码区,但更多是在RAM区。在RAM区就可以修改向量的入口地址了。为了实现这个功能,NVIC中有一个寄存器,称为“向量表偏移量寄存器”(在地址0xE000_ED08处),通过修改它的值就能重定位向量表。但必须注意的是:向量表的起始地址是有要求的:必须先求出系统中共有多少个向量,再把这个数字向上“圆整”到2的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有32个中断,则共有32+16(系统异常)=48个向量,向上圆整到2的整次幂后值为64,因此向量表重定位的地址必须能被64*4=256整除,从而合法的起始地址可以是:0x0, 0x100, 0x200等。向量表偏移量寄存器的定义如表7.7所示。

如果需要动态地更改向量表,则对于任何器件来说,向量表的起始处都必须包含以下向量:
 主堆栈指针(MSP)的初始值
 复位向量
 NMI
 硬fault服务例程
后两者也是必需的,因为有可能在引导过程中发生这两种异常。
可以在SRAM中开出一块空间用于存储向量表。在引导期间先填写好各向量,然后在引导完成后,就可以启用内存中的新向量表,从而实现向量可动态调整的能力。

向量表:
向量表其实是一个word(32位)型数组,其中每一项代表一种异常的起始地址,当有异常发生时,相应的异常处理函数将被执行。向量表是可以重定位的,重定位由NVIC来控制。复位时,重定位控制寄存器初始值为0,所以在复位时,向量表必须存放在0地址处。向量表定义如下:

复位时序:

当处理器复位之后,会从存储器中读取两个word的数据。地址0x00000000数据为SP(栈寄存器)初始值,地址0x00000004数据为程序的起始地址,也就是说复位之后,程序将从该处开始执行。例如:

SDRAM配置

1、NVIC

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

NVIC 特性

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

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

2、中断向量表

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

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

主要特性

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

中断与事件配置

硬件中断选择

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

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

硬件事件选择

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

配置 23 事件线的屏蔽位 (EXTI_EMR) 配置事件线的触发选择位 (EXTI_RTSR and EXTI_FTSR)

软件 中断/事件 选择

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

配置 23 事件/中断 线的屏蔽位 (EXTI_IMR, EXTI_EMR) 设置软件中断寄存器的应答位 (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传输的例子:<喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxibG9ja3F1b3RlPg0KCcjnufvF5NbDzqrW0LbPtcS7sKOs0OjSqtTa1tC2z7L6yfrWrrrzo6y9+Mjr1tC2z7SmwO26r8r9o6zU2tbQts+0psDtuq/K/dbQtKW3okRNQbLZ1/ejrMi7uvO9+NDQRE1BoaMgyOe5+8Xk1sPOqsrCvP61xLuwo6zWsb3T08nKwrz+1+7W1crks/bC9rPlwLS0pbeiRE1BstnX96OssrvQ6NKqvq25/dbQts+0psDtuq/K/b340NBETUG1xLSlt6KhozwvYmxvY2txdW90ZT4NCjxwPsrCvP6/ydLUvbW1zUNQVbXEuLq6yaOszOG438HLz+zTpsvZtsg8L3A+DQo8aDEgaWQ9"4内核最重要的两个模块scbsystem-controller-block-nvicnested-vectored-interrupt-controller">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,组编号反转。这三种方法如果按照内核分组写出来之后会发现会有优先级完全一样的情况出现,所以不可取。

要了解优先级分组,就要明确两个概念:抢占优先级(组优先级)、响应优先级(子优先级)

抢占优先级:可以被中断嵌套。也就是在一个中断发生的时候,另一个抢占优先级比此中断级别高的中断可以打断正在进行的中断,直到更高优先级的中断执行完毕之后,才会返回来继续执行这个被打断的中断 响应优先级:不可以被中断嵌套。也就是说在多个中断同时发生的时候,只能够优先相应较高优先级的中断,并且如果在中断过程中有更高优先级中断发生的时候,正在进行的中断也不能够被打断。
抢占优先级与响应优先级的关系有点像 TCP/IP 协议中的网络号与子网号的区别,两个中断也是先比较抢占优先级然后才是比较响应优先级

6、程序编写

<code class="hljs cs">#define SUM_NVIC_PRIOTITY_BITS 4    //一共用了4个位

/* 中断优先级分组
* group_num : 分组号,上面有列出各个分组对应的优先级
*/
static void set_priority_group(u8 group_num)
{
u32 temp = , temp1 = ; group_num = group_num % (SUM_NVIC_PRIOTITY_BITS + ); //因为只有5个组,所以限制数量
temp1 = (((~group_num) & 0x07) << ); //取反区低三位
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 = ; sub_priority_bits = SUM_NVIC_PRIOTITY_BITS - prioritygroup;
ASSERT(sub_priority_bits >= ); //断言,如果小于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的,参照内核头文件
{
/* 根据内核头文件中的 SCB 结构体对应内核手册部分推算 */
SCB->SHP[((uint32_t)(irq_num) & 0xF)-] = ((g_priority << sub_priority_bits) | sub_priority) << ( - SUM_NVIC_PRIOTITY_BITS);
}
else
{
NVIC->IP[(uint32_t)(irq_num)] = ((g_priority << sub_priority_bits) | sub_priority) << ( - SUM_NVIC_PRIOTITY_BITS);
}
}
/* 使能相应的中断,为必须的 */
void NVIC_enable_irq(IRQn_Type irq)
{
NVIC->ISER[(uint32_t)((int32_t)irq) >> ] |= (uint32_t)( << ((uint32_t)((int32_t)irq) % ));
} void EX_irq_config(GPIOx_SELECT gpiox,GPIOx_pn_SELECT gpiox_n,GPIO_IRQ_TRIGGER trigger)
{
RCC->APB2ENR |= ( << ); //使能SYSCFG模块,只有使能之后对SYSCFG寄存器的设置才会有效
gpio_init(gpiox, gpiox_n, BYM_PULL_UP, BYM_GPI, BYM_HIGH_LEVEL, BYM_PUSH_PULL); //初始化GPIO为输入,内部上拉 SYSCFG->EXTICR[gpiox_n/] &= ~(0xF << ((gpiox_n % ) * )); //
SYSCFG->EXTICR[gpiox_n/] |= (gpiox << ((gpiox_n % ) * ));//映射 Px_n 到 EXTIn中断线 EXTI->IMR |= ( << gpiox_n); //解除屏蔽
EXTI->RTSR |= ((trigger & 0x01) << gpiox_n); //设置触发沿
EXTI->FTSR |= ((trigger >> ) << gpiox_n);
} /* 只支持外部中断的中断标志清除 */
void EX_irq_clear(u8 irq)
{
EXTI->PR |= ( << irq);
}</code>

7、测试

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

主程序

<code class="hljs perl">NVIC_set_priority(, , EXTI3_IRQn, );
NVIC_set_priority(, , EXTI2_IRQn, );
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);</code>

中断服务程序

<code class="hljs scss">void EXTI3_IRQHandler(void)
{
if( == gpio_get(BYM_GPIOE, BYM_Px3))
{
printf("Key 1 down \r\n");
delay_ms();
printf("Key 1 end \r\n");
}
EX_irq_clear();
} void EXTI2_IRQHandler(void)
{
if( == gpio_get(BYM_GPIOE, BYM_Px2))
{
printf("Key 2 down \r\n");
delay_ms();
printf("Key 2 end \r\n");
}
EX_irq_clear();
}</code>

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

1
2
Key 1 down
Key 1 end

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

1
2
3
4
Key 2 down
Key 1 down
Key 2 end
Key 1 end

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

主程序改变如下

1
2
3
4
5
6
<code class="hljs perl">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);</code>

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

1
2
Key 1 down
Key 1 end

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

1
2
Key 2 down
Key 2 end

8、程序编写思路

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

STM32之中断的更多相关文章

  1. (二)stm32之中断配置

    一.stm32的中断和异常 Cortex拥有强大的异常响应系统,它能够打断当前代码执行流程事件分为异常和中断,它们用一个表管理起来,编号为0~15为内核异常,16以上的为外部中断,这个表就是中断向量表 ...

  2. 转载:STM32之中断与事件---中断与事件的区别

    这张图是一条外部中断线或外部事件线的示意图,图中信号线上划有一条斜线,旁边标志19字样的注释,表示这样的线路共有19套.图中的蓝色虚线箭头,标出了外部中断信号的传输路径,首先外部信号从编号1的芯片管脚 ...

  3. STM32外部中断具体解释

      一.基本概念 ARM Coetex-M3内核共支持256个中断,当中16个内部中断,240个外部中断和可编程的256级中断优先级的设置.STM32眼下支持的中断共84个(16个内部+68个外部), ...

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

    STM32之中断与事件---中断与事件的区别  http://blog.csdn.net/flydream0/article/details/8208463 这张图是一条外部中断线或外部事件线的示意图 ...

  5. stm32之中断配置

    一.stm32的中断和异常 Cortex拥有强大的异常响应系统,它能够打断当前代码执行流程事件分为异常和中断,它们用一个表管理起来,编号为0~15为内核异常,16以上的为外部中断,这个表就是中断向量表 ...

  6. STM32的中断系统

    STM32的中断系统 STM32具有十分强大的中断系统,将中断分为了两个类型:内核异常和外部中断.并将所有中断通过一个表编排起来,下面是stm32中断向量表的部分内容: 上图-3到6这个区域被标黑了, ...

  7. stm32定时器中断类型分析

    一直在用的stm32定时器的中断都是TIM_IT_Update更新中断,也没问为什么,直到碰到有人使用TIM_IT_CC1中断,才想到这定时器的中断类型究竟有什么区别,都怪当时学习stm32的时候不够 ...

  8. 转别人的 STM32外部中断使用注意事项

    前言:这些问题都是我之前在工作中遇到的,后来觉得需要总结,自己记忆不好,所以在这个给自己打个mark. 一:触发方式 STM32 的外部中断是通过边沿来触发的,不支持电平触发: 二:外部中断分组 ST ...

  9. STM32外部中断初理解

    PA0,PB0...PG0--->EXTI0 PA1,PB1...PG1--->EXTI1 ....... PA15,PB15...PG15--->EXTI15 以上为GPIO和中断 ...

  10. stm32之中断系统

    概述: 提供中断控制器,用于总体管理异常,称之为“嵌套向量中断控制器:Nested Vectored Interrupt Controller (NVIC) VIC:中断管理器: NVIC:内嵌中断管 ...

随机推荐

  1. SQL语句往Oracle数据库中插入日期型数据(to_date的用法)

    Oracle 在操作数据库上相比于其他的 T-sql 有微小的差别,但是在插入时间类型的数据是必须要注意他的 to_date 方法,具体的情况如下: --SQL语句往Oracle数据库中插入日期型数据 ...

  2. Unix系统中system函数的返回值

    网上关于system函数的返回值说明很多很详细但却不直观,这里搬出apue 3rd Editon中实现system函数的代码来说明其返回值. #include <sys/wait.h> # ...

  3. Java虚拟机读写其他进程的数据--Process对象

    使用Runtime对象的exec()方法可以运行平台上的其他程序,该方法产生一个Process对象,Process对象代表由该Java程序启动的子进程. Process类提供了3个方法,用于让程序和其 ...

  4. == 和 equals 的用法

    在java中,boolean.byte.short.int.long.char.float.double这八种是基本数据类型,其余都是引用类型. “==”是比较两个变量的值是否相等, “equals” ...

  5. 新浪云git 上传 nodejs项目

    1 .新建一个空文件夹: 2.在当前文件夹下,初始化本地git: 3.将要上传的nodejs工程,拷贝到这里: 这一步很容易出问题,所以最好不要拷贝别人给的node_modules文件,尽量做到现用现 ...

  6. python sys.path.append()和sys.path.insert()

    python程序中使用 import XXX 时,python解析器会在当前目录.已安装和第三方模块中搜索 xxx,如果都搜索不到就会报错. 使用sys.path.append()方法可以临时添加搜索 ...

  7. admin- 源码解析(流程)

    首先我们需要了解一个知识点:---单例模式--- 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中 ...

  8. CentOS跨网段访问

    centos6.2_64删除虚拟网卡 virbr0 卸载以下组件,然后重启系统 yum remove libvirt yum remove libvirt-python 来源:http://www.i ...

  9. grub2 设置Windows为默认启动系统

    1. 首先找到Windows的菜单menuentry.<blockquote># cat /boot/grub2/grub.cfg | grep Windows 结果: menuentry ...

  10. Jmeter图形结果

    样本数目:总共发送到服务器的请求数 最新样本:代表时间的数字,是服务器响应最后一个请求的时间 吞吐量:服务器每分钟处理的请求数.是指在没有帧丢失的情况下,设备能够接受的最大速率. 平均值:总运行时间除 ...