流程总结
本文以“LED流水灯”为例,说明在X210开发板上进行裸机开发的流程。
步骤一:搭建嵌入式Linux开发环境
在虚拟机中安装与配置Linux系统,并安装交叉编译工具链;
在win主机上安装dnw软件、九鼎烧写SD卡软件等内容。
步骤二:编写并编译相关文件
涉及哪些文件、如何编写等内容。
步骤三:将镜像文件下载至开发板
如何将镜像文件下载至SD卡,然后让开发板从SD卡启动?
如何利用USB配合dnw软件,将镜像文件下载至开发板?
一、搭建嵌入式Linux开发环境
1、安装与配置Linux系统
步骤略,Linux安装包资源见链接。
2、安装交叉编译工具链
(1)linux中安装软件的方法
第一种:在线安装。比如使用命令“apt-get install vim”来安装vim软件。
第二种:自己下载安装包来安装。缺陷是下载的安装包和系统不一定匹配。
第三种:利用源代码安装。
这里采用第二种方式安装交叉编译工具链,安装包资源见链接。
(2)交叉编译工具链的选择原则三星官方在开发S5PV210时,使用的交叉编译工具链是arm-2009q3这个版本,因此我们也选择这个版本,以避免出现古怪的问题。
(3)交叉编译工具链的安装步骤步骤1:打开虚拟机,创建/usr/local/arm文件夹。
步骤2:利用共享文件夹将arm-2009q3.tar.bz2复制到Linux系统中。
步骤3:利用命令“tar -jxvf arm-2009q3.tar.bz2”进行解压。
至此工具链安装完毕,应用程序安装在/usr/local/arm/arm-2009q3/bin目录。
步骤4:在/usr/local/arm/arm-2009q3/bin目录执行“./arm-none-linux-gnueabi-gcc -v”,如果在输出中有“gcc version 4.4.1 ”字样,即表示安装成功。
(4)将交叉编译工具链导出到环境变量环境变量就是操作系统的全局变量。每一个环境变量对操作系统来说都是唯一的,它的名字和所代表的意义都是唯一的。其中PATH这个环境变量,表示系统在查找可执行程序时会搜索的路径范围。
1)在当前用户的当前终端导出
在终端使用命令“export PATH=/usr/local/arm/arm-2009q3/bin:$PATH”后,在该终端下可以不带路径地执行交叉编译工具链里的命令,但关闭此终端再打开另外一个终端就失效了。
2)在当前用户的所有终端导出
在~/.bashrc中添加export PATH=/usr/local/arm/arm-2009q3/bin:$PATH 即可。但要注意,我们导出这个环境变量是在当前用户,登录到其他用户下是没用的。
(5)为交叉编译工具链创建符号链接
这步操作仅仅为了缩短某些命令的长度,可以将这些操作写成mk-arm-linux-.sh脚本。ln arm-none-linux-gnueabi-addr2line -s arm-linux-addr2line ln arm-none-linux-gnueabi-ar -s arm-linux-ar ln arm-none-linux-gnueabi-as -s arm-linux-as ln arm-none-linux-gnueabi-c++ -s arm-linux-c++ ln arm-none-linux-gnueabi-c++filt -s arm-linux-c++filt ln arm-none-linux-gnueabi-cpp -s arm-linux-cpp ln arm-none-linux-gnueabi-g++ -s arm-linux-g++ ln arm-none-linux-gnueabi-gcc -s arm-linux-gcc ln arm-none-linux-gnueabi-gcc-4.4.1 -s arm-linux-gcc-4.4.1 ln arm-none-linux-gnueabi-gcov -s arm-linux-gcov ln arm-none-linux-gnueabi-gdb -s arm-linux-gdb ln arm-none-linux-gnueabi-gdbtui -s arm-linux-gdbtui ln arm-none-linux-gnueabi-gprof -s arm-linux-gprof ln arm-none-linux-gnueabi-ld -s arm-linux-ld ln arm-none-linux-gnueabi-nm -s arm-linux-nm ln arm-none-linux-gnueabi-objcopy -s arm-linux-objcopy ln arm-none-linux-gnueabi-objdump -s arm-linux-objdump ln arm-none-linux-gnueabi-ranlib -s arm-linux-ranlib ln arm-none-linux-gnueabi-readelf -s arm-linux-readelf ln arm-none-linux-gnueabi-size -s arm-linux-size ln arm-none-linux-gnueabi-sprite -s arm-linux-sprite ln arm-none-linux-gnueabi-strings -s arm-linux-strings ln arm-none-linux-gnueabi-strip -s arm-linux-strip
3、usb启动方式
(1)dnw软件的介绍
dnw是三星公司编写的一款绿色软件,它通过USB线将电脑主机中的镜像文件下载并烧写至开发板。相关软件资源见链接。
(2)dnw驱动的安装步骤
dnw软件可以直接打开使用,但前提是已经安装dnw驱动,相关资源见链接。
步骤1:破解数字签名
1)打开“dnw驱动\dnw_driver_win7-64\dseo13b.exe”。
2)选择Enable Text Mod—>Next—>确定。
3)选择Sign a System File—>Next,然后输入无签名的驱动程序文件(即secbulk.inf文件)所在的全路径(这里是C:\Users\XJH\Desktop\dnw驱动\dnw_driver_win7-64\inf64),然后点击OK,提示成功后点击确定,退出软件。
P.S.经过上面步骤之后,进行下面的步骤2时,驱动还是安装不上,提示“第三方 INF不包含数字签名信息”。后来参考win10禁止数字签名进行操作之后,dnw驱动得以成功安装。
步骤2:安装dnw驱动
4)重启电脑,使用usb线连接电脑主机和开发板,并设置开发板为usb启动方式(将启动方式选择开关拨到近POWER键的一端)。
5)打开设备管理器,长按POWER启动开发板,然后更新SEC S5PC110 Test B/D.的驱动程序,这里选择C:\Users\XJH\Desktop\dnw驱动\dnw_driver_win7-64\inf64\secbulk.inf文件。
步骤3:确认安装成功
6)如果开发板开机从usb启动后,设备管理器那里显示已经安装好设备,而且dnw软件界面的顶端显示[COM:x] [USB:OK] [ADDR:0xd0020010],则说明dnw驱动已经安装好。
(3)dnw软件的使用步骤
注意到X210开发板使用了软开关,但是目前还没有涉及操作系统,没有去处理开关,因此所有的裸机实验中必须一直按着POWER键才能保持开机,一旦手抬起来就会关机。
步骤1:使用usb线连接电脑主机和开发板
要确保已经成功安装dnw驱动。
步骤2:设置开发板的启动方式为usb启动
即把启动方式选择开关拨到近POWER键的一端。
步骤3:配置镜像文件下载地址为0xd0020010
打开dnw软件,选择“Configuration->options”,设置Download Address为0xd0020010,它表示将镜像文件下载到IRAM的0xd0020010。以usb启动方式做裸机实验时,镜像文件不需要16字节的校验头,所以直接将镜像文件下载到0xd0020010这个地址。
步骤4:下载镜像文件至开发板注意一直按着POWER键,然后在dnw软件的菜单USB Port中选择Transmit—>Transmit,然后选择要下载的镜像文件,下载完后通过观察实验现象来进行验证。比如下载链接文件夹里的led.bin文件,可以观察到三个led灯的流水灯效果。
(4)usb启动方式的本质
usb启动方式主要用来调试程序,它其实是把裸机程序当作BL1来使用。
4、SD卡启动方式
(1)SD卡启动必做的设置
要想以SD卡启动方式来调试程序,必须先完成以下的设置。
设置1:设置开发板以选择SD卡方式启动
即将启动方式选择开关拨向远离POWER键的一端。
设置2:破坏板载inand中的启动代码
S5PV210第一层启动,是从SD0通道(即板载的inand)启动的,当inand启动做校验和失败才会转为SD2通道(即SD卡,接近复位键的那个卡槽)启动。我们做裸机实验时,如果想通过SD卡来提供裸机程序镜像,则需要先破坏板载inand中的uboot,才可以强迫开发板从SD2通道启动,从而执行我们的裸机程序。
破坏方法见博文:如何破坏开发板iNand中的uboot。
(2)将镜像文件下载至SD卡的方法
将镜像文件下载到SD卡有以下两种方法。这里的“下载”不是将镜像文件210.bin直接复制到SD卡中,而是通过某些技术手段将镜像文件放到SD卡中的某些合适的扇区位置。
方法一:在windows系统中利用九鼎官方提供的工具来下载
P.S.这个软件之前用来烧写uboot至SD卡,那能不能用来烧写裸机代码镜像文件呢?我个人觉得可以的,因为存储在SD卡中的uboot镜像应该也有16字节的校验头,而存储在SD卡中的210.bin也有16字节的检验头,而IROM中对SD卡的读取方式是一定的(从哪个地方开始读,读取多少内容)。现在唯一的问题是,九鼎官方提供的工具,它是不是和write2sd文件一样,也是把镜像文件下载至sd卡的第一扇区呢?如果是则没问题,可以在windows系统中利用这个工具下载逻辑代码镜像文件。(经验证,可以!)
首先将SD卡插入电脑主机,然后以管理员身份打开软件(软件资源见链接),点击Browse选择待下载的镜像文件,点击Add,然后点击START。下载完成后,将SD卡插入开发板的SD2口即可。
方法二:在linux系统中利用write2sd文件下载
write2sd文件是一个linux脚本,负责将由sd卡启动的镜像文件210.bin烧写至sd卡第一扇区。
#!/bin/sh sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
没有插SD卡之前,linux系统的/dev/sd*内容如下:
root@ubuntu:/dev# ls sd* sda sda1 sda2 sda5 root@ubuntu:/dev#
将SD卡插入电脑主机后,在虚拟机的菜单中下拉“虚拟机”,选择“可移动设备”,选择SD卡对应的设备,此时linux系统的/dev/sd*内容如下。由此可知,SD卡对应的设备文件是/dev/sdb(为何不是/dev/sdb1)。
root@ubuntu:/dev# ls sd* sda sda1 sda2 sda5 sdb sdb1 root@ubuntu:/dev# df sdb -h 文件系统 容量 已用 可用 已用% 挂载点 udev 490M 4.0K 490M 1% /dev root@ubuntu:/dev# df sdb1 -h 文件系统 容量 已用 可用 已用% 挂载点 /dev/sdb1 7.6G 16K 7.6G 1% /media/xjh/2FD6-2155 root@ubuntu:/dev# ls -l sdb1 brw-rw---- 1 root disk 8, 17 Aug 30 17:23 sdb1 root@ubuntu:/dev# ls -l sdb brw-rw---- 1 root disk 8, 16 Aug 30 17:23 sdb
确定210.bin和write2sd文件在同一路径后,执行命令“./write2sd ”可将x210.bin下载至SD卡的第一个扇区。
root@ubuntu:/dev# cd /home/xjh/iot/embedded_basic/arm_basic/leds流水灯 root@ubuntu:/home/xjh/iot/embedded_basic/arm_basic/leds流水灯# ls 210.bin led.bin led.elf led_elf.dis led.o led.S Makefile mkv210_image.c mkx210 write2sd root@ubuntu:/home/xjh/iot/embedded_basic/arm_basic/leds流水灯# make clean rm *.o *.elf *.bin *.dis mkx210 -f root@ubuntu:/home/xjh/iot/embedded_basic/arm_basic/leds流水灯# make arm-linux-gcc -o led.o led.S -c arm-linux-ld -Ttext 0x0 -o led.elf led.o arm-linux-objcopy -O binary led.elf led.bin arm-linux-objdump -D led.elf > led_elf.dis gcc mkv210_image.c -o mkx210 ./mkx210 led.bin 210.bin root@ubuntu:/home/xjh/iot/embedded_basic/arm_basic/leds流水灯# ls 210.bin led.bin led.elf led_elf.dis led.o led.S Makefile mkv210_image.c mkx210 write2sd root@ubuntu:/home/xjh/iot/embedded_basic/arm_basic/leds流水灯# cat write2sd #!/bin/sh sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1 root@ubuntu:/home/xjh/iot/embedded_basic/arm_basic/leds流水灯# ./write2sd 记录了32+0 的读入 记录了32+0 的写出 16384字节(16 kB)已复制,1.01548 秒,16.1 kB/秒 root@ubuntu:/home/xjh/iot/embedded_basic/arm_basic/leds流水灯#
(3)以SD卡启动方式运行镜像文件
将镜像文件下载至SD卡之后,将SD卡插进SD2通道(接近复位键的那个卡槽),设置以SD卡方式启动(将启动方式选择开关拨向远离POWER键的一端),然后要一直按着POWER键让开发板保持开机状态,开机后就会运行镜像文件。比如将链接文件夹里的210.bin文件下载至SD卡,以SD卡启动方式运行后,可以观察到三个led灯的流水灯效果。
二、编写相关文件
1、Makefile文件
LED流水灯例子所使用的Makefile文件内容如下:
led.bin: led.o arm-linux-ld -Ttext 0x0 -o led.elf $^arm-linux-objcopy -O binary led.elf led.binarm-linux-objdump -D led.elf > led_elf.disgcc mkv210_image.c -o mkx210./mkx210 led.bin 210.bin%.o : %.Sarm-linux-gcc -o $@ $< -c%.o : %.carm-linux-gcc -o $@ $< -c .PHONY clean clean:rm *.o *.elf *.bin *.dis mkx210 -f
(1)链接地址的指定
链接地址是指希望程序下载到开发板的地址。分析可知,该Makefile文件直接指定了链接地址为0x0:
arm-linux-ld -Ttext 0x0 -o led.elf $^
也有其他例子的Makefile文件通过链接脚本link.lds来指导链接,在链接脚本指定链接地址:
arm-linux-ld -Tlink.lds -o led.elf $^
链接脚本link.lds内容如下(根据实际情况会有变化):
SECTIONS {. = 0xd0024000; //链接地址,表示我们希望该程序下载到该地址中去运行。//但实际可能不是下载到这个位置,因此可能需要重定位。.text : {start.o* (.text)}.data : {* (.data)}bss_start = .; .bss : {* (.bss)}bss_end = .; }
2、mkv210_image.c文件
该文件由友善之臂的裸机教程提供,代码解释见博文:mkv210_image.c文件详解,
该文件将usb启动时使用的镜像文件led.bin,转换成sd卡启动时使用的镜像文件210.bin。
3、write2sd 文件
它是一个linux脚本文件,负责将由sd卡启动的镜像文件210.bin烧写至sd卡第一扇区。
文件的全部内容如下,相关代码含义见博文:dd命令:用于读取、转换并输出数据。
#!/bin/sh sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
4、主要文件
这里的主要文件,是指操控硬件寄存器的文件。
(1)简单情形的文件
在LED流水灯例子里,指的是用汇编语言编写的led.S文件,文件内容如下。
/** 文件名: led.s * 描述: 流水灯*/#define GPJ0CON 0xE0200240 #define GPJ0DAT 0xE0200244.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了 _start:// 第一步:把所有引脚都设置为输出模式,代码不变ldr r0, =0x11111111 // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数ldr r1, =GPJ0CON // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令str r0, [r1] // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去// 要实现流水灯,只要在主循环中实现1圈的流水显示效果即可 flash:// 第1步:点亮LED1,其他熄灭//ldr r0, =((0<<3) | (1<<4) | (1<<5)) // 清清楚楚的看到哪个灭,哪个是亮ldr r0, =~(1<<3)ldr r1, =GPJ0DATstr r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮// 然后延时bl delay // 使用bl进行函数调用// 第2步:点亮LED2,其他熄灭 ldr r0, =~(1<<4)ldr r1, =GPJ0DATstr r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮// 然后延时bl delay // 使用bl进行函数调用// 第3步:点亮LED3,其他熄灭 ldr r0, =~(1<<5)ldr r1, =GPJ0DATstr r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮// 然后延时bl delay // 使用bl进行函数调用b flash// 延时函数:函数名:delay delay:ldr r2, =9000000ldr r3, =0x0 delay_loop: sub r2, r2, #1 //r2 = r2 -1cmp r2, r3 // cmp会影响Z标志位,如果r2等于r3则Z=1,下一句中eq就会成立bne delay_loopmov pc, lr // 函数调用返回
(2)复杂情形的文件
当led.S文件内容比较复杂时,可以将部分代码写成函数放在另外一个文件中,然后在led.S文件中进行函数调用。比如将 led.S 文件重组为start.S文件、led.c文件等文件,其中start.S文件负责流程控制,而 led.c文件内容是一些函数的定义,start.S文件调用led.c文件中的函数。
start.S文件内容如下:
/** 文件名: led.S * 描述: 演示汇编设置栈并且调用C语言程序来点亮LED*/#define WTCON 0xE2700000 #define SVC_STACK 0xd0037d80.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了 _start:// 第1步:关看门狗(向WTCON的bit5写入0即可)ldr r0, =WTCONldr r1, =0x0str r1, [r0]// 第2步:设置SVC栈//因为接下来将要调用C语言函数,因此需要在函数调用语句之前完成SVC栈的设置。ldr sp, =SVC_STACK// 从这里之后就可以开始调用C程序了bl led_blink // led_blink是C语言实现的一个函数// 汇编最后的这个死循环不能丢b .
led.c文件内容如下:
#define GPJ0CON 0xE0200240 #define GPJ0DAT 0xE0200244void delay(void) { // volatile 让编译器不要优化,这样才能真正的减,才能消耗时间,实现delayvolatile unsigned int i = 900000; while (i--); }// 该函数要实现led闪烁效果 void led_blink(void) {// led初始化,也就是把GPJ0CON中设置为输出模式unsigned int *p = (unsigned int *)GPJ0CON;unsigned int *p1 = (unsigned int *)GPJ0DAT;*p = 0x11111111;while (1){// led亮*p1 = ((0<<3) | (0<<4) | (0<<5));// 延时delay();// led灭*p1 = ((1<<3) | (1<<4) | (1<<5));// 延时delay();} }
另外需要更改Makefile文件中规则所依赖的文件:
led.bin: start.o led.o #更改依赖文件# 操作的内容不变%.o : %.Sarm-linux-gcc -o $@ $< -c -nostdlib #这里添加了-nostdlib这个编译选项 %.o : %.carm-linux-gcc -o $@ $< -c -nostdlib .PHONY clean clean:rm *.o *.elf *.bin *.dis mkx210 -f
三、编译相关文件
上述文件编写好之后,执行make指令即可得到led.bin镜像文件和210.bin镜像文件。
led.bin文件是usb启动方式的镜像文件,210.bin文件是sd卡启动方式的镜像文件。
相比于led.bin文件,210.bin文件多了16字节的校验头。