u-boot移植第一弹——制作可用的BL1

我的BL1源代码来源于网路,博客地址是http://blog.csdn.net/xiaojiaohuazi/article/details/8265757。在这里记录下自己的移植心得。

         为什么要制作这个BL1呢,对于官方以及很多人说的它是u-boot启动的第二阶段,这里我不做过多说明,我想说的是,这个BL1可以让你对内存操作的理解更加深入,对于后面移植u-boot的理解有帮助。

         实际上在BL1阶段只需要做串口和内存的初始化就可以了,初始化串口的目的是调试使用,这个是必须要的;初始化内存那不用说了,你想拷贝代码,不初始化内存怎么能行的。

         但是在这里我还加入了一个LED的操作代码,为什么要加入呢,对于学习一个东西,不能说会移植了就算学会了,我想有问题的时候会调试才是更重要的,在开发时也是如此,有了调试手段就有了查错的手段,那么找问题就可以很方便的定位了。所以加入这个LED的控制目的也就是为了调试使用,这也是看到网上有人说过,仔细想想,非常好用,O(∩_∩)O。

         首先看看我的BL1的所有文件:

这里只说明移植的时候涉及到的文件,build、main.c、mem_setup.S、s5pv210.h、start.S这几个文件。其中build是我加入的一个文件,目的在操作编译烧写的时候减少输入命令,build文件的内容如下:

#!/bin/sh
make
./mkv210_image bl.bin blSD2.bin
dd iflag=dsync oflag=dsync if=blSD2.bin of=/dev/sdc seek=1

做的工作就是编译,制作BL1,烧写到SD卡。

这个BL1启动的入口也是在start.S文件,看看做的是什么:

.text

.global _start

_start:

	bl mem_ctrl_asm_init

	ldr sp, =0xD0035400

	bl main

loop:

	b loop

第一步就是初始化内存也就是进入mem_ctrl_asm_init这个函数进行执行,这个函数的代码如下:

mem_ctrl_asm_init:
	/* DMC0 Drive Strength (Setting 2X) */
	
	ldr	r0, =ELFIN_GPIO_BASE

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_0DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_1DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_2DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_3DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_4DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_5DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_6DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_7DRV_SR_OFFSET]

	ldr	r1, =0x00002AAA
	str	r1, [r0, #MP1_8DRV_SR_OFFSET]


	/* DMC1 Drive Strength (Setting 2X) */
	
	ldr	r0, =ELFIN_GPIO_BASE
	
	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_0DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_1DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_2DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_3DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_4DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_5DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_6DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_7DRV_SR_OFFSET]

	ldr	r1, =0x00002AAA
	str	r1, [r0, #MP2_8DRV_SR_OFFSET]
	
	/* DMC0 initialization at single Type*/
	ldr	r0, =APB_DMC_0_BASE

	ldr	r1, =0x00101000				@PhyControl0 DLL parameter setting, manual 0x00101000
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00000086				@PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0x00101002				@PhyControl0 DLL on
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00101003				@PhyControl0 DLL start
	str	r1, [r0, #DMC_PHYCONTROL0]

find_lock_val:
	ldr	r1, [r0, #DMC_PHYSTATUS]		@Load Phystatus register value
	and	r2, r1, #0x7
	cmp	r2, #0x7				@Loop until DLL is locked
	bne	find_lock_val
	
	and	r1, #0x3fc0 
	mov	r2, r1, LSL #18
	orr	r2, r2, #0x100000
	orr	r2 ,r2, #0x1000	
		
	orr	r1, r2, #0x3				@Force Value locking
	str	r1, [r0, #DMC_PHYCONTROL0]

#if 0	/* Memory margin test 10.01.05 */
	orr	r1, r2, #0x1				@DLL off
	str	r1, [r0, #DMC_PHYCONTROL0]
#endif
	/* setting DDR2 */
	ldr	r1, =0x0FFF2010				@ConControl auto refresh off
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =DMC0_MEMCONTROL			@MemControl BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	
	ldr	r1, =DMC0_MEMCONFIG_0			@MemConfig0 256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
	str	r1, [r0, #DMC_MEMCONFIG0]

	ldr	r1, =DMC0_MEMCONFIG_1			@MemConfig1
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =0xFF000000				@PrechConfig
	str	r1, [r0, #DMC_PRECHCONFIG]
	
	ldr	r1, =DMC0_TIMINGA_REF			@TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
	str	r1, [r0, #DMC_TIMINGAREF]
	
	ldr	r1, =DMC0_TIMING_ROW			@TimingRow	for @200MHz
	str	r1, [r0, #DMC_TIMINGROW]

	ldr	r1, =DMC0_TIMING_DATA			@TimingData	CL=3
	str	r1, [r0, #DMC_TIMINGDATA]
	
	ldr	r1, =DMC0_TIMING_PWR			@TimingPower
	str	r1, [r0, #DMC_TIMINGPOWER]

	ldr	r1, =0x07000000				@DirectCmd	chip0 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00020000				@DirectCmd	chip0 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00030000				@DirectCmd	chip0 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000542				@DirectCmd	chip0 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000442				@DirectCmd	chip0 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010780				@DirectCmd	chip0 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x07100000				@DirectCmd	chip1 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00120000				@DirectCmd	chip1 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00130000				@DirectCmd	chip1 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00100542				@DirectCmd	chip1 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00100442				@DirectCmd	chip1 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00110780				@DirectCmd	chip1 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]
		
	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]
		
	ldr	r1, =0x0FF02030				@ConControl	auto refresh on
	str	r1, [r0, #DMC_CONCONTROL]
		
	ldr	r1, =0xFFFF00FF				@PwrdnConfig
	str	r1, [r0, #DMC_PWRDNCONFIG]
		
	ldr	r1, =DMC0_MEMCONTROL				@MemControl 0x00202400		BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]

	/* DMC1 initialization */
	ldr	r0, =APB_DMC_1_BASE

	ldr	r1, =0x00101000				@Phycontrol0 DLL parameter setting
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00000086				@Phycontrol1 DLL parameter setting
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0x00101002				@PhyControl0 DLL on
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00101003				@PhyControl0 DLL start
	str	r1, [r0, #DMC_PHYCONTROL0]
find_lock_val1:
	ldr	r1, [r0, #DMC_PHYSTATUS]		@Load Phystatus register value
	and	r2, r1, #0x7
	cmp	r2, #0x7				@Loop until DLL is locked
	bne	find_lock_val1
	
	and	r1, #0x3fc0 
	mov	r2, r1, LSL #18
	orr	r2, r2, #0x100000
	orr	r2, r2, #0x1000
		
	orr	r1, r2, #0x3				@Force Value locking
	str	r1, [r0, #DMC_PHYCONTROL0]

#if 0	/* Memory margin test 10.01.05 */
	orr	r1, r2, #0x1				@DLL off
	str	r1, [r0, #DMC_PHYCONTROL0]
#endif

	/* settinf fot DDR2 */
	ldr	r0, =APB_DMC_1_BASE

	ldr	r1, =0x0FFF2010				@auto refresh off
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =DMC1_MEMCONTROL			@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]

	ldr	r1, =DMC1_MEMCONFIG_0			@MemConfig0	512MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
	str	r1, [r0, #DMC_MEMCONFIG0]

	ldr	r1, =DMC1_MEMCONFIG_1			@MemConfig1
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =0xFF000000
	str	r1, [r0, #DMC_PRECHCONFIG]

	ldr	r1, =DMC1_TIMINGA_REF			@TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
	str	r1, [r0, #DMC_TIMINGAREF]

	ldr	r1, =DMC1_TIMING_ROW			@TimingRow	for @200MHz
	str	r1, [r0, #DMC_TIMINGROW]

	ldr	r1, =DMC1_TIMING_DATA			@TimingData	CL=3
	str	r1, [r0, #DMC_TIMINGDATA]

	ldr	r1, =DMC1_TIMING_PWR			@TimingPower
	str	r1, [r0, #DMC_TIMINGPOWER]


	ldr	r1, =0x07000000				@DirectCmd	chip0 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00020000				@DirectCmd	chip0 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00030000				@DirectCmd	chip0 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000542				@DirectCmd	chip0 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000442				@DirectCmd	chip0 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010780				@DirectCmd	chip0 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x07100000				@DirectCmd	chip1 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00120000				@DirectCmd	chip1 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00130000				@DirectCmd	chip1 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110440				@DirectCmd	chip1 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00100542				@DirectCmd	chip1 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00100442				@DirectCmd	chip1 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110780				@DirectCmd	chip1 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x0FF02030				@ConControl	auto refresh on
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =0xFFFF00FF				@PwrdnConfig	
	str	r1, [r0, #DMC_PWRDNCONFIG]

	ldr	r1, =DMC1_MEMCONTROL			@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	//下面为点灯代码,用于调试
	ldr r0, =0xE0200C00  
	ldr r1, =0x11000000  
	str r1, [r0]  
	ldr r1, =0xc0  
	str r1, [r0, #4]
	//上面为点灯代码,用于调试	
	
	mov	pc, lr

可以看到我在该函数的最后增加了LED灯的代码,具体的含义请参考帖子 http://blog.csdn.net/kevinshq/article/details/7968409不同的板子LED接口可能不同,这样的话就需要修改寄存器的地址了,也可能没有LED,如果是这种情况就需要使用其他的提醒功能来代替LED了(最好是能够看到现象的)。LED代码放在这里在启动板子的时候就可以观察LED的状态来判断内存初始化这部分代码是否启动了。

好,现在回到该函数的起始部分,对于该函数已经有人做过了分析,这里我就引用之博文地址为http://blog.csdn.net/mutemob/article/details/12974483。在这之外我们需要注意一个问题,那就是下面代码的位置处


有可能你的代码红色部分为0x00202400这种格式的16进制,最好是改成上图所示的请款个,目的是为了方便修改。

从start.S文件中可以知道,第二个执行的函数为main函数,函数位置在main.c。

         main.c文件源代码请下载下来观看,这里我就不一次全部贴上来了。我们从main主函数出发,看看都做了些什么:

int main (void)
{
	__attribute__((noreturn)) void (*uboot)(void); 
	uart_init(); //串口初始化
	mem_test(); //内存测试
	printf ("copy uboot from sd to sdram ddr2\r\n"); 
	copy_uboot_to_ram(); 
	printf ("jump to u-boot image\r\n"); 
	//下面代码是控制班子上的LED,可用于调试 
	//*(volatile unsigned int *)0xE0200C00 = 0x11000000;  
	//*(volatile unsigned int *)0xE0200C04 = 0x000000c0; 
	//上面代码是控制班子上的LED,可用于调试 
	/* Jump to U-Boot imag

	uboot = (void *)0x33e00000; 
	(*uboot)(); 
	return 1;
}

开始进行了串口的初始化,然后是内存的测试,接着是u-boot的拷贝阶段,最后跳转到u-boot的位置开始执行。我的u-boot的起始地址是0x33e00000,不同的板子地址可能有所不同,请调整为自己的u-boot起始地址即可,也就是把0x33e00000换成自己的板子的地址。

       串口的初始化我的板子是Real210板子,调试口使用的是串口2,所以在网络上原来的代码基础上把他的串口初始化从串口0修改到了串口2,在源码的s5pv210.h中可以看到各寄存器的定义。串口的初始化是在文件uart.c中进行,这里我不再说明,具体的请参阅s5pv210 pdf进行对比学习。

       内存的测试函数mem_test()代码如下:

void mem_test()
{
	#define test_mem1 0x30000000
	#define test_mem2 0x33e00004
	#define test_mem3 0x3f000f08
	#define test_mem4 0x3ffff0fc
	#define test_mem5 0x3ffffffc	//内存地址是每隔4个字节存放的,所以访问时是4个字节访问
	#define test_mem6 0x40000000
	#define test_mem7 0x43e00004
	#define test_mem8 0x4f00f008
	#define test_mem9 0x4ffffff8   
	#define test_mem10 0x4ffffffc

	unsigned int *p1 = (volatile unsigned int *)test_mem1;
	unsigned int *p2 = (volatile unsigned int *)test_mem2;
	unsigned int *p3 = (volatile unsigned int *)test_mem3;
	unsigned int *p4 = (volatile unsigned int *)test_mem4;
	unsigned int *p5 = (volatile unsigned int *)test_mem5;
	unsigned int *p6 = (volatile unsigned int *)test_mem6;
	unsigned int *p7 = (volatile unsigned int *)test_mem7;
	unsigned int *p8 = (volatile unsigned int *)test_mem8;
	unsigned int *p9 = (volatile unsigned int *)test_mem9;
	unsigned int *p10 = (volatile unsigned int *)test_mem10;
	

	printf ("******s5pv210 Test********\r\n");
	printf ("********By ZheGao********\r\n");

	*p1 = 0x12345678;
	if (*p1 == 0x12345678) {
		printf ("1: equ\r\n");
	} else {
		printf ("1: not equ\r\n");
	}

	*p2 = 0x12345678;
	if (*p2 == 0x12345678) {
		printf ("2: equ\r\n");
	} else {
		printf ("2: not equ\r\n");
	}

	*p3= 0xfffffff0;
	if (*p3 == 0xfffffff0) {
		printf ("3: equ\r\n");
	} else {
		printf ("3: not equ\r\n");
	}
	*p4 = 0x12345678;
	if (*p4 == 0x12345678) {
		printf ("4: equ\r\n");
	} else {
		printf ("4: not equ\r\n");
	}

	*p5 = 0x12345678;
	if (*p5 == 0x12345678) {
		printf ("5: equ\r\n");
	} else {
		printf ("5: not equ\r\n");
	}

	*p6= 0xfffffff0;
	if (*p6 == 0xfffffff0) {
		printf ("6: equ\r\n");
	} else {
		printf ("6: not equ\r\n");
	}
	*p7 = 0x12345678;
	if (*p7 == 0x12345678) {
		printf ("7: equ\r\n");
	} else {
		printf ("7: not equ\r\n");
	}

	*p8 = 0x12345678;
	if (*p8 == 0x12345678) {
		printf ("8: equ\r\n");
	} else {
		printf ("8: not equ\r\n");
	}

	*p9= 0x12345678;
	if (*p9 == 0x12345678) {
		printf ("9: equ\r\n");
	} else {
		printf ("9: not equ\r\n");
	}
	*p10= 0x12345678;
	if (*p10 == 0x12345678) {
		printf ("10: equ\r\n");
	} else {
		printf ("10: not equ\r\n");
	}

}

从最开始宏定义可以知道测试的内存地址,这里把我板子的全部内存做了隔段测试,我的内存地址是从0x30000000到0x4fffffff总共512MB,注意在测试的时候内存地址需要隔4个字节进行测试,也就是最后一位只能取值0、4、8、c这四个值,原因是,我们的内存是32位的也就是4个字节,所以在测试访问的时候需要隔4个字节,注意不能取最后一位除0、4、8、c以外的值,否者就会死掉。

copy_uboot_to_ram()这个拷贝函数的说也参考一个博文http://blog.csdn.net/shangguobuliuhen/article/details/9844371

同样在main函数中也有LED灯的代码,不过这次换成了C语言的写法,放在这里也是为了判断代码执行的位置,进而判断代码会死在哪里,这个LED定位的思想要灵活运用,可以为你学习和调试带来极大的帮助。就在u-boot中一样我们使用串口打印信息来进行调试一个道理。

第一阶段告一段落,后面继续更新,下一个就是让u-boot能够启动并能进入控制台,过些天在写上来吧。有问题得朋友可以留言,大家共同探讨。





  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值