ARM 系列 -- FS2410 开发板上的中断编程

一、目的
中断服务程序在操作系统中无疑占有非常重要的地位 , 编写中断程序不仅要会运用底层的汇编语言 , 还要了解 ARM 的体系架构 。那这一节我们就通过中断编程来响应 FS2410开发板上的 16 个按键 , 实现依次按下16个键时 , D9~D12 四个 LED 从 0~15 进行计数 , 并通过上个实验实现的 uart_printf 向串口发送数据 Kn is pressed! 。

二、代码
我们直接分析代码 , 代码中只有简略的注释 , 必要时我会在整个代码文件的后面对相应的细节进行解释 。先来分析 head.s:

@文件 head.s
.text
.global _start
_start:
@ Set vector table for interrupt
b reset
b HandleIRQ
b HandleIRQ
b HandleIRQ
b HandleIRQ
b HandleIRQ
b HandleIRQ @ handle irq interrupt here
b HandleIRQ
reset:
ldr r0, =0x53000000@ Close Watch Dog Timer
mov r1, #0x0
str r1, [r0]

@ disable all interrupts
mov r1, #0x4A000000
mov r2, #0xffffffff
str r2, [r1, #0x08] @ set INTMSK
ldr r2, =0x7ff
str r2, [r1, #0x1C] @ set INTSUBMSK

bl memory_setup@ Initialize memory setting
bl flash_to_sdram@ Copy code to sdram

msr cpsr_c, #0xd2@ set irq mode stack
ldr pc, =set_sp @ jump to addr 0x3000000
set_sp:
ldr sp, =0x31000000
msr cpsr_c, #0xdf@ set system mode stack
ldr sp, =0x32000000
bl init_irq @ Call init_irq
msr cpsr_c, #0x5f@ set system mode and open the irq

ldr sp, =0x34000000@ Set stack pointer
bl main
loop:
b loop
HandleIRQ:
sub lr, lr,#4 @ get the return addr
stmdb sp!, { r0-r12,lr } @ store used registers in stack
ldr lr, =int_return @ set retrun addr
ldr pc, =EINT_Handle @ jump to the interrup processing function
int_return:
ldmia sp!, { r0-r12,pc }^

【ARM 系列 -- FS2410 开发板上的中断编程】呵呵 , 不知不觉 head.s 的代码已经很长了 , 我们来看一下它的执行流程:
(1) 设置中断向量表 。你也许在这里有疑问 , 为什么一开始就有 8 个分支跳转指令?我们先来研究一下 ARM 如何响应异常/中断 , 看下表:
-------------------------------------------------------------
Exception Mode Address
-------------------------------------------------------------
Reset Supervisor0x00000000
Undefined Undefined0x00000004
Software InteruptSupervistor0x00000008
Prefetch AbortAbort 0x0000000C
Data AbortAbort0x00000010
IRQ (interupt)IRQ0x00000018
FIQ (fast interupt)FIQ0x0000001C
-------------------------------------------------------------
可以看出 ARM 支持 7 种异常/中断 , 每种异常/中断都有固定的地址 , 这个地址叫 中断向量 , 一般我们会在这个地址放一条分支跳转指令 , 当异常/中断发生时 , ARM 就到这个地址执行这个跳转指令 , 从而调用相应的中断服务程序 。
等等 , 这里是不是有点问题?呵呵 , 你也许已经发现了 , 这里只有 7 种异常/中断 , 那我们的程序怎么会有 8 条分支跳转指令呢? 因为中断向量即地址 0x00000014 被ARM保留用做将来扩展之用 , 但我们还需用一条指令(4字节)来填充这个位置 , 只不过它不会被 ARM 执行 。
(2) 关闭看门狗
(3) 暂时屏蔽所有中断 。
1.地址 0x4A000008 是中断屏寄存器 INTMSK 的端口地址 , 复位 INTMSK 会导致所有的中断源被屏掉 。
2.地址 0x4A00001C 是子中断屏寄存器 INTSUBMSK 的端口地址 , 它的低 11 位对应外部 11 个中断源 , 高 21 位保留不用 。复位它的低 11 位会导致相应的外部中断被屏 。
(4) 初始化内存 SDRAM 设置
(5) Self-copying: 从 Nand Flash 将自身复制到 SDRAM
(6) 进入 IRQ 模式 , 设置 IRQ 模式下的堆栈寄存器
(7) 进入系统模式 , 并设置系统模式下的堆栈寄存器
(8) 系统模式下调用 init_irq , 这个函数用于初始化一些用于响应按键的中断寄存器
(9) 再次进入系统模式 , 并打开当前程序状态寄存器 cpsr 的 IRQ 中断位 , 这样 ARM 就能响应 IRQ 中断了
(10)执行主函数 main 后返回 , 然后进入死循环 , 等待中断发生
(11)中断发生时 , ARM 响应中断并于 0x00000018 处执行 b HandleIRQ 跳转指令调用中断服务程序 , 处理完毕后返回循环处再等待下次中断的发生 , 如此往复...
这就是中断处理的基本流程了 :-) ,  以下文件的代码在前面随笔均有详细说明 , 这里就仅附简略注释了

@ 文件 flash.s
@ 作用:设置 Nand Flash 的控制寄存器、读取 Nand Flash
@ 中的代码到 SDRAM 的指定位置 , 更多细节请参考我前面的随笔
.equ NFCONF, 0x4e000000
.equ NFCMD, 0x4e000004
.equ NFADDR, 0x4e000008
.equ NFDATA, 0x4e00000c
.equ NFSTAT, 0x4e000010
.equ NFECC, 0x4e000014
.global flash_to_sdram
flash_to_sdram:
@ Save return addr
mov r10,lr

@ Initialize Nand Flash
mov r0,#NFCONF
ldr r1,=0xf830
str r1,[r0]

@ First reset and enable Nand Flash
ldr r1,[r0]
bic r1, r1, #0x800
str r1,[r0]

ldr r2,=NFCMD
mov r3,#0xff
str r3,[r2]

@ for delay
mov r3, #0x0a
1:
subs r3, r3, #1
bne 1b

@ Wait until Nand Flash bit0 is 1
wait_nfstat:
ldr r2,=NFSTAT
ldr r3,[r2]
tst r3,#0x01
beq wait_nfstat

@ Disable Nand Flash
ldr r0,=NFCONF
ldr r1,[r0]
orr r1,r1,#0x8000
str r1,[r0]

@ Initialzie stack
ldr sp,=4096

@ Set arguments and call
@ function nand_read defined in nand_read.c
ldr r0,=0x30000000
mov r1,#0
mov r2,#1024*40
bl nand_read

@ return
mov pc,r10

    推荐阅读