以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
内容总结
本文以“LED闪烁”为例,说明在X210开发板上进行裸机开发的流程。
步骤1:搭建嵌入式Linux开发环境
- 在虚拟机中安装与配置Linux系统,并安装交叉编译工具链;
- 在win主机上安装dnw软件、九鼎烧写SD卡软件等内容。
步骤2:编写与编译文件
- 涉及哪些文件、如何编写等内容。
步骤3:将镜像文件下载至开发板
- 讲述如何将镜像文件下载至SD卡,然后让开发板从SD卡启动?
- 讲述如何利用USB配合dnw软件,将镜像文件下载至开发板。
因为串口通信——uart stdio的移植(将printf的输出显示从屏幕重定位到串口)将printf函数的输出重定位到了串口0(靠近网口的那个串口),而后面课程的裸机代码中的ptintf函数也是如此,所以但凡涉及printf函数输出显示的实验,都要需要把串口线接在串口0。(反过来,如果你将printf函数重定位到串口2,则使用printf函数时,需要把串口线接在串口2。)
IROM中其实已经把串口0和串口2初始化了,而且IROM选择通过串口2输出以下信息,所以我们需要将串口线接在串口2;如果我们将串口线接在串口0,则不会有下面的信息。
SD checksum Error
SD Init Error
Uart negotiation Error
Insert an OTG cable into the connector!
另外uboot中也是选择串口2输出信息的(也可以通过修改代码来选择串口0),所以uboot启动前要把串口线接在串口2。
一、搭建嵌入式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
二、编写与编译文件
LED闪烁的完整案例见链接,文件组织结构如下图:
1、编写主要文件
这里说的主要文件,指的是涉及操控硬件寄存器的文件,比如简单情形的led.S文件,或者复杂情形的start.S文件、led.c文件。
(1)简单情形的 led.S 文件
比如LED流水灯案例中的 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, =~(1<<3)
ldr r1, =GPJ0DAT
str r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
// 然后延时
bl delay // 使用bl进行函数调用
// 第2步:点亮LED2,其他熄灭
ldr r0, =~(1<<4)
ldr r1, =GPJ0DAT
str r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
// 然后延时
bl delay // 使用bl进行函数调用
// 第3步:点亮LED3,其他熄灭
ldr r0, =~(1<<5)
ldr r1, =GPJ0DAT
str r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
// 然后延时
bl delay // 使用bl进行函数调用
b flash
//延时函数,函数名为delay
delay:
ldr r2, =9000000
ldr r3, =0x0
delay_loop:
sub r2, r2, #1 // r2 = r2 -1
cmp r2, r3 // cmp会影响Z标志位,如果r2等于r3则Z=1,下一句中eq就会成立
bne delay_loop
mov pc, lr // 函数调用返回
值得注意的是,用汇编写的函数,末尾应该添加 “mov pc,lr” 语句。
(2)复杂情形的 start.S 、led.c 等文件
当 led.S 文件内容比较复杂时,可以将部分代码写成函数放在另外一个文件中,然后在 led.S 文件中进行函数调用即可。比如将 led.S 文件重组为start.S文件、led.c文件等文件,其中start.S文件负责流程控制,而 led.c文件内容则是一些函数的定义,然后start.S文件调用led.c文件中的函数。
比如“LED闪烁”案例中(见上面链接)的 start.S文件 内容如下:
/*
* 文件名:start.S
* 作者: XJH
* 描述: LED闪烁
*/
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第0步:开发板置锁
ldr r0, =0xE010E81C
ldr r1, [r0]
ldr r2, =0x301
orr r1, r1, r2
str r1, [r0]
// 第1步:关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
// 第2步:设置SVC栈
ldr sp, =SVC_STACK
// 第3步:开/关icache
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0;
// 第4步:初始化ddr
bl sdram_asm_init
// 第5步:重定位
// adr指令用于加载_start当前运行地址
adr r0, _start // adr加载时就叫短加载
// ldr指令用于加载_start的链接地址:0xd0024000
ldr r1, =_start // ldr加载时如果目标寄存器是pc就叫长跳转,如果目标寄存器是r1等就叫长加载
// bss段的起始地址
ldr r2, =bss_start // 就是我们重定位代码的结束地址,重定位只需重定位代码段和数据段即可
cmp r0, r1 // 比较_start的运行时地址和链接地址是否相等
beq clean_bss // 如果相等说明不需要重定位,所以跳过copy_loop,直接到clean_bss
// 如果不相等说明需要重定位,那么直接执行下面的copy_loop进行重定位
// 重定位完成后继续执行clean_bss。
// 用汇编来实现的一个while循环
copy_loop:
ldr r3, [r0], #4 // 源
str r3, [r1], #4 // 目的 这两句代码就完成了4个字节内容的拷贝
cmp r1, r2 // r1和r2都是用ldr加载的,都是链接地址,所以r1不断+4总能等于r2
bne copy_loop
// 清bss段,其实就是在链接地址处把bss段全部清零
clean_bss:
ldr r0, =bss_start
ldr r1, =bss_end
cmp r0, r1 // 如果r0等于r1,说明bss段为空,直接下去
beq run_on_dram // 清除bss完之后的地址
mov r2, #0
clear_loop:
str r2, [r0], #4 // 先将r2中的值放入r0所指向的内存地址(r0中的值作为内存地址),
cmp r0, r1 // 然后r0 = r0 + 4
bne clear_loop
run_on_dram:
// 长跳转到led_blink开始第二阶段
ldr pc, =led_blink // ldr指令实现长跳转
//汇编最后的这个死循环不能丢
b .
led.c 文件内容如下:
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
void delay(void)
{ // volatile 让编译器不要优化,这样才能真正的减,才能消耗时间,实现delay
volatile 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();
}
}
2、编写其他文件
(1)编写 Makefile 文件
LED流水灯项目中的Makefile文件内容如下:
led.bin: led.o
arm-linux-ld -Ttext 0x0 -o led.elf $^
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
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c
.PHONY clean
clean:
rm *.o *.elf *.bin *.dis mkx210 -f
LED闪烁项目中的Makefile文件内容如下:
led.bin: start.o led.o #更改依赖文件
arm-linux-ld -Ttext 0x0 -o led.elf $^
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
%.o : %.S
arm-linux-gcc -o $@ $< -c -nostdlib #这里添加了-nostdlib这个编译选项
%.o : %.c
arm-linux-gcc -o $@ $< -c -nostdlib
.PHONY clean
clean:
rm *.o *.elf *.bin *.dis mkx210 -f
(2)编写 link.lds 链接脚本
链接地址,即希望将来把程序下载到哪个地址。
这个地址可以由 Makefile文件直接指定,比如下面语句指定链接地址为0x0:
arm-linux-ld -Ttext 0x0 -o led.elf $^
Makefile用 -Ttext 0x0 指定链接地址为0x0,这意味着我们认为这个程序将来会放在0x0地址中运行,但实际上运行时的地址是0xd0020010(我们用dnw下载时指定的下载地址)。这两个地址看似不同但实际相同,因为S5PV210内部把SRAM的地址0xd002_0010映射到地址0x0000_0000。把链接地址设为0x0000_0000,就等价于链接地址是0xd002_0010。BL0执行完后会自动跳转到0xd0020010这个地址开始运行,这个地址是CPU设计决定的。
也可以由Makefile文件通过链接脚本link.lds来指导链接,在链接脚本指定链接地址:
arm-linux-ld -Tlink.lds -o led.elf $^
链接脚本link.lds内容如下(根据实际情况会有变化):
SECTIONS
{
. = 0xd0024000; //链接地址,表示我们希望该程序下载到该地址中去运行。
//但实际可能不是下载到这个位置,因此可能需要重定位。
.text : {
start.o //这个表明程序入口在start.S文件?
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
(3)编写 mkv210_image.c文件
该文件由友善之臂的裸机教程提供,相关解释见博文mkv210_image.c文件详解。
该文件负责将usb启动时使用的镜像文件led.bin,转换成SD卡启动时使用的镜像文件210.bin。
(4)编写 write2sd 文件
该文件负责将由SD卡启动的镜像文件210.bin烧写至SD卡第1扇区开始的地方。
文件的全部内容如下,相关解释见博文dd命令:用于读取、转换并输出数据。
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
3、总结裸机代码的固定步骤
裸机代码的前几个步骤比较固定,都是一些初始化操作,而且有些操作在BL0中已经完成,这里重新再设置一遍也不会出错。另外,DDR的初始化和重定位这两个步骤看情况是否需要。
(1)供电置锁
iROM中的BL0没有设置开发板供电置锁。
如果我们不软件进行这个步骤,则裸机课程实验中得一直按着POWER键。
// 第0步:开发板置锁
ldr r0, =0xE010E81C
ldr r1, [r0]
ldr r2, =0x301
orr r1, r1, r2
str r1, [r0]
(2)关看门狗
iROM中的BL0已经关了看门狗,但我们这里重新设置一遍也没有错。
开启看门狗是为了防止机器故障时自动复位。如果开启看门狗后,没有及时去喂狗,则系统会自动复位。没有操作系统前,喂狗这个操作需要编写代码完成,因此为了减少编码工作,这里选择直接把看门狗关闭。(其实BL0已经关了看门狗,这里重新设置一遍也没事。)
// WTCON(0xE2700000),其中bit5是看门狗的开关:0代表关,1代表开
// 第1步:关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
(3)初始化时钟
iROM中的BL0已经初始化了时钟,但我们这里重新设置一遍也没有错。
这部分的内容,见博客时钟系统——S5PV210的时钟系统的理论及操作。
// 第2步:初始化时钟
bl clock_init
(4)设置SVC栈
C语言的运行需要一定的条件,这些条件由汇编来提供。C语言运行时主要是需要栈。比如C语言中的局部变量都是用栈来实现的。如果汇编部分没有给C部分设置合理的栈地址,那么C代码中定义的局部变量就会落空,整个程序就死掉了。
我们现在要设置栈,不可能也没有必要去设置所有的栈,我们先要设置自己的模式,然后设置自己的模式下的栈到合理合法的位置即可。
我们如何访问SVC模式下的SP呢?很简单,先把模式设置为SVC,再直接操作SP。但是因为我们复位后就已经是SVC模式了,所以直接设置SP即可。
由于栈必须是当前一段可用的内存(这段内存必须已经初始化,而且这段内存只会被我们用作栈,不会被其他程序征用),当前CPU刚启动时,外部的SDRAM尚未初始化,目前可用的内存只有内部的SRAM(因为它不需初始化即可使用)。因此只能在SRAM中找一段内存来作为SVC的栈。结合iROM_application_note中的memory map,可知SVC栈应该设置为0xd0037D80。
// 第3步:设置SVC栈
ldr sp, =SVC_STACK //#define SVC_STACK 0xd0037d80
(5)开关icache
iROM中的BL0已经打开了icache,我们这里重新设置一遍也没有错。
寄存器和SDRAM之间的读取速度差异太大,SDRAM的读取速度远不能满足寄存器的需要,没有cache的话,会拉低系统的整体速度。210内部有32KB icache和32kb dcache,icache是用来缓存指令的;dcache是用来缓存数据的。
// 第4步:开/关icache
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0;
(6)DDR的初始化
具体内容见博文S5PV210——SDRAM的初始化。
(7)代码的重定位(涉及清bss段)
具体内容见博文重定位的简介与操作。
4、编译文件
上述文件编写好之后,在“LED闪烁”文件夹目录下执行make指令,则会生成 usb 启动方式的镜像文件 led.bin,以及SD卡启动方式的镜像文件 210.bin。相比于led.bin文件,210.bin文件多了16字节的校验头。
三、将镜像文件下载至开发板
将镜像文件下载至开发板,有usb启动方式和SD卡启动方式,可以根据实际需要任选其一。
1、usb启动方式
usb启动方式需要使用dnw软件,相关软件资源见链接。
(1)dnw软件的简介
dnw软件是三星公司编写的一款绿色软件,它通过USB线将电脑主机中的镜像文件下载并烧写至开发板。
(2)dnw驱动的安装步骤
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,然后选择要下载的镜像文件,下载完后通过观察实验现象来进行验证。
(4)usb启动方式的本质
usb启动方式主要用来调试程序,它其实是把裸机程序当作BL1来使用。
2、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卡中的某些合适的扇区位置。
方法1:在 Linux 中利用三星提供的sd_fusing文件夹来烧写(好像只适用于烧写uboot)
方法1最后的步骤类似于下面提到的方法3,都是通过dd命令来烧写的。只不过方法1在执行dd命令前,会对SD卡进行一些分区操作。
sd_fusing文件夹中的sd_fusing.sh这个脚本的部分内容如下(具体分析见分析三星提供的sd_fusing文件夹):
#<BL1 fusing>
bl1_position=1
uboot_position=49
echo "BL1 fusing"
./mkbl1 ../u-boot.bin SD-bl1-8k.bin 8192
dd iflag=dsync oflag=dsync if=SD-bl1-8k.bin of=$1 seek=$bl1_position
rm SD-bl1-8k.bin
####################################
#<u-boot fusing>
echo "u-boot fusing"
dd iflag=dsync oflag=dsync if=../u-boot.bin of=$1 seek=$uboot_position
方法2:在 windows 中利用九鼎提供的工具来烧写(可以烧写uboot、<=16KB的裸机镜像文件)
这个软件之前用来烧写uboot至SD卡的,那能不能用来烧写裸机代码镜像文件呢?
这需要了解九鼎提供的这个工具的内部细节,但奈何没有提供源码。
猜想:如果这个工具只是把一个无论多大的镜像文件下载到SD卡第1扇区开始的区域,当这个文件大于16KB时,就可能存在问题。因为BL0只取SD卡第1扇区开始的16KB内容来执行,剩下的(镜像文件大小-16KB)的内容还躺在SD卡中,如果前16KB没有完成将SD卡中剩下的内容拷贝到IRAM中的工作,则会执行不完全(即只执行镜像文件前16KB的内容)。但是uboot也大于16KB,这个工具把整个uboot下载到SD卡第1扇区开始的区域后,为何uboot也可以正常启动呢?我猜想这是因为uboot的前16KB完成了uboot的重定位。而裸机镜像文件如果大于16KB,而前16KB中又没有相关的重定位或者拷贝操作,则会出错。观察所有裸机的镜像文件210.bin,其大小好像都没有超过16KB的,都是等于16KB(因为mkv210_image.c这个文件规定210.bin文件大小就是16KB,这侧面说明了所有裸机文件不能超过16KB)。
这个猜想应该是正确的,正如开发板——X210开发板的SD卡启动方式中提到的三星推荐的启动方式、分散加载(大于16KB)、uboot的启动方式。
首先将SD卡插入电脑主机,然后以管理员身份打开软件(软件资源见链接),点击Browse选择待下载的镜像文件,点击Add,然后点击START。下载完成后,将SD卡插入开发板的SD2口即可。
方法3:在Linux系统中利用write2sd文件下载
write2sd文件负责将SD卡启动的镜像文件210.bin烧写至sd卡第一扇区(这里说的是形式1),该文件内容如下:
#形式1(普通的裸机镜像文件:只有1个,且小于16KB)
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
#形式2(分散加载情形:大于16KB,需要分割成两个)
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45
首先我们来确认一下SD卡对应的设备文件。没有插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键让开发板保持开机状态(如果供电置锁则不需要一直按着),开机后就会运行镜像文件。