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

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

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

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

GNU ARM汇编--(四)中断汇编之非嵌套中断处理_第1张图片

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

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

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

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

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

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

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

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

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

GNU ARM汇编--(四)中断汇编之非嵌套中断处理_第2张图片

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

GNU ARM汇编--(四)中断汇编之非嵌套中断处理_第3张图片

下面给出汇编代码:

[cpp] view plain copy print ?
  1. /* 
  2. simple interruption 
  3. copyleft@[email protected] 
  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  

lds文件:

[cpp] view plain copy print ?
  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. }  

makefile:

[cpp] view plain copy print ?
  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  

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

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

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、中断处理程序的写法:

[cpp] view plain copy print ?
  1. irq:  
  2.     sub     lr,lr,#4  
  3.     stmfd   sp!,{r0-r12,lr}  
  4.     bl irq_isr  
  5.     ldmfd  sp!,{r0-r12,pc}^   
值得注意的是ldmfd  sp!,{r0-r12,pc}^ 会自动的从spsr_irq中恢复到cpsr中.

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

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


你可能感兴趣的:(GNU ARM汇编--(四)中断汇编之非嵌套中断处理)