全志T113应用程序开发全流程记录
Contents
全志T113应用程序开发全流程记录
起因
T113 是全志(Allwinner)的三核 SoC(1× Cortex-A7 @1.2GHz + 2× HiFi4 DSP),跑 Linux 做带屏的中小型产品性价比很高。但从前零开始把一个应用程序做到"上电即运行"的量产状态,中间要穿过环境搭建、内核裁剪、设备树、打包烧写、自启动、开机动画、显示、GPIO、输入等一系列环节,每个环节的资料都散落在不同的文档里。这篇把整条链路串成一篇全流程记录,既是备忘,也给同样用 Tina SDK 做 T113 开发的人一个整体地图。
需求
整条流水线就是一个嵌入式 Linux 产品的完整生命周期,覆盖以下环节:
- 虚拟机开发环境建设
- SDK 编译体系与编译选项配置修改
- 设备树(dts)配置修改
- 编译与打包
- 烧录固件(OpenixSuit)
- 固化应用(打进固件,而不是每次 adb push)
- 应用自启动
- 修改启动图片与启动声音
- 修改显示驱动相关配置
- GPIO 控制
- 输入设备(USB / SD / KEY)
虚拟机开发环境建设
为什么用虚拟机
全志的交叉编译工具链、打包脚本全部是 Linux 下的,Windows 原生没法跑。选虚拟机而不是双系统/WSL 的原因:快照可以随便回滚(折腾 SDK 很容易把环境搞脏)、和 Windows 宿主机共享剪贴板/文件方便、随时迁移。
环境要求
- Ubuntu 18.04(SDK 推荐版本,工具链是在这个版本的 glibc 环境下验证的;用太新的发行版容易碰到工具链和 glibc 的兼容问题)
- 磁盘 至少 80GB(SDK 源码解压后 20GB+,编译过程中 out 目录还会继续膨胀)
- 内存 8GB 起(
make -j并行编译很吃内存) - CPU 核数给足,首次全量编译 1~2 小时很正常
装好系统后先补齐编译依赖:
|
|
其中 lib32z1/lib32stdc++6 这两个 32 位库容易漏——SDK 里部分预编译的打包工具是 32 位的,缺了会在 pack 阶段报 No such file or directory,而且报错信息完全不提示是缺库。
共享目录
Windows 侧的工程放在共享目录里(VMware Tools 挂载,/mnt/share),比如本项目:
|
|
注意:共享目录(hgfs)只适合放源码和交换文件,不要直接在共享目录里编译。hgfs 不支持符号链接、没有真实的权限位、大小写行为也和 ext4 不一致,链接阶段各种莫名其妙的错误都是它引起的。正确做法是拷回虚拟机本地 ext4 分区再编。
编译体系与编译选项
Tina SDK 的结构
Tina 是全志基于 OpenWrt 深度改造的构建系统,目录结构:
|
|
理解一条主线就够了:lunch 选板 → make 编译(bootloader/内核/OpenWrt 包/rootfs)→ make pack 打包成 img。
加载开发环境
|
|
输出,我这里选择5,我自己创建好的板子配置:
|
|
lunch 本质是设置一批环境变量(TARGET_PRODUCT、TARGET_PLATFORM、TARGET_BUILD_VARIANT 等),后续 make/pack 全靠它们定位"给哪块板编"。所以每开一个新终端都要重新 source + lunch,否则 make 找不到目标。
编译选项配置修改
两套 menuconfig,管的层面不同,不要混淆:
make menuconfig —— 目标系统(rootfs)配置
决定最终文件系统里有什么:busybox 特性、alsa 音频库、libgpiod、你的应用程序包等。选中的包会被编进固件的 rootfs。
|
|
改完保存会写入 out/t113_musicpanel/.config,下次 make 时生效。
make kernel_menuconfig —— 内核配置
决定内核驱动和特性:USB host、各种文件系统、INPUT 设备、GPIO 等。对应内核 .config。
|
|
典型要开/确认的项:
|
|
menuconfig 陷阱:直接改 kernel/.config 没用,Tina 会用保存的 defconfig 覆盖;要持久化必须通过 kernel_menuconfig 保存,它会写回板级 defconfig。
设备树(dts)配置修改
dts 在哪、怎么生效
T113 Tina 的板级设备树在:
|
|
(musicpanel 对应 lunch 选的板级配置名。自己建板时,惯例是拷一份最接近的板子目录改名,再逐项改。)
编译时这份 dts 被编成 dtb,和内核一起打进 boot 包,不需要单独烧 dtb 分区。改完 dts 后要重新编译打包烧写才生效。
运行时想确认板子实际用的是哪份设备树,看:
|
|
常改的节点
外设的开关和引脚分配都在 dts 里,改外设 = 改对应节点 + 重新打包。几个高频场景:
|
|
引脚配置的因果链:pinctrl 把引脚切换到复用功能(mux)并设置上下拉/驱动能力,外设驱动 probe 时向 pinctrl 子系统申请这些引脚。所以"引脚不工作"先查两件事——status 是不是 okay、pinctrl 引脚和原理图是否对得上。
编译与打包
|
|
首次全量编译较久;增量编译只重编改动部分。内核单独改动时 make 会自动识别。编不过先别急着重来,串口板子上看不到的编译错误都在终端输出里。
编译产物在 out/t113_musicpanel/ 下,其中:
|
|
打包:
|
|
pack 按 device/config/chips/t113/configs/musicpanel/ 下的分区表(sys_partition.fex)把各镜像组装成整包刷机镜像,输出在 out/t113_musicpanel/images/ 下的 tina_*_cube*.img。
分区表决定了 bootlogo、rootfs 等各分区大小,rootfs 快装满时要么裁剪要么改分区表:
|
|
烧录固件
日常主流程就是四步循环:选择板子(lunch)→ 编译(make)→ 打包(pack)→ 烧录测试。烧录这环目前用的是第三方的 OpenixSuit。
工具选型
| 工具 | 性质 | 说明 |
|---|---|---|
| PhoenixSuit | 官方,Windows 闭源 | 全志配套的图形烧写工具 |
| LiveSuit | 官方 | 跨平台老工具,体验一般 |
| OpenixSuit | 第三方开源(Rust) | 跨平台(Linux/Windows),一个工具覆盖 PhoenixSuit(FEL 烧写)、PhoenixCard(SD 启动卡制作)、awimgunpack(img 解包)三件套的事,脚本化批量烧写友好 |
选 OpenixSuit 的原因:开源、可在 Linux 虚拟机里直接跑(不用来回切 Windows)、能命令行驱动,产线/老化脚本可以一把梭。
FEL 模式
全志 SoC 的 BootROM 里固化了一段 USB 下载协议(FEL),这是不依赖 eMMC 内容的最后防线——板子变砖也能救回来。进入方式:
- 按住 FEL/升级键再上电(原理图上找 FEL_KEY/UBOOT 引脚)
- 系统还活着时,uboot 命令行里输入
efex重启直接进 FEL - OTG 口连 PC,
lsusb能看到全志的 FEL 设备即说明就位
烧录
|
|
烧写时串口(115200)能看到下载进度和重启后的完整启动 log。烧完必看串口——这一步的 log 是"启动不了/改了没生效"类问题的第一现场,比在系统里瞎猜快十倍。
开发节奏:把烧录循环压缩成 push 循环
整包烧录一轮几分钟,一天迭代几十次会浪费大量时间。按改动层级选更新方式,是整个生命周期里最重要的节奏:
| 改动类型 | 更新方式 | 耗时 |
|---|---|---|
| 应用代码 | adb push app_launcher /tmp/ && /tmp/app_launcher |
秒级 |
| 配置/资源文件(开机图、音、脚本) | adb push 到对应分区挂载点/路径 | 秒级 |
| 内核、dts、rootfs 布局、分区表 | 只能重新 make + pack + 烧录 | 分钟级 |
应用级改动先 push 验证,跑通了再固化进固件重打包——这也是下一节"固化应用"存在的意义。
SD 启动卡
OpenixSuit 同样能把整包写进 SD 卡做成启动卡(板子拨到 SD 启动/或不焊 eMMC)。适合不动板子原有系统做验证、批量老化测试、给客户的演示卡。
固化应用
adb push 上的程序断电就没(写到 tmpfs 或者下次烧写被覆盖),量产要固化:把应用打进 rootfs。两条路:
正规做法:做成 Tina 包
在 openwrt/package/ 下建自己的包:
|
|
包 Makefile 里 INSTALL_FILES/INSTALL_BIN 把二进制装到 rootfs 的 /usr/bin/,然后 make menuconfig 里选中它,重新 make + pack。好处是有依赖管理、版本可控。
快速做法:rootfs overlay
Tina 允许板级目录直接覆盖 rootfs(在板子配置目录下的 base-files 类 overlay 目录,具体目录名以手头 SDK 的 device/config/chips/t113/configs/musicpanel/ 实际结构为准),把二进制丢进去对应路径即可。适合调试期。
应用源码本身放在共享目录 /mnt/share/linux_t113_project/app_launcher,在虚拟机内用 SDK 的交叉工具链编译:
|
|
应用自启动
Busybox init 从 /etc/init.d/ 里按文件名排序跑 S??* 脚本。加一个自启动脚本(overlay 或包里都行):
|
|
要点:
START=99保证轮到自己时声卡、显示、输入设备都已就绪;太早启动会碰到/dev/fb0还没出来- 帧缓冲应用要设好
QT_QPA_PLATFORM=linuxfb之类的平台变量(lvgl 则是各自的显示接口配置) - 要"独占整机性能"时,还可以把启动级别设高于其他不必要服务,或者干脆裁掉用不上的服务
修改启动声音和启动图片
启动画面有三个层次,改哪层取决于你想要什么效果:
1. U-Boot 阶段的 bootlogo(静态图)
最早的画面,存在 bootlogo 分区,格式 BMP(分辨率要和屏幕一致,否则花屏/黑屏)。换图:把新 BMP 放进板级打包配置目录替换原 bootlogo.bmp,重新 make pack(不动其他镜像)烧写。它不占系统资源,是"产品上电第一眼"的标准做法。
2. 内核 logo
内核把自己的 logo 打在 fbcon 上,改内核源码 drivers/video/logo/ 下的 logo 文件并配 CONFIG_LOGO。一般产品会把它关掉(menuconfig 里 Device Drivers → Graphics support → Bootup logo),避免和 bootlogo/application 之间闪一下企鹅。
3. 进系统后的开机动画/开机音
应用起来前后的过渡动画与声音。做法一是 SDK 的 bootanimation 机制(bootresource 分区放动画 zip 和音频,由启动阶段的播放程序处理,是否支持以手头 SDK 版本为准);做法二最稳——自启动脚本里在拉起主程序的同时播放开机音:
|
|
alsa 的 aplay 播 WAV 最省事;MP3 需要 madplay/gplay 之类解码器(menuconfig 里选)。
修改显示驱动相关配置
T113 的显示链路:DE(Display Engine) → panel/LVDS-RGB 输出 → 屏幕,驱动框架在内核里,产品级改动基本不用动 C 代码,改 dts 就够:
- 屏幕时序:panel 节点的
hfp/hbp/hsync/vfp/vbp/vsync、xres/yres、像素时钟,数据手册给多少填多少,花屏/偏移/闪屏九成是时序错 - 接口类型:RGB 并口 / LVDS / MIPI 在 panel 节点
compatible和数据格式字段里切换 - 背光:PWM 通道 + 占空比,
brightness节点;不亮先万用表量 PWM 脚有没有波形 - 旋转:dts 里
rotation = <90>或应用侧各自处理
验证与调试:
|
|
最后一条是经典排查法:能刷出雪花,说明 DE→panel 通路没问题,问题在应用层;刷不出雪花,回头查 dts 时序和电源。
GPIO
找到 GPIO 编号
T113 的 GPIO 按 bank 分组(PA、PB、PC……),每个 bank 32 个脚。用户空间访问有新旧两套接口:
libgpiod(新接口,推荐):
|
|
bank 编号换算:线号 = bank序号×32 + 组内脚号,PA 是第 0 组。menuconfig/包里选 libgpiod(make menuconfig → Libraries),C 代码里链 libgpiod 用 gpiod_chip_open_by_name() + gpiod_line_get_value()。
sysfs(老接口,内核逐步弃用,但脚本调试快):
|
|
注意事项
- 被 dts 里某个外设
pinctrl占用的脚不能再用 GPIO 方式操作,内核会拒绝申请——两块 subsystem 抢一根物理脚是 GPIO 最常见的"不工作" - 输出高不一定等于供电:确认引脚驱动能力(drive strength)够不够带负载,继电器/LED 大电流一律上三极管或驱动芯片
输入:USB / SD / KEY
USB
USB host(插 U 盘、键盘)在 dts 的 usbc 节点里开,供电使能脚 usb_drv_vbus_gpio 别漏。U盘热插拔:
|
|
自动挂载走 mdev hotplug 脚本(busybox 环境)监听 add/remove 事件。做"插 U 盘升级固件"的产品逻辑,就是在挂载点轮询/监听升级文件。
USB 另一个方向是 device 模式(OTG):T113 出厂固件普遍用 adb 调试(adb push app_launcher /tmp/、adb shell),比反复烧写快得多,调试期主力。
SD 卡
SD 卡走 sdc 节点(cd-gpios 配插拔检测),设备是 /dev/mmcblk?p1。两个用途:
- 数据卡:vfat 挂载存资源文件,和应用一起构成产品
- 启动卡:PhoenixCard 把整包镜像写进 SD 卡做成启动盘,给"没有烧写器/批量老化测试"的场景用
KEY(按键)
GPIO 直连按键:dts 里配 gpio-keys 节点,内核自动生成 input 设备:
|
|
LRADC 按键(全志特色):一根 AD 引脚挂一串不同阻值的分压电阻,每个按键一个电压区间,省 IO。dts 里配 lradc 节点的键值表。
用户空间统一从 input 子系统读:
|
|
C 代码里 read() /dev/input/event* 得到 struct input_event,按 type=EV_KEY, value=1/0 分按下/抬起处理。注意:应用要收到按键,make kernel_menuconfig 里 Event interface 必须开着(前面内核配置一节已列)。
踩坑记录与注意事项
- 共享目录里编译必出玄学错误:hgfs 没有符号链接和真实权限,链接/安装阶段报错。源码拷到 ext4 本地再编
- 新终端忘记 source + lunch:make 报找不到目标、pack 打出空包,都是环境变量丢了
- 改
.config不生效:Tina 用 defconfig 覆盖,配置改动必须走menuconfig/kernel_menuconfig保存 - 改了 dts 没效果:dts 要重编进 boot 包,只
make不make pack、或者烧写时没选新 img,都等于没改 - pack 报 32 位程序无法执行:缺
lib32z1/lib32stdc++6 - bootlogo 黑屏/花屏:BMP 分辨率与屏幕不符、或不是工具要求的 BMP 子格式;先按屏幕原生分辨率出图
- 自启动的应用崩:START 序号太小,
/dev/fb0、声卡、input 还没就绪;调到 99 或脚本里加就绪等待 - GPIO 申请被拒:脚被 dts 里其他外设的 pinctrl 占了,查
gpioinfo的 consumer 字段 - 按键无反应:
Event interface没开;或按键在/proc/bus/input/devices里根本没枚举出来(dts 没配对) - 一切烧写/启动问题先看串口:115200 的启动 log 能直接看到卡在 bootloader、内核还是 rootfs 挂载,比瞎猜快十倍
Author 软件开发大郭
LastMod 2025-08-28