1. 为什么一块2016年的开发板至今还有人学
第一次接触RK3399是在一个做边缘计算设备的项目里,当时团队要选一颗能跑双屏异显、还要带NPU做轻量推理的主控。翻了一圈市面上的方案,全志H6性能够但接口少,树莓派4还没出,最后锁定在RK3399上。这颗芯片是瑞芯微2016年发布的,六核big.LITTLE架构,双核Cortex-A72加四核Cortex-A53,GPU是Mali-T860MP4,放到今天看参数不算亮眼,但它的接口丰富程度和资料沉淀,让它在工控、商显、教育硬件这些领域一直有生命力。
很多人学RK3399是被"逼"的——公司项目要用,或者买了一块板子想跑通某个外设。但学着学着就发现,这颗芯片能讲的东西太多了:从uboot移植、kernel裁剪、设备树配置,到GPU驱动、VPU硬解、NPU调用,再到Android和Linux双系统的适配,几乎涵盖了嵌入式Linux的完整链路。它不像那些"一键烧录就能跑"的玩具板,你想改点东西就得真正理解启动流程和驱动模型。
这篇内容适合三类人:一是刚拿到RK3399开发板、不知道从哪下手的新手;二是做过其他ARM平台、想迁移到RK3399的工程师;三是需要评估RK3399是否适合自己项目的技术选型者。我会从整体思路讲到具体实操,把踩过的坑和验证过的方案都摊开说,尽量让你少走弯路。
2. 学习RK3399之前必须想清楚的几件事
2.1 你到底要学哪个层面
RK3399的学习路径分叉很早,你得先明确目标。如果只是想在板子上跑个应用,那学会烧录固件、配置网络、交叉编译就够了,一两天能上手。但如果要做板级支持包开发,比如换一块屏幕、加一个传感器、改一个电源管理策略,那就得深入uboot和kernel。再往上,如果要做系统级定制,比如裁剪Android、适配新的文件系统、优化启动速度,那涉及的面就更广了。
我见过不少人一上来就啃TRM(技术参考手册),两千多页的PDF看到第三章就放弃了。正确的做法是带着问题去查手册,而不是通读。比如你要配置一个I2C设备,那就去查I2C控制器的寄存器章节和时钟树部分,其他章节暂时不用管。
2.2 硬件选型的几个关键点
市面上的RK3399板子五花八门,选板子的时候有几个参数必须看:
| 参数项 | 推荐配置 | 原因 |
|---|---|---|
| 内存 | 4GB LPDDR4 | 2GB跑Android会紧张,4GB是甜点 |
| 存储 | eMMC 16GB以上 | 比SD卡稳定,读写速度快一个量级 |
| 调试口 | 板载USB转串口 | 省去额外买调试线的麻烦 |
| 电源 | 支持DC和Type-C供电 | 调试时Type-C方便,量产用DC |
| 扩展接口 | 40pin排针+PCIe | 方便接外设和做扩展实验 |
有些板子为了压缩成本省掉了调试串口,这个对新手很不友好。串口是你看启动日志的唯一窗口,没有它你连uboot跑到哪一步都不知道。
2.3 开发环境的搭建逻辑
RK3399的开发环境分两部分:主机端和目标板端。主机端建议用Ubuntu 18.04或20.04,太新的版本有些老工具链会出兼容问题。需要装的工具包括:
sudo apt-get install repo git-core gitk git-gui gcc-arm-linux-gnueabihf \ u-boot-tools device-tree-compiler gcc-aarch64-linux-gnu mtools parted \ libudev-dev libssl-dev python3 python3-pip这里有个细节:RK3399是64位芯片,但uboot阶段可能用到32位工具链,所以两个架构的交叉编译器都要装。另外repo工具是拉取SDK用的,瑞芯微的SDK用repo管理多个git仓库,手动clone会漏掉依赖。
目标板端就是你的开发板本身,需要准备一张至少8GB的TF卡做启动盘,以及一根USB Type-A转Type-C的数据线用于烧录。有些板子支持Maskrom模式,按住恢复键上电就能进入烧录状态,这个后面实操部分会细说。
3. 从零跑通第一个系统的完整流程
3.1 获取SDK与目录结构解读
瑞芯微官方SDK通过repo管理,拉取命令大致是这样:
mkdir rk3399-sdk && cd rk3399-sdk repo init -u https://github.com/rockchip-linux/manifests -b master repo sync -j8同步完成后你会看到一堆目录,新手容易懵。核心目录就几个:
u-boot/:引导程序源码,负责初始化DDR、加载kernelkernel/:Linux内核源码,驱动都在这里buildroot/或debian/:根文件系统构建脚本device/rockchip/:板级配置,包括分区表、打包脚本rkbin/:预编译的二进制,比如DDR初始化固件、BL31
rkbin这个目录特别重要,里面是闭源的DDR训练固件和ARM Trusted Firmware。很多人编译报错就是因为rkbin没同步全,或者版本和uboot不匹配。
3.2 编译uboot的关键配置
进入u-boot目录后,先选配置:
make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- rk3399_defconfig然后根据你的板子修改configs/rk3399_defconfig,常见的改动点包括:
CONFIG_DEFAULT_DEVICE_TREE:指定设备树文件名CONFIG_SYS_MMC_ENV_DEV:环境变量存在哪个设备CONFIG_BOOTDELAY:启动延时,调试时设长一点方便进命令行
编译命令:
make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)生成的u-boot.bin和u-boot.itb在根目录下。注意RK3399用的是FIT格式的uboot镜像,u-boot.itb才是最终烧录的文件,u-boot.bin只是中间产物。
注意:如果你改了DDR频率或者时序,必须同步更新
rkbin里的DDR初始化固件,否则uboot阶段就会挂死,串口没有任何输出。
3.3 内核编译与设备树修改
内核编译相对标准化:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip_defconfig make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rk3399-evb.dtb make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)设备树文件在arch/arm64/boot/dts/rockchip/下面,你的板子对应哪个dtb要看具体型号。改设备树是RK3399开发中最频繁的操作,比如要加一个I2C传感器,就在对应的i2c节点下添加子节点:
&i2c4 { status = "okay"; clock-frequency = <100000>; mysensor: mysensor@48 { compatible = "myvendor,mysensor"; reg = <0x48>; interrupt-parent = <&gpio1>; interrupts = <RK_PC5 IRQ_TYPE_LEVEL_LOW>; }; };这里clock-frequency设100kHz是保守值,长走线或者多设备挂载时建议先用100k调通再往上提。中断引脚要查原理图确认GPIO编号,RK_PC5这种宏定义在include/dt-bindings/pinctrl/rockchip.h里。
3.4 打包与烧录的实操细节
瑞芯微的打包脚本在device/rockchip/rk3399/下面,执行:
./build.sh kernel ./build.sh最终生成rockdev/目录下的update.img,这是一个包含uboot、kernel、rootfs的完整固件包。烧录工具有Windows版的RKDevTool和Linux版的upgrade_tool,我习惯用Linux版:
sudo upgrade_tool uf update.img烧录前板子要进入Maskrom模式:断开电源,按住恢复键,插入Type-C线,等两秒松开。upgrade_tool ld能列出设备就说明识别成功了。
实操心得:第一次烧录建议用TF卡启动而不是直接写eMMC。TF卡刷坏了重新写就行,eMMC刷坏了可能得拆机短接。等TF卡启动验证没问题了,再用
dd命令把镜像写到eMMC。
4. 外设调试与性能优化的实战经验
4.1 GPIO与中断调试的常见陷阱
RK3399的GPIO分5组,每组32个引脚,命名规则是GPIO0到GPIO4。在sysfs里操作GPIO:
echo 138 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio138/direction echo 1 > /sys/class/gpio/gpio138/value138这个编号怎么来的?计算公式是组号*32 + 组内偏移。比如GPIO4_C6,组号是4,C6对应偏移是2*8+6=22,所以编号是4*32+22=150。这个计算经常有人搞错,尤其是B组和C组的偏移。
中断调试更容易出问题。设备树里配了中断但驱动收不到,先检查三个地方:一是引脚复用有没有配成中断功能,二是触发方式(上升沿/下降沿/电平)和硬件是否匹配,三是中断控制器有没有使能。用cat /proc/interrupts能看到中断计数,如果数字不涨说明硬件层面就没触发。
4.2 双屏异显的配置方法
RK3399支持HDMI和DP同时输出,也可以HDMI加MIPI DSI。配置双屏要在设备树里把两个显示通道都使能:
&hdmi { status = "okay"; }; &dp { status = "okay"; }; &vopb { status = "okay"; }; &vopl { status = "okay"; };VOPB和VOPL是两个视频输出处理器,大分辨率屏建议接VOPB。然后在uboot的cmdline里加video=HDMI-A-1:1920x1080@60 video=DP-1:1280x800@60。如果两个屏分辨率不同,要确认带宽是否够,4K加2K同时输出可能会丢帧。
4.3 NPU调用的入门路径
RK3399本身不带NPU,但很多板子外挂了RK1808或者通过PCIe接了算力卡。如果只是用CPU跑推理,建议用NCNN或者TFLite,量化到int8后MobileNet跑一帧大概几十毫秒。要用硬件加速的话,瑞芯微提供了RKNN工具链,流程是:
- 在PC上用RKNN-Toolkit把模型转成
.rknn格式 - 把
.rknn文件和推理程序放到板子上 - 调用
rknn_init、rknn_run、rknn_outputs_get完成推理
转换时要注意输入尺寸和归一化参数必须和训练时一致,否则精度会掉得厉害。我遇到过一次转换后精度从75%掉到30%,查了半天发现是均值方差填反了。
5. 问题排查速查与避坑清单
5.1 启动失败的分层排查法
RK3399启动失败时,串口日志是唯一线索。按阶段排查:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 串口无输出 | DDR初始化失败 | 检查rkbin版本,换DDR固件 |
| 卡在BL31 | ATF配置错误 | 确认BL31二进制和芯片版本匹配 |
| uboot能进但kernel起不来 | 设备树或cmdline错误 | 对比官方evb的dts |
| kernel起来但挂载rootfs失败 | 分区表或文件系统类型不对 | 检查parameter.txt和mkfs参数 |
| 系统起来但外设不工作 | 驱动未编译或设备树未使能 | 看dmesg和/sys下的设备节点 |
5.2 编译报错的典型场景
repo sync卡住不动,多半是网络问题,可以加--force-sync重试。编译uboot报dtc版本过低,升级device-tree-compiler就行。内核编译报undefined reference,通常是defconfig里漏了某个驱动选项,用make menuconfig搜一下补上。
还有一个坑是Python版本。老版SDK的打包脚本是Python2写的,Ubuntu 20.04默认Python3,需要手动装Python2或者改脚本。我建议直接改脚本,Python2已经停止维护了。
5.3 性能调优的几个抓手
CPU调频用cpufreq子系统,查看当前策略:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor默认是interactive,跑计算密集型任务可以改成performance锁最高频。GPU调频在/sys/class/devfreq/下面,Mali-T860的可用频率档位有200MHz到800MHz。
散热是个容易被忽视的问题。RK3399满载功耗能到5W以上,不加散热片跑十分钟就会降频。实测加一个20x20mm的铝散热片,持续编译时CPU频率能稳定在1.8GHz不降。
避坑提醒:改CPU频率前先确认供电能力。有些板子的PMIC余量不足,锁最高频会导致系统不稳定甚至重启。用
cat /sys/kernel/debug/regulator/regulator_summary能看到各路电源的负载情况。
6. 后续可以深入的方向
跑通系统只是起点,RK3399能玩的东西还很多。想往底层走可以研究uboot的启动流程,从BootROM到TPL到SPL再到uboot proper,每一级做了什么、怎么传递参数,搞清楚了整个ARM启动链路就通了。想往应用走可以研究GStreamer硬编解码,RK3399的VPU支持H.264/H.265的4K编解码,用mpp库能直接调用硬件单元,比软编快一个数量级。
我个人在实际操作中的体会是,RK3399的学习曲线前陡后平。刚开始配环境、烧固件、调串口会觉得处处是坑,但一旦系统跑起来,后面加外设、改驱动、做优化都是在这个框架里填内容。关键是别怕看日志,串口输出的每一行都有意义,看懂了日志就看懂了系统在干什么。另外建议养成记录的习惯,每次改了什么配置、报了什么错、怎么解决的,记下来。RK3399的坑很多是重复的,下次遇到翻记录比重新查快得多。