uboot编译运行应用程序笔记

uboot编译运行应用程序笔记

前言

​ uboot运行的应用程序通常是自己编写的裸机程序,最大的缺点是加载到内存运行后无法返回至uboot命令行。笔者最近在学习uboot时无意间发现uboot带有应用程序的相关例子。下图为编译uboot时的输出信息:
在这里插入图片描述
​ 笔者随即在网络上寻找相关资料,但资料很少。故笔者将自己的调试uboot应用程序的方法整理总结下来,远远谈不上教程,想直接看结果的可以移步至总结章节

环境

  • 硬件平台:IMX6ULL

  • uboot版本:uboot-2016.03

  • gcc 版本:4.9.4 (Linaro GCC 4.9-2017.01)

  • 开发板内存:起始地址-0x80000000,大小-0x20000000

  • zImage加载地址:0x80800000

    注:本文的目录的相对地址均以uboot源码的根目录为起始。

过程

​ 在前言中了解到既然编译出了hello_wold.bin应用程序,那么我们将其加载到板子上的内存上运行一下观察效果。hello_wold.c程序如下:

/*
 * (C) Copyright 2000
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <exports.h>

int hello_world (int argc, char * const argv[])
{
	int i;

	/* Print the ABI version */
	app_startup(argv);
	printf ("Example expects ABI version %d\n", XF_VERSION);
	printf ("Actual U-Boot ABI version %d\n", (int)get_version());

	printf ("Hello World\n");

	printf ("argc = %d\n", argc);

	for (i=0; i<=argc; ++i) {
		printf ("argv[%d] = \"%s\"\n",
			i,
			argv[i] ? argv[i] : "<NULL>");
	}

	printf ("Hit any key to exit ... ");
	while (!tstc())
		;

	/* consume input */
	(void) getc();

	printf ("\n\n");
	return (0);
}

​ 通过代码可以了解到,此程序主要是打印一些调试信息。

​ 启动板子,进入命令行模式,配置好网络,输入以下命令:

tftp 80000000 hello_world.bin
go 80000000

​ 输出如下:
在这里插入图片描述
​ 可以观察到没有任何调试信息输出,但应用程序可以正常返回。据此可以推断程序是可以运行的,但print函数没效果,很有可能是链接地址和加载地址不一致所导致的,在程序运行时去print函数所在的位置找不到该函数。

​ 为了验证这个想法,在上面的hello_world.c中添加如下代码:

......
printf ("Hello World\n");
/* 新添加的代码 */
volatile unsigned int *pRegs;
pRegs = (volatile unsigned int *)(0x020AC000 + 0x0);
*pRegs &= ~(1 << 3);
*pRegs |= (1 << 3);

​ 此代码的作用是直接操作GPIO的数据寄存器关闭开发板上一个led灯,在先前的uboot初始化中我开启了这个led。假如关闭了这个led,则验证了上面的想法是正确的。

​ 在uboot根目录下使用make编译,观察输出信息:
在这里插入图片描述
​ 可以观察到确实重新生成了hello_world.c,再次将hello_world.bin加载到内存运行,结果led灯确实关闭了,猜想正确。

​ 接下来需要考虑的是如何修改hello_world.bin的链接地址。

​ 为了了解到hello_world.c是如何编译的,需要打开完整的uboot编译信息,在在uboot根目录下输入以下命令:

make distclean
make sky_board_defconfig
make -j6 V=1 | grep hello_world
  • make distclean:清理工程
  • make sky_board_defconfig:生成默认的.config文件,根据自己的工程环境修改即可
  • make -j6 V=1 | grep hello_world:-j6 多线程编译,V=1 打开全部的调试信息,grep hello_world 只输出带有 hello_world 字样的调试信息。

​ 调试信息输出如下:
在这里插入图片描述
​ 与hello_world相关的只有4条信息,从第二条信息可以了解到hello_world的代码段链接在0xc100000地址。

​ 于是在Makefile里寻找编译该语句的命令,打开hello_world.c所在文件夹下的Makefile,即相对路径为./examples/standalone/Makefile。观察到有下面的命令:
在这里插入图片描述
​ 在上图的第64行就是将代码段连接在CONFIG_STANDALONE_LOAD_ADDR宏定义所在的地址下。在uboot根目录下输入下面的命令来寻找与之相关的代码段:

grep -nR CONFIG_STANDALONE_LOAD_ADDR

​ 输出如下:
在这里插入图片描述
​ 在./arch/arm/config.mk目录下发现了0xc100000地址,使用vim查看该代码段

vim ./arch/arm/config.mk +12

​ 代码段如下:
在这里插入图片描述
​ 此代码段就是控制uboot应用程序的链接地址的,可以直接修改0xc100000的值。本着程序设计的原则,可以在开发板的头文件中修改,笔者的开发板头文件目录为./include/configs/sky_board.h,添加以下代码:

#define CONFIG_STANDALONE_LOAD_ADDR 0x80800000

​ 将链接地址修改为zImage镜像的加载地址,这样程序在运行时绝对不会干扰到uboot的正常运行。

​ 在命令行中使用make -j6 V=1 | grep hello_world重新编译uboot,观察输出信息:
在这里插入图片描述
​ 链接地址修改为0x80800000,将程序加载到内存地址为0x80800000处中运行,输出如下:
在这里插入图片描述
​ 成功打印应用程序的调试信息!!!

总结

目的:实现uboot应用程序正常返回,并且可以使用相关库函数和uboot提供的函数。

实现

  1. 在开发板头文件中添加应用程序链接地址。如在笔者的环境下,在./include/configs/sky_board.h中添加#define CONFIG_STANDALONE_LOAD_ADDR 0x80800000代码段。
  2. ./examples/standalone目录下添加自己的.c文件。如在./examples/standalone目录下添加test.c文件
  3. 编写test.c文件。
#include <common.h>
#include <exports.h>
// 函数名称须与文件名一致,否则会弹出警告
int test(int argc, char *const argv[])
{
    int i;
    app_startup(argv);				// 必须调用此函数
    for (i = 0;i <= argc; ++i) {	// 打印所有命令行参数
		printf ("argv[%d] = \"%s\"\n", i, argv[i] ? argv[i] : "<NULL>");
	}
    printf ("\n\n");
	return (0);
}
  1. 修改./examples/standalone目录下的Makefile。如果添加的c文件名为test.c,则在适当位置添加如下代码段extra-y += test

  2. 使用make或者make examples命令编译即可。

    上传至开发板运行如下:
    在这里插入图片描述
    注:应用程序链接地址必须和内存加载地址保持一致

举个栗子

要求:在Linux启动之前,让LED灯闪烁三次,周期为500ms。

思路:编写led闪烁的应用程序,先将应用程序加载到内存中,再通过fatwrite命令将应用程序写入到系统分区中,修改bootcmd使每次启动时都从系统分区加载应用程序并运行。

步骤

  1. 编写led.c应用程序
  #include <common.h>
  #include <exports.h>
  
  int led(int argc, char *const argv[])
  {
      int i;
  	volatile unsigned int *pRegs;
      pRegs = (volatile unsigned int *)(0x020AC000 + 0x0);
      app_startup(argv);
  
      for (i = 0; i < 3; i++) {
          *pRegs &= ~(1 << 3);
          printf("LED ON \r\n");
          mdelay(500);
          *pRegs |= (1 << 3);
          printf("LED OFF \r\n");
          mdelay(500);
      }
  
  	return (0);
  }
  1. 修改Makefile,添加合适位置extra-y += led

  2. 使用make examples编译应用程序

  3. 修改bootcmd。在bootcmd中添加如下代码:(使用setenv修改,或者直接修改代码)

  "fatload mmc 0:1 80800000 led.bin;"
  "go 80800000;"
  1. 在uboot命令行中使用tftp 80800000 led.bin将应用程序加载到内存0x80800000处。
    在这里插入图片描述
  2. 使用fatwrite mmc 0:1 80800000 led.bin 1a5命令将内存起始位置为0x80800000,大小为0x01a5的数据拷贝到mmc0(SD卡)的1分区(系统分区)处,文件名为led.bin
    在这里插入图片描述
  3. 使用fatls mmc 0:1查看分区文件内容,观察到出现led.bin,重启开发板即可
    在这里插入图片描述
    打印输出如下
    在这里插入图片描述
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值