《ARM Cortex-M0 权威指南》笔记(7)—中断控制和系统控制
1. NVIC和系统控制块特性
Cortex-M0处理器集成了嵌套向量中断控制器(NVIC),其特性主要包含以下几点:
灵活的中断管理:使能/禁止,优先级配置;
硬件嵌套中断支持
向量化的异常入口
中断屏蔽
Cortex-M0处理器中的NVIC支持最多32个外部中断和一个不可屏蔽中断(NMI),中断输入请求可以是电平触发,也可以脉冲触发。每个外部中断都可以独立地使能或禁止,并且其挂起状态也可以手动地设置和清除。
NVIC的寄存器经过了存储器映射,并且用C语言访问也很容易,其寄存器的起始地址为 0xE000E100。Cortex-M0处理器,对NVIC寄存器的访问必须是每次一个字。
SCB寄存器也可以按照字来访问,起始地址为0xE000E010,SCB寄存器涉及的特性包括SysTick寄存器操作、系统异常管理、优先级控制和休眠模式控制。
STM32F030xx中的core_cm0.h文件定义:
/* Memory mapping of Cortex-M0 Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */
2. 中断控制寄存器
中断控制寄存器,用于控制中断请求(异常编号为16及以上)的使能和禁止。寄存器的宽度根据支持的中断数量而不同,最大为32位,最小为1位。可以通过两个独立的地址编程这个寄存器,使能中断时使用SETENA地址,而禁止中断则使用CLRENA地址。
将设置和清除操作分为两个不同地址。主要优势有两点:
减少了使能中断所需要的步骤,因此也就减少了程序代码并且降低了执行时间;
多个应用程序进程同时访问寄存器,可能会导致已变成的控制信息丢失,而设置和清除分离则能放置这种情况发生;
例如,使能中断#2,编程NVIC只需要一次访问:
*((volatile unsigned long * )(0xE000E100)) = 0x04; // 使能中断#2
或者汇编表示:
LDR R0, = 0xE000E100 ;在R0中设置地址 MOVS R1, #0x4 ;中断#2 STR R1, [R0] ;设置中断使能
开发中,建议使用符合CMSIS设备驱动库里的NVIC控制函数来使能或禁止中断,以提高代码的可移植性。
void NVIC_EnableIRQ(IRQn_Type IRQn); // 使能中断,IRQn为0时对应中断#0 void NVIC_DisableIRQ(IRQn_Type IRQn); // 禁止中断,IRQn为0时对应中断#0
3. 中断挂起状态寄存器
如果一个中断发生了,却无法立即处理,这个中断请求将被挂起。挂起状态保存在一个寄存器中,如果处理器的当前优先级还没有降低到可以处理挂起的请求,并且没有手动清除挂起状态,该状态将一直保持合法。
通过操作中断设置挂起(SETPEND)和中断清除挂起(CLRPEND)两个寄存器来访问或修改中断挂起状态。类似中断使能控制寄存器,中断挂起状态寄存器也是物理上为一个寄存器,而通过两个地址来实现设置和清除相关位。
中断挂起状态寄存器允许使用软件来触发中断。如果中断已经使能并且没有被屏蔽掉,当前还没有更高优先级的中断在运行,这时该中断的服务程序就会几乎立即得以执行。
例如,若触发了中断#2,代码如下:
*((volatile unsigned long *)(0xE000E100)) = 0x4; // 使能中断#2 *((volatile unsigned long *)(0xE000E200)) = 0x4; // 挂起中断#2
清除#2的挂起状态,代码如下:
*((volatile unsigned long *)(0xE000E280)) = 0x4; // 挂起中断#2的挂起状态
开发中,建议使用符合CMSIS设备驱动库函数,如下:
void NVIC_SetPendingIRQ(IRQn_Type IRQn); // 设置中断挂起 void NVIC_ClearPendingIRQ(IRQn_Type IRQn); // 清除中断挂起 uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn); // 返回true表示中断挂起状态位1
4. 中断优先级
每个外部中断都有一个对应的优先级寄存器,每个优先级都是2位宽,并且使用中断优先级寄存器的最高两位,每个寄存器占1个字节(8位)。Cortex-M0中的NVIC寄存器只支持字传输,这样每次访问都会同时涉及4个中断优先级寄存器。
未使用的位读出为0,写入这些位的操作会被忽略,而读出时则为0。
由于每次访问优先级寄存器就相当远访问4个中断的优先级,如果只想改变其中的1个,需要将整个字读出,修改1个字节,然后写回整个字。
例如,如果将中断#2的优先级设置为0xC0,代码如下:
unsigned long temp; // 临时变量 temp = *((volatile unsigned long *)(0xE000E400)); // 获取IRP0 temp = temp & (0xFF00FFFF) | (0xC0 << 16); // 修改优先级 *((volatile unsigned long *)(0xE000E400)) = tmp; // 设置IRP0
开发中,建议使用符合CMSIS设备驱动库函数,如下:
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority); // 设置中断或异常优先级 uint32_t NVIC_GetPriority(IRQn_Type IRQn); // 返回中断或异常优先级
5. 异常屏蔽寄存器(PRIMASK)
有些对时间敏感的应用,需要在一段较短的时间内禁止所有中断。对于这种应用,Cortex-M0没有使用中断使能/禁止控制寄存器来禁止所有中断然后再恢复,而是提供了一个单独的特性,特殊寄存器中有一个被称为PRIMASK,通过它可以屏蔽掉NMI和硬件错误异常的其它所有中断和系统异常。
关于PRIMASK寄存器,可以查看之前章节 2.2 PRIMASK 寄存器
例如,设置PRIMASK(禁止中断),代码如下:
MOVS R0, #1 ;PRIMASK的新值 MSR PRIMASK, R0 ;将R0的值送到PRIMASK中
另外,也可以使用CPS指令来设置或清除PRIMASK:
CPSIE i ;清除PRIMASK(使能中断) CPSID i ;设置PRIMASK(禁止中断)
若使用C语言,建议使用以下函数:
void __enable_irq(void); // 清除PRIMASK void __disable_irq(void); // 设置PRIMASK
6. 中断处理
Cortex-M0 处理器允许两种形式的中断请求:电平触发和脉冲输入。
使用电平触发中断,当中断事件发生时,由于外设连接到了NVIC上,中断信号会得到确认。在处理器执行中断服务并且清除外设的中断信号以前,该信号保持高电平。在NVIC内部,当检测有中断发生时,该中断的挂起状态会被置位,当处理器接收该中断并且开始执行中断服务程序后,挂起状态就会被清除。
有些中断源会产生脉冲形式的中断请求(至少持续1个时钟周期)。在这种情况下,在中断得到服务之前,挂起状态寄存器将会一直保持该请求。
7. 系统异常控制寄存器
除了外部中断,有些异常也有可编程的优先级和挂起状态寄存器。对于Cortex-M0,只有3个与OS相关的系统异常才具有可编程的优先级,他们包括SVC、PenSV和SysTick,其他像NMI和硬件错误等系统异常的优先级则固定。
系统优先级寄存器:
CMSIS中,系统优先级寄存器方式:
SCB相关struct的定义:
/** \brief Structure type to access the System Control Block (SCB). */ typedef struct { __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ uint32_t RESERVED0; __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ uint32_t RESERVED1; __IO uint32_t SHP[2]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ } SCB_Type;
8. 系统控制寄存器
NVIC地址区域(0xE000E000 ~ 0xE000EFFF)中包含了多个系统控制寄存器,因此NVIC的整个存储区域又被称为系统控制空间(SCS)。
8.1 CPU ID基址寄存器
CPU ID基址寄存器中包含了处理器ID,并且只读的,它为应用软件以及调试器提供了处理器内核类型和版本信息。当前发行的Cortex-M0处理器的CPU ID为0x410CC200,如下图:
CMSIS中,可以使用“SCB -> CPUID“ 来访问CPUID寄存器,如下:
软件可以使用这个寄存器来识别CPU类型,CPUID的Bit[7:4]为“0”代表Cortex-M0,“1”代表Cortex-M1,“3”代表Cortex-M3,以及“4”则代表Cortex-M4。
8.2 应用中断和复位控制寄存器
应用中断和复位控制寄存器(AIRCR)具有多个功能,可以用于应用程序请求系统复位,识别系统的大小端以及清除所有的异常活动状态(只能由调试器完成)。
CMSIS中,可以使用“SCB -> AIRCR”来访问。
使用SIRCR寄存器复位系统,可以使用CMSIS函数:
void NVIC_SystemReset(void);
8.3 配置和控制寄存器
Cortex-M0处理器上的配置和控制寄存器(CCR)为只读,它决定了栈的双字对齐设置和非对齐访问处理。
STACKALIGN为1,表示当产生异常压栈时,栈帧总是自动对齐到双字对齐的存储器位置上。UNALIGN_TRP位为1,表示试图执行非对齐访问的操作,会导致错误异常发生。
CMSIS中,可以使用“SCB -> CCR”来访问。