好的,经过前面两篇文章,我们已经能够编译和烧写u-boot到sd卡上了,接下来就是调试,完善功能了。
说到调试,在此之前我想的调试方法是在串口还没有初始化之前,就只能通过点灯大法来调试了; 等到串口初始化完成后,就可以通过串口打印,这样调试就方便多了。不过后来看到了两篇文章,介绍的是通过jlink来进行源码级别的调试,但是我现在一来没有电烙铁,没办法将tiny4412上的jtag接口引出来,所以也就放弃了,有兴趣的朋友可以试一试使用jtag来进行源码级的调试和学习。后续我也会尝试使用jlink来进行调试和学习,毕竟现在也只是学习阶段,通过源码级别的调试,应该能学到更多东西。
blog的链接我就放这里了:
1. http://www.cnblogs.com/humaoxiao/p/4181078.html
2. http://www.cnblogs.com/humaoxiao/p/4166230.html
既然要用点灯调试,那么必然要做点灯的相关代码编写了,因为不确定代码跑到哪里就出现问题了,所以点灯代码还是使用汇编来编写会比较通用写。话不多说,下面开始写代码。
首先要了解的是tiny4412上LED灯的相关信息,截图如下:
看上面的截图可以了解到,只要将GPM4_0,GPM4_1,GPM4_2,GPM4_3设置为输出引脚,要让灯点亮的话,只要将引脚的设置为输出低电平就OK了。
下面编写测试代码:
diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S
index 7eee54b..83eed98 100644
--- a/arch/arm/cpu/armv7/start.S
+++ b/arch/arm/cpu/armv7/start.S
@@ -85,6 +85,10 @@ switch_to_hypervisor_ret:
#endif
#endif
/* 添加的测试代码,测试LED代码是否正常,测试完毕需要删除 */
+ bl LED1_ON
+ bl LED2_ON
+ bl LED3_ON
+ bl LED4_ON
bl _main
/*------------------------------------------------------------------------------*/
@@ -293,3 +297,81 @@ ENTRY(cpu_init_crit)
b lowlevel_init @ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
#endif
+
+ENTRY(LED1_ON)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf //将GPM4CON[0:3]清零
+ orr r1, r1, #0x1 //将GPM4_0设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #0 //将CPM4DAT的第[0]清零,清零就是输出低电平了
+ str r1, [r0] //将设置的值写回GPM4DAT
+
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将继续执行
+ENDPROC(LED1_ON)
+
+ENTRY(LED2_ON)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf0 //将GPM4CON[4:7]清零
+ orr r1, r1, #0x10 //将GPM4_1设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #2 //将CPM4DAT的第[1]清零,清零就是输出低电平了
+ str r1, [r0] //将设置的值写回GPM4DAT
+
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将继续执行
+ENDPROC(LED2_ON)
+
+ENTRY(LED3_ON)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf00 //将GPM4CON[8:11]清零
+ orr r1, r1, #0x100 //将GPM4_2设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #4 //将CPM4DAT的第[2]清零,清零就是输出低电平了
+ str r1, [r0] //将设置的值写回GPM4DAT
+
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将继续执行
+ENDPROC(LED3_ON)
+
+ENTRY(LED4_ON)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf000 //将GPM4CON[12:15]清零
+ orr r1, r1, #0x1000 //将GPM4_3设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #8 //将CPM4DAT的第[3]位清零,清零就是输出低电平了
+ str r1, [r0] //将设置的值写回GPM4DAT
+
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将继续执行
+ENDPROC(LED4_ON)
OK,添加完上面的代码,我们可以通过之前介绍的方法将重新编译的代码烧写到SD卡,启动开发板可以看到四个LED灯都亮起来了,代码代码是OK的。既然已经添加了点灯的代码了,那么熄灯的代码也就简单了,继续添加熄灯的代码如下:
diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S
index 83eed98..8ed8719 100644
--- a/arch/arm/cpu/armv7/start.S
+++ b/arch/arm/cpu/armv7/start.S
@@ -89,6 +89,13 @@ switch_to_hypervisor_ret:
bl LED2_ON
bl LED3_ON
bl LED4_ON
+
+ bl DELAY
+
+ bl LED1_OFF
+ bl LED2_OFF
+ bl LED3_OFF
+ bl LED4_OFF
bl _main
/*-----------------------------------------------------------------------------
-*/
ENDPROC(LED4_ON)
+ENTRY(LED1_OFF)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf //将GPM4CON[0:3]清零
+ orr r1, r1, #0x1 //将GPM4_0设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #1 //将CPM4DAT的第[0]位清零,清零就是输出低电平了
+ orr r1, r1, #1 //将GPM4DAT的第[0]为设置为1,输出高电平,LED灯熄灭
+ str r1, [r0] //将设置的值写回GPM4DAT
+
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将后继续执行
+ENDPROC(LED1_OFF)
+ENTRY(LED2_OFF)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf0 //将GPM4CON[4:7]清零
+ orr r1, r1, #0x10 //将GPM4_1设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #2 //将CPM4DAT的第[1]位清零,清零就是输出低电平了
+ orr r1, r1, #2 //将GPM4DAT的第[1]为设置为1,输出高电平,LED灯熄灭
+ str r1, [r0] //将设置的值写回GPM4DAT
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将后继续执行
+ENDPROC(LED2_OFF)
+ENTRY(LED3_OFF)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf00 //将GPM4CON[8:11]清零
+ orr r1, r1, #0x100 //将GPM4_2设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #4 //将CPM4DAT的第[0]位清零,清零就是输出低电平了
+ orr r1, r1, #4 //将GPM4DAT的第[0]为设置为1,输出高电平,LED灯熄灭
+ str r1, [r0] //将设置的值写回GPM4DAT
+
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将后继续执行
+ENDPROC(LED3_OFF)
+
+ENTRY(LED4_OFF)
+ /*
+ * GPM4CON Address = 0x110002E0
+ * GPM4DAT Address = 0x110002E4
+ */
+ ldr r0, =0x110002E0
+ ldr r1, [r0] //读出GPM4CON的值
+ bic r1, r1, #0xf000 //将GPM4CON[0:3]清零
+ orr r1, r1, #0x1000 //将GPM4_3设置为输出引脚
+ str r1, [r0] //将设置的值写回GPM4CON
+
+ ldr r0, =0x110002E4
+ ldr r1, [r0] //读出GPM4DAT的值
+ bic r1, r1, #8 //将CPM4DAT的第[0]位清零,清零就是输出低电平了
+ orr r1, r1, #8 //将GPM4DAT的第[0]为设置为1,输出高电平,LED灯熄灭
+ str r1, [r0] //将设置的值写回GPM4DAT
+
+ mov pc, lr //最后不要忘记这一条,将lr的值写回pc,将后继续执行
+ENDPROC(LED4_OFF)
+ENTRY(DELAY)
+ /*
+ * 延时函数
+ */
+ ldr r1, =0x000F0000
+LOOP:
+ sub r1, r1, #0x01
+ cmp r1, #0x00
+ bne LOOP
+ mov pc, lr
+
+ENDPROC(DELAY)
OK,点灯,熄灯和延时函数都能够正常运行了,不过现在还有一个问题,就是怎么在其他汇编文件或者C文件中调用我们编写的点灯和熄灯代码呢?
最简单的方法当然是查看uboot源代码是如何实现的了,我查看代码时就发现如下内容:
在start.s文件中,reset函数最后会有一个bl _main的操作,_main函数就是在./arch/arm/lib/crt0.S中定义的,使用的是ENTRY伪指令来定义的,那么是不是说明,只要是用ENTRY定义了函数都可以在全局中调用,然后我百度看了很多文章,都会这么说ENTRY这个伪指令的用法:ENTRY伪指令用于指定汇编程序的入口点。在一个完整的汇编程序中至少要有一个ENTRY(也可以有多个,当有多个ENTRY时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。但是我感觉这个说法明显是不对的啊,在一个源文件中最多只能有一个ENTRY(可以没有)显然是不对的,我们查看start.S最初的代码就可以发现,一个源文件中ENTRY还真不止一个。
好的,说了这么多,最后还是用个实例来说明内容吧,最后我还有一个疑问,希望了解这个的朋友可以留言交流交流。
diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S
index 8ed8719..9c5ce83 100644
--- a/arch/arm/cpu/armv7/start.S
+++ b/arch/arm/cpu/armv7/start.S
@@ -465,7 +465,7 @@ ENTRY(DELAY)
/*
* 延时函数
* 修改延时时间,这样现象会更清晰点
*/
- ldr r1, =0x000F0000
+ ldr r1, =0x00F00000
LOOP:
sub r1, r1, #0x01
cmp r1, #0x00
diff --git a/arch/arm/lib/crt0.S b/arch/arm/lib/crt0.S
index 8415f77..5e28fa7 100644
--- a/arch/arm/lib/crt0.S
+++ b/arch/arm/lib/crt0.S
@@ -69,6 +69,7 @@ ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
+ bl LED1_ON
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
@@ -82,6 +83,8 @@ ENTRY(_main)
#else
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#endif
+
+ bl LED2_ON
mov r0, sp
bl board_init_f_alloc_reserve
mov sp, r0
我们在crt0.S文件中添加LED1和LED2的点灯过程,最后我发现只有LED2_ON是能亮的,但是LED1_ON却不亮,这就是我上面提到的问题了,LED2灯都亮了,表明程序是可以执行到bl LED1_ON那里的,但是为什么不亮呢?这个问题还希望知道为什么的朋友可以告知我一声。
好的,这一篇就到这里了。