GNU ARM汇编--(四)中断汇编之非嵌套中断处理

   

        在写这篇blog之前,不得不感慨一句:纸上得来终觉浅,绝知此事要躬行.作为EE出身的,虽然好久好久没用汇编写单片机的中断了,但自我感觉对中断的理解还是比较深入的,本以为在GNU ARM汇编下搞个中断会很容易,谁知道断断续续花了我几周.完全用汇编写中断和用c中的_irq写中断还是有区别的,谁用谁知道.还是那句话:深入细节是必须的,也是值得的.

        这一篇blog的理论知识主要来源于:《ARM System Developer's Guide》.

        ARM的异常和相应的模式之间的对应关系见下表:


当一个异常导致模式的改变时,内核自动地:

1、把cpsr保存到相应模式下的spsr

2、把pc保存到相应模式下的lr

3、设置cpsr为相应异常模式

4、设置pc为相应异常处理程序的入口地址

从异常中断处理程序返回包含下面两个操作:

1、从spsr_mode中恢复内容到cpsr中

2、从lr_mode中恢复内容到pc中,返回到异常中断的指令的下一条政令处执行.

上面刚提到了异常发生时内核的一些动作,那对与IRQ或者FIQ而言,还多一项变化:禁用相关的中断IRQ或FIQ,禁止同类型的其他中断被触发.

 对于最简单的非嵌套中断处理的处理流程如下:


下面给出汇编代码:

/*
simple interruption
copyleft@dndxhej@gmail.com
*/

.equ   NOINT, 0xc0
.equ	WTCON,	0x53000000
.equ 	GPBCON,	0x56000010  	@led
.equ	GPBDAT,	0x56000014  	@led
.equ   GPBUP,        0x56000018    @led
.equ 	GPFCON, 0x56000050  	@interrupt config
.equ	EINTMASK, 0x560000a4
.equ 	EXTINT0,  0x56000088
.equ 	EXTINT1,  0x5600008c
.equ 	EXTINT2,  0x56000090
.equ	INTMSK,	 0x4A000008
.equ   EINTPEND,     0x560000a8

.equ   INTSUBMSK,    0X4A00001C

.equ   SRCPND,   0X4A000000
.equ   INTPND,   0X4A000010

.global _start
_start:		b	reset
		ldr     pc, _undefined_instruction
		ldr 	pc, _software_interrupt
		ldr	pc, _prefetch_abort
		ldr	pc, _data_abort
		ldr	pc, _not_used
		@b	irq
		ldr 	pc, _irq
		ldr 	pc, _fiq



_undefined_instruction:		.word undefined_instruction
_software_interrupt:		.word software_interrupt
_prefetch_abort:		.word prefetch_abort
_data_abort:			.word data_abort
_not_used:			.word not_used
_irq:				.word irq
_fiq:				.word fiq

.balignl 16,0xdeadbeef

reset:


	ldr     r3, =WTCON
	mov	r4, #0x0                     
	str	r4, [r3]	@ disable watchdog    

	ldr	r0, =GPBCON
	ldr	r1, =0x15400
	str	r1, [r0]

	ldr	r2, =GPBDAT
	ldr	r1, =0x160
	str	r1, [r2]

	bl delay


    msr cpsr_c, #0xd2 @进入中断模式
    ldr sp, =3072 @中断模式的栈指针定义

    msr cpsr_c, #0xdf @进入系统模式
    ldr sp, =4096 @设置系统模式的栈指针




@--------------------------------------------

	ldr	r0, =GPBUP
	ldr	r1, =0x03f0  
	str	r1, [r0]      
   


	ldr	r0, =GPFCON
	ldr	r1, =0x2ea@0x2    
	str	r1, [r0]  

	ldr	r0, =EXTINT0
	ldr	r1, =0x8f888@0x0@0x8f888      @~(7|(7<<4)|(7<<8)|(7<<16))
	str	r1, [r0]  

	ldr	r0, =EINTPEND
	ldr	r1, =0xf0@0b10000
	str	r1, [r0]  

	ldr	r0, =EINTMASK
	ldr	r1, =0x00@0b00000
	str	r1, [r0]  



	ldr	r0, =SRCPND
	ldr	r1, =0xff@0x1@0b11111
	str	r1, [r0]  

	ldr	r0, =INTPND
	ldr	r1, =0xff@0x1@0b11111
	str	r1, [r0]  

	ldr	r0, =INTMSK
	ldr	r1, =0xffffff00@0b00000
	str	r1, [r0]  

	MRS r1, cpsr
	BIC r1, r1, #0x80
	MSR cpsr_c, r1


	bl     main

irq:
	sub 	lr,lr,#4
	stmfd	sp!,{r0-r12,lr}
	bl irq_isr
	ldmfd  sp!,{r0-r12,pc}^ 



irq_isr:



	ldr	r2, =GPBDAT
	ldr	r1, =0x0e0
	str	r1, [r2]


         ldr r0,=EINTPEND
         ldr r1,=0xf0
         str r1,[r0] 

	ldr	r0, =SRCPND
	ldr	r1, =0x3f@0b11111
	str	r1, [r0]  



	ldr	r0, =INTPND
	ldr	r1, =0x3f@0b11111
	str	r1, [r0]  



	mov pc,lr


delay:
	
	ldr r3,=0xffff

delay1:
	sub r3,r3,#1

	cmp r3,#0x0

	bne delay1

	mov pc,lr




main:
ledloop:

	ldr r1,=0x1c0
	str r1,[r2]
	bl delay

	ldr r1,=0x1a0
	str r1,[r2]
	bl delay

	ldr r1,=0x160
	str r1,[r2]
	bl delay

	ldr r1,=0x0e0
	str r1,[r2]
	bl delay


	b ledloop




undefined_instruction:
			nop
software_interrupt:
			nop
prefetch_abort:	
			nop
data_abort:
			nop
not_used:
			nop
fiq:
			nop

lds文件:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)

SECTIONS{
    . = 0x00000000;
    .text : {
        *(.text)
        *(.rodata)
    }

    .data ALIGN(4): {
        *(.data)
    }

    .bss ALIGN(4): {
        *(.bss)
    }
}

makefile:

CROSS =  arm-linux-
CFLAGS = -nostdlib

int.bin: start.S 
	${CROSS}gcc $(CFLAGS) -c -o start.o start.S
	${CROSS}ld -Tint.lds start.o  -o int.elf
#	${CROSS}ld -Ttext-segment 0x30000000 start.o  -o int.elf
	${CROSS}objcopy -O binary -S int.elf int.bin
#	rm -f  *.o


clean:
	rm -f *.elf *.o
	rm -f int.bin

该程序实现的流水灯,然后四个按键可以实现外部中断.

代码中值得注意的地方有几点:

1、lds文件中的地址配为0x00000000,因为程序是download到nandflash中运行的.最开始这里写的是0x30000000,那在异常向量表中:

        @b    irq
        ldr     pc, _irq

就出现了一个问题:只能用b irq跳转,无法用ldr pc, _irq跳转.当时就觉得奇怪,找了半天原因.后来才知道b跳转和用ldr伪指令只有区别的:

b是位置无关的,ldr不是位置无关的

b的范围只能是前后16M,总共32M,而ldr是4G

ldr的跳转是根据_irq:                .word irq的值,这个值是链接的时候确定的,也就是与链接地址相关.

所以在lds中链接地址改为0x00000000后,b和ldr都是正确的.

具体可以用dump看一下实际效果:

当lds中是0x30000000时,arm-linux-objdump -d int.elf结果如下:

30000000 <_start>:
30000000:    ea00000e     b    30000040 <reset>
30000004:    e59ff014     ldr    pc, [pc, #20]    ; 30000020 <_undefined_instruction>
30000008:    e59ff014     ldr    pc, [pc, #20]    ; 30000024 <_software_interrupt>
3000000c:    e59ff014     ldr    pc, [pc, #20]    ; 30000028 <_prefetch_abort>
30000010:    e59ff014     ldr    pc, [pc, #20]    ; 3000002c <_data_abort>
30000014:    e59ff014     ldr    pc, [pc, #20]    ; 30000030 <_not_used>
30000018:    e59ff014     ldr    pc, [pc, #20]    ; 30000034 <_irq>
3000001c:    e59ff014     ldr    pc, [pc, #20]    ; 30000038 <_fiq>

而lds中是0x00000000时,dump的结果如下:

00000000 <_start>:
   0:    ea00000e     b    40 <reset>
   4:    e59ff014     ldr    pc, [pc, #20]    ; 20 <_undefined_instruction>
   8:    e59ff014     ldr    pc, [pc, #20]    ; 24 <_software_interrupt>
   c:    e59ff014     ldr    pc, [pc, #20]    ; 28 <_prefetch_abort>
  10:    e59ff014     ldr    pc, [pc, #20]    ; 2c <_data_abort>
  14:    e59ff014     ldr    pc, [pc, #20]    ; 30 <_not_used>
  18:    e59ff014     ldr    pc, [pc, #20]    ; 34 <_irq>
  1c:    e59ff014     ldr    pc, [pc, #20]    ; 38 <_fiq>

解决了这第一个问题,总算可以用ldr跳入中断向量了.

2、中断处理程序的写法:

irq:
	sub 	lr,lr,#4
	stmfd	sp!,{r0-r12,lr}
	bl irq_isr
	ldmfd  sp!,{r0-r12,pc}^ 

值得注意的是ldmfd  sp!,{r0-r12,pc}^ 会自动的从spsr_irq中恢复到cpsr中.

stmfd等价于stmdb,ldmfd等价于ldmia.因为arm使用FD(向低地址整长的满栈),所以堆栈处理都用fd的后缀即可.

3、记得在中断处理程序中清除中断,不然的话会一直响应那个中断.
















  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值