STM32F10xxx_异常与中断

更新记录

version status description date author
V1.0 C Create Document 2018.10.27 John Wan

status:

C―― Create,

A—— Add,

M—— Modify,

D—— Delete。


1、异常与中断的概念

  对于几乎所有的微控制器,中断都是一种常见的特性。中断一般是由硬件(如外设和外部输入引脚)产生的时间,它会引起程序偏离正常的流程(如给外设提供服务),所有的Cortex-M处理器都会提供一个专门用于中断处理的嵌套向量中断控制器(NVIC)。那么除了中断请求以外,还有其他需要服务的事件,将其称为"异常"。在ARM的说法中,中断也是一种异常。除了NVIC系统控制块(SCB)寄存器中也包含了一些常用与系统异常的寄存器。

图1 典型微控制器中的各种中断源《ARM Cortex-M3与Cortex-M4权威指南CnR3》P157

注:后续的用词,中断:即表示外设中断;系统异常:编号从1~15的各种异常;异常:指系统异常与中断。

2、异常的流程

graph TD
A(接收请求:处理器确认外设的中断请求)-->B
B(保护现场:处理器暂停当前执行的任务)-->C
C(中断处理:处理器执行外设的中断服务程序ISR)-->D
D(还原现场:处理器继续执行之前暂停的任务)

2.1 中断请求的来源

  • 系统异常
  • 外设中断

  Cortex-M处理器的异常架构具有多种特性,支持多个系统异常和外部中断。编号1~15的为系统异常,16及以上的则为中断输入。包括所有中断在内的多数异常,都具有可编程的优先级,一些系统异常则具有固定的优先级。不同的微控制器的中断编号、优先级都可能不同,具体的中断编号由芯片商根据需求进行配置,查阅对应的指导手册。

图2 系统异常列表《ARM Cortex-M3与Cortex-M4权威指南CnR3P158》

图3 中断列表《ARM Cortex-M3与Cortex-M4权威指南CnR3》P159

  而CMSIS-Core定义的中断标识是用枚举实现,从数值0开始(代表中断#0)表示中断编号,系统异常的编号为负数。之所以CMSIS-Core使用另外一条编号系统,是因为这样可稍微提高部分API函数的效率(例如设置优先级)。

2.2 处理器接受中断请求的条件

  • 处理器正在运行(未被暂停或处于复位状态)
  • 异常处于使能状态(NMI和HardFault为特殊情况,他们总是使能的)
  • 异常的优先级高于当前等级
  • 异常未被异常屏蔽寄存器屏蔽

2.2.1 异常的使能状态

  1. 中断的使能

  由NVIC寄存器中的中断设置使能寄存器(ISER)中断清除使能寄存器(ICER)控制,可进行字、半字或字节进行访问。需要说明的是ISER/ICER寄存器都是32位宽,每个位代表一个中断输入,如果存在32个以上的外部中断,则ISER/ICER寄存器可能不止一个。需要强调的是该寄存器只进行中断的设置,即异常编号为16开始的外部中断#0,不包括前16个异常类型的系统异常。

  1. 系统异常的使能

  由SCB寄存器中的系统处理控制和状态寄存器(SHCSR)控制,其中只能设置异常编号为4、5、6的存储管理错误、总线错误、使用错误三种系统异常的使能

2.2.2 异常的优先级

  每个异常都有一个优先级,其中绝大部分的优先等级可编辑,编程范围为0~255,极少部分的系统异常的优先等级是固定的且优先级为负数,这样可方便处理器进行判断是否应该接受异常以及何时接受并执行异常处理,大致分为两种决定的场景:

  • 正常的流程被中断
  • 中断的流程被中断(中断的嵌套)
  1. 优先级的定义

  只有更高优先级的异常(优先级编号更小)可以抢占低优先级的异常(优先级编号更大)Cortex-M3处理器在设计上具有3个固定的最高优先级以及256个可编程优先级(最多具有128个抢占等级),可编程优先级的实际数量由芯片设计商决定,多数使用Cortex-M3的芯片,支持的优先级较少,这样可降低NVIC的复杂度,降低功耗且增加速度。

  在Cortex-M3处理器中每个异常都存在一个8bit的优先级寄存器来设置其中断优先级等级(例如后面讲的NVIC->IP[n]),而该8bit的优先级寄存器进一步分为两个部分:抢占优先级与子优先级,可利用系统控制块SCB中的应用中断和复位控制寄存器AIRCR的BIT[10:8](见图4)来配置优先级的分组共23=8个分组(见图5),而这里的分组就决定了8bit的优先级寄存器中抢占优先级占几位,子优先级占几位:

图4 SCB_AIRCR优先级分组《ARM Cortex-M3与Cortex-M4权威指南CnR3》P180

图5 Cortex-M优先级分组定义《ARM Cortex-M3与Cortex-M4权威指南CnR3》P162

  上面是Cortex-M3的最大允许设置范围,前面提到,可编程优先级的实际数量根据芯片商的各种需求可进行裁剪设计,通过芯片商的手册可以查询,例如在STM32F10xxx中就限制了只能使用8bit的优先级寄存器高四位BIT[7:4],称其有效宽度,那么优先级分组的方式就只有5个分组,在这5个分组中,抢占优先级最多只能占4bit,即抢占优先级等级最多只能设置24 = 16个,见下图。[1]

图6 STM32F10xxx的优先级分组配置《STM32F10XXX_programming_mannual_PM0056_ENV6》P135

  在处理器已经在运行一个中断处理时能否产生另外一个中断,是由该中断的抢占优先级决定的。子优先级只会用在具有两个相同分组优先级的异常同时产生的情形,此时,具有更高子优先级(数值更小)的异常会被首先处理。

问题:

(1)为什么前面提到Cortex-M3有256个可编程优先级,而最多只具备128个抢占优先级?

如图5,优先级分组时总会配置一种情况,即达到分组的最大宽度时,抢占优先级最多只能分配7位,保留一位的子优先级。

(2)为什么8bit的优先级寄存器的宽度设置是通过移除LSB用高位表示,而不是通过移除MSB用低位表示?

便于不同处理器之间进行移植,按照这种方式,在具有4位优先级配置寄存器的设备上写的程序,就可能会在具有3位优先级配置寄存器的设备上运行。例如某应用程序,IRQ#0的优先级为0x05,IRQ#1的优先级为0x03,那么移除了最高位bit2之后,则IRQ#0的优先级会变为0x01,高于IRQ#1的优先级。如果IRQ#0的优先级为0x50,IRQ#1的优先级为0x30,那么移除最低位bit0之后,还是IRQ#0的优先级较低。

  1. 中断的优先级

  每个中断都有对应的优先级寄存器,可根据在系统控制块SCB应用中断和复位控制寄存器AIRCR配置的优先级分组所规定的抢占优先级范围和子优先级范围来设定抢占优先级等级以及子优先级等级。其优先级分组有几种情况由芯片商决定。该寄存器可通过字节、半字和字访问,优先级寄存器的数量取决于芯片中实际存在的外部中断数。可通过中断优先级寄存器NVIC->IP[n]来进行设置.

图7 中断优先级寄存器《ARM Cortex-M3与Cortex-M4权威指南CnR3》P176

  1. 系统异常的优先级

  关于可编辑的系统异常优先级,原理同中断的优先级设定,其可通过系统处理优先级寄存器 SCB->SHP[0~11]来进行设置。

图8 系统处理优先级寄存器《ARM Cortex-M3与Corte-M4权威指南CnR3》P181

  1. CMSIS-Core设置异常优先级
core_cm3.h

优先级分组
static __INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) //设置优先级分组
static __INLINE uint32_t NVIC_GetPriorityGrouping(void) //读取优先级分组 优先级等级
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)//设置异常的优先级
static __INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) //读取异常的优先级
  1. STM32F10xxx库文件中设置异常优先级
misc.h

/** @defgroup Preemption_Priority_Group
* @{
*/ #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */ #define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \
((GROUP) == NVIC_PriorityGroup_1) || \
((GROUP) == NVIC_PriorityGroup_2) || \
((GROUP) == NVIC_PriorityGroup_3) || \
((GROUP) == NVIC_PriorityGroup_4)) #define IS_NVIC_PREEMPTION_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) #define IS_NVIC_SUB_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) #define IS_NVIC_OFFSET(OFFSET) ((OFFSET) < 0x000FFFFF) misc.c /** @defgroup MISC_Private_Defines
* @{
*/ #define AIRCR_VECTKEY_MASK ((uint32_t)0x05FA0000) /**
* @brief Configures the priority grouping: pre-emption priority and subpriority.
* @param NVIC_PriorityGroup: specifies the priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
* 4 bits for subpriority
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
* 3 bits for subpriority
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
* 2 bits for subpriority
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
* 1 bits for subpriority
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
* 0 bits for subpriority
* @retval None
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
  1. 中断的设置步骤

(1)设置优先级分组,STM32F10XXX默认的优先级分组是2位抢占优先级,2位子优先级。(SCB->AIRCR的复位为0x05FA0000,如果不进行设置bit[10:8]为0x05)

(2)设置中断的抢占优先级与子优先级。

(3)在NVIC或外设中使能中断

2.2.3 异常的屏蔽

用于异常屏蔽的特殊寄存器,包括PRIMASK、FAULTMASK、BASEPRI:

  1. PRIMASK

  在许多应用中,可能都需要暂时禁止所有中断以执行一些时序关键的任务,此时可以使用PRIMASK寄存器。该寄存器只能在特权状态下访问。

  PRIMASK寄存器用于禁止除NMIHardFault外的所有异常。实际上它是将当前优先级改为0(最高可编程等级),以不被其它异常所中断

CMSIS-Core的core_cm3.c中提供以下函数进行查询与设置:

/**
* @brief Return the Priority Mask value
*
* @return PriMask
*
* Return state of the priority mask bit from the priority mask register
*/
__ASM uint32_t __get_PRIMASK(void)
{
mrs r0, primask
bx lr
} /**
* @brief Set the Priority Mask value
*
* @param priMask PriMask
*
* Set the priority mask bit in the priority mask register
*/
__ASM void __set_PRIMASK(uint32_t priMask)
{
msr primask, r0
bx lr
} core_cm3.h中: #define __enable_irq __enable_interrupt /*!< global Interrupt enable */
#define __disable_irq __disable_interrupt /*!< global Interrupt disable */

关于下面的core_cm3.h中可参考开关中断

  1. FAULTMASK

  行为上与PRIMASK寄存器类似,只是它实际上会将当前优先级修改为-1,这样甚至是HardFlaut处理也会被屏蔽,则当FAULTMASK置位时,只有NMI异常处理才能执行

CMSIS-Core的core_cm3.c中提供以下函数:

/**
* @brief Return the Fault Mask value
*
* @return FaultMask
*
* Return the content of the fault mask register
*/
__ASM uint32_t __get_FAULTMASK(void)
{
mrs r0, faultmask
bx lr
} /**
* @brief Set the Fault Mask value
*
* @param faultMask faultMask value
*
* Set the fault mask register
*/
__ASM void __set_FAULTMASK(uint32_t faultMask)
{
msr faultmask, r0
bx lr
} core_cm3.h中: static __INLINE void __enable_fault_irq() { __ASM ("cpsie f"); }
static __INLINE void __disable_fault_irq() { __ASM ("cpsid f"); }

  FAULTMASK也只能在特权状态下访问,不过不能在NMIHardFault处理中设置。

  FAULTMASK会在退出异常处理被自动清除,从NMI中退出时除外。因此可以利用此特性,如果在低优先级的异常处理中触发了一个高优先的异常(NMI除外),但是想要先处理完低优先级的异常处理再进行高优先的处理,那么可以:

  • 设置FAULTMASK禁止所有异常(NMI除外)
  • 设置高优先级异常的挂起状态
  • 退出处理

  由于在FAULTMASK置位时,挂起的高优先级异常处理无法执行,高优先级的异常就会在FAULTMASK被清除前继续保持挂起状态,低优先级处理完成后才会将其清除。因此,可以强制让高优先级处理在低优先级处理结束后开始执行。

  1. BASEPRI

  有些情况下,可能只想禁止优先级低于某特定等级的中断,只需将所需的屏蔽优先级写入BASEPRI寄存器。只能在特权状态下访问。

CMSIS-Core的core_cm3.c中提供以下函数:

/**
* @brief Return the Base Priority value
*
* @return BasePriority
*
* Return the content of the base priority register
*/
__ASM uint32_t __get_BASEPRI(void)
{
mrs r0, basepri
bx lr
} /**
* @brief Set the Base Priority value
*
* @param basePri BasePriority
*
* Set the base priority register
*/
__ASM void __set_BASEPRI(uint32_t basePri)
{
msr basepri, r0
bx lr
}

2.2.4 异常的请求设置与查询

  1. 中断输入与挂起行为

  每个中断都有多个属性:

  • 每个中断都可被禁止(默认)或使能。
  • 每个中断都可被挂起(等待服务的请求)或解除挂起。
  • 每个中断可处于活跃(正在处理)或非活跃状态。

  为了支持这些属性,NVIC中包含了多个可编程寄存器,用于中断使能控制、挂起状态和只读的活跃状态位。

  当NVIC的中断输入被确认后,它就会引发该中断的挂起状态,被存储在NVIC的可编程寄存器中,即使该中断请求在处理器还没来的急处理,就已经被取消,只要在NVIC中已经被挂起过,那么就算有效,这就是NVIC支持脉冲中断请求特性。

  当中断正被处理时,它就会处于活跃状态,处理完成,活跃状态自动清除。但对于许多微控制器设计,外设会产生电平触发的中断,因此ISR必须要手动清除中断请求,如写入外设中的某个寄存器。

(1)、当中断处于活跃状态是,处理器无法在中断完成和异常返回前再次接受同一个中断请求。

(2)、若中断请求产生时处理器正在处理另一个具有更高优先级的中断,而在处理器队该中断请求做出响应之前,通过软件代码将挂起状态清除掉,那么该请求就会被取消且不会再得到处理。

(3)、若外设持续保持某个中断请求,那么即使软件尝试着清除该挂起状态,挂起状态还是会再次置位的。

(4)、若在得到处理后,中断源仍在继续保持中断请求,那么这个中断就会再次进入挂起状态且再次得到处理器的服务。

(5)、对于脉冲中断请求,若在处理器开始处理前,中断请求信号产生了多次,它们会被当作一次中断请求。

(6)、中断的挂起状态可以在其正被处理时再次置位。

(7)、即使中断被禁止了,它的挂起状态仍可置位。在这种情况下,若中断稍后被使能了,可以被触发并得到服务。有些时候,这种情况并不是所希望的,因此需要在使能NVIC中的中断前手动清除挂起状态。

  1. 中断挂起的设置、清除、查询

  中断的挂起状态可通过中断设置挂起寄存器 NVIC->ISPR[n]中断清除挂起寄存器 NVIC->ICPR[n]访问,若存在32个以上的外部中断输入,则寄存器可能不止一个。

CMSIS-Core中core_m3.h提供以下函数:

static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)
static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
  1. 中断的活跃状态查询

  每个外部中断都有一个活跃状态位,当处理器开始执行中断处理时,该位会被置1,而在执行中断返回时会被清零,通过中断活跃状态寄存器 NVIC->IABR[n]进行查询。如果被抢占,还是会处于活跃状态。当外部中断的数量超过32个,寄存器不止一个。

CMSIS-Core中core_m3.h提供以下函数:

static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
  1. 系统异常的挂起设置、清除、查询

  通过中断控制和状态寄存器 SCB->ICSR进行访问。

  1. 系统异常的活跃状态查询

  通过系统处理控制和状态寄存器 SCB->SHCSR进行访问。

2.3 如何暂停当前任务进入异常流程

2.4 如何执行异常处理,并触发异常返回

2.5 如何进行异常返回,执行暂停的任务


  1. 参考《STM32中文参考手册_V10》P130,《STM32F10xxx_20xxx_21xxx_L1xxx_Programming_Mannual_PM0056_ENV6》P135 Binary Point ↩︎

STM32F10xxx_异常与中断的更多相关文章

  1. Linux内核--异常和中断的区别

          相信大家都知道非常著名的两个名词:异常和中断,不过,你真的理解这两个名词在说什么吗?它们之间有什么区别呢?       1.中断       大家都知道,当我们在敲击键盘的同时就会产生中断 ...

  2. 浅析arm的异常、中断和arm工作模式的联系

    说到异常向量,会让人联想到中断向量.其实,中断是属于异常的子集的,也就是说中断其实是异常其中的一种. 回到异常向量,他其实是一张表格,每个格子里存放的是一个地址,或者是一个跳转命令,不管是哪个,其目的 ...

  3. RT-thread内核之异常与中断

    一.什么是中断? 中断有两种,一种是CPU本身在执行程序的过程中产生的,一种是由CPU外部产生的. cpu外部中断,就是通常所讲的“中断”(interrupt).对于执行程序来说,这种“中断”的发生完 ...

  4. 【译】x86程序员手册31- 第9章 异常和中断

    Chapter 9 Exceptions and Interrupts 第9章 异常和中断 Interrupts and exceptions are special kinds of control ...

  5. s3c2440裸机-异常中断(一. 异常、中断的原理与流程)

    1.异常中断概述 在arm架构的处理器中,cpu有7中工作模式,2中工作状态. 1.CPU模式(Mode): 7种Mode: 除了usr/sys,其他5种都是异常模式.我们知道中断属于异常的2中,中断 ...

  6. Java异常的中断和恢复

    中断:抛出一个异常类的实例而终止现有程序的执行:恢复:不是抛出一个异常类的实例,而是调用一个用于解决问题的方法或就地解决问题. 在Java中,对那些要调用方法的客户程序员,我们要通知他们可能从自己的方 ...

  7. 《Cortex-M0权威指南》之体系结构---异常和中断

    转载请注明来源:cuixiaolei的技术博客 异常会引起程序控制的变化.在异常发生时,处理器停止当前的任务,转而执行异常处理程序,异常处理完成后,会继续执行刚才的任务.异常分为很多种,中断是其中之一 ...

  8. VC++ Debug产生异常时中断程序执行Break on Exception

    It is possible to instruct the debugger to break when an exception occurs, before a handler is invok ...

  9. Linux中断技术、门描述符、IDT(中断描述符表)、异常控制技术总结归类

    相关学习资料 <深入理解计算机系统(原书第2版)>.pdf http://zh.wikipedia.org/zh/%E4%B8%AD%E6%96%B7 独辟蹊径品内核:Linux内核源代码 ...

随机推荐

  1. 【学习笔记】QT常用类及应用

    一.QT基类: QObject 二.QT中常用的库 QT中的类根据功能划分在不同的库中,在用户属性.pro文件中可以看到. 三.Qt基本对话框的使用 常用5类: 通过类名可以直接调用类的静态成员函数. ...

  2. .SpringIOC容器

    创建对象 SpringIOC容器,是spring核心内容. 作用: 创建对象 & 处理对象的依赖关系 IOC容器创建对象: 创建对象, 有几种方式: 1) 调用无参数构造器 2) 带参数构造器 ...

  3. tensorflow自动写诗

    1.目录结构 2.入口类 # coding = utf-8 """ 注意:RNN使用的数据为序列化的数据 RNN网络:主要由多个LSTM计算单元组成,依靠BPTT算法进行 ...

  4. 什么是APJ与使用Spring Data JPA 基于Hibernate

    目录结构 首先在Maven项目中添加依赖包 <!-- https://mvnrepository.com/artifact/org.springframework.data/ spring-da ...

  5. Redis 集群部署

    一.下载所需软件包 redis wget http://download.redis.io/releases/redis-4.0.6.tar.gz ruby wget https://cache.ru ...

  6. 一台物理机器一个IP配置多个域名多套程序的方法

    1.安装nginx cd /usr/local/ wget http://nginx.org/download/nginx-1.2.8.tar.gz tar -zxvf nginx-1.2.8.tar ...

  7. 转 - JS 中 call 和 apply 以及 bind 的区别

    转自 https://blog.csdn.net/wyyandyou_6/article/details/81488103

  8. webdriver的八种定位

    转自https://zhuanlan.zhihu.com/p/54588889 在UI层面的自动化测试开发中,元素的定位与操作是基础,也是经常遇到的困难所在.webdriver提供了8种定位: 1. ...

  9. Zookeeper白话解释

    官方的解释:Zookeeper提供了诸如统一命名空间服务,配置服务和分布式锁等分布式基础服务. 嗯,说上面这个话的人,良心不会痛吗? Zookeeper功能如上边说到的:统一命名空间服务 其他就tm跟 ...

  10. navicat建立本地连接出错解决

    使用navicat建立本地连接时报错: 2.设置用户配置项 (1) 查看用户信息 select host,user,plugin,authentication_string from mysql.us ...