请选择 进入手机版 | 继续访问电脑版
查看: 235|回复: 2

[单片机资料] stm32中断详解_附程序讲解

[复制链接]

签到天数: 377 天

[LV.9]元老将成

发表于 2018-8-8 21:31:33 | 显示全部楼层 |阅读模式
1、NVIC

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

  • NVIC 特性


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




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

2、中断向量表

其实中断向量表在STM32F4XX启动文件里面就可以看出来

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传输的例子:

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

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

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

SCB[td]
地址
寄存器名
读写权限
特权
复位值
作用描述
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[td]
地址
寄存器名
读写权限
特权
复位值
作用描述
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位),也就是说分组情况如下

[td]
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、程序编写
  1. <font size="3">#define SUM_NVIC_PRIOTITY_BITS 4    //一共用了4个位

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

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

  14.     SCB->AIRCR = temp;
  15. }

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

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

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

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

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

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

  58. /* 只支持外部中断的中断标志清除 */
  59. void EX_irq_clear(u8 irq)
  60. {
  61.     EXTI->PR |= (1 << irq);
  62. }
  63. </font>
复制代码



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

主程序

  1. <font size="3">NVIC_set_priority(1, 2, EXTI3_IRQn, 2);
  2. NVIC_set_priority(2, 1, EXTI2_IRQn, 2);
  3. EX_irq_config(BYM_GPIOE, BYM_Px3, IRQ_BOTHEDGE);
  4. EX_irq_config(BYM_GPIOE, BYM_Px2, IRQ_BOTHEDGE);
  5. NVIC_enable_irq(EXTI3_IRQn);
  6. NVIC_enable_irq(EXTI2_IRQn);</font>
复制代码

中断服务程序

  1. <font size="3">void EXTI3_IRQHandler(void)
  2. {
  3.     if(0 == gpio_get(BYM_GPIOE, BYM_Px3))
  4.     {
  5.         printf("Key 1 down \r\n");
  6.         delay_ms(1000);
  7.         printf("Key 1 end \r\n");
  8.     }
  9.     EX_irq_clear(3);
  10. }

  11. void EXTI2_IRQHandler(void)
  12. {
  13.     if(0 == gpio_get(BYM_GPIOE, BYM_Px2))
  14.     {
  15.         printf("Key 2 down \r\n");
  16.         delay_ms(1000);
  17.         printf("Key 2 end \r\n");
  18.     }
  19.     EX_irq_clear(2);
  20. }</font>
复制代码

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

Key 1 downKey 1 end

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

Key 2 down Key 1 down Key 2 endKey 1 end测试抢占优先级相同而响应优先级不同的情况

主程序改变如下

  1. <font size="3">NVIC_set_priority(1, 2, EXTI3_IRQn, 2);
  2. NVIC_set_priority(1, 1, EXTI2_IRQn, 2);
  3. EX_irq_config(BYM_GPIOE, BYM_Px3, IRQ_BOTHEDGE);
  4. EX_irq_config(BYM_GPIOE, BYM_Px2, IRQ_BOTHEDGE);
  5. NVIC_enable_irq(EXTI3_IRQn);
  6. NVIC_enable_irq(EXTI2_IRQn);</font>
复制代码

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

Key 1 downKey 1 end

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

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






签到天数: 674 天

[LV.9]元老将成

发表于 2018-8-9 08:31:45 | 显示全部楼层
不错的资料
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

返回顶部