硬件环境tiny6410
-
- 汇编实现:
.global _start
_start:
// 把外设的基地址告诉CPU
ldr r0, =0x70000000 //对于6410来说,内存(0x00000000~0x60000000),外设(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);
}
}