cortex的异常和中断

cortex的异常和中断
cortex的异常和中断

Cortex-M3 异常和中断---基于NXP LPC177x/8x

分类:我的小问题集2013-04-10 08:26 1805人阅读评论(0) 收藏举报

目录(?)[+]

0.前言

本文想解决的问题有:

?如何开启、关闭中断

?如何开启、关闭异常

?LPC177x/8x支持的中断优先级个数

?复位后,异常/中断默认的优先级

?如何设置异常/中断的优先级

?什么是优先级组,如何设置优先级组,复位后的优先级组

1. Cortex-M3的异常/中断屏蔽寄存器组

注:只有在特权级下,才允许访问这3个寄存器。

名字功能描述

PRIMASK 只有单一比特的寄存器。置为1后,就关掉所有可屏蔽异常,只剩下NMI和硬Fault可以响应。默认值是0,表示没有关闭中断。

FAULTMASK 只有单一比特的寄存器。置为1后,只有NMI可以响应。默认值为0,表示没有关异常。

BASEPRI 该寄存器最多有9位(由表达优先级的位数决定)。定义了被屏蔽优先级的阈值。当它被设置为某个值后,所有优先级号大于等于此值的中断都被关。若设

置成0,则不关断任何中断,0为默认值。

注:寄存器BASEPRI的有效位数受系统中表达优先级的位数影响,如果系统中只使用3

个位来表达优先级,则BASEPRI有意义的值仅为0x00、0x20、0x40、0x60、0x80、0xA0、

0xC0和0xE0

使用MRS/MSR指令访问这三个寄存器,比如:

MRS R0, BASEPRI ;读取BASEPRI到R0中

MSR BASEPRI, R0 ;将R0数据写入到BASEPRI中

为了快速的开关中断,CM3还专门设置了一条CPS指令,有四种用法:

CPSID I ;PRIMASK=1,关中断

CPSIE I ;PRIMASK=0,开中断

CPSID F ;FAULTMASK=1,关异常

CPSIE F ;FAULTMASK=0,开异常

CMSIS-M3微控制器软件接口标准中的core_cm3.h给出了开关中断或异常的函数:

1.1 开/关中断

1:/**

2: * @brief Set the Priority Mask value

3: *

4: * @param priMask PriMask

5: *

6: * Set the priority mask bit in the priority mask register

7: */

8:static __INLINE void __set_PRIMASK(uint32_t priMask)

9: {

10:register uint32_t __regPriMask __ASM("primask");

11: __regPriMask = (priMask);

12: }

使用__set_PRIMASK(1)关闭中断;__setPRIMASK(0)开启中断。

一些说明:__INLINE是宏定义,对应__inline,这是keil编译器自定义关键字,表示这个函数是内联函数,但并不是强制性内联,编译器最终决定是否内联。

__ASM(“primask”): __ASM也是一个宏,对应__asm,这是keil编译器自定义关键字,关于这个关键字,有相当多的用法,可以在C中内嵌汇编语言、内嵌汇编函数、指定汇编

标号以及本代码中的声明一个已命名寄存器变量。这里,已命名的寄存器是("primask"),也

就是说寄存器变量__regPriMask等同于编译器已命名的primask。语法为:

register type var-name __asm(reg);

keil编译器已命名的寄存器变量为:

处理器

寄存器__asm修饰的字符

APSR"apsr" All processors

CPSR"cpsr"All processors

BASEPRI"basepri"Cortex-M3, Cortex-M4

BASEPRI_MAX"basepri_max"Cortex-M3, Cortex-M4 CONTROL"control"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 DSP"dsp"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 EAPSR"eapsr"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 EPSR"epsr"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 FAULTMASK"faultmask"Cortex-M3, Cortex-M4

IAPSR"iapsr"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 IEPSR"iepsr"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 IPSR"ipsr"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 MSP"msp"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 PRIMASK"primask"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 PSP"psp"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 PSR"psr"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4

r0 to r12"r0" to "r12"All processors

r14 or lr"r14" or "lr"All processors

r13 or sp"r13" or "sp"All processors

r15 or pc"r15" or "pc"All processors

SPSR"spsr"All processors, apart from Cortex-M series processors. XPSR"xpsr"Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4

1.2 开/关异常

1:/**

2: * @brief Set the Fault Mask value

3: *

4: * @param faultMask faultMask value

5: *

6: * Set the fault mask register

7: */

8:static __INLINE void __set_FAULTMASK(uint32_t faultMask)

9: {

10:register uint32_t __regFaultMask __ASM("faultmask");

11: __regFaultMask = (faultMask & 1);

12: }

使用__set_FAULTMASK(1)来关闭中断和异常;使用__set_FAULTMASK(0)开启中断和异常.

1.3 更精确的优先级屏蔽

1:/**

2: * @brief Set the Base Priority value

3: *

4: * @param basePri BasePriority

5: *

6: * Set the base priority register

7: */

8:static __INLINE void __set_BASEPRI(uint32_t basePri)

9: {

10:register uint32_t __regBasePri __ASM("basepri");

11: __regBasePri = (basePri & 0xff);

12: }

比如想屏蔽优先级不高于0x60的中断,则使用代码:__set_BASEPRI(0x60);如果想取消中

断屏蔽,则使用__set_BASEPRI(0)即可。

2.异常/中断和优先级

Cortex-M3的异常包括系统异常和外设中断,系统异常是Cortex-M3内核自带的一些异常,比如复位、总线Fault和SysTick等等(见表2-1),外设中断是指制造CPU的厂家

加入的,比如串口、定时器中断等等(见表2-2)。

注:关于异常和中断,想要分个清清楚楚实在有点困难。异常和中断都可以“中断”正常执行

的代码流,区别在于,异常是Cortex-M3内核产生的“中断”信号,而中断是Cortex-M3内核

外部(片上外设或外部中断信号)产生的“中断”信号。希望你看懂了,有时候你心里明白,

但要讲的清清楚楚着实难!

表2-1:系统异常

编号类型优先级简介

0 N/A N/A 无

1 复位-3(最高)复位

2 NMI -2 不可屏蔽中断(来自外部NMI输入脚)

3 硬Fault -1 只要FAULTMASK没有置位,硬Fault服务例程会被强制执

4 存储器管理Fault 可编程MPU访问违例以及访问非法位置均可引发。企图在“非执行

区”取址也会引发此Fault。

5 总线Fault 可编程总线收到了错误响应,原因可以使预取流产或数据流产,企

图访问协处理器也会引发此Fault

6 用法Fault 可编程由于程序错误导致的异常。通常是使用了一条无效指令,或

者是非法的状态转换,例如尝试切换到ARM状态

7~10 保留保留保留

11 SVCall 可编程执行系统服务调用指令(SVC)引发的异常

12 调试监视器可编程调试器(断点、数据观察点,或者是外部调试请求)

13 保留保留保留

14 PendSV 可编程为系统设备而设的“可挂起请求”

15 SysTick 可编程系统节拍时钟定时器(SysTick)

表2-2:外设中断

编号类型优先级简介

16IRQ #0 可编程外设中断#0

17IRQ #1 可编程外设中断#1

...... 可编程...

255IRQ #239 可编程外设中断#239

注:表2-1和2-2中的“编号”有着特殊的意义,一是特殊功能寄存器IPSR中会记录当前正

在服务的异常并给出了它的编号;二是优先级完全相同的多个异常同时挂起时,则先响应异

常编号最小的那一个。

一个发生的异常如果不能被立即响应,就称它被“挂起”,值得一提的是,对于被挂起的中断/异常,中断/异常信号不必由其产生者保持,NVIC的挂起状态寄存器会来保持这个

信号。所以哪怕后来挂起的中断源释放了中断请求信号,曾经的中断请求也不会丢失。

除了复位、NMI和硬Fault三个异常具有固定的优先级外,其它所有异常和中断的优先级都是可以编程的。这就涉及到优先级配置寄存器。Cortex-M3优先级配置寄存器共8

位,所以可以有256级的可编程优先级。但是大多数Cortex-M3芯片都会精简设计。

LPC177x/8x使用了优先级配置寄存器的5位,所以有32级可编程优先级。复位后,对于所有优先级可编程的异常,其优先级都被初始化为0(最高优先级)

2.1 设置异常/中断的优先级

2.1.1 系统异常优先级设置

SHPR1-SHPR3寄存器用于设置有可编程优先级的系统异常,可设置的优先级为0到31。SHPR1-SHPR3可按字节访问。为了提高软件效率,CMSIS简化了SCB寄存器的

表述。在CMSIS中,字节数组SHP[0] 到SHP[12]对应于寄存器SHPR1至SHPR3。

表2-3:SHPR1寄存器的位分配

位名称功能

[31:24] PRI_7 保留

[23:16] PRI_6 系统处理程序6的优先级,用法Fault

[15:8] PRI_5 系统处理程序5的优先级,总线Fault

[7:0] PRI_4 系统处理程序4的优先级,存储器管理Fault

表2-3:SHPR2寄存器的位分配

位名称功能

[31:24]PRI_11系统处理程序11的优先级,SVCall

[23:0]-保留

表2-4:SHPR3寄存器的位分配

位名称功能

[31:24]PRI_15系统处理程序15的优先级,SysTick 异常

[23:16]PRI_14系统处理程序14的优先级,PendSV

[15:0]-保留

注:每个PRI_N域为8位宽,但是处理器仅实现每个域的位[7:3],位[2:0]读取值为零并忽

略写入值。

2.1.2 外设中断优先级设置

LPC177x/8x微处理器的中断优先寄存器IPR0~IPR10用于设置外设中断优先级,控制41个外设中断。每个IPRx可以按字节访问,在CMSIS中,字节数组IP[0] 到IP[40]

对应于寄存器IPR0~IPR10。

2.1.3 系统异常/外设中断优先级设置C代码

1:/**

2: * @brief Set the priority for an interrupt

3: *

4: * @param IRQn The number of the interrupt for set priority

5: * @param priority The priority to set

6: *

7: * Set the priority for the specified interrupt. The interrupt

8: * number can be positive to specify an external (device specific)

9: * interrupt, or negative to specify an internal (core) interrupt.

10: *

11: * Note: The priority cannot be set for every core interrupt. */

12:static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t

priority)

13: {

14:if(IRQn < 0) { /* set Priority for Cortex-M3 System Interrupts

*/

15: SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 -

__NVIC_PRIO_BITS)) & 0xff);

16: }

17:else { /* set Priority for device specific Interrupts */

18: NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 -

__NVIC_PRIO_BITS)) & 0xff);

19: }

20: }

其中,参数IRQn为中断ID号,可以为负,也可以为正。当IRQn为负时,设置系统异常的优先级,当IRQn大于等于0时,设置外设中断优先级。__NVIC_PRIO_BITS是

指使用到的优先级配置寄存器的位数,LPC177x/8x使用了5位。为什么要使用

(8-__NVIC_PRIO_BITS)呢?这是因为优先级配置寄存器是高位对齐的(MSB),这主要方

面不同CPU间的移植。参数priority为要设置的优先级值,为0~31,数值越低,表示优先

级越大。LPC177x/8x的中断ID为:

系统异常ID:

标号中断ID描述NonMaskableInt_IRQn -14 不可屏蔽中断

MemoryManagement_IRQn -12 Cortex-M3内存管理中断

BusFault_IRQn -11 Cortex-M3 总线Fault中断

UsageFault_IRQn -10 Cortex-M3 用法Fault 中断

SVCall_IRQn -5 Cortex-M3 SV Call中断

DebugMonitor_IRQn -4 Cortex-M3 调试监视中断

PendSV_IRQn -2 Cortex-M3 Pend SV中断

SysTick_IRQn -1 Cortex-M3 系统Tick中断

外设中断ID:

标号中断ID描述标号中断ID描述

WDT_IRQn0看门狗EINT3_IRQn21外中断3 TIMER0_IRQn1定时器0ADC_IRQn22AD转换TIMER1_IRQn2定时器1BOD_IRQn23欠压检测TIMER2_IRQn3定时器2USB_IRQn24USB

TIMER3_IRQn4定时器3CAN_IRQn25CAN

UART0_IRQn5UART0DMA_IRQn26通用DMA UART1_IRQn6UART1I2S_IRQn27I2S

UART2_IRQn7UART2ENET_IRQn28以太网UART3_IRQn8UART3MCI_IRQn29SD/MMC卡I/F PWM1_IRQn9PWM1MCPWM_IRQn30电机控制PWM I2C0_IRQn10I2C0QEI_IRQn31正交编码接口I2C1_IRQn11I2C1PLL1_IRQn32PLL1锁存

I2C2_IRQn12I2C2USBActivity_IRQn33USB活动Reserved0_IRQn13保留CANActivity_IRQn34CAN活动SSP0_IRQn14SSP0UART4_IRQn35UART4

SSP1_IRQn15SSP1SSP2_IRQn36SSP2

PLL0_IRQn16PLL0锁存LCD_IRQn37LCD

RTC_IRQn17RTC GPIO_IRQn38GPIO

EINT0_IRQn18外中断0PWM0_IRQn39PWM0

EINT1_IRQn19外中断1EEPROM_IRQn40EEPROM

EINT2_IRQn20外中断2

2.2 设置异常/中断的优先级组

Cortex-M3的异常/中断是可以抢占的,高抢占优先级中断可以抢占低抢占优先级中断。NVIC中有个名字叫做“应用程序中断及复位控制寄存器(AIRCR)”的寄存器,该寄存

器的bit[10:8]称为优先级分组(PRIGROUP)段,表示的值为0~7,分别对应8个不同的

抢占优先级设置。比如优先级分组段为0时,则8位优先级配置寄存器(LPC177x/8x只使用了其中的5位)的bit[7:1]表示抢占优先级,bit[0:0]表示非抢占优先级;再比如优先级分组段为1时,则8位优先级配置寄存器的bit[7:2]表示抢占优先级,bit[1:0]表示非抢占优先级,依次类推。

复位后,优先级分组(PRIGROUP)段默认值为0,也就是则8位优先级配置寄存器(LPC177x/8x只使用了其中的5位)的bit[7:1]表示抢占优先级,bit[0:0]表示非抢占优先级。而LPC177x/8x只使用了8位优先级配置寄存器其中的bit[7:3],所以对于LPC177x/8x 微处理器而言,复位后默认32级优先级全部为可抢占优先级。

2.2.1 设置优先级寄存器组的C代码

1:/**

2: * @brief Set the Priority Grouping in NVIC Interrupt Controller 3: *

4: * @param PriorityGroup is priority grouping field

5: *

6: * Set the priority grouping field using the required unlock sequence.

7: * The parameter priority_grouping is assigned to the field

8: * SCB->AIRCR [10:8] PRIGROUP field. Only values from 0..7 are used. 9: * In case of a conflict between priority grouping and available

10: * priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set.

11: */

12:static __INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)

13: {

14: uint32_t reg_value;

15:/* only values 0..7 are used */

16: uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); 17:

18: reg_value = SCB->AIRCR; /* read old register configuration

*/

19:/* clear bits to change */

20: reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); 21:/* Insert write key and priorty group */

22: reg_value = (reg_value |

23: (0x5FA << SCB_AIRCR_VECTKEY_Pos) |

24: (PriorityGroupTmp << 8));

25: SCB->AIRCR = reg_value;

26: }

其中,参数PriorityGroup为要设置的优先级分组(PRIGROUP)段的值,取值范围

为0~7.由于操作AIRCR寄存器需要访问钥匙,所以要把0x05FA写入到该寄存器的

bit[31:16]中,否则写入的值会被忽略。

需要注意的是,在一个设计好的产品中,如果没有十足的把握,不要修改优先级组,

不然会有大恶魔纠缠你的。

最后,本文所使用的代码,全部由CMSIS-M3提供。

C语言程序编辑或调试中常见的错误

常见错误和程序分析 (1)忘记定义变量。例如: void main() { x=3; y=6; printf(“%d\n”,x+y); } C要求对程序中用到的美一个变量都必须定义其类型,上面程序中没有对x,y 进行定义。应在函数体的开头加int x,y; (2)输入输出的数据类型与所用格式说明符不一致。例如,若a已定义为整数,b已定义为实型: a=3;b=4.5; /*对a和b赋值*/ printf(“%f %d\n”,a,b); 编译时不给出出错信息,但运行结果将与原意不符,输出为0.000000 16402它们并不是按照赋值的规则进行转换(如把4.5转换为4),而是将数据在存储单元中的形式按格式符的要求组织输出(如b占4个字节,只把最后2个字节中的数据按%d作为整数输出)。 (3)未注意int型的数据的数值范围。Turbo C等编译系统,对一个整型数据分配2个字节。因此一个整数的范围为-2的13次方到2的15次方减1,即-32768~32767常见这样的程序段: int num; num=89101; printf(“%d”,num); 得到的却是23565,原因是89101已超过32767。2个字节容纳不下89101,则将高位截去,即将超过低16位的数截去,也即89101-65536=23565,有时还会出现负数。这种情况应改为: Long int num; num=89101; printf(“%ld”,num); 注意,如果只定义num为long型,而在输出时扔用%d说明符,也会出现以上错误。 (4)在输出语句scanf中忘记使用变量的地址符。例如: scanf(“%d%d”,a,b); 这是很多初学者刚学C语言时常见的疏忽,应写为scanf(“%d%d”,&a,&b); (5)输入数据的形式与要求不符。例如有以下scanf函数: scanf(“%d%d”,&a,&b); 有人输入 3 , 4 ,这是错的数据间应该用空格来分隔,读者可以用printf(“%d%d”,a,b);来验证下。应该输入 3 4,除非函数是scanf(“%d,%d”,&a,&b); 还应注意不能企图用

第7章异常处理与程序调试技术(精)

第7章异常处理与程序调试技术 第1节异常处理概述 一、异常的概念 所谓异常是指程序在运行时遇到的一些无法预知,需要特别处理的状况。例如程序要打开一个不存在的文件、网络连接中断、操作数越界、装载一个不存在的类等问题。 二、Delphi的异常处理机制 Delphi提供了一套默认的自动异常处理机制,即当程序发生错误后会自动退出当前运行的程序模块,并以对话框的形式给出相应的错误信息,但是并不会引起应用程序的非法终止。 Delphi的异常处理机制是建立在保护块的概念上的。保护块是用保留字try和end封装起来的一段代码。当应用程序发生错误时,系统会自动创建相应的异常类的对象,该异常对象通常包含了错误的相关信息。程序可以捕获并处理这个异常对象,如果程序中没有定义对该异常的处理,则系统会自动产生一个消息框来描述这个错误,并保证程序不会非法终止。 三、异常类 在Delphi中,所有的异常类都派生自Exception类,该类是在SysUtils单元中定义的,如果一个程序的Uses语句中包含了SysUtils单元,则运行中发生错误时就会引发一个异常。 需要注意的是,异常不同于错误,编程者可以引发一个自定义异常。例如: Raise Exception.Create('My Error'); 第2节异常处理的语法 Delphi提供了两种处理异常的基本结构:try…except和try…finally。前者主要用于编写异常处理代码,后者主要用于保护系统资源。 一、try…except…end try…except…end形成一个异常响应保护块。正常情况下except后面的语句并不会被执行,而当一条或多条try块中的语句执行失败时,则发生异常,程序自动跳到except块中,进入异常响应处理模块中去执行。当异常被响应后异常类自动清除。

相关文档
最新文档