开发板——X210开发板裸机开发流程与细节(总结篇)

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

内容总结

本文以“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键让开发板保持开机状态(如果供电置锁则不需要一直按着),开机后就会运行镜像文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天糊土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值