一:u-boot的简单移植
1.进入开发板提供的源码文件包,解压uboot源码包。
cd /home/bunfly/source_code/
tar xf uboot_tiny4412-20130729.tgz
2.进入uboot文件夹,更改uboot中tiny4412的配置文件。将225 中的Tiny4412更改为bunfly。
cd uboot_tiny4412
vim include/configs/tiny4412.h
更改255行为#define CONFIG_SYS_PROMPT "bunfly # "
3. 回到u-boot文件夹,编译u-boot
make tiny4412_config
make
4.进入sd_fuse文件夹 编译
cd sd_fuse
make
5.编译结束后生成tiny4412文件夹,进入文件夹。插入sd卡到电脑。烧写u-boot到sd卡中
cd tiny4412/
sd卡会自动加载,所以先卸载sd卡 umount /media/aaa
参看sd卡挂载点:fdisk -l 发现sd卡挂载在/dev/sdb中
烧写: ./sd_fusing.sh /dev/sdb
6.显示烧写成功后将sd插入开发板,开发板设置从sd卡启动。显示下图表示烧写成功。
二:u-boot中编写hello world 程序
U-Boot作为嵌入式Linux系统的引导,不具有标准C库中的内容。要使用printf函数,就需要u-boot中提供的。u-boot函数内容在u-boot源码文件中
的System.map文件中。System.map文件是被内核所使用的符号表。符号表是一个在符号名称与它们的存储器位置间的查询表格。符号名称可能是变量的名称或是函数名称。当要查询符号名称的位置或是特定位置的符号名称时,就会需要System.map。
查找System.map中printf中的位置 在538行, c3e114d8 T printf,使用c3e114d8就表示使用printf函数,下面是helloword函数的汇编代码实现:
1 .global main 2 main: 3 mov ip,sp 4 stmfd sp!,{fp,ip,lr} 5 sub fp,ip,#4 6 7 ldr r0, =string 8 ldr r2, haha 9 blx r2 10 11 sub sp,fp,#8 12 ldmfd sp,{fp,sp,pc} 13 14 haha: 15 .word 0xc3e114d8 16 string: 17 .asciz "hello world\n" 18 .align 2
注意:blx表示跳转到寄存器。
编译时候有三个步骤:
1.arm-none-linux-gnueabi-gcc -c hello.s -o hello.o
2.arm-none-linux-gnueabi-ld -Ttext=0x40008000 hello.o -o hello
3.arm-none-linux-gnueabi-objcopy -Ielf32-littlearm -Obinary hello hello.bin
第一步:只编译,不链接标准C库的内容,因为不需要。
第二步:链接时指定程序分配地址从40008000开始分配
第三步:去文件头,将linux文件头lelf32装换成ARM的文件头。
完成三个步骤后,通过dnw将hello.bin文件传到开发板40008000地址中。通过 go 执行代码。
开发板:dnw 40008000
宿主机:dnw hello.bin
开发板:go 40008000
结果如下图:
接下来是输出hellowrold字符用C代码实现:
1 int (*printf)(char *,...) = 0xc3e114d8; 2 int main() 3 { 4 printf("hello world\n"); 5 6 }
C语言编写相当简洁有木有。第一行定义函数指针指向u-boot中的printf函数。编译,运行过程和上面相同。
为了以后方便,编写Makefile文件,实现编译过程:
1 name=hello 2 bin=${name}.bin 3 o=${name}.o 4 tar=${name}.c 5 ${bin}:${name} 6 arm-none-linux-gnueabi-objcopy -Ielf32-littlearm -Obinary $^ $@ 7 ${name}:${o} 8 arm-none-linux-gnueabi-ld -Ttext=0x50005000 $^ -o $@ 9 ${o}:${tar} 10 arm-none-linux-gnueabi-gcc -c $^ -o $@ 11 clean: 12 rm -f ${bin} ${o} ${name}
三:LED驱动编写
首先要明白开发板的构造。开发板分为核心板和底板。核心板的电路图文件路径:schematics/Tiny4412/Tiny4412_1306_sch.pdf
底板电路图路径在:schematics/Tiny4412SDK 1306Tiny4412SDK_1306_sch.pdf
开发板数据手册在:datasheet/Exynos_4_Quad_User_Manaul_Public_REV100-0.pdf
整个编写过程都是围绕着这三个文件进行。
因为LED灯在核心板上,所以先查看核心板电路图,打开电路图,看开发板中led灯的标号分别为LED1,LED2,LED3,LED4,LED5,LED6
通过查找功能,找到LED的电路图:
可以观察到的是,LED灯的一端已经接高电平,只要给另一端加低电平LED就会被点亮。
继续查找LED1在核心板中的引脚定义。
由上图可知LED1对应的灯就是GPM4_0, 最后一步就是使用GPM4_0为关键字在芯片数据手册中查看引脚的具体使用说明了。
GPM4CON 置一输出,表示控制,置零输入,表示检测,GPM4DAT表示它的值和引脚的电压状态对于,值为一时表示高电平,值为零时表示底电平。下面是控制LED1的汇编代码:
1 .global main 2 main: 3 mov ip,sp 4 stmfd sp!,{fp,ip,lr} 5 sub fp,ip,#4 6 7 ldr r0,gpmcon 8 mov r1,#1 9 str r1,[r0] 10 11 ldr r0,gpmdat 12 mov r1,#0 13 str r1,[r0] 14 15 sub sp,fp,#8 16 ldmfd sp,{fp,sp,pc} 17 18 gpmcon: 19 .word 0x110002e0 20 gpmdat: 21 .word 0x110002e4
将GOM4CON对于位置1,GOM4DAT对应位置0。LED灯就亮了。
然后是C语言代码实现四个LED灯闪烁:
1 void (*udelay)(int) = 0xc3e25f90; 2 void abc(void) 3 { 4 volatile unsigned long *GPM4CON = 0x110002e0; 5 volatile unsigned long *GPM4DAT = 0x110002e4; 6 7 *GPM4CON = 0x1111; 8 while(1){ 9 *GPM4DAT = 0xf;//led off 10 udelay(250000); 11 *GPM4DAT = 0x0; 12 udelay(250000);//led on 13 } 14 16 }
这里用到udelay延迟函数。同样也是在System.map中查询到的,单位是微秒。
还有实现跑马灯和流水灯:
1 void (*udelay)(int) = 0xc3e25f90; 2 3 void abc(void) 3 4 { 4 5 volatile unsigned long *GPM4CON = 0x110002e0; 5 6 volatile unsigned long *GPM4DAT = 0x110002e4; 6 7 7 8 *GPM4CON = 0x1111; 8 9 unsigned long tmp = 0x0f; 9 10 while(1){ 10 11 if((tmp & 0x0f) == 0x00) 11 12 tmp =0x0f; 12 13 *GPM4DAT = tmp << 1 ; //跑马 13 14 tmp = *GPM4DAT; 14 15 udelay(250000); 15 16 } 16 17 17 18 return; 18 19 }
1 void (*udelay)(int) = 0xc3e25f90; 2 void abc(void) 3 { 4 volatile unsigned long *GPM4CON = 0x110002e0; 5 volatile unsigned long *GPM4DAT = 0x110002e4; 6 7 *GPM4CON = 0x1111; 8 unsigned long i = 0; 9 while(1){ 10 *GPM4DAT = 0xf; 11 *GPM4DAT &= ~(1 << i); 12 udelay(500000); 13 i++; 14 if(i ==4) 15 i=0; 16 } 17 18 return; 19 }