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,禁止同类型的其他中断被触发.

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


下面给出汇编代码:

  1. /* 
  2. simple interruption 
  3. copyleft@dndxhej@gmail.com 
  4. */  
  5.   
  6. .equ   NOINT, 0xc0  
  7. .equ    WTCON,  0x53000000  
  8. .equ    GPBCON, 0x56000010      @led  
  9. .equ    GPBDAT, 0x56000014      @led  
  10. .equ   GPBUP,        0x56000018    @led  
  11. .equ    GPFCON, 0x56000050      @interrupt config  
  12. .equ    EINTMASK, 0x560000a4  
  13. .equ    EXTINT0,  0x56000088  
  14. .equ    EXTINT1,  0x5600008c  
  15. .equ    EXTINT2,  0x56000090  
  16. .equ    INTMSK,  0x4A000008  
  17. .equ   EINTPEND,     0x560000a8  
  18.   
  19. .equ   INTSUBMSK,    0X4A00001C  
  20.   
  21. .equ   SRCPND,   0X4A000000  
  22. .equ   INTPND,   0X4A000010  
  23.   
  24. .global _start  
  25. _start:     b   reset  
  26.         ldr     pc, _undefined_instruction  
  27.         ldr     pc, _software_interrupt  
  28.         ldr pc, _prefetch_abort  
  29.         ldr pc, _data_abort  
  30.         ldr pc, _not_used  
  31.         @b  irq  
  32.         ldr     pc, _irq  
  33.         ldr     pc, _fiq  
  34.   
  35.   
  36.   
  37. _undefined_instruction:     .word undefined_instruction  
  38. _software_interrupt:        .word software_interrupt  
  39. _prefetch_abort:        .word prefetch_abort  
  40. _data_abort:            .word data_abort  
  41. _not_used:          .word not_used  
  42. _irq:               .word irq  
  43. _fiq:               .word fiq  
  44.   
  45. .balignl 16,0xdeadbeef  
  46.   
  47. reset:  
  48.   
  49.   
  50.     ldr     r3, =WTCON  
  51.     mov r4, #0x0                       
  52.     str r4, [r3]    @ disable watchdog      
  53.   
  54.     ldr r0, =GPBCON  
  55.     ldr r1, =0x15400  
  56.     str r1, [r0]  
  57.   
  58.     ldr r2, =GPBDAT  
  59.     ldr r1, =0x160  
  60.     str r1, [r2]  
  61.   
  62.     bl delay  
  63.   
  64.   
  65.     msr cpsr_c, #0xd2 @进入中断模式  
  66.     ldr sp, =3072 @中断模式的栈指针定义  
  67.   
  68.     msr cpsr_c, #0xdf @进入系统模式  
  69.     ldr sp, =4096 @设置系统模式的栈指针  
  70.   
  71.   
  72.   
  73.   
  74. @--------------------------------------------  
  75.   
  76.     ldr r0, =GPBUP  
  77.     ldr r1, =0x03f0    
  78.     str r1, [r0]        
  79.      
  80.   
  81.   
  82.     ldr r0, =GPFCON  
  83.     ldr r1, =0x2ea@0x2      
  84.     str r1, [r0]    
  85.   
  86.     ldr r0, =EXTINT0  
  87.     ldr r1, =0x8f888@0x0@0x8f888      @~(7|(7<<4)|(7<<8)|(7<<16))  
  88.     str r1, [r0]    
  89.   
  90.     ldr r0, =EINTPEND  
  91.     ldr r1, =0xf0@0b10000  
  92.     str r1, [r0]    
  93.   
  94.     ldr r0, =EINTMASK  
  95.     ldr r1, =0x00@0b00000  
  96.     str r1, [r0]    
  97.   
  98.   
  99.   
  100.     ldr r0, =SRCPND  
  101.     ldr r1, =0xff@0x1@0b11111  
  102.     str r1, [r0]    
  103.   
  104.     ldr r0, =INTPND  
  105.     ldr r1, =0xff@0x1@0b11111  
  106.     str r1, [r0]    
  107.   
  108.     ldr r0, =INTMSK  
  109.     ldr r1, =0xffffff00@0b00000  
  110.     str r1, [r0]    
  111.   
  112.     MRS r1, cpsr  
  113.     BIC r1, r1, #0x80  
  114.     MSR cpsr_c, r1  
  115.   
  116.   
  117.     bl     main  
  118.   
  119. irq:  
  120.     sub     lr,lr,#4  
  121.     stmfd   sp!,{r0-r12,lr}  
  122.     bl irq_isr  
  123.     ldmfd  sp!,{r0-r12,pc}^   
  124.   
  125.   
  126.   
  127. irq_isr:  
  128.   
  129.   
  130.   
  131.     ldr r2, =GPBDAT  
  132.     ldr r1, =0x0e0  
  133.     str r1, [r2]  
  134.   
  135.   
  136.          ldr r0,=EINTPEND  
  137.          ldr r1,=0xf0  
  138.          str r1,[r0]   
  139.   
  140.     ldr r0, =SRCPND  
  141.     ldr r1, =0x3f@0b11111  
  142.     str r1, [r0]    
  143.   
  144.   
  145.   
  146.     ldr r0, =INTPND  
  147.     ldr r1, =0x3f@0b11111  
  148.     str r1, [r0]    
  149.   
  150.   
  151.   
  152.     mov pc,lr  
  153.   
  154.   
  155. delay:  
  156.       
  157.     ldr r3,=0xffff  
  158.   
  159. delay1:  
  160.     sub r3,r3,#1  
  161.   
  162.     cmp r3,#0x0  
  163.   
  164.     bne delay1  
  165.   
  166.     mov pc,lr  
  167.   
  168.   
  169.   
  170.   
  171. main:  
  172. ledloop:  
  173.   
  174.     ldr r1,=0x1c0  
  175.     str r1,[r2]  
  176.     bl delay  
  177.   
  178.     ldr r1,=0x1a0  
  179.     str r1,[r2]  
  180.     bl delay  
  181.   
  182.     ldr r1,=0x160  
  183.     str r1,[r2]  
  184.     bl delay  
  185.   
  186.     ldr r1,=0x0e0  
  187.     str r1,[r2]  
  188.     bl delay  
  189.   
  190.   
  191.     b ledloop  
  192.   
  193.   
  194.   
  195.   
  196. undefined_instruction:  
  197.             nop  
  198. software_interrupt:  
  199.             nop  
  200. prefetch_abort:   
  201.             nop  
  202. data_abort:  
  203.             nop  
  204. not_used:  
  205.             nop  
  206. fiq:  
  207.             nop  
/*
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文件:

  1. OUTPUT_FORMAT("elf32-littlearm""elf32-littlearm""elf32-littlearm")  
  2. OUTPUT_ARCH(arm)  
  3. ENTRY(_start)  
  4.   
  5. SECTIONS{  
  6.     . = 0x00000000;  
  7.     .text : {  
  8.         *(.text)  
  9.         *(.rodata)  
  10.     }  
  11.   
  12.     .data ALIGN(4): {  
  13.         *(.data)  
  14.     }  
  15.   
  16.     .bss ALIGN(4): {  
  17.         *(.bss)  
  18.     }  
  19. }  
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:

  1. CROSS =  arm-linux-  
  2. CFLAGS = -nostdlib  
  3.   
  4. int.bin: start.S   
  5.     ${CROSS}gcc $(CFLAGS) -c -o start.o start.S  
  6.     ${CROSS}ld -Tint.lds start.o  -o int.elf  
  7. #   ${CROSS}ld -Ttext-segment 0x30000000 start.o  -o int.elf   
  8.     ${CROSS}objcopy -O binary -S int.elf int.bin  
  9. #   rm -f  *.o   
  10.   
  11.   
  12. clean:  
  13.     rm -f *.elf *.o  
  14.     rm -f int.bin  
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、中断处理程序的写法:

  1. irq:  
  2.     sub     lr,lr,#4  
  3.     stmfd   sp!,{r0-r12,lr}  
  4.     bl irq_isr  
  5.     ldmfd  sp!,{r0-r12,pc}^   
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、记得在中断处理程序中清除中断,不然的话会一直响应那个中断.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值