2、Bootloader移植(U-Boot):U-Boot源码获取与目录结构分析、智能音箱板级配置创建、DDR初始化与时钟配置实战、网络启动与烧录验证
好,咱们进入第二章。Bootloader移植,说白了就是给智能音箱的CPU“接生”。没有它,内核就起不来。我这些年做过的项目,十有八九的启动问题都出在这一步。所以这一章,咱们把U-Boot从源码到跑起来,整个流程捋一遍。
2.1 U-Boot源码获取与目录结构分析
先说说源码怎么拿。U-Boot官方维护在Denx的Git仓库里。我个人习惯用稳定版本,比如当前智能音箱主流的v2023.04或v2024.01。别追最新版,容易踩坑。
git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot
git checkout v2024.01
拿到代码后,别急着编译。先看看目录结构。我刚开始学的时候,面对一堆文件夹也懵。其实核心就这几个:
| 目录/文件 | 作用 |
|---|---|
| arch/ | CPU架构相关代码。智能音箱用ARM,所以重点看arch/arm/ |
| board/ | 板级初始化代码。每个板子一个文件夹,咱们要新建的就在这里 |
| configs/ | 板级默认配置。执行make xxx_defconfig时读取 |
| drivers/ | 各种外设驱动。串口、网卡、Flash都在这里 |
| include/configs/ | 板级头文件。定义DDR大小、时钟频率等宏 |
| dts/ | 设备树源文件。新版本U-Boot用设备树描述硬件 |
2.2 智能音箱板级配置创建
好,现在咱们来创建自己的板级配置。假设芯片是全志H3,板子叫“smart_speaker_v1”。
第一步:创建板级目录
mkdir board/sunxi/smart_speaker_v1
把参考板的board.c、Kconfig、Makefile复制过来。然后修改board.c里的板级初始化函数。嗯,这里要注意:智能音箱一般没有显示屏,所以LCD初始化可以删掉。但I2C和音频相关的GPIO一定要配好。
第二步:创建设备树
cp arch/arm/dts/sun8i-h3-orangepi-pc.dts arch/arm/dts/sun8i-h3-smart-speaker.dts
修改设备树,把网卡PHY地址、DDR大小、SPI Flash型号改成你的硬件参数。我曾经遇到过一个问题:设备树里DDR容量写错了,结果U-Boot死活只认一半内存。查了两天才发现是dts里少了个0。
第三步:创建默认配置
cp configs/orangepi_pc_defconfig configs/smart_speaker_v1_defconfig
修改这个文件,把CONFIG_TARGET_xxx改成你的板子名。还要加上智能音箱需要的功能:比如CONFIG_CMD_SOUND(音频测试命令)、CONFIG_USB_HOST(USB升级功能)。
2.3 DDR初始化与时钟配置实战
这是整个移植过程中最头疼的部分。DDR初始化一旦出错,板子直接“变砖”。我刚开始做的时候,就因为时序参数配错,连续烧了3块板子……后来学乖了,先用厂家提供的DDR训练工具。
DDR初始化流程:
- 配置DDR控制器时钟(PLL)
- 设置DDR时序参数(tRCD、tRP、tCL等)
- 执行ZQ校准
- 初始化DDR控制器寄存器
- 做内存读写测试验证
以全志H3为例,DDR初始化代码在board/sunxi/dram_sun8i_h3.c里。关键参数在dram_para结构体中:
static struct dram_para dram_para = {
.clock = 408, // DDR时钟频率,单位MHz
.type = 3, // DDR3
.rank_num = 1, // 单Rank
.density = 2048, // 每颗颗粒2Gb
.io_width = 16, // 16位总线
.bus_width = 32, // 32位总线(2颗颗粒)
.cas_latency = 9, // CL=9
.tpr0 = 0x30926692, // 时序参数
.tpr1 = 0x1090, // 时序参数
.tpr2 = 0x1a0c8, // 时序参数
};
时钟配置: 智能音箱的音频处理对时钟精度要求高。我建议PLL音频时钟用24.576MHz的整数倍,这样I2S的采样率才准。别问我怎么知道的——产品量产了才发现播放音乐变调,那叫一个酸爽。
2.4 网络启动与烧录验证
板子能跑起来了,接下来就是验证。网络启动是开发阶段最常用的方式,省得反复烧写Flash。
网络启动配置:
# 在U-Boot命令行设置
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10
setenv netmask 255.255.255.0
setenv gatewayip 192.168.1.1
setenv bootcmd 'tftp 0x42000000 zImage; tftp 0x43000000 sun8i-h3-smart-speaker.dtb; bootz 0x42000000 - 0x43000000'
saveenv
然后PC端启动TFTP服务器,把内核和设备树放到tftp目录。上电后U-Boot会自动从网络加载内核。我第一次看到串口打印出“Starting kernel ...”时,激动得差点跳起来。
烧录验证: 网络启动没问题后,就要把U-Boot烧到SPI Flash里。智能音箱一般用SPI NOR Flash,容量16MB左右。
# 在U-Boot命令行
sf probe # 检测SPI Flash
sf erase 0 0x100000 # 擦除前1MB
tftp 0x40000000 u-boot-sunxi-with-spl.bin
sf write 0x40000000 0 0x100000 # 写入U-Boot
烧完后断电重启。如果串口能正常打印U-Boot信息,说明移植成功。如果没反应……别慌,检查一下SPI Flash的CS引脚有没有接对。我有个同事就是CS脚虚焊,折腾了一整天。
- 串口能正常输出U-Boot启动信息
- DDR容量识别正确(用md命令读几个地址验证)
- 网卡能ping通PC
- SPI Flash读写正常
- 能通过网络加载并启动内核
好了,这一章的内容就这些。U-Boot移植说白了就是三板斧:配DDR、调时钟、搞网络。把这几个搞定了,后面的内核移植就顺风顺水。下一章咱们聊聊Linux内核的移植,到时候见。