×
单片机 > 单片机程序设计 > 详情

OK6410裸机之中断向量控制器

发布时间:2020-06-08 发布时间:
|

statr.S文件:

.globl _start

_start:

    // 0 地址 

    b reset                                                  // 复位时,cpu跳到0地址 

    ldr pc, =undefined_instruction          // cpu遇到不能识别的指令时 

    ldr pc, _vector_swi                               // 当执行swi指令时, 进入swi模 式 

    b halt     @ldr    pc, _prefetch_abort  // 预取中止异常 

    b halt     @ldr    pc, _data_abort        // 数据访问异常 

    b halt     @ldr    pc, _not_used           // 没用到 

    ldr    pc, _irq                                        // 0x18 中断异常 

    b halt     @ldr    pc, _fiq                     // 快中断异常 

_irq :

    .word vector_irq

_vector_swi:

    .word vector_swi

vector_swi:

    // 1. 保存现场 

    ldr sp, =0x56000000

    stmdb sp!, {r0-r12, lr}  // lr就是swi的下一条指令地址 

    // 2. 处理异常 

    mrs r0, cpsr

    ldr r1, =swi_str

    bl print_cpsr

    // 3. 恢复现场 

    ldmia sp!, {r0-r12, pc}^ // ^表示把spsr恢复到cpsr 

    

swi_str:

    .word 0x00697773         // swi 

    

undefined_instruction:

    // 1. 保存现场 

    ldr sp, =0x55000000

    stmdb sp!, {r0-r12, lr}

    // 2. 处理异常 

    mrs r0, cpsr

    ldr r1, =und_str

    bl print_cpsr

    // 3. 恢复现场 

    ldmia sp!, {r0-r12, pc}^  // ^表示把spsr恢复到cpsr 

und_str:

    .word 0x00646e75         // und 

usr_str:

    .word 0x00727375         // usr 

vector_irq:

    // 1. 保存现场 

    ldr sp, =0x54000000

    sub lr, lr, #4               //只有swi和und异常lr指向下一条指令,其他异常发生的时候lr都是指向下两条指令

    stmdb sp!, {r0-r12, lr}  // lr就是swi的下一条指令地址 

    // 2. 处理异常 

    bl do_irq

    

    // 3. 恢复现场 

    ldmia sp!, {r0-r12, pc}^  // ^表示把spsr恢复到cpsr 

reset:

// 硬件相关的设置 

    // Peri port setup 

    ldr r0, =0x70000000

    orr r0, r0, #0x13

    mcr p15,0,r0,c15,c2,4       @ 256M(0x70000000-0x7fffffff)

    

// 关看门狗 

    // 往WTCON(0x7E004000)写0 

    ldr r0, =0x7E004000

    mov r1, #0

    str r1, [r0]

    

    // 设置栈 

    ldr sp, =8*1024

    // 设置时钟 

    bl clock_init

    bl ddr_init

    bl init_uart

// 把程序的代码段、数据段复制到它的链接地址去     

    adr r0, _start             // 获得_start指令当前所在的地址 : 0

    ldr r1, =_start           // _start的链接地址 0x51000000 

    

    ldr r2, =bss_start      // bss段的起始链接地址 

    

    sub r2, r2, r1

    

    cmp r0,r1

    beq clean_bss

    

    bl copy2ddr

    cmp r0, #0

    bne halt

        

// 清BSS 

// 把BSS段对应的内存清零 

clean_bss:

    ldr r0, =bss_start

    ldr r1, =bss_end

    mov r3, #0

    cmp r0, r1

    ldreq pc, =on_ddr

clean_loop:

    str r3, [r0], #4

    cmp r0, r1    

    bne clean_loop        

    ldr pc, =on_ddr

on_ddr:

    bl irq_init

    mrs r0, cpsr

    bic    r0,r0,#0x9f  // 清cpsr的I位,M4~M0 

    orr    r0,r0,#0x10

    msr    cpsr,r0       // 进入user mode 

    ldr sp, =0x57000000

    ldr r1, =usr_str

    bl print_cpsr

    

    swi 0

    // cpu进入svc模式

    // 把之前的cpsr保存到spsr_svc 

    // 切换到r13_svc, r14_svc

    // 把swi的下一条指令存到r14(lr)_svc

    // 跳到地址8

              

    bl hello

undef:

    .word 0xff000000

    // cpu进入Undefined模式

    // 把之前的cpsr保存到spsr_und 

    // 切换到r13_und, r14_und

    // 把下一条指令存到r14(lr)_und

    // 跳到地址4

swi_ret:

    bl main

halt:

    b halt    

=====================================================================

irq.c文件:

#define GPNCON     (*((volatile unsigned long *)0x7F008830))

#define GPNDAT     (*((volatile unsigned long *)0x7F008834))

#define EINT0CON0  (*((volatile unsigned long *)0x7F008900))

#define EINT0MASK  (*((volatile unsigned long *)0x7F008920))

#define EINT0PEND  (*((volatile unsigned long *)0x7F008924))

#define PRIORITY    (*((volatile unsigned long *)0x7F008280))

#define SERVICE     (*((volatile unsigned long *)0x7F008284))

#define SERVICEPEND (*((volatile unsigned long *)0x7F008288))

#define VIC0IRQSTATUS  (*((volatile unsigned long *)0x71200000))

#define VIC0FIQSTATUS  (*((volatile unsigned long *)0x71200004))

#define VIC0RAWINTR    (*((volatile unsigned long *)0x71200008))

#define VIC0INTSELECT  (*((volatile unsigned long *)0x7120000c))

#define VIC0INTENABLE  (*((volatile unsigned long *)0x71200010))

#define VIC0INTENCLEAR (*((volatile unsigned long *)0x71200014))

#define VIC0PROTECTION (*((volatile unsigned long *)0x71200020))

#define VIC0SWPRIORITYMASK (*((volatile unsigned long *)0x71200024))

#define VIC0PRIORITYDAISY  (*((volatile unsigned long *)0x71200028))

#define VIC0VECTADDR0      (*((volatile unsigned long *)0x71200100))

#define VIC0VECTADDR1      (*((volatile unsigned long *)0x71200104))

#define VIC0ADDRESS           (*((volatile unsigned long *)0x71200f00))

void eint0_3_irq(void)

{

    int i;

    

    printf("eint0_3_irq\n\r");  // K1~K4 

    for (i = 0; i < 4; i ++)

    {

        if (EINT0PEND & (1<

        {

            if (GPNDAT & (1<

            {

                printf("K%d released\n\r", i+1);

            }

            else

            {

                printf("K%d pressed\n\r", i+1);

            }

        }

    }

}

void eint4_11_irq(void)

{

    int i;

    printf("eint4_11_irq\n\r"); // K5~K6 

    for (i = 4; i < 6; i ++)

    {

        if (EINT0PEND & (1<

        {

            if (GPNDAT & (1<

            {

                printf("K%d released\n\r", i+1);

            }

            else

            {

                printf("K%d pressed\n\r", i+1);

            }

        }

    }

}

void irq_init(void)

{

    // 配置GPIO引脚为中断引脚 

    // GPN0~5 设为中断引脚 

    GPNCON &= ~(0xfff);

    GPNCON |= 0xaaa;

    // 设置中断触发方式为: 双边沿触发 

    EINT0CON0 &= ~(0xfff);

    EINT0CON0 |= 0x777;

    // 使能中断 

    EINT0MASK &= ~(0x3f);

    // 在中断控制器里使能这些中断 

    VIC0INTENABLE |= (0x3); // bit0: eint0~3, bit1: eint4~11  

//发生中断时硬件会把VIC0VECTADDR0赋给VIC0ADDRESS(第一组为VIC0ADDRESS,第二组为VIC1ADDRESS)

    VIC0VECTADDR0 = eint0_3_irq;  

    

//发生中断时硬件会把VIC0VECTADDR1赋给VIC0ADDRESS(第一组为VIC0ADDRESS,第二组为VIC1ADDRESS)

    VIC0VECTADDR1 = eint4_11_irq; 

    // 设置优先级 

}

//在中断向量控制器中有两个中断组,每一组有32个中断,在第一组中断发生时硬件会自动把VIC0VECTADDR

//寄存器的值给VIC0ADDRESS,在第二组中断发生时硬件会自动把VIC1VECTADDR寄存器的值给

//VIC1ADDRESS,只要提前把中断服务函数的地址赋给对应的寄存器,在发生中断的时候就会在do_irq函数中

//把VIC0ADDRESS或则VIC1ADDRESS值给函数指针执行就可以运行中断服务函数

void do_irq(void)

{

    int i = 0;

    void (*the_isr)(void);

    //第一组的32个中断对应为VIC0ADDRESS,第二组的32个中断对应为VIC1ADDRESS

    the_isr = VIC0ADDRESS; 

        

    // 2.1 分辨是哪个中断 

    // 2.2 调用它的处理函数     

    // 2.3 清中断     

    the_isr();

    

    EINT0PEND   = 0x3f;  // 清中断 

    VIC0ADDRESS = 0;

}

=====================================================================

Makefile文件:

CC      = arm-linux-gcc

LD      = arm-linux-ld

AR      = arm-linux-ar

OBJCOPY = arm-linux-objcopy

OBJDUMP = arm-linux-objdump

INCLUDEDIR     := $(shell pwd)/include

CFLAGS             := -Wall -Os -fno-builtin-printf 

CPPFLAGS        := -nostdinc -I$(INCLUDEDIR)

export     CC AR LD OBJCOPY OBJDUMP INCLUDEDIR CFLAGS CPPFLAGS 

objs := start.o sdram.o nand.o clock.o uart.o irq.o  main.o lib/libc.a

uart.bin: $(objs)

    ${LD} -Tuart.lds -o uart.elf $^

    ${OBJCOPY} -O binary -S uart.elf $@

    ${OBJDUMP} -D uart.elf > uart.dis

.PHONY : lib/libc.a

lib/libc.a:

    cd lib; make; cd ..

    

%.o:%.c

    ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM 汇编的必知必会