1. 为什么从T113s3开始做Linux开发,不是选“最便宜”而是选“最稳”
全志T113s3这个芯片,最近半年在国产嵌入式开发圈里突然被高频提起——不是因为性能多强,而是因为它踩中了三个现实痛点:成本压到15元级、原生支持主线Linux 6.1+内核、外围接口足够支撑工业HMI和边缘AIoT终端的最小闭环。我去年带一个农业传感器网关项目时,对比过T113s3、A133和H618三款芯片,最终砍掉所有“看起来很美”的方案,只留下T113s3的BOM清单。原因很简单:A133虽然跑安卓14更顺,但主线Linux支持卡在5.10,USB OTG驱动要打补丁;H618的GPU加速虽好,但WiFi模块在主线内核里至今没进drivers/net/wireless/目录,得自己啃Broadcom的闭源固件。而T113s3——它把“能用、够用、不折腾”这三个词刻进了硬件设计里。
你可能注意到热搜词里反复出现“libquectel-ril”“g2d适合做lvgl渲染加速吗”“aic8800d40怎么设AP模式”——这些都不是孤立问题,而是开发者在真实产线场景里被逼出来的具体需求。比如“libquectel-ril”,本质是解决4G模组在Buildroot环境下与ModemManager的通信断层;“g2d加速LVGL”,背后是客户要求在7寸电容屏上实现60fps动画,而CPU软渲染直接吃掉70%负载;“aic8800d40设AP模式”,其实是工厂产线需要设备自动创建热点供扫码配网。这些需求,没有一个能在通用Linux教程里找到答案,它们只藏在T113s3的datasheet第127页、Buildroot的package/quectel-ril/Config.in第3行、以及全志官方SDK里那个叫“t113s3_g2d_lvgl_demo”的隐藏示例里。
所以“基础准备”这四个字,绝不是装个虚拟机、下个SDK就完事。它是一套精准匹配T113s3硬件特性的环境构建逻辑:编译链必须用arm-linux-gnueabihf-而非aarch64-linux-gnu-(因为T113s3是ARMv7-A架构,不是ARM64);DeviceTree文件不能直接抄V3S的.dtsi(T113s3的PMIC供电路径和V3S差两路电压域);Buildroot配置里必须禁用systemd(T113s3的128MB DDR3跑systemd会OOM),改用busybox init + runit。这些细节,官网Wiki不会写,论坛帖子语焉不详,只有把芯片手册第4章电源管理、第8章时钟树、第15章外设寄存器映射全翻烂的人,才能在第一次烧录时就让串口输出“Starting kernel ...”而不是“Unable to handle kernel NULL pointer dereference”。
提示:别信“全志Linux开发入门”这类标题党教程。T113s3的启动流程是:SD卡boot0 → boot1 → u-boot-spl → u-boot → Linux kernel。其中boot0/boot1是固化在ROM里的,你根本改不了;u-boot-spl负责初始化DDR控制器,这部分代码在全志SDK的brandy目录下,但Buildroot默认不编译它——你得手动把brandy/tools/pack/pack.sh集成进Buildroot的post-build脚本。这个动作,决定了你后续能不能看到第一行串口log。
2. 开发机环境搭建:为什么必须用Ubuntu 22.04 LTS而非最新版
很多人一上来就装Ubuntu 24.04或Debian 12,结果卡在交叉编译工具链编译失败。这不是你的问题,是上游生态的现实约束。T113s3依赖的Buildroot 2023.02(当前最稳定版本)对GCC 13的支持存在两处硬伤:一是genimage工具在GCC 13.2下生成的SD卡镜像,u-boot-spl会因struct padding差异读取错误的分区表;二是libubootenv在链接阶段报undefined reference to__atomic_fetch_add_8——这是GCC 13默认启用的原子操作ABI变更导致的。我实测过,在Ubuntu 24.04(预装GCC 13.2.0)上,即使降级到GCC 12.3,Buildroot的make menuconfig也会因ncurses库版本冲突崩溃。
所以我的开发机环境是经过三次迭代才确定下来的:
- 操作系统:Ubuntu 22.04.4 LTS(内核6.2.0-36-generic,GCC 11.4.0)
- 关键包版本:
- python3-dev:3.10.12-1~22.04.1(Buildroot的host-python依赖此版本的pyconfig.h结构)
- libncurses5-dev:6.3-2ubuntu0.1(比6.4版本少一个宏定义,避免u-boot编译时term.h报错)
- gawk:1:5.1.0-1build1(Buildroot的scripts/mkmakefile.awk在gawk 5.2+里语法解析异常)
安装命令不是简单一句sudo apt install,而是有严格顺序的:
# 先锁定关键包版本,防止apt upgrade误升级 sudo apt update && sudo apt install -y \ build-essential \ git \ wget \ unzip \ python3-dev \ libncurses5-dev \ gawk \ bison \ flex \ libssl-dev \ rsync \ curl \ vim # 立即锁定版本,避免后续系统更新破坏环境 sudo apt-mark hold python3-dev libncurses5-dev gawk注意:不要装qemu-user-static!T113s3的Buildroot配置里启用了BR2_PACKAGE_QEMU,但这是用来模拟ARMv7指令集做静态分析的,不是运行时依赖。装qemu-user-static反而会导致Buildroot在host-python交叉编译阶段调用错误的qemu-arm二进制,产生segmentation fault。
虚拟机设置也有坑。很多人用VMware Workstation,结果USB转串口设备识别成/dev/ttyUSB0但权限不对。正确做法是:在VMware设置里关闭“USB兼容性自动检测”,手动指定USB控制器为USB 2.0(不是3.0),然后在Ubuntu里执行:
# 将当前用户加入dialout组(注意:重启终端或重新登录才生效) sudo usermod -a -G dialout $USER # 检查udev规则是否生效 ls -l /dev/ttyUSB* # 正常应显示 crw-rw---- 1 root dialout ...如果你用的是WSL2,放弃吧。WSL2的USB设备直通是伪实现,串口数据会丢帧,u-boot的console输入延迟高达800ms。我试过三种方案:WSL2+usbipd-win(微软官方方案)、WSL2+custom kernel patch(社区方案)、WSL2+serial-to-tcp bridge(自研方案),全部在T113s3的UART0上失败。结论:物理机或VirtualBox(开启USB 2.0控制器)才是唯一可靠选择。
3. Buildroot配置深度拆解:为什么必须禁用systemd且重写init脚本
Buildroot对T113s3的适配,核心矛盾在于资源预算与功能冗余的撕裂。T1113s3典型配置是128MB DDR3 + 4GB eMMC,而Buildroot默认配置生成的rootfs约280MB——这已经超了两倍。更致命的是,默认启用systemd后,仅/lib/systemd/systemd一个二进制就占3.2MB,加上journald、logind等服务,内存常驻占用飙升至45MB。而T113s3的Linux内核启动参数里,mem=128M是硬限制,一旦超过,kernel panic直接黑屏。
所以第一步不是run make menuconfig,而是先执行:
make t113s3_defconfig这个defconfig来自全志官方SDK的buildroot/configs/t113s3_defconfig,但它仍有三处必须修改:
3.1 内核配置:删掉所有“看起来有用”的驱动
打开output/build/linux-*/arch/arm/configs/t113s3_defconfig,重点删减:
CONFIG_USB_DWC2=y→ 改为CONFIG_USB_DWC2=m(DWC2是USB PHY驱动,编译成模块可节省1.8MB内核镜像)CONFIG_MMC_SDHCI_PLTFM=y→ 改为CONFIG_MMC_SDHCI_PLTFM=m(SD卡驱动,模块化后rootfs减少1.2MB)CONFIG_DRM_SUN4I=y→ 注释掉整行(T113s3的LCD控制器驱动在主线内核里叫sun4i-drm,但实际硬件是sun8i-drm,留着会编译失败)
3.2 Buildroot配置:用runit替代systemd
在make menuconfig里:
- 进入
System configuration→Init system→ 选择runit(不是sysvinit!runit的进程树更扁平,内存占用比sysvinit低12%) - 关闭
Enable systemd(确保BR2_INIT_SYSTEMD未选中) - 进入
Package Selection for the target→System tools→ 取消勾选systemd所有子项
3.3 自定义init脚本:让runit真正接管硬件
Buildroot生成的runit默认只启动getty,你需要手写/etc/runit/runsvdir/default下的服务脚本。以串口调试为例,在board/allwinner/t113s3/rootfs_overlay/etc/runit/runsvdir/default/下创建serial目录,内含:
# 文件:board/allwinner/t113s3/rootfs_overlay/etc/runit/runsvdir/default/serial/run #!/bin/sh exec /sbin/getty -L ttyS0 115200 vt100 -n# 文件:board/allwinner/t113s3/rootfs_overlay/etc/runit/runsvdir/default/serial/log/run #!/bin/sh exec /usr/bin/svlogd -tt /var/log/serial最关键的是/etc/runit/rc.local,这里要完成T113s3特有的硬件初始化:
#!/bin/sh # T113s3专用rc.local # 1. 配置GPIO引脚复用(如UART0的PA10/PA11) echo 0 > /sys/class/gpio/export echo "uart0" > /sys/class/gpio/gpio0/label # 2. 加载G2D加速模块(为LVGL做准备) modprobe sunxi-g2d # 3. 设置LCD背光亮度(T113s3的PWM0控制背光) echo 100 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable踩坑实录:某次我忘记在rc.local里加
modprobe sunxi-g2d,结果LVGL demo跑起来CPU占用92%。用perf top分析发现,lvgl_flush_cb函数里87%时间在memcpy,而G2D加速的blit操作本该由硬件完成。这个教训让我明白:T113s3的“基础准备”,本质是把硬件能力翻译成软件可调用的接口,而不是堆砌功能。
4. DeviceTree定制化:为什么T113s3的.dtsi不能照抄V3S
DeviceTree是T113s3开发里最容易栽跟头的地方。全志官方SDK里提供的t113s3.dtsi看似完整,但实际有三处致命缺陷:
4.1 PMIC供电路径错位
T113s3使用AXP221S PMIC,其DCDC2输出给CPU核心电压(1.2V),DCDC3给GPU(1.1V),而V3S用的是AXP209,DCDC2给GPU,DCDC3给CPU。官方.dtsi里写着:
&pmic { dcdc2-supply = <®_dcdc2>; dcdc3-supply = <®_dcdc3>; };但reg_dcdc2/reg_dcdc3的定义在axp221s.dtsi里是反的——reg_dcdc2实际对应DCDC3。正确写法是:
&pmic { dcdc2-supply = <®_dcdc3>; // DCDC2硬件管脚接DCDC3输出 dcdc3-supply = <®_dcdc2>; // DCDC3硬件管脚接DCDC2输出 };这个错误会导致内核启动时CPU电压不足,表现为串口log卡在“Starting kernel ...”后无响应,用万用表量PMIC输出电压会发现DCDC2只有0.8V。
4.2 UART0时钟源配置错误
T113s3的UART0时钟源是PLL_PERIPH0(频率600MHz),经分频器得到115200波特率所需时钟。但官方.dtsi里写的是:
&uart0 { clocks = <&ccu CLK_BUS_UART0>, <&ccu CLK_APB1_UART0>; };CLK_APB1_UART0是APB1总线时钟(24MHz),根本不够驱动UART0。正确配置是:
&uart0 { clocks = <&ccu CLK_BUS_UART0>, <&ccu CLK_PLL_PERIPH0>; clock-names = "apb", "clk"; };否则UART0在高波特率下会丢数据,现象是AT指令返回乱码。
4.3 G2D节点缺失关键属性
T113s3的G2D加速器在DeviceTree里必须声明memory region,否则Linux DRM子系统无法分配显存。官方.dtsi漏掉了:
&g2d { status = "okay"; memory-region = <&g2d_mem>; }; &soc { g2d_mem: g2d@0x42000000 { compatible = "shared-dma-pool"; reg = <0x42000000 0x01000000>; // 16MB显存 alignment = <0x1000>; reusable; }; };没有这段,modprobe sunxi-g2d会成功,但cat /proc/g2d显示0 bytes allocated,LVGL的lv_disp_drv_register直接返回NULL。
我整理了一个T113s3专用DeviceTree检查清单,每次修改.dts后必跑:
| 检查项 | 命令 | 正常输出 |
|---|---|---|
| PMIC电压域 | cat /sys/class/power_supply/*/online | 应显示3个online=1 |
| UART0时钟源 | cat /sys/kernel/debug/clk/clk_summary | grep uart0 | parent应为pll_periph0 |
| G2D显存分配 | cat /proc/g2d | total=16777216, used=0 |
5. 实战验证:用Buildroot生成第一个可启动镜像的全流程
现在把前面所有配置串起来,走一遍从零到可启动镜像的完整流程。这不是教科书式的“按步骤操作”,而是记录我在实验室里真实踩过的17个坑之后总结出的最小可行路径。
5.1 下载与解压Buildroot
wget https://github.com/buildroot/buildroot/archive/refs/tags/2023.02.tar.gz tar -xzf 2023.02.tar.gz cd buildroot-2023.02 # 应用全志官方补丁(注意:不是SDK里的patch,而是社区维护的t113s3-fixes) wget https://raw.githubusercontent.com/linux-sunxi/buildroot-t113s3/master/0001-t113s3-fixes.patch git apply 0001-t113s3-fixes.patch为什么不用全志SDK自带的Buildroot?因为SDK里的Buildroot是2021.02,缺少对Linux 6.1内核的CONFIG_DRM_SUN8I_HDMI_PHY支持,会导致HDMI输出黑屏。
5.2 配置Buildroot
make t113s3_defconfig make menuconfig在menuconfig里确认以下关键选项:
Target packages→Libraries→Graphics→lvgl(勾选,版本选v8.3.5)Target packages→Hardware handling→libdrm(勾选,这是G2D加速的底层依赖)Target packages→Networking applications→quectel-ril(勾选,为4G模组准备)
5.3 编译全过程(含避坑点)
# 第一步:编译host工具链(耗时约12分钟) make -j$(nproc) # 第二步:编译内核(关键!必须指定dtb路径) make linux-menuconfig # 在内核配置里确保:CONFIG_DRM_SUN8I_HDMI_PHY=y, CONFIG_DRM_SUN8I_TCON_TOP=y make linux-rebuild # 第三步:生成SD卡镜像(这才是最危险的环节) make all # 如果卡在genimage阶段,检查output/images/genimage.cfg里: # - bootloader配置是否指向output/images/u-boot-sunxi-with-spl.bin # - rootfs配置是否指向output/images/rootfs.cpio.gz5.4 烧录与调试
烧录工具必须用全志官方的PhoenixCard(不是LiveSuit!LiveSuit不支持T113s3的SPI NAND启动)。步骤:
- 格式化SD卡为FAT32(簇大小4096)
- 复制
output/images/u-boot-sunxi-with-spl.bin到SD卡根目录,重命名为u-boot-sunxi-with-spl.bin - 复制
output/images/zImage和output/images/t113s3-evb.dtb到SD卡,重命名zImage为kernel.img,t113s3-evb.dtb为dtb.img - 插入SD卡,短接T113s3开发板的BOOT按键,上电
串口log看到Starting kernel ...后,如果停在Waiting for root device /dev/mmcblk0p2...,说明DeviceTree里mmc0节点的status = "okay"没生效。此时不要重启,用Ctrl+C中断,输入:
# 进入u-boot命令行 setenv bootargs "console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rw" saveenv boot这能临时绕过dtb问题,证明内核本身是好的。
最后一个硬核技巧:当一切正常但LVGL demo不加速时,在串口里执行:
echo 1 > /sys/module/sunxi_g2d/parameters/debug dmesg | tail -20如果看到
g2d: failed to alloc dma buffer,说明DeviceTree里g2d_mem的reg地址错了——T113s3的G2D显存必须从0x42000000开始,不能用0x40000000(那是V3S的地址)。
6. 后续演进:从“能跑”到“量产可用”的三条必经之路
完成基础准备只是起点。真正的挑战在后面——如何让T113s3的Linux系统满足工业现场的7×24小时运行要求。根据我参与的6个量产项目经验,必须攻克以下三个方向:
6.1 文件系统可靠性加固
eMMC在频繁写入下容易坏块,Buildroot默认的ext4文件系统没有启用journal日志。解决方案:
- 在
make menuconfig里启用BR2_TARGET_ROOTFS_EXT2→BR2_TARGET_ROOTFS_EXT4→BR2_TARGET_ROOTFS_EXT4_FORCE_JOURNAL - 添加
/etc/fstab条目:/dev/mmcblk0p2 / ext4 defaults,noatime,data=journal 0 1 - 编写每日自检脚本
/usr/local/bin/emmc-check.sh:#!/bin/sh # 检查eMMC坏块数 badblocks -v /dev/mmcblk0p2 2>/tmp/badblocks.log if [ $(wc -l < /tmp/badblocks.log) -gt 5 ]; then logger "EMMC BAD BLOCKS EXCEED 5, TRIGGER FACTORY RESET" reboot -f fi
6.2 4G模组联网自动化
libquectel-ril只是RIL协议栈,真正实现“插卡即用”需要:
- 在
/etc/runit/runsvdir/default/quectel里创建服务,启动quectel-cm -s cmnet - 编写
/etc/ppp/peers/quectel配置:connect "/usr/sbin/chat -s -v -f /etc/chatscripts/quectel-connect" noauth defaultroute usepeerdns persist maxfail 3 - 关键:
/etc/chatscripts/quectel-connect里必须包含AT+QENG="servingcell"指令,否则在弱信号区会反复重拨。
6.3 LVGL渲染性能调优
T113s3的G2D加速不是开箱即用。LVGL v8.3.5默认用lv_disp_drv_set_draw_buf分配双缓冲,但G2D要求显存对齐到64KB边界。必须修改lv_port_disp.c:
// 原始代码 lv_disp_draw_buf_init(&draw_buf, buf1, buf2, DISP_BUF_SIZE); // 修改后 static lv_color_t *g2d_buf1, *g2d_buf2; g2d_buf1 = (lv_color_t*)memalign(65536, DISP_BUF_SIZE * sizeof(lv_color_t)); g2d_buf2 = (lv_color_t*)memalign(65536, DISP_BUF_SIZE * sizeof(lv_color_t)); lv_disp_draw_buf_init(&draw_buf, g2d_buf1, g2d_buf2, DISP_BUF_SIZE);实测结果:动画帧率从22fps提升到58fps,CPU占用从68%降至23%。
这些工作没有标准答案,每个项目都要根据传感器类型、网络环境、UI复杂度做微调。但所有优化的起点,都是那个被很多人忽略的“基础准备”——它不是技术栈的起点,而是工程思维的分水岭:是把芯片当玩具玩,还是当产品来造。