uboot成功移植到STM32F103ZET6(二)

本文详细介绍了将u-boot移植到STM32F103ZET6微控制器的过程,包括board.c中gd_t指针的处理、堆初始化、后台初始化函数的选择,以及main_loop.c中的人机交互和命令行功能。作者针对IAR或MDK环境进行了特殊处理,避免了代码重定位,简化了内存管理,并对不需要的初始化函数进行了屏蔽。文章还探讨了如何在STM32上实现uboot的环境变量重定位、设备列表初始化和控制台初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接上一贴:uboot成功移植到STM32F103ZET6(一)

软件平台:IAR for ARM 7.70
硬件主板:浩宇电子STM32F103ZET6最小系统板
STM32库:HAL库
uboot版本:1.1.6
参考uboot代码:TQ2440开发板

上一篇文章我们讲到了移植需要准备的工程,并且最后分析了下uboot移植到STM32的目的和面临的问题。
这篇文章开始着手从源代码一步一步分析问题并给出本渣找到的解决方案。
接下来先分析board.c文件。

第一步:
uboot在第一阶段的末尾通过一条指令“ldr pc, _start_armboot”来跳转到start_armboot()函数,该函数在board.c实现。
先解决上篇文章最后提到的第一个问题,在start_armboot()启动时要先保存板级信息结构指针gd的值(gd_t结构体的起始物理地址),计算原理是将重定位后的uboot映像起始地址减去堆空间大小再减去gd_t结构体大小即可,图示如下:

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));在上篇文章我们提及过,由于MDK或IAR已经帮我们完成了第一阶段的工作,因此本渣把板级信息结构体指针gd直接以全局变量的方式定义了(board.c),并没有以通用寄存器R8来存放:

 上篇文章提到的第二个问题,也与该指针相关。按照原设计uboot的全局变量空间是事先已经确定下来的,必须放在堆空间前面。
但是本渣并不希望把代码做重定向,否则仿真会失效!那怎么处理这个问题好呢?这个重要变量占据的内存其实并不大,也就36个字节,完全可以在单片机内定义。本渣为了不新建多余的全局变量,直接在堆里面申请了,包括bd结构体的内存空间也一并搞定!

这里可能有人会杠,认为内存堆初始化在gd初始化后面才调用,因此不能用malloc来为gd申请内存堆空间。没错,原设计的确是内存堆初始化在后,不过别忘了我们现在移植的平台是IAR或MDK,可以直接使用C库的内存堆管理,uboot的内存堆初始化可以直接注释掉:

 第二步:
在全局变量gd初始化完毕后,接下来是调用各个后台初始化相关函数:

uboot把所有初始化相关函数统一存放到函数指针数组init_sequence
TQ2440需要初始化CPU、中断、SDRAM等,但是在STM32上暂时用不上,所以本渣屏蔽了没用的初始化函数。

本渣觉得在board_init里初始化一些后续用到的底层硬件比较合理,因此把NOR Flash初始化放这里面来了:

 

第三步:
接下来是比较重要的几个初始化,第一个是环境变量的重定位(含初始化),第二个是设备列表初始化(目前只有控制台用到),第三个是控制台初始化。至于USB初始化,本渣目前还没移植USB的库,后续再补上。

环境变量和控制台涉及的内容太多,这两个就放到后续再详细讲。
我们可以看到start_armboot()在执行完一堆初始化之后,最终是跳到main_loop()来循环执行。

 

第四步:
接下来分析main_loop.c文件(在uboot里是main.c,为了避免冲突本渣斗胆改文件名了)。分析main_loop()的源码可知,首先从环境变量里获取bootdelay的值,这是主板上电后的倒计时数值(单位:秒)。如果在倒计时内用户按下了空格键,则会判断是否从NOR Flash启动,如果是则进入到某个菜单选项,否则直接启动Linux。由于我们用不到Linux,所以本渣屏蔽了相关的功能,只保留了菜单选项。
TQ2440开发板在这个菜单里面提供了修改一些系统参数的功能,本渣目前还没需要修改什么参数,后续有需要的话再修改这个菜单吧:

 run_command()是uboot人机交互的核心组成,接下来就详细说下uboot的命令行功能。
接着往下看代码,人机交互是一个死循环:

人机交互功能看上去很高大上,实际上就是串口的输入输出控制。以前有不少人移植过uboot的命令行功能到MCU上,网上资料很多,有兴趣的读者可以自行搜索相关资料。本渣大概解释下人机交互最重要的2个函数:
int readline (const char *const prompt)
int run_command (const char* cmd, int flag)
其中readline()负责从串口读取命令字符串,而run_command()则负责解析命令字符串。
由于这两个函数涉及的内容太多,本篇文章不作过多的解释,本渣在源代码里有详细解释这些函数,如有解释不清楚的可以在论坛上提问,本渣能解答的一定尽量解答。

下篇文章详细解释命令制作与存储,敬请期待吧!^_^

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值