# TXW82x FPV SDK 项目笔记 > 版本:v2.7.0.7-42229 > 芯片厂商:HugeIC(宏晶) > 记录日期:2026-06-09 --- ## 一、项目概述 这是一个 **TXW82x FPV(第一人称视角)嵌入式 Wi-Fi 视频传输 SDK**,主要用于**无人机/航模/婴儿监视器/对讲机**等实时无线视频传输设备的固件开发。 - **目标芯片**:TXW82x(C-SKY CK804DF 内核,32位 RISC 处理器,192MHz,带硬件浮点和 DSP) - **双核架构**:CPU0 运行 WiFi MAC 层固件(`txw82xcore.bin`),CPU1 运行应用固件,通过邮箱 RPC 通信 - **编程语言**:几乎全是 C 语言(1584 个 .c 文件),少量汇编用于启动代码 - **支持的 RTOS**:AliOS (Rhino) 和 OpenHarmony LiteOS-M(二选一) --- ## 二、目录结构 | 目录 | 说明 | |------|------| | `project/` | **主工程目录**,包含应用代码、配置、构建脚本、入口文件 | | `sdk/` | **核心 SDK**,包含应用模块、HAL、驱动、库 | | `csky/` | **C-SKY CPU 支持**,包含 RTOS 内核(AliOS Rhino)、DSP/NN 库、minilibc | | `ohos/` | **OpenHarmony LiteOS-M 内核**(可选的第二个 RTOS) | | `libs/` | **预编译静态库**(19 个 .a 文件,WiFi、ISP、Video、Audio 等闭源模块) | | `tools/` | 工具目录(此版本为空) | | `doc/` | 文档目录(此版本为空) | | `no_WPA3/` | 不含 WPA3 的核心固件备选 | --- ## 三、系统架构(从上到下) ``` ┌─────────────────────────────────────────┐ │ 用户应用层 (project/app/) │ │ FPV │ Demo │ 婴儿监视器 │ 对讲机 等 │ ├─────────────────────────────────────────┤ │ SDK 应用模块 (sdk/app/) │ │ RTSP │ Video │ Audio │ UI/LVGL │ MQTT │ ├─────────────────────────────────────────┤ │ SDK 库 (sdk/lib/) │ │ 视频│音频│网络│蓝牙│加密│文件系统│多媒体 │ ├─────────────────────────────────────────┤ │ HAL 硬件抽象层 (sdk/hal/) │ │ 40 个外设 HAL 模块 │ ├─────────────────────────────────────────┤ │ 驱动层 (sdk/driver/) │ │ 16 类外设驱动 │ ├─────────────────────────────────────────┤ │ OSAL 操作系统抽象层 (sdk/osal/) │ │ AliOS (Rhino) │ OpenHarmony (LiteOS) │ ├─────────────────────────────────────────┤ │ 芯片启动 (sdk/chip/txw82x/) │ │ 向量表 │ 中断 │ 系统初始化 │ ├─────────────────────────────────────────┤ │ 预编译核心库 (libs/*.a) │ │ WiFi MAC │ ISP │ Video │ Audio │ Flash │ ├─────────────────────────────────────────┤ │ TXW82x 硬件 (CK804DF 双核) │ │ WiFi│H264│JPEG│ISP│LCD│USB│SD│PSRAM │ └─────────────────────────────────────────┘ ``` --- ## 四、关键源文件 ### 4.1 入口和配置(最先看) | 文件 | 行数 | 说明 | |------|------|------| | `project/project_config.h` | - | 主配置,`CUSTOMER_ID` 选择产品类型 | | `project/sys_config.h` | - | 系统配置(OS选择、内存布局) | | `project/config.cfg` | - | 引脚复用/GPIO 配置 | | `project/main.c` | 286 | **主入口**,初始化看门狗→CPU RPC→堆→配置→事件→WiFi→网络→启动应用 | ### 4.2 系统初始化 | 文件 | 行数 | 说明 | |------|------|------| | `project/device.c` | 725 | 所有硬件外设初始化 | | `project/events.c` | 180 | WiFi/网络事件处理 | | `project/wifi.c` | 171 | WiFi 参数设置(ACS、速率控制、发射功率) | | `project/network.c` | - | LwIP 网络栈初始化 | | `project/syscfg.c/h` | - | 系统配置持久化 | ### 4.3 应用层 | 文件 | 行数 | 说明 | |------|------|------| | `project/app/app_fpv.c` | 597 | FPV 应用初始化,视频/音频流、RTSP、对讲 | | `project/app/app_demo.c` | - | 通用 Demo | | `project/app/app_tunning.c` | - | ISP 调参 | | `project/app/app_bbm_cam.c` | - | 婴儿监视器-摄像头端 | | `project/app/app_bbm_lcd.c` | - | 婴儿监视器-LCD 端 | | `project/app/app_walkie_talkie.c` | - | 对讲机 | | `project/app/fpv_mem.c` | - | FPV 内存管理 | ### 4.4 SDK 层 | 目录 | 说明 | |------|------| | `sdk/app/spook/` | RTSP 服务器(RTP/H264/MJPEG 流媒体) | | `sdk/app/video_app/` | 视频采集、编码 | | `sdk/app/audio_msi/` | 音频 ADC/DAC | | `sdk/app/recorder/` | 视频录制(MP4/AVI) | | `sdk/app/intercom/` | 双向对讲 | | `sdk/app/ui/` | LVGL UI 界面 | | `sdk/app/http/` | HTTP 客户端/服务端 | | `sdk/app/mqtt/` | MQTT 客户端 | | `sdk/app/babyprotocol/` | 婴儿监视器协议 | | `sdk/app/walkie-talkie/` | 对讲机应用 | | `sdk/app/decode/` | JPEG/H264 解码 | | `sdk/app/scale_msi/` | 图像缩放 | | `sdk/app/mp4/` | MP4 容器封装 | --- ## 五、支持的产品配置 通过 `project_config.h` 中的 `CUSTOMER_ID` 切换: | ID | 产品 | 说明 | |----|------|------| | 1 | FPV Demo | FPV 实时视频传输 | | 2 | ISP Tunning Demo | ISP 图像调参 | | 3 | 720P Camera | H264 主码流 + H264 子码流 + RTSP + MP4 录制 | | 4 | 1080P Camera | H264 1080P@25fps + MJPG 子码流 + RTSP + MP4 录制 | | 5 | 720P→1080P | 720P 上采样到 1080P | | 6 | UVC Camera | USB 视频类摄像头 | | 7 | Walkie-Talkie | 对讲机 | | 8 | LCD Demo | LCD 显示 | | 9 | Baby Monitor LCD | 婴儿监视器 LCD 端 | | 10 | Baby Monitor Camera | 婴儿监视器摄像头端 | | 11 | LCD MP4 Player | LCD MP4 播放器| --- ## 六、构建系统 ### 6.1 开发环境 - **IDE**:C-SKY Development Kit (CDK) V2.24.19 - **工作区文件**:`project/txw82xApp.cdkws` - **工具链**:`csky-elfabiv2-gcc`(C-SKY 架构交叉编译器 V3.10.33) - **编译器标志**:`-mcpu=e804df -mhard-float -melrw -Os -g3 -Wall -ffunction-sections -fdata-sections` ### 6.2 编译流程 1. **PreBuild**:`prebuild.sh` 清除旧目标文件 + `precompile.exe` 生成代码 2. **Build**:`make -f txw82xApp.mk -j 8`(编译 500+ 源文件 + 链接 19 个预编译库) 3. **PostBuild**:`BuildBIN.sh` - 拷贝 `.elf`、`.map`、`.hex` 到工程根目录 - `crc.exe` CRC 校验 - `BinScript.exe` 合并核心固件 + 应用 hex → `txw82xApp.bin` - `makecode.exe` 生成 SPI Flash 镜像 - `merge.bat` 创建带时间戳的最终 `.bin` ### 6.3 如何构建 在 CDK IDE 中打开 `project/txw82xApp.cdkws`,点击 Build;或命令行在 `project/` 目录下运行 `make -f cdkws.mk`(需工具链在 PATH 中)。 --- ## 七、硬件资源 ### 7.1 内存布局 | 区域 | 起始地址 | 大小 | 用途 | |------|----------|------|------| | ISRAM | 0x04001000 | 168 KB | 核心代码 | | SRAM | 0x20006B44 | 256 KB | 堆/数据 | | SRAM0 | 0x20001100 | ~12 KB | 部分 sleep 用 | | SRAM2 | 0x20068000 | ~14 KB | 最后 256 字节 CoreSetting | | FLASH | 0x10000000 | 2 MB | 固件存储 | | PSRAM | 0x28000000 | 8 MB | 外部 PSRAM | ### 7.2 片上外设 - WiFi(802.11 b/g/n,AP + STA 模式,HT MCS0-7) - 蓝牙低功耗(BLE) - MIPI CSI(摄像头接口,支持双摄) - DVP(并口摄像头接口) - H.264 硬件编码器 - JPEG 硬件编码/解码器 - ISP(图像信号处理器,AE/AWB/gamma/IR-cut) - VPP(视频后处理器) - LCD 控制器(MIPI DSI + MCU 接口) - Audio ADC/DAC、I2S、PDM - USB 2.0 OTG + USB 1.1 Host - SD 卡主机(SDIO) - SPI、I2C、UART、PWM、ADC、DMA、SHA、AES - PSRAM 接口(外部 8MB) ### 7.3 支持的图像传感器 GC1084, GC2053, GC2083, GC2145, GC0308, GC0329, OV2640, OV7725, OV9734, SC1346, SC2331, SC2336P, BF3A03, BF30A2, BF2013, BF3703, BF3720, IMX219, H62, H63P, F37P, F38P, TP9950 等 ### 7.4 支持的 LCD 面板 ST7701S(MIPI), ST7789V, ST7735, ST7796, ILI9881C, JD9852 等 --- ## 八、网络协议栈 - **LwIP** TCP/IP 协议栈(DHCP 客户端/服务端、DNS、NAT) - **RTSP/RTP** 服务器(spook),支持 H264/MJPEG 实时流 - **HTTP** 客户端/服务端 - **MQTT** 客户端 - **libcurl**(完整 HTTP 客户端库) - WiFi AP 模式默认 IP:192.168.169.1 - 多种 WiFi 速率控制算法(FPV/航拍/IPC/婴儿监视器场景) --- ## 九、预编译库说明 | 库文件 | 说明 | |--------|------| | `libcore.a` | WiFi MAC(LMAC/UMAC)核心 | | `libwifi.a` | WiFi 驱动 | | `libvideo.a` | 视频编码管线 | | `libisp.a` | 图像信号处理 | | `libaudio_app.a` | 音频应用 | | `libaudio_drver.a` | 音频驱动 | | `libflash.a` | Flash 访问 | | `libpsram.a` | PSRAM 驱动 | | `libusb.a` | USB 协议栈 | | `libsd.a` | SD 卡主机 | | `libcommon.a` | 通用工具 | | `libatcmd.a` | AT 命令解析 | --- ## 十、学习路径建议 1. **先读配置**:`project/project_config.h` → `project/sys_config.h` → `project/config.cfg` 2. **跟主线**:从 `project/main.c` 的 `main()` 函数开始,按初始化流程往下读 3. **读应用**:根据 `CUSTOMER_ID` 找到对应的应用文件(如 FPV 看 `app_fpv.c`) 4. **理解 SDK 层**:需要用到具体功能时再看 `sdk/app/` 下的对应模块 5. **底层驱动**:需要硬件调试时再看 `sdk/hal/` 和 `sdk/driver/` 6. **构建验证**:安装 CDK IDE,打开 `.cdkws` 工作区,尝试编译和烧录 --- ## 十一、如何添加/修改功能 ### 11.1 项目文件职责速查 ``` project/ ├── project_config.h ← 全局宏配置(功能开关、参数) ├── main.c ← 程序入口,系统初始化流程 ├── events.c ← 系统事件分发(WiFi连接/断开/DHCP等) ├── wifi.c ← WiFi初始化和参数配置 ├── network.c ← 网络初始化 ├── syscfg.c/h ← 系统参数持久化存储(Flash读写) ├── atcmd.c ← AT命令注册表 ├── device.c ← 所有硬件外设初始化 ├── app/ │ ├── app_fpv.c ← FPV图传应用主逻辑(当前激活:CUSTOMER_ID=11) │ ├── app_demo.c ← 通用Demo │ ├── app_bbm_lcd.c ← 婴儿监视器LCD端 │ ├── app_bbm_cam.c ← 婴儿监视器摄像头端 │ ├── app_walkie_talkie.c ← 对讲机 │ ├── app_tunning.c ← ISP调参 │ └── user_app.h ← 应用层对外接口声明 ├── module_code/ ← 硬件模块用户层代码 │ ├── gen422_user.c ← H.264辅码流(gen420)用户层 │ ├── h264_user.c ← H.264编码用户层 │ ├── jpg_user.c ← JPEG编解码用户层 │ └── prc_user.c ← PRC电源/复位用户层 ├── config.cfg ← 引脚配置脚本 └── pin_param.h ← 生成的引脚映射 ``` ### 11.2 添加新功能模块(6步流程) #### 步骤1:在 `project_config.h` 添加宏开关 在对应 `CUSTOMER_ID` 分支(当前为ID=11)中添加: ```c #define MY_FEATURE_EN 1 // 我的新功能使能,0=禁用,1=启用 ``` #### 步骤2:创建功能代码文件 在 `project/app/` 下新建 `app_my_feature.c`: ```c #include "sys_config.h" #include "basic_include.h" // 初始化函数,会被 main.c 的 sys_app_init() 调用 int sys_app_my_feature_init(void) { // 在此初始化你的功能模块 os_printf("my feature init ok\r\n"); return 0; } ``` #### 步骤3:在 `user_app.h` 声明接口 ```c int sys_app_my_feature_init(void); ``` #### 步骤4:在 `main.c` 的 `sys_app_init()` 中注册 在 `sys_app_init()` 函数内添加: ```c #ifdef MY_FEATURE_EN sys_app_my_feature_init(); #endif ``` #### 步骤5:处理系统事件(如需监听WiFi/网络事件) 有两种方式,根据复杂度选择: **方式A:轻量事件**(直接在 `events.c` 的 `sys_event_hdl()` 中添加): ```c #if MY_FEATURE_EN my_feature_event_handler(event_id, data, priv); #endif ``` 适合:只需响应几个简单事件的场景。 **方式B:复杂事件**(用 `sys_event_take()` 注册独立处理函数): 在 `main()` 函数的正常模式分支中添加: ```c sys_event_take(0xffffffff, my_feature_event_hdl, 0); ``` 适合:需要完整事件处理链的复杂模块。注意:每次调用消耗 16 字节 heap。 **WiFi协议栈回调**(在 `events.c` 的 `sys_wifi_event_cb()` 中添加): ```c #if MY_FEATURE_EN ret |= my_wifi_event_hdl(ifidx, evt, param1, param2); #endif ``` > **重要**:`sys_wifi_event_cb()` 在WiFi协议栈Task中执行,**禁止耗时操作**(如DHCP、Flash读写、长循环)。 > 需要耗时逻辑时,通过 `sys_event_post()` 将事件转发到 `sys_event_hdl()` 处理。 #### 步骤6:添加AT调试命令(可选) 在 `atcmd.c` 的 `static_atcmds[]` 表中注册: ```c #ifdef MY_FEATURE_EN { "AT+MYCMD", my_atcmd_handler }, #endif ``` AT命令处理函数格式: ```c int32 my_atcmd_handler(const char *cmd, char *argv[], uint32 argc) { // 解析参数、执行操作、返回结果 os_printf("my feature cmd received\r\n"); return ATCMD_RESULT_DONE; } ``` ### 11.3 修改现有功能——按目标定位文件 | 修改目标 | 修改文件 | 说明 | |---------|---------|------| | 功能开关/使能 | `project_config.h` | 改宏值(0↔1)或添加新宏 | | WiFi功率/速率/信道 | `project_config.h` 的 `WIFI_*` 宏 | 参数配置 | | WiFi初始化逻辑 | `wifi.c` | ACS扫描、速率控制、FEM芯片初始化 | | 网络IP/DHCP配置 | `project_config.h` 的 `NET_*/DHCPD_*` 宏 | IP地址池、子网等 | | 网络初始化流程 | `network.c` + `syscfg.c` | LwIP初始化、DHCP服务端 | | FPV图传逻辑 | `app/app_fpv.c` | 视频/音频流、RTSP、对讲、录像 | | 系统事件处理 | `events.c` | WiFi连接/断开/DHCP/LTE事件分发 | | 程序启动流程 | `main.c` | 初始化顺序、应用入口注册 | | 系统参数持久化 | `syscfg.h`(结构体)+ `syscfg.c`(默认值/保存) | Flash参数读写 | | GPIO引脚配置 | `config.cfg` + `pin_param.h` | 引脚复用映射 | | 调试命令 | `atcmd.c` | AT命令注册表 | | 传感器型号支持 | `project_config.h` 的 `DEV_SENSOR_*` | 传感器选择 | | 内存分配大小 | `CONFIG_PSRAM_AVHEAP_SIZE` / `CONFIG_AVHEAP_SIZE` | PSRAM/SRAM音视频堆大小 | | 编解码参数 | `module_code/` 下的文件 | H264/JPEG用户层配置 | | LCD驱动选择 | `project_config.h` 的 `LCD_*` 宏 | ST7701S/ST7789V等 | ### 11.4 关键注意事项 1. **内存规划**:新功能若需大块内存,需调整 `CONFIG_PSRAM_AVHEAP_SIZE`(PSRAM)或 `CONFIG_AVHEAP_SIZE`(SRAM),系统需保留约 20KB 空闲 SRAM 防止 malloc 失败 2. **syscfg 兼容性**:修改 `struct sys_config`(`syscfg.h`)时**只能在末尾追加字段**,不能修改已有字段的顺序或类型,否则会导致 Flash 中已存储的参数数据错乱(CRC校验失败后回退到默认值) 3. **事件处理线程安全**: - `sys_wifi_event_cb()` → WiFi协议栈Task上下文 → **禁止耗时操作** - `sys_event_hdl()` → 独立系统事件Task上下文 → 可以执行耗时操作(DHCP、Flash读写等) - 耗时逻辑必须用 `sys_event_post()` 从回调转发到事件Task 4. **条件编译**:用 `#ifdef MY_FEATURE_EN` 包裹新代码,确保不影响其他 `CUSTOMER_ID` 分支 5. **CUSTOMER_ID 切换**:修改 `project_config.h` 顶部的 `CUSTOMER_ID` 值即可切换整个产品配置,每个ID分支独立配置互不影响 6. **双核通信**:CPU0 运行WiFi MAC固件(不可修改),CPU1 运行应用固件,通过 `cpu_rpc_init()` 建立邮箱通信,应用层通过 RPC 接口调用CPU0的WiFi功能 ### 11.5 系统事件ID一览 用于 `sys_event_hdl()` 和 `sys_event_post()` 的事件: | 事件宏 | 说明 | |--------|------| | `SYS_EVENT(SYS_EVENT_WIFI, SYSEVT_WIFI_CONNECTTED)` | WiFi连接成功 | | `SYS_EVENT(SYS_EVENT_WIFI, SYSEVT_WIFI_DISCONNECT)` | WiFi断开连接 | | `SYS_EVENT(SYS_EVENT_NETWORK, SYSEVT_LWIP_DHCPC_DONE)` | DHCP客户端获取IP完成 | | `SYS_EVENT(SYS_EVENT_LTE, SYSEVT_LTE_CONNECTED)` | LTE网卡连接 | | `SYS_EVENT(SYS_EVENT_LTE, SYSEVT_LTE_DISCONNECTED)` | LTE网卡断开 | WiFi协议栈回调事件(`sys_wifi_event_cb`): | 事件宏 | 说明 | |--------|------| | `IEEE80211_EVENT_CONNECTED` | WiFi关联成功 | | `IEEE80211_EVENT_DISCONNECTED` | WiFi断开 | | `IEEE80211_EVENT_CONNECT_FAIL` | WiFi连接失败 | | `IEEE80211_EVENT_RSSI` | RSSI信号强度更新 | | `IEEE80211_EVENT_EVM` | EVM信号质量更新 | | `IEEE80211_EVENT_CHANNEL_CHANGE` | 信道切换 | | `IEEE80211_EVENT_INTERFACE_ENABLE` | WiFi接口使能 | ### 11.6 常用AT命令速查 | AT命令 | 说明 | |--------|------| | `AT+RST` | 软件复位 | | `AT+SYSDBG` | 系统调试开关 | | `AT+SYSCFG` | 打印系统配置 | | `AT+HEAP` | 打印堆内存信息 | | `AT+SSID` | 设置WiFi SSID | | `AT+KEY` | 设置WiFi密码 | | `AT+WIFIMODE` | 设置WiFi模式(sta/ap) | | `AT+CHANNEL` | 设置WiFi信道 | | `AT+SCAN` | 扫描WiFi | | `AT+LOADDEF` | 恢复出厂默认配置 | | `AT+FPV_HEAP` | 打印FPV内存状态 | | `AT+PING` | Ping测试 | | `AT+IPERF2` | 网络带宽测试 | 串口参数:UART0,921600 波特率(见 `atcmd.c` 的 `sys_atcmd_init()`)。 --- ## 十二、LCD UI 自由修改完全指南 本项目使用 **LVGL v8** 图形库,所有UI代码都用C语言编写。 ### 12.1 UI代码在哪里 ``` sdk/app/ui/ ├── main_ui.c ← UI入口,选择显示哪种UI风格 ├── main_ui.h ← 入口函数声明 ├── lvgl_ui.h ← 所有UI页面函数声明 ├── preview_ui.c ← 摄像头预览页面(参考这个学习) ├── mp4_player_ui.c ← MP4播放页面 ├── mp4_recorder_ui.c ← MP4录像页面 ├── photo_ui.c ← 拍照页面 └── takephoto_ui.c ← 拍照页面(拇指相机版) sdk/app/interface_management/ └── interface_mgnt_msi.c ← LVGL初始化入口(第122-153行) sdk/app/app_lcd/ ├── app_lcd.h ← LCD显示接口 ├── lvgl_osd_msi.c ← LVGL与LCD显示桥接 └── lcd_video_msi.c ← 视频流显示到LCD ``` ### 12.2 UI启动调用链 ``` app_fpv.c: hardware_init() ↓ interface_mgnt_msi.c: lvgl_init_msi(w, h, rotate) ↓ (第122行) lv_init() // LVGL库初始化 lv_port_disp_init_msi() // 显示屏初始化 lv_style_init(&g_style) // 全局黑色背景样式 ↓ main_ui(NULL) // ← UI从这里开始(第148行) ↓ 创建GUI线程,开始刷新屏幕 ``` **`main_ui()` 函数**(`sdk/app/ui/main_ui.c` 第68行)是所有UI的入口: ```c lv_obj_t *main_ui(lv_obj_t *base_ui) { lv_group_t *group = lv_group_create(); lv_indev_set_group(indev_keypad, group); lv_obj_t *ui = lv_list_create(lv_scr_act()); lv_obj_set_size(ui, LV_PCT(100), LV_PCT(100)); switch(DEFINE_UI) { // 根据 DEFINE_UI 宏选择UI case MINI_DV_UI: main_Mini_DV_ui(ui, group); break; case POCKET_CAMERA_UI: main_pocket_camera_ui(ui, group); break; case BBM_UI: lv_obj_add_style(ui, &g_style, 0); break; // 在这里添加自己的UI case } return ui; } ``` ### 12.3 添加自定义UI的步骤 #### 步骤1:在 `main_ui.c` 的枚举中添加UI类型 ```c enum UI_MODE { MINI_DV_UI, IPC_UI, BBM_UI, CHILDREN_UI, POCKET_CAMERA_UI, LLM_VISION_UI, MY_SMART_CAMERA_UI, // ← 新增 }; ``` #### 步骤2:在 `lvgl_ui.h` 中声明函数 ```c lv_obj_t *my_home_ui(lv_group_t *group, lv_obj_t *base_ui); ``` #### 步骤3:在 `main_ui.c` 的 switch 中添加 case ```c case MY_SMART_CAMERA_UI: my_home_ui(ui, group); break; ``` #### 步骤4:在 `project_config.h` 对应 CUSTOMER_ID 分支中选择UI ```c #define DEFINE_UI MY_SMART_CAMERA_UI ``` #### 步骤5:新建 `sdk/app/ui/my_home_ui.c` 编写UI代码 ### 12.4 LVGL v8 核心控件速查 #### 基本概念 ```c lv_obj_t // 所有UI控件的类型(按钮、标签、容器等) lv_group_t // 按键组,物理按键通过组来切换焦点 lv_style_t // 样式(颜色、字体、边距等) lv_scr_act() // 获取当前屏幕对象 ``` #### 容器(用于布局) ```c lv_obj_t *container = lv_obj_create(lv_scr_act()); lv_obj_set_size(container, 320, 240); // 像素 lv_obj_set_size(container, LV_PCT(100), LV_PCT(50)); // 百分比 lv_obj_set_pos(container, 0, 0); // x, y 像素 lv_obj_align(container, LV_ALIGN_CENTER, 0, 0); // 居中 lv_obj_align(container, LV_ALIGN_TOP_MID, 0, 10); // 顶部居中偏移10px lv_obj_add_flag(container, LV_OBJ_FLAG_HIDDEN); // 隐藏 lv_obj_clear_flag(container, LV_OBJ_FLAG_HIDDEN); // 显示 ``` #### 文字标签 ```c lv_obj_t *label = lv_label_create(parent_obj); lv_label_set_text(label, "Hello World"); lv_label_set_text_fmt(label, "WiFi: %s", ssid); lv_obj_set_style_text_color(label, lv_color_white(), 0); lv_obj_set_style_text_font(label, &lv_font_montserrat_16, 0); ``` #### 按钮 ```c lv_obj_t *btn = lv_btn_create(parent_obj); lv_obj_set_size(btn, 80, 40); lv_obj_t *btn_label = lv_label_create(btn); lv_label_set_text(btn_label, "Click Me"); lv_obj_add_event_cb(btn, my_btn_callback, LV_EVENT_PRESSED, my_data); lv_group_add_obj(group, btn); // 加入按键组(物理按键可选它) ``` #### 列表按钮(本项目大量使用) ```c lv_obj_t *btn = lv_list_add_btn(list_obj, NULL, "预览"); // 一步创建 lv_group_add_obj(group, btn); ``` ### 12.5 UI页面编写模板(核心套路) 参考 `preview_ui.c`,每个UI页面都遵循这个模式: ```c // 1. 定义页面状态结构体(保存控件引用) struct my_page_s { lv_group_t *last_group; // 上一个按键组(用于返回时恢复) lv_obj_t *base_ui; // 主界面容器 lv_obj_t *now_ui; // 当前页面容器(退出时删除) lv_group_t *now_group; // 当前页面按键组 lv_obj_t *label_status; // 页面内控件 }; // 2. 退出页面的回调 static void exit_my_page(lv_event_t *e) { struct my_page_s *s = lv_event_get_user_data(e); lv_indev_set_group(indev_keypad, s->last_group); // 恢复按键组 lv_obj_clear_flag(s->base_ui, LV_OBJ_FLAG_HIDDEN); // 显示主界面 if (s->now_group) lv_group_del(s->now_group); if (s->now_ui) lv_obj_del(s->now_ui); lv_free(s); } // 3. 进入页面的回调 static void enter_my_page(lv_event_t *e) { struct my_page_s *s = lv_event_get_user_data(e); lv_obj_add_flag(s->base_ui, LV_OBJ_FLAG_HIDDEN); // 隐藏主界面 lv_obj_t *page = lv_obj_create(lv_scr_act()); s->now_ui = page; lv_obj_add_style(page, &g_style, 0); lv_obj_set_size(page, LV_PCT(100), LV_PCT(100)); // 在这里添加UI控件... lv_group_t *group = lv_group_create(); lv_indev_set_group(indev_keypad, group); // lv_group_add_obj(group, btn); // 把按钮加入按键组 s->now_group = group; } // 4. 对外接口(在主界面列表中创建入口按钮) lv_obj_t *my_page_ui(lv_group_t *group, lv_obj_t *base_ui) { struct my_page_s *s = lv_malloc(sizeof(struct my_page_s)); s->last_group = group; s->base_ui = base_ui; lv_obj_t *btn = lv_list_add_btn(base_ui, NULL, "我的页面"); lv_group_add_obj(group, btn); lv_obj_add_event_cb(btn, enter_my_page, LV_EVENT_PRESSED, s); return btn; } ``` ### 12.6 视频流绑定到LCD 摄像头画面不是LVGL控件画的,而是通过 **MSI流** 直接写到LCD的Video层: ```c // 开启摄像头 → LCD显示 struct msi *s = scale3_normal_msi2(S_PREVIEW_SCALE3, FSTYPE_YUV_P0, w, h); msi_do_cmd(s, MSI_CMD_SCALE3_NORMAL, MSI_SCALE3_START, 1); msi_add_output(s, NULL, R_VIDEO_P0); msi_add_output(s, NULL, R_VIDEO_P1); msi_cmd(R_VIDEO_P0, MSI_CMD_LCD_VIDEO, MSI_VIDEO_ENABLE, 1); msi_cmd(R_VIDEO_P1, MSI_CMD_LCD_VIDEO, MSI_VIDEO_ENABLE, 1); s->enable = 1; // 关闭视频显示(退出预览时) msi_cmd(R_VIDEO_P0, MSI_CMD_LCD_VIDEO, MSI_VIDEO_ENABLE, 0); msi_cmd(R_VIDEO_P1, MSI_CMD_LCD_VIDEO, MSI_VIDEO_ENABLE, 0); msi_del_output(s, NULL, R_VIDEO_P0); msi_del_output(s, NULL, R_VIDEO_P1); s->enable = 0; msi_do_cmd(s, MSI_CMD_SCALE3_NORMAL, MSI_SCALE3_START, 0); msi_destroy(s); ``` **LCD显示分两层**: - **OSD层**:LVGL绘制的UI(按钮、文字)—— 在上层 - **Video层**:摄像头实时视频 —— 在下层 两层叠加,所以可以在视频画面上叠加半透明UI按钮。 ### 12.7 布局技巧 #### Flex弹性布局(推荐) ```c lv_obj_t *row = lv_obj_create(parent); lv_obj_set_size(row, LV_PCT(100), 60); lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW); // 横向排列 lv_obj_set_flex_align(row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_style_pad_column(row, 10, 0); // 子元素间距10px ``` #### 绝对定位 ```c lv_obj_set_size(obj, 320, 30); // 宽320 高30 lv_obj_set_pos(obj, 0, 0); // 左上角 ``` ### 12.8 颜色和样式 ```c lv_obj_set_style_bg_color(obj, lv_color_make(0xFF, 0x00, 0x00), 0); // 红色背景 lv_obj_set_style_bg_opa(obj, LV_OPA_50, 0); // 50%透明度(半透明叠加视频) lv_obj_set_style_text_color(label, lv_color_white(), 0); // 白色文字 lv_obj_set_style_border_width(obj, 0, 0); // 去掉边框 lv_obj_set_style_radius(obj, 0, 0); // 去掉圆角 lv_obj_set_style_pad_all(obj, 5, 0); // 四边内边距5px ``` ### 12.9 按键事件处理 本项目用物理按键,LVGL通过 `lv_group_t` 管理焦点: ```c // 物理按键上下 → group自动切换焦点 // 短按 → LV_EVENT_PRESSED(确认) // 长按 → LV_EVENT_LONG_PRESSED(长按) lv_obj_add_event_cb(obj, callback, LV_EVENT_PRESSED, user_data); static void my_callback(lv_event_t *e) { void *my_data = lv_event_get_user_data(e); // 用户数据 lv_obj_t *target = lv_event_get_target(e); // 触发事件的控件 } ``` ### 12.10 定时刷新UI(实时时间/状态) ```c // 创建(页面进入时) lv_timer_t *timer = lv_timer_create(timer_cb, 1000, my_data); // 每1000ms static void timer_cb(lv_timer_t *t) { struct my_page_s *s = t->user_data; lv_label_set_text_fmt(s->label_time, "Time: %d", os_get_tick_count()); } // 删除(退出页面时) lv_timer_del(s->timer); ``` ### 12.11 完整示例:自定义"预览+按钮列表"UI ```c // sdk/app/ui/my_home_ui.c #include "lvgl/lvgl.h" #include "lvgl_ui.h" #include "basic_include.h" #include "lib/multimedia/msi.h" #include "scale3_normal_msi.h" extern lv_indev_t *indev_keypad; extern lv_style_t g_style; struct my_home_s { lv_group_t *last_group; lv_obj_t *base_ui; lv_obj_t *now_ui; lv_group_t *now_group; lv_obj_t *label_status; lv_timer_t *timer; struct msi *video_msi; uint16_t w, h; }; static void exit_home(lv_event_t *e) { struct my_home_s *s = lv_event_get_user_data(e); if (s->video_msi) { msi_cmd(R_VIDEO_P0, MSI_CMD_LCD_VIDEO, MSI_VIDEO_ENABLE, 0); msi_del_output(s->video_msi, NULL, R_VIDEO_P0); s->video_msi->enable = 0; msi_do_cmd(s->video_msi, MSI_CMD_SCALE3_NORMAL, MSI_SCALE3_START, 0); msi_destroy(s->video_msi); } if (s->timer) lv_timer_del(s->timer); lv_indev_set_group(indev_keypad, s->last_group); lv_obj_clear_flag(s->base_ui, LV_OBJ_FLAG_HIDDEN); if (s->now_group) lv_group_del(s->now_group); if (s->now_ui) lv_obj_del(s->now_ui); lv_free(s); } static void home_timer_cb(lv_timer_t *t) { struct my_home_s *s = t->user_data; lv_label_set_text_fmt(s->label_status, "WiFi:%s | RTSP://554", sys_status.wifi_connected ? "OK" : "--"); } static void enter_home(lv_event_t *e) { struct my_home_s *s = lv_event_get_user_data(e); lv_obj_add_flag(s->base_ui, LV_OBJ_FLAG_HIDDEN); lv_obj_t *page = lv_obj_create(lv_scr_act()); s->now_ui = page; lv_obj_add_style(page, &g_style, 0); lv_obj_set_size(page, LV_PCT(100), LV_PCT(100)); // 顶部状态栏 lv_obj_t *top = lv_obj_create(page); lv_obj_set_size(top, LV_PCT(100), 30); lv_obj_set_pos(top, 0, 0); lv_obj_set_style_bg_opa(top, LV_OPA_70, 0); s->label_status = lv_label_create(top); lv_label_set_text(s->label_status, "WiFi:-- | RTSP://554"); lv_obj_set_style_text_color(s->label_status, lv_color_white(), 0); // 启动视频流到LCD s->video_msi = scale3_normal_msi2(S_PREVIEW_SCALE3, FSTYPE_YUV_P0, s->w, s->h); if (s->video_msi) { msi_do_cmd(s->video_msi, MSI_CMD_SCALE3_NORMAL, MSI_SCALE3_START, 1); msi_add_output(s->video_msi, NULL, R_VIDEO_P0); msi_cmd(R_VIDEO_P0, MSI_CMD_LCD_VIDEO, MSI_VIDEO_ENABLE, 1); s->video_msi->enable = 1; } // 底部按钮栏 lv_obj_t *bottom = lv_obj_create(page); lv_obj_set_size(bottom, LV_PCT(100), 50); lv_obj_align(bottom, LV_ALIGN_BOTTOM_MID, 0, 0); lv_obj_set_flex_flow(bottom, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(bottom, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_t *btn_back = lv_btn_create(bottom); lv_label_create(btn_back)->lv_label_set_text("返回"); lv_obj_add_event_cb(btn_back, exit_home, LV_EVENT_PRESSED, s); lv_obj_t *btn_rec = lv_btn_create(bottom); lv_label_create(btn_rec)->lv_label_set_text("录像"); lv_obj_t *btn_photo = lv_btn_create(bottom); lv_label_create(btn_photo)->lv_label_set_text("拍照"); lv_group_t *group = lv_group_create(); lv_indev_set_group(indev_keypad, group); lv_group_add_obj(group, btn_back); lv_group_add_obj(group, btn_rec); lv_group_add_obj(group, btn_photo); s->now_group = group; s->timer = lv_timer_create(home_timer_cb, 1000, s); } lv_obj_t *my_home_ui(lv_group_t *group, lv_obj_t *base_ui) { struct my_home_s *s = lv_malloc(sizeof(struct my_home_s)); s->last_group = group; s->base_ui = base_ui; s->video_msi = NULL; s->timer = NULL; s->w = 320; s->h = 240; lv_obj_t *btn = lv_list_add_btn(base_ui, NULL, "Smart Camera"); lv_group_add_obj(group, btn); lv_obj_add_event_cb(btn, enter_home, LV_EVENT_PRESSED, s); return btn; } ``` ### 12.12 远程WiFi看摄像头(STA模式配网) SDK已内置RTSP服务器(`sdk/app/spook/`),`app_fpv.c` 中的 `spook_init()` 自动启动。 **RTSP流地址**: | 地址 | 编码 | 说明 | |------|------|------| | `rtsp://设备IP:554/h264` | H.264 | 视频流(带宽小画质好,推荐) | | `rtsp://设备IP:554/webcam` | MJPEG | 视频流(兼容性好) | | `rtsp://设备IP:554/custom` | MJPEG | 自定义MJPEG流 | **AT命令配网步骤**(串口921600波特率): ``` AT+SSID=你家WiFi名称 ← 设置WiFi名称 AT+KEY=你家WiFi密码 ← 设置WiFi密码 AT+WIFIMODE=sta ← 切换为STA模式(连接路由器) AT+SAVE ← 保存配置到Flash AT+RST ← 重启生效 ``` 重启后设备自动连接路由器,串口会打印获取到的IP地址。手机装VLC播放器,同一WiFi下打开 `rtsp://设备IP:554/h264` 即可看到画面。 ### 12.13 调试技巧 | 技巧 | 方法 | |------|------| | 查看UI效果 | 改代码后编译烧录,屏幕直接看 | | 调整预览分辨率 | `scale3_normal_msi2()` 的第3、4个参数 | | LCD屏幕分辨率 | `project/config.cfg` 中配置 | | 按键焦点高亮 | LVGL默认在焦点控件周围显示高亮边框 | | RTSP视频流 | 手机VLC → 打开网络流 → `rtsp://设备IP:554/h264` | | CPU占用 | 串口发 `AT+SYSDBG=top,1` | | 恢复默认 | 串口发 `AT+LOADDEF` | ### 12.14 UI修改文件速查 | 要改什么 | 改哪个文件 | |---------|-----------| | 添加新UI页面 | 新建 `sdk/app/ui/xxx_ui.c` | | 声明新UI函数 | `sdk/app/ui/lvgl_ui.h` | | 注册UI入口 | `sdk/app/ui/main_ui.c`(枚举+switch) | | 选择UI风格 | `project/project_config.h`(`DEFINE_UI` 宏) | | 修改全局样式 | `sdk/app/interface_management/interface_mgnt_msi.c` 第134-140行 | | LVGL字体/配置 | `sdk/lib/lvgl/lv_conf.h` | | LCD分辨率/旋转 | `project/config.cfg` + `pin_param.h` | | 视频流分辨率 | UI代码中 `scale3_normal_msi2()` 的参数 |