ARM裸机点LED

ARM 217浏览


硬件环境tiny6410

    1. 汇编实现:
.global _start

_start:
    // 把外设的基地址告诉CPU
    ldr r0, =0x70000000                     //对于6410来说,内存(0x000000000x60000000),外设(0x70000000-0x7fffffff)
    orr r0, r0, #0x13                       //外设大小:256M
    mcr p15,0,r0,c15,c2,4                   //把r0的值(包括了外设基地址+外设大小)告诉cpu

    // 关看门狗
    ldr r0, =0x7E004000
    mov r1, #0
    str r1, [r0]

    // 设置GPKCON0
    ldr r1, =0x7F008800
    ldr r0, =0x11110000
    str r0, [r1]

    mov r2, #0x1000
led_blink:
    // 设置GPKDAT,使GPK_4/5/6/7引脚输出低电平,LED亮
    ldr r1, =0x7F008808
    mov r0, #0
    str r0, [r1]

    // 延时
    bl delay

    // 设置GPKDAT,使GPK_4/5/6/7引脚输出高电平,LED灭
    ldr r1, =0x7F008808
    mov r0, #0xf0
    str r0, [r1]

    // 延时
    bl delay

    sub r2, r2, #1
    cmp r2,#0
    bne led_blink

halt:
    b halt


delay:
    mov r0, #0x1000000
delay_loop:
    cmp r0, #0
    sub r0, r0, #1
    bne delay_loop
    mov pc, lr

Makefile:

led.bin: start.o
    arm-linux-ld -Ttext 0x50000000 -o led.elf $^
    arm-linux-objcopy -O binary led.elf led.bin
    arm-linux-objdump -D led.elf > led_elf.dis
%.o : %.S
    arm-linux-gcc -o $@ $< -c

%.o : %.c
    arm-linux-gcc -o $@ $< -c

clean:
    rm *.o *.elf *.bin *.dis  -rf
  • 2 . 用汇编跳转到C实现
// 设置栈
    ldr sp, =0x0c002000

    // 调用C函数点灯
    bl main
    //与全部用汇编实现不同的是加入了栈,有了栈就可以调用其他函数。调用前程序执行地址入栈,结束后出站便可以恢复调用前执行到的语句。

/*******  C  *******/
    void delay()
{
    volatile int i = 0x10000;
    while (i--);
}

int main()
{
    int i = 0x10;

    // 配置引脚
    volatile unsigned long *gpkcon0 = (volatile unsigned long *)0x7F008800;
    volatile unsigned long *gpkdat = (volatile unsigned long *)0x7F008808;

    *gpkcon0 = 0x11110000;

    // 跑马灯
    while (1)
    {
        *gpkdat = i;
        i++;
        if (i == 0x100 )
            i = 0x10;
        delay();
    }

    return 0;
}

虽然功能是一样的,但是发现灯闪烁的频率比直接用汇编实现的要低些,说明汇编的效率还是比c高。
这里makefile不过是led.bin 的依赖文件多了一个main.c 而已。

  • 3 .加入icache

icache是cpu和内存中间的一种缓存,使用它可以加快cpu去指令的速度,不必每次都去内存中取。它是可以随时开启的,所以越早开启越好。

    // 开启icaches
#ifdef  CONFIG_SYS_ICACHE_OFF
    bic r0, r0, #0x00001000                 @ clear bit 12 (I) I-cache
#else
    orr r0, r0, #0x00001000                 @ set bit 12 (I) I-cache
#endif
    mcr p15, 0, r0, c1, c0, 0

汇编中加入以上代码便可以开启icache 。

  • 4 . 按键控制LED
    我的板子是tinySDK 1312B,但是我看原理图似乎和手册上说的对不上号,原理图上写的四个按键是链接到EINT16 ~ EINT19 的,对应的GPIO口也是GPL 端口,但是手册上说是GPN口,并且用程序配置为GPN口去控制LED还真是能控制,我也不太清楚什么情况,先用着吧,反正原理一样,有机会再请教大佬。
#define GPKCON0 (*(volatile unsigned long *)0x7F008800)
#define GPKDAT (*(volatile unsigned long *)0x7F008808)

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

void main()
{
    int dat = 0;

    // 配置GPK4-7为输出功能
    GPKCON0 = 0x11110000;

    // 所有LED熄灭
    GPKDAT = 0x000000f0;

    // 配置GPN为输入功能
    GPNCON = 0;

    // 轮询的方式查询按键事件
    while(1)
    {
        dat = GPNDAT;

        if(dat & (1<<0))                // KEY1被按下,则LED1亮,否则LED1灭
            GPKDAT |= 1<<4;
        else
            GPKDAT &= ~(1<<4);

        if(dat & (1<<1))                // KEY2被按下,则LED2亮,否则LED2灭
            GPKDAT |= 1<<5;
        else
            GPKDAT &= ~(1<<5);

        if(dat & (1<<2))                // KEY3被按下,则LED3亮,否则LED3灭
            GPKDAT |= (1<<6);
        else
            GPKDAT &= ~(1<<6);

        if(dat & (1<<3))                // KEY4被按下,则LED4亮,否则LED4灭
            GPKDAT |= 1<<7;
        else
            GPKDAT &= ~(1<<7);

    }
}