第二章 开发环境搭建:交叉编译工具链安装、Yocto/BSP源码获取、内核源码树配置、TFTP/NFS调试环境搭建

做嵌入式Linux开发,环境搭建是绕不开的第一道坎。很多新手上来就急着写驱动,结果编译都过不了,白白浪费时间。我刚开始接触R-Car平台时也踩过不少坑,今天就把这些经验整理出来,帮你把环境一次搭好。

2.1 交叉编译工具链安装

说白了,交叉编译就是在PC上编译出能在ARM架构上跑的程序。R-Car平台用的是ARM Cortex-A系列核心,所以我们需要一套针对ARM的交叉工具链。

2.1.1 工具链选择

瑞萨官方推荐使用Linaro GCC工具链。我个人习惯用gcc-arm-9.2-2019.12版本,这个版本在R-Car H3/M3/N3E上我都验证过,稳定性不错。

工具链版本 适用平台 下载方式
gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu R-Car H3/M3/N3E (64位) Linaro官网
gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf R-Car E3/D3 (32位) Linaro官网
R-Car BSP自带工具链 全系列 Yocto SDK

2.1.2 安装步骤

安装其实很简单,解压后配置环境变量就行。但这里有个坑——路径不能有中文和空格。

# 下载工具链
wget https://releases.linaro.org/components/toolchain/binaries/9.2-2019.12/aarch64-linux-gnu/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz

# 解压到指定目录
sudo tar -xvf gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/

# 配置环境变量(建议写入~/.bashrc)
export PATH=/opt/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin:$PATH
export CROSS_COMPILE=aarch64-none-linux-gnu-
export ARCH=arm64

# 验证安装
aarch64-none-linux-gnu-gcc --version
注意:我曾经遇到过工具链版本不匹配导致内核编译失败的情况。R-Car BSP 3.x系列建议用gcc 9.x,BSP 4.x系列建议用gcc 10.x。版本不对,编译时会出现莫名其妙的语法错误。

2.2 Yocto/BSP源码获取

瑞萨的BSP是基于Yocto构建的。你想想看,Yocto的好处是什么?它能把整个Linux系统——内核、文件系统、应用层——全部自动化构建出来。但坏处也很明显:第一次构建要下载好几个G的源码,耗时几个小时。

2.2.1 获取BSP源码

瑞萨官方通过GitHub发布BSP,你需要先注册一个瑞萨的账号才能访问。我记得第一次搞的时候,光权限申请就等了两天。

# 以R-Car H3为例,获取BSP 4.x版本
mkdir rcar-bsp
cd rcar-bsp

# 初始化repo
repo init -u https://github.com/renesas-rcar/rcar-bsp.git -b v4.0.0

# 同步源码(这一步很慢,建议用代理)
repo sync -j4

2.2.2 构建最小系统

源码同步完成后,就可以开始构建了。这里我建议先构建一个最小系统,验证环境没问题再搞全功能版本。

# 设置构建环境
source poky/oe-init-build-env build

# 修改local.conf,添加R-Car H3的MACHINE配置
echo 'MACHINE = "h3ulcb"' >> conf/local.conf
echo 'DL_DIR = "/path/to/downloads"' >> conf/local.conf

# 开始构建(第一次大概需要4-6小时)
bitbake core-image-minimal
小技巧:Yocto构建时会把下载的源码包缓存在DL_DIR目录。我习惯把这个目录单独放在一个大分区,这样下次重建时就不用重新下载了。另外,repo sync时如果网络不稳定,可以加--no-clone-bundle参数。

2.3 内核源码树配置

Yocto构建完成后,内核源码会在build/tmp/work/h3ulcb-poky-linux/linux-renesas/目录下。但说实话,直接在Yocto工作目录里改内核代码很不方便。我习惯单独拉一份内核源码出来。

2.3.1 独立内核源码树

# 从Yocto构建目录复制内核源码
cp -r build/tmp/work/h3ulcb-poky-linux/linux-renesas/4.19.0+gitAUTOINC+.../git/ ~/rcar-kernel

# 进入内核目录
cd ~/rcar-kernel

# 使用R-Car H3的默认配置
make ARCH=arm64 defconfig

# 生成.config文件
make ARCH=arm64 menuconfig

2.3.2 关键配置项

在menuconfig里,有几个配置项是R-Car平台必须开启的。我列个表,你对照着检查一下:

配置项 路径 说明
CONFIG_ARCH_RENESAS System Type → ARM system type 瑞萨SoC支持
CONFIG_ARCH_R8A7795 System Type → Renesas SoC R-Car H3支持
CONFIG_PINCTRL_RZA2 Pin controllers 引脚控制
CONFIG_GPIO_RCAR GPIO support GPIO驱动
CONFIG_I2C_RCAR I2C support I2C控制器
核心要点:内核配置不是越多越好。我见过有人把能开的选项全开了,结果编译出来的内核镜像有50多MB,根本烧不进去。建议只开启R-Car平台必需的驱动,其他模块编译成可加载模块(.ko),按需加载。

2.4 TFTP/NFS调试环境搭建

驱动开发过程中,最烦的就是每次修改都要重新烧写镜像。用TFTP+NFS组合,可以省掉这个步骤。TFTP负责加载内核和设备树,NFS负责挂载根文件系统。这样你改完驱动,只需要重新编译内核模块,然后重启目标板就行了。

2.4.1 搭建TFTP服务器

# 安装TFTP服务
sudo apt-get install tftpd-hpa

# 配置TFTP目录
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp

# 修改配置文件 /etc/default/tftpd-hpa
# TFTP_DIRECTORY="/srv/tftp"
# TFTP_OPTIONS="--secure --create"

# 重启服务
sudo systemctl restart tftpd-hpa

2.4.2 搭建NFS服务器

# 安装NFS服务
sudo apt-get install nfs-kernel-server

# 创建共享目录
sudo mkdir -p /srv/nfs/rootfs

# 解压Yocto构建的根文件系统
sudo tar -xvf build/tmp/deploy/images/h3ulcb/core-image-minimal-h3ulcb.tar.bz2 -C /srv/nfs/rootfs

# 配置/etc/exports
echo "/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports

# 重启NFS服务
sudo systemctl restart nfs-kernel-server

2.4.3 U-Boot启动参数设置

目标板启动时,需要在U-Boot里设置启动参数。这里我以R-Car H3的ULCB开发板为例:

# 设置IP地址(根据你的网络环境修改)
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10

# 设置TFTP加载内核和设备树
setenv bootcmd 'tftp 0x48080000 Image; tftp 0x48000000 r8a7795-h3ulcb.dtb; booti 0x48080000 - 0x48000000'

# 设置NFS根文件系统
setenv bootargs 'console=ttySC0,115200 root=/dev/nfs nfsroot=192.168.1.10:/srv/nfs/rootfs rw ip=192.168.1.100'

# 保存并启动
saveenv
boot
注意:TFTP和NFS的路径权限一定要设置正确。我曾经因为NFS目录权限问题,目标板启动时一直卡在"VFS: Unable to mount root fs",折腾了半天才发现是/srv/nfs/rootfs目录的owner是root,而NFS配置里没有加no_root_squash。

2.5 环境验证

环境搭好后,最好做个快速验证。我一般会写一个简单的Hello World驱动,编译后通过NFS传到目标板,加载测试。

// hello.c
#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_init(void)
{
    printk(KERN_INFO "Hello, R-Car H3!\n");
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye, R-Car H3!\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
# 编译(使用交叉工具链)
make -C ~/rcar-kernel M=$(pwd) modules

# 拷贝到NFS目录
cp hello.ko /srv/nfs/rootfs/home/root/

# 在目标板上加载
insmod hello.ko
# 查看输出
dmesg | tail

如果能看到"Hello, R-Car H3!"的输出,说明整个开发环境就通了。嗯,到这里,你的开发环境就算正式搭建完成了。

R-Car开发环境搭建流程 主机(Ubuntu 20.04) 交叉工具链 Yocto BSP 内核源码树 TFTP服务器 NFS服务器 R-Car H3目标板 U-Boot启动 内核加载 NFS根文件系统 驱动加载测试 流程说明: 1. 主机安装交叉工具链,编译内核和驱动 2. Yocto构建BSP,生成内核镜像和根文件系统 3. TFTP服务器提供内核和设备树加载 4. NFS服务器提供根文件系统挂载 5. 目标板通过U-Boot从TFTP加载内核,从NFS挂载根文件系统