《嵌入式 - ARM》第4章 ARM中断

4.1 SWI中断处理

前面我们学习ARM工作模式中,处理器模式切换可以通过软件控制进行切换,即修改CPSR模式位,但这是在特权模式下,当我们处于用户模式下,是没有权限实现模式转换的。若想实现模式切换,只能由另一种方法来实现,即通过外部中断或是异常处理过程进行切换。于是ARM指令集中提供了两条产生异常的指令,通过这两条指令可以用软件的方法实现异常,其中一个就是中断指令SWI 。

4.1.1软件中断

软中断是利用硬件中断的概念,用软件方式进行模拟,实现从用户模式切换到特权模式并执行特权程序的机制。

硬件中断是由电平的物理特性决定,在电平变化时引发中断操作,而软中断是通过一条具体指令SWI,引发中断操作,也就是说用户程序里可以通过写入SWI指令来切换到特权模式,当CPU执行到SWI指令时会从用户模式切换到管理模式下,执行软件中断处理。由于SWI指令由操作系统提供的API封装起来,并且软件中断处理程序也是操作系统编写者提前写好的,因此用户程序调用API时就是将操作权限交给了操作系统,所以用户程序还是不能随意访问硬件。

软件中断指令(Software Interrupt, SWI)用于产生软中断,实现从用户模式变换到管理模式,CPSR保存到管理模式的SPSR中,执行转移到SWI向量。在其他模式下也可以使用SWI指令,处理器同样切换到管理模式。

1、SWI指令格式如下:
SWI{cond} immed_24
其中:
immed_24 24位立即数,值为从0――16777215之间的整数。
SWI指令后面的24立即数是干什么用的呢?用户程序通过SWI指令切换到特权模式,进入软中断处理程序,但是软中断处理程序不知道用户程序到底想要做什么?SWI指令后面的24位用来做用户程序和软中断处理程序之间的接头暗号。通过该软中断立即数来区分用户不同操作,执行不同内核函数。如果用户程序调用系统调用时传递参数,根据ATPCSC语言与汇编混合编程规则将参数放入R0~R4即可。

2、指令举例
使用SWI指令时,通常使用以下两种方法进行参数传递,SWI异常处理程序可以提供相关的服务,这两种方法均是用户软件协定。SWI异常中断处理程序要通过读取引起软件中断的SWI指令,以取得24为立即数。
1)、指令中24位的立即数指定了用户请求的服务类型,中断服务的参数通过通用寄存器传递。
如下面这个程序产生一个中断号位12 的软中断:

MOV R0,#34       ;设置功能号为34
SWI 12                              ;产生软中断,中断号为12

2)、指令中的24位立即数被忽略,用户请求的服务类型有寄存器R0的值决定,参数通过其他的通用寄存器传递。
如下面的例子通过R0传递中断号,R1传递中断的子功能号:

MOV R0, #12                  ;设置12号软中断
MOV R1, #34                  ;设置功能号为34
SWI  0

下面的例子通过系统调用函数int led_on(int led_no)实现点亮第led_no 个LED灯,由于C语言里没有SWI 指令对应的语句,因此这儿要用到C语言与汇编混合编程,led_on函数里将参数led_no的值传递给R0,通过软中断SWI指令切换到软中断管理模式,同时R0 软中断方式点亮LED灯,用户通过SWI #1指令可以点灯,具体点亮哪个灯,通过R0保存参数传递,如果亮灯成功返回对应LED号。

#define __led_on_swi_no         1                 // 软中断号1,调用管理模式下的do_led_on函数  
int led_on(int led_no)
{  
int ret;   // 返回值  
_asm
{   
// 由于C程序中没有SWI对应表达式,所以使用混合编程  
mov  r0, led_no        // 根据ATPCS规则,r0存放第一个参数  
swi    __led_on_swi_no          // 产生SWI软中断,中断号为__led_on_swi_no  
mov  ret, r0            // 软中断处理结束,取得中断处理返回值,传递给ret变量  
         }  
         return ret;      // 将ret返回给调用led_on的语句  
}  

4.1.2软中断处理

CPU执行到swi xxx执行后,产生软件中断,由异常处理部分知识可知,软中断产生后CPU将强制将PC的值置为异常向量表地址0x08,在异常向量表0x08处安放跳转指令b HandleSWI,这样CPU就跳往我们自己定义的HandleSWI处执行。

1–保护现场
软中断处理中通过STMFD SP!, {R0-R12, LR} 要保存程序执行现场,将R0~R12通用寄存器数据保存在管理模式下SP栈内,LR由硬件自动保存软中断指令下一条指令的地址(后面利用LR的地址取得SWI指令编码),该寄存器值也保存在SP栈内,将来处理完毕之后返回;

2–获取SWI指令编码
由SWI指令编码知识可知,SWI指令低24位保存有软中断号,通过LDR R4, [LR, #-4]指令,取得SWI指令编码(LR为硬件自动保存SWI xxx指令的下一条指令地址,LR – 4就是SWI指令地址),将其保存在R4寄存器中。通过BIC R4, R4, #0xFF000000 指令将SWI指令高8位清除掉,只保留低24位立即数,取得SWI指令编码;

3–根据SWI指令做出相应操作
根据24位立即数中的软中断号判断用户程序的请求操作。如果24位立即数为1,表示led_on系统调用产生的软中断,则在管理模式下调用对应的亮灯操作do_led_on。如果24位立即数为2,表示led_off系统调用产生的软中断,则调用灭灯操作do_led_on,根据ATPCS调用规则,R0~R3做为参数传递寄存器,在软中断处理中没有使用这4个寄存器,而是使用R4作为操作寄存器的。

4–返回并恢复现场
执行完系统调用操作之后,返回到swi_return(在调用对应系统操作时,通过LDREQ LR, =swi_return设置了返回地址),执行返回处理,通过LDMIA SP!, {R0-R12, PC}^ 指令将用户寄存器数据恢复到R0~R12,将进入软中断处理时保存的返回地址LR的值恢复给PC,实现程序返回,同时还恢复了状态寄存器。切换回用户模式下程序中继续执行。

; 异常向量表开始  
; 0x00: 复位Reset异常  
         b       Reset   
  ; 0x04: 未定义异常(未处理)  
HandleUndef  
           b       HandleUndef   
   ; 0x08: 软件中断异常,跳往软件中断处理函数HandleSWI  
    b         HandleSWI  
… …  
; 省略其它异常向量和对应处理  
… …  
;***********************************************************************  
; 软中断处理  
;***********************************************************************   
  
IMPORT do_led_on  
IMPORT do_led_off  
HandleSWI  
         STMFD     SP!, {R0-R12,  LR}            ; 保存程序执行现场  
         LDR R4, [LR, #-4]                                   ; LR - 4 为指令" swi xxx" 的地址,低24位是软件中断号  
         BIC   R4, R4, #0xFF000000                    ; 取得ARM指令24位立即数  
         CMP          R4, #1                                    ; 判断24位立即数,如果为1,调用do_led_on系统调用  
         LDREQ     LR, =swi_return                    ; 软中断处理返回地址  
         LDREQ     PC, = do_led_on                    ; 软中断号1对应系统调用处理  
         CMP          R4, #2                ; 判断24位立即数,如果为2,调用do_led_off系统调用  
         LDREQ     LR, =swi_return                    ; 软中断处理返回地址  
         LDREQ     PC, = do_led_off                            ; 软中断号2对应系统调用处理  
         MOVNE    R0, #-1                                   ; 没有该软中断号对应函数,出错返回-1  
swi_return   
         LDMIA     SP!, {R0-R12, PC}^             ; 中断返回, ^表示将spsr的值复制到cpsr  

其实讲到这,会产生一个疑问,什么时候需要我们从用户模式切换到管理模式?我们应该记得系统调用,就是用户态向内核态的切换。

4.1.3系统调用

操作系统的主要功能是为应用程序的运行创建良好的环境,保障每个程序都可以最大化利用硬件资源,防止非法程序破坏其它应用程序执行环境,为了达到这个目的,操作系统会将硬件的操作权限交给内核来管理,用户程序不能随意使用硬件,使用硬件(对硬件寄存器进行读写)时要先向操作系统发出请求,操作系统内核帮助用户程序实现其操作,也就是说用户程序不会直接操作硬件,而是提供给用户程序一些具备预定功能的内核函数,通过一组称为系统调用的(system call)的接口呈现给用户,系统调用把应用程序的请求传给内核,调用相应的内核函数完成所需的处理,将处理结果返回给应用程序。

这好比我们去银行取款,用户自己的银行帐户不可能随意操作,必须要有一个安全的操作流程和规范,银行里的布局通常被分成两部分,中间用透明玻璃分隔开,只留一个小窗口,面向用户的是用户服务区,工作人员所在区域为内部业务操作区,取款时,将银行卡或存折通过小窗口交给业务员,并且告诉他要取多少钱,具体取钱的操作你是不会直接接触的,业务员会将银行帐户里减掉取款金额,将现金给你。上述操作流程可以很好保护银行系统,银行系统的操作全部由业务员来实现,用户只能向业务员提出自己的服务请求。银行里的小窗口就类似与操作系统的系统调用接口,是将用户请求传递给内核的接口。

在这里插入图片描述

图1

操作系统里将用户程序运行在用户模式下,并且为其分配可以使用内存空间,其它内存空间不能访问,内核态运行在特权模式下,对系统所有硬件进行统一管理和控制。从前面所学知识可以了解到,用户模式下没有权限进行模式切换,这也就意味着用户程序不可能直接通过切换模式去访问硬件寄存器,如果用户程序试图访问没有权限的硬件,会产生异常。这样用户程序被限制起来,如果用户程序想要使用硬件时怎么办呢?用户程序使用硬件时,必须调用操作系统提供的API接口才可以,而操作系统API接口通过软件中断方式切换到管理模式下,实现从用户模式下进入特权模式。

At91rm9200处理器对应的linux2.4.19内核系统调用对应的软中断定义如下:

#if defined(__thumb__)                             //thumb模式  
#define __syscall(name)                          \  
    "push    {r7}\n\t"                           \  
    "mov    r7, #" __sys1(__NR_##name) "\n\t"    \  
    "swi    0\n\t"                               \  
    "pop    {r7}"  
#else                                              //arm模式  
#define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t"  
#endif  
  
#define __sys2(x) #x  
#define __sys1(x) __sys2(x)  
#define __NR_SYSCALL_BASE    0x900000               //此为OS_NUMBER << 20运算值  
#define __NR_open            (__NR_SYSCALL_BASE+ 5) //0x900005   

举一个例子来说:open系统调用,库函数最终会调用__syscall(open),宏展开之后为swi #__NR_open,即,swi #0x900005触发中断,中断号0x900005存放在[lr,#-4]地址中,处理器跳转到arch/arm/kernel/entry-common.S中vector_swi读取[lr,#-4]地址中的中断号,之后查询arch/arm/kernel/entry-common.S中的sys_call_table系统调用表,该表内容在arch/arm/kernel/calls.S中定义,__NR_open在表中对应的顺序号为
__syscall_start:
.long SYMBOL_NAME(sys_open) //第5个

将sys_call_table[5]中内容传给pc,系统进入sys_open函数,处理实质的open动作

注:用到的一些函数数据所在文件,如下所示
arch/arm/kernel/calls.S声明了系统调用函数
include/asm-arm/unistd.h定义了系统调用的调用号规则

vector_swi定义在arch/arm/kernel/entry-common.S
vector_IRQ定义在arch/arm/kernel/entry-armv.S
vector_FIQ定义在arch/arm/kernel/entry-armv.S

arch/arm/kernel/entry-common.S中对sys_call_table进行了定义:

    	.type    sys_call_table, #object  
ENTRY(sys_call_table)  
#include "calls.S"                                 //将calls.S中的内容顺序链接到这里  

源程序:

ENTRY(vector_swi)  
    save_user_regs  
    zero_fp  
    get_scno                                        //将[lr,#-4]中的中断号转储到scno(r7)  
    arm710_bug_check scno, ip  
#ifdef CONFIG_ALIGNMENT_TRAP  
    ldr    ip, __cr_alignment  
    ldr    ip, [ip]  
    mcr    p15, 0, ip, c1, c0                       @ update control register  
#endif  
    enable_irq ip  
    str    r4, [sp, #-S_OFF]!                       @ push fifth arg  
    get_current_task tsk  
    ldr    ip, [tsk, #TSK_PTRACE]                   @ check for syscall tracing  
    bic    scno, scno, #0xff000000                  @ mask off SWI op-code  
//#define OS_NUMBER    9[entry-header.S]  
//所以对于上面示例中open系统调用号scno=0x900005  
//eor scno,scno,#0x900000  
//之后scno=0x05  
    eor    scno, scno, #OS_NUMBER << 20             @ check OS number  
//sys_call_table项为calls.S的内容  
    adr    tbl, sys_call_table                      @ load syscall table pointer  
    tst    ip, #PT_TRACESYS                         @ are we tracing syscalls?  
    bne    __sys_trace  
    adrsvc    al, lr, ret_fast_syscall              @ return address  
    cmp    scno, #NR_syscalls                       @ check upper syscall limit  
//执行sys_open函数  
    ldrcc    pc, [tbl, scno, lsl #2]                @ call sys_* routine  
    add    r1, sp, #S_OFF  
2:  mov    why, #0                                  @ no longer a real syscall  
    cmp    scno, #ARMSWI_OFFSET  
    eor    r0, scno, #OS_NUMBER << 20               @ put OS number back  
    bcs    SYMBOL_NAME(arm_syscall)      
    b    SYMBOL_NAME(sys_ni_syscall)                @ not private func  
    /* 
     * This is the really slow path. We're going to be doing 
     * context switches, and waiting for our parent to respond. 
     */  
__sys_trace:  
    add    r1, sp, #S_OFF  
    mov    r0, #0                                   @ trace entry [IP = 0]  
    bl    SYMBOL_NAME(syscall_trace)  
/* 
//2007-07-01 gliethttp [entry-header.S] 
//Like adr, but force SVC mode (if required) 
  .macro adrsvc, cond, reg, label 
     adr\cond \reg, \label 
  .endm 
//对应反汇编: 
//add lr, pc, #16 ; lr = __sys_trace_return 
*/  
    adrsvc    al, lr, __sys_trace_return            @ return address  
    add    r1, sp, #S_R0 + S_OFF                    @ pointer to regs  
    cmp    scno, #NR_syscalls                       @ check upper syscall limit  
    ldmccia    r1, {r0 - r3}                        @ have to reload r0 - r3  
    ldrcc    pc, [tbl, scno, lsl #2]                @ call sys_* routine  
    b    2b  
  
__sys_trace_return:  
    str    r0, [sp, #S_R0 + S_OFF]!                 @ save returned r0  
    mov    r1, sp  
    mov    r0, #1                                   @ trace exit [IP = 1]  
    bl    SYMBOL_NAME(syscall_trace)  
    b    ret_disable_irq  
    .align    5  
#ifdef CONFIG_ALIGNMENT_TRAP  
    .type    __cr_alignment, #object  
__cr_alignment:  
    .word    SYMBOL_NAME(cr_alignment)  
#endif  
  
    .type    sys_call_table, #object  
ENTRY(sys_call_table)  
#include "calls.S"  

4.2轮询方式

以KEY2控制LED3亮灭为例:

在这里插入图片描述

图2

【0】检测按键k2,按键k2按下一次,灯LED2闪一次。

【1】查看原理图,连接引脚和控制逻辑
(1)按键k2 连接在GPX1_1引脚
(2)控制逻辑
k2 按下 ---- K2闭合 ---- GPX1_1 低电压
k2 常态 ---- K2打开 ---- GPX1_1 高电压

【2】查看相应的芯片手册
【2-1】循环检测GPX1_1引脚输入的电平,为低电压时,按键按下
(1)配置GPX1_1引脚功能为输入,设置内部上拉下拉禁止。
GPX1.CON = GPX1.CON &(~(0xf<<4)) ;
GPX1.PUD = GPX1.PUD & ~(0x3 << 2);
  (2)循环检测:

while(1)  
{  
    if(!(GPX1.DAT & (0x1<<1)))  // 返回为真,按键按下  
    {      
        msdelay(10);  
        if(!(GPX1.DAT & (0x1<<1))) //二次检测,去抖  
        {  
            GPX2.DAT |= 0x1 << 7;  //Turn on LED2  
            mydelay_ms(500);  
            GPX2.DAT &= ~(0x1<<7);  //Turn off LED2  
            mydelay_ms(500);  
            while(!(GPX1.DAT & (0x1<<1)));  
        }  
    }  
}  

这种轮询方式始终占着CPU,不利于操作。

4.3 IRQ中断方式

将K2按下时,GPX1_1引脚获得的电平,作为异常事件。使能异常处理,k2每按下一次,响应一次异常处理。SPI 传递流程如下示:

在这里插入图片描述

图3

注:
Exynos4412中断控制器包括160个中断控制源,这些中断源来自软中断(SGI),私有外部中断(PPI),公共外部中断(SPI)。
Exynos4412采用GIC中断控制器,主要是因为Contex-A9 是多核处理器,GIC(Generic Interrupt Controller)通用中断控制器用来选择使用哪个CPU接口,具体主要有两个功能:
1)分配器:设置一个开关,是否接收外部中断源;为该中断源选择CPU接口;
2)CPU接口:设置一个开发,是否接受该中断源请求;
具体实现如下:

1、外设一级 —设置 GPIO控制器

1-- 将GPX1_1引脚的上拉和下拉禁止
GPX1PUD[3:2]= 0b00;
2 – 将GPX1_1引脚功能设置为中断功能 WAKEUP_INT1[1] — EXT_INT41[1]
GPX1CON[7:4] = 0xf
3 – EXT_INT41CON 配置触发电平
当前配置成下降沿触发:
EXT_INT41CON[6:4] = 0x2
4 – EXT_INT41_FLTCON0 配置中断引脚滤波
默认就是打开的,不需要配置
5 – EXT_INT41_MASK 中断使能寄存器
使能INT41[1]
EXT_INT41_MASK[1] = 0b0
6 – EXT_INT41_PEND 中断状态寄存器
当GPX1_1引脚接收到中断信号,中断发生,中断状态寄存器EXT_INT41_PEND 相应位会自动置1
注意:中断处理完成的时候,需要清除相应状态位。置1清0.
EXT_INT41_PEND[1] =0b1

2、中断控制器
1-- 找到外设中断名称和GIC中断控制器对应的名称
 查看芯片手册(本例:Exynos_4412 – 9.2表)
WAKEUP_INT1[1] — EXT_INT41[1] — INT[9] — SPI[25]/ID[57]
其对应INT[9],中断ID为57,这是非常重要的,在后面的寄存器设置中起很大作用;
下面是外设与中断控制器处理具体流程:

在这里插入图片描述

图4

2 – GIC使能
ICDDCR =1;
使能分配器。

3 – 使能相应中断到分配器
ICDISER.ICDISER1 |= (0x1 << 25); //57/32 =1…25 取整数(那个寄存器) 和余数(哪位)
ICDISER用于使能相应中断到分配器,一个bit控制一个中断源,一个ICDISER可以控制32个中断源,这里INT[9] 对应的中断ID为57,所以在ICDSER1中进行设置,57/32 =1余25,所以这里在ICDISER1第25位置一。

4 – 选择CPU接口
设置SPI[25]/ID[57]由那个cpu处理,当前设置为cpu0的irq中断
ICDIPTR.ICDIPTR14 |= 0x01<<8; //SPI25 interrupts are sent to processor 0 //57/4 = 14…1 14号寄存器的[15:8]
ICDIPTR寄存器每8个bit 控制一个中断源

5 – 全局使能cpu0中断处理
CPU0.ICCICR |= 0x1;
使能中断到CPU。

6 – 优先级屏蔽寄存器,设置cpu0能处理所有的中断。
CPU0.ICCPMR = 0xFF;

3、ARM内核(cpu0)
前面两步设置好,就可以等待中断的发生了,当中断发生时,ARM内核的处理过程如下:
1-- 四大步三小步 — 硬件

在这里插入图片描述

图5

(1)拷贝 CPSR 到 SPSR _ < mode>

(2)设置适当的 CPSR 位:
    (2-1)–改变处理器状态进入 ARM 态
    (2-2)–改变处理器模式进入相应的异常模式
    (2-3)–设置中断禁止位禁止相应中断 (如果需要)

(3)保存返回地址到 LR_
 
(4)设置 PC 为相应的异常向量

2 – 中断服务程序 — start.S 汇编

 	.arch armv7-a
	.arm
	.globl	_start
_start:
	B		reset
	NOP
	NOP
	NOP
	NOP
	NOP
	@@@@ B		irq_handler
	@@@@ LDR		PC, [Rx]
	LDR		PC, __irq_handler
	NOP
__irq_handler:	.word irq_handler

reset:
	MOV		R0, #0x40000000
	mcr		p15,0,R0,c12,c0,0

	/* Switch Into SVC Mode */
	MRS		R0, CPSR
	BIC		R0, R0, #0x1F
	ORR		R0, R0, #0x0D3 @@ Disable IRQ and Disable FIQ
	MSR		CPSR_c, R0

	/* svc Mode Stack Initialization */
	LDR		SP, =svc_stack_top

	/* Switch Into IRQ Mode */
	MSR		CPSR_c, #0x0D2
	/* irq Mode Stack Initialization */
	LDR		SP, =irq_stack_top

	/* Switch Into USR Mode and Enable IRQ */
	MSR		CPSR_c, #0x50 @@ 0b0(I)1(F)0(T)_10000
	LDR		SP, =usr_stack_top
	BL		main
__die:
	B		__die
	.align 4
	/* void do_irq(void) */
irq_handler:
	STMFD	SP!, {R0-R12, LR}
	BL		do_irq
	LDMFD	SP!, {R0-R12, PC}^

	/** void __delay(void)*/
	.global __delay
__delay:
	PUSH	{R2, LR}

	MOV		R2, #0x20000000
loop1:
	SUBS	R2, R2, #0x1
	BNE		loop1

	POP		{R2, LR}
	MOV		PC, LR

	.data
	.space 8192
usr_stack_top:
	.space 1024
__reserved:
	.space 4096
svc_stack_top:
	.space 4096
irq_stack_top:

	.end

3–中断处理程序 — do_irq函数 c语言(函数原型void name(void))
(1) 读取正在处理的中断ID寄存器(ICCIAR)
irq_num = (CPU0.ICCIAR & 0x1FF);
(2)根据irq_num,分支处理中断

switch(irq_num)  
{  
    case 57:  
        break;  
    ....  
}  

(3)清除中断状态位
(3-1)i.外设级,EXT_INT41_PEND |= 0x1 << 1;
(3-2)ii.GIC级,ICDICPR.ICDICPR1 |= 0x1 << 25;
(3-3)iii.CPU0级 CPU0.ICCEOIR = (CPU0.ICCEOIR & ~(0x1FF)) | irq_num;
下面是C 程序(完整代码清查看附件):

#include "exynos_4412.h"  
#include "led.h"  

/* 
 *  裸机代码,不同于LINUX 应用层, 一定加循环控制 
 */  
int main (void)  
{  
    GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引脚功能为外部中断  
    GPX1.PUD = GPX1.PUD & (~(0x3 << 2));  //关闭上下拉电阻  
    EXT_INT41_CON = EXT_INT41_CON &(~(0xf << 4))|(0x2 << 4); //外部中断触发方式  
    EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1));  //使能中断  
    ICDDCR = 1;  //使能分配器  
    ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25); //使能相应中断到分配器  
    ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口  
    CPU0.ICCPMR = 255; //中断屏蔽优先级  
    CPU0.ICCICR = 1;   //使能中断到CPU  
    led_init();  
    while(1)  
    {  
  
    }  
   return 0;  
}  

总结:
(1)IRQ模式
中断IRQ引脚,中断GIC
(2)初始化IRQ
 配置GPIO
 GPIOCON配置使用中断功能
 GPIOPUD禁止上拉或者下拉
 EXT_INT_CON配置GPIO中断触发方式
 EXT_INT_MASK使能中断
 GIC配置
CPU配置
 ICDDCR使能CPU接口
 ICCICR_CPUn使能CPUn的只能中断
 ICCPMR_CPUn配置中断的最低优先级
GPIO配置
 ICDISER_CPU使能GIC-GPIO中断
 ICDIPTR_CPU配置执行的CPUn中断
 ICDIPR_CPU配置GIC-GPIO优先级
注意:配置ICDISER_CPU、ICDIPTR_CPU需要通过中断号计算得到。
(3)中断处理
(4)中断清除
 EXT_INT_PEND关闭GPIO中断
 ICDICPR_CPU关闭GIC-GPIO中断
 ICCEOIR_CPUn关闭中中断

本章参考代码

点击进入

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 《ARM Cortex-M3与Cortex-M4权威指南(第三版)》是一本深入浅出介绍ARM Cortex-M3与Cortex-M4处理器原理的权威指南。本书作者 Joseph Yiu 博士具有丰富的ARM处理器软硬件开发经验和教学经验,能够从理论和实践两个方面详细讲解这两种处理器的特点和优势。 本书对Cortex-M3与Cortex-M4处理器内部结构、寄存器、中断处理机制、存储器系统、指令系统等方面进行了详细的解释和分析,并提供了丰富的实例和代码来帮助读者更好的理解和掌握这些知识。此外,本书还专门介绍了M3和M4在不同应用场合下的特点和优势,读者可以根据自己的需求选择合适的处理器。 总之,《ARM Cortex-M3与Cortex-M4权威指南(第三版)》是一本非常有价值的ARM处理器开发技术书籍,既适合初学者深入学习,也适合从事ARM处理器软硬件设计的工程师作为参考书籍。 ### 回答2: 《Arm Cortex-M3与Cortex-M4权威指南(第三版)PDF》是一本极具权威性的指南,介绍了两个极为流行的嵌入式处理器平台Cortex-M3和Cortex-M4。该书由Joseph Yiu所著,总共分为三部分,共31,涵盖了Cortex-M3和Cortex-M4的体系结构、编程模型、内存映射和预测性代码执行等方面的知识。 该书从掌握机器指令、中断处理和嵌入式系统的基本原理开始,系统地介绍了Cortex-M3和Cortex-M4的体系结构和编程模型。它对ARM处理器的内部机制、中断处理和系统级编程进行了详细的阐述;对器件的外部接口和预测性代码执行等也作出了深入的讲解。此外,该书还介绍了Cortex-M3和Cortex-M4的调试工具和开发环境。 最后,该书通过一组实际的引导程序和案例,展示了基于Cortex-M3和Cortex-M4的具体设计方案。这对于嵌入式开发人员来说,是一本不可多得的指南。 综上所述,《Arm Cortex-M3与Cortex-M4权威指南(第三版)PDF》是一本全面介绍Cortex-M3和Cortex-M4处理器的权威指南,它的内容深入浅出,适合各级别的读者阅读。无论是初学者还是有经验的开发人员,都可以从中获得实际的指导和借鉴。它是嵌入式处理器的必读之书。 ### 回答3: 《ARM Cortex-M3与Cortex-M4权威指南(第三版)PDF》是一本关于ARM Cortex-M3和Cortex-M4处理器的权威指南,对于计算机科学、嵌入式系统、电子工程等领域的学者和工程师都具有非常重要的参考价值。 本书主要介绍了ARM Cortex-M3和Cortex-M4处理器的体系结构、指令集、内存、异常处理、能效与低功耗、外设以及调试和优化等方面的内容。通过阅读本指南,用户可以深入了解这两种处理器的基本工作原理,具体的编程方法和调试技巧。 另外,本书还介绍了一些实用的嵌入式系统设计技巧和开发工具,包括CMSIS组件库、IDE、仿真器、调试器等,这些工具可以帮助用户更加快速、高效地进行嵌入式系统开发和调试。 总之,ARM Cortex-M3与Cortex-M4权威指南(第三版)PDF是一本非常实用的嵌入式系统开发指南,对于想要深入了解ARM Cortex-M3和Cortex-M4处理器的学者和工程师都是非常有价值的参考读物。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bruceoxl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值