diff --git "a/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\344\275\234\344\270\232/\347\254\254\344\272\224\345\244\251\344\275\234\344\270\232.md" "b/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\344\275\234\344\270\232/\347\254\254\344\272\224\345\244\251\344\275\234\344\270\232.md" new file mode 100644 index 0000000000000000000000000000000000000000..adeb9c8f68b50cfa3ba82c7beef888de7b393eea --- /dev/null +++ "b/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\344\275\234\344\270\232/\347\254\254\344\272\224\345\244\251\344\275\234\344\270\232.md" @@ -0,0 +1,125 @@ +# 第五天作业:AHT10 采集 + MQTT 上云 + 文件系统落盘 + 云端控灯 + +> 平台:STM32F407 星火号(rt-spark)· RT-Thread 4.0.3 · 第3组 许伟威 + +## 一、任务清单 + +1. 使用 AHT10 软件包采集温湿度,每 2 秒通过 MQTT 上传到 EMQX; +2. 梳理文件系统启动流程,挂载 **font** 分区,把数据同步写入 `Data.txt`(格式 `Temp: XX ; Humi: XX ; Count: N`); +3. 云端发指令控制板载 LED 翻转; +4. 熟悉其他软件包。 + +## 二、软件包与配置 + +启用的软件包(版本按 4.0.3 内核适配): + +| 软件包 | 版本 | 用途 | +|--------|------|------| +| aht10 | v2.1.0 | 温湿度 | +| rw007 | v2.1.0 | WiFi | +| umqtt | v1.0.0 | MQTT | +| cJSON | v1.7.15 | JSON | +| fal | v1.0.0 | 分区管理 | +| rt_vsnprintf_full | latest | %f 浮点打印 | + +组件开启:`RT_USING_SENSOR`、`RT_USING_I2C`(软I2C3)、`RT_USING_SPI`+`SFUD`、`RT_USING_WIFI`、`RT_USING_SAL`+`RT_USING_LWIP`、`RT_USING_DFS`+`ELMFAT`。 + +## 三、板级改动 + +- `board.h`:开启 `BSP_USING_SPI2`(W25Q64 CS=PB12、RW007 CS=PF10 共用)、`BSP_USING_I2C3`(PE0/PE1) +- `board.c`:补 `HAL_SPI_MspInit`(PB13/14/15 AF5) +- `drv_spi.c`:修正 F4 SPI2/3 走 APB1 时钟 +- `drivers/spi_flash_init.c`(新增):拉高 RW007 CS → 挂 norflash0 → SFUD 探测 W25Q64 +- `drivers/filesystem_mount.c`(新增):FAL 初始化 + 挂载 font→/font、filesystem→/flash(首次自动 mkfs) +- `drivers/include/fal_cfg.h`(新增):W25Q64 分区表 + +## 四、应用代码(applications/main.c 核心逻辑) + +```c +int main(void) +{ + rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT); + led_set(0); + + wifi_join(); // 等待 wlan 就绪 + rt_wlan_connect + 等 DHCP 拿到 IP + wait_netdev_ready(); + + g_aht10 = aht10_init("i2c3"); // AHT10 软 I2C3 + + mqtt_client_start(); // umqtt 连 broker.emqx.io:1883 + + while (1) + { + mqtt_try_subscribe(); // 上线后订阅 led 指令主题 + collect_and_upload(); // 读温湿度 → JSON 发布 → 写 Data.txt + rt_thread_mdelay(2000); // 2 秒周期 + } +} +``` + +采集与上传: + +```c +temp = aht10_read_temperature(g_aht10); +humi = aht10_read_humidity(g_aht10); +g_count++; + +root = cJSON_CreateObject(); +cJSON_AddNumberToObject(root, "temp", temp); +cJSON_AddNumberToObject(root, "humi", humi); +cJSON_AddNumberToObject(root, "count", g_count); +json = cJSON_PrintUnformatted(root); +umqtt_publish(g_mqtt, UMQTT_QOS0, "rsoc26/xuweiwei/aht10", json, strlen(json), 100); + +data_file_append(temp, humi, g_count); // 追加写 /font/Data.txt +``` + +云端控灯回调: + +```c +static void mqtt_sub_cb(void *client, void *msg_data) +{ + struct umqtt_pkgs_publish *msg = (struct umqtt_pkgs_publish *)msg_data; + if (!strncmp(msg->payload, "on", 2)) led_set(1); + else if (!strncmp(msg->payload, "off", 3)) led_set(0); + else led_toggle(); // 其余指令翻转 +} +``` + +## 五、文件系统挂载流程 + +``` +rt_components_init() + └─ INIT_COMPONENT_EXPORT SFUD 探测 W25Q64 → 注册 "norflash0" + └─ INIT_ENV_EXPORT fal_init() → fal_blk_device_create("font") + → dfs_mount("font","/font","elm") (首次 dfs_mkfs) +``` + +W25Q64 分区表(8MB):`easyflash(512K) | download(1M) | wifi_image(512K) | font(3M) | filesystem(3M)` + +## 六、Data.txt 内容格式 + +``` +Temp: 28.3 ; Humi: 55.6 ; Count: 1 +Temp: 28.4 ; Humi: 55.7 ; Count: 2 +Temp: 28.4 ; Humi: 55.8 ; Count: 3 +... +``` + +## 七、验证方法 + +1. 烧录 `Debug/rtthread.bin`,串口 115200; +2. 看到 `wifi connected` + `network ready, ip=...` + `aht10 initialized` + `mqtt online`; +3. MQTT.fx 订阅 `rsoc26/xuweiwei/aht10` 收 JSON,向 `rsoc26/xuweiwei/led` 发 `on`/`off`/任意消息观察板载红灯; +4. msh 执行 `data_file_show` 查看 Data.txt、`list_device` 查看 norflash0/font 设备、`fal probe` 看分区。 + +## 八、编译产物 + +``` +text data bss filename +368796 3224 63544 rtthread.elf +``` + +## 九、踩坑总结 + +详见 `笔记/day5_learning_notes.md` 第八节(软件包无 Kconfig、AHT10 缺 sensor.h、RW007 引脚、SPI 共享总线、SPI2 时钟域、串口 V1/V2、_sbrk、rt_vsnprintf_full 等 8 项)。 diff --git "a/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\344\275\234\344\270\232/\350\256\272\345\235\233\345\270\226_\347\254\254\344\272\224\345\244\251.md" "b/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\344\275\234\344\270\232/\350\256\272\345\235\233\345\270\226_\347\254\254\344\272\224\345\244\251.md" new file mode 100644 index 0000000000000000000000000000000000000000..195d542cf19cca0a109fb91b53290c4bab5547e7 --- /dev/null +++ "b/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\344\275\234\344\270\232/\350\256\272\345\235\233\345\270\226_\347\254\254\344\272\224\345\244\251.md" @@ -0,0 +1,61 @@ +# 【RSOC26】AHT10 温湿度 + MQTT 上云 EMQX + 文件系统落盘 + 云端控灯(STM32F407 星火号) + +> RSOC26 第 5 天作业记录 · 平台:STM32F407 星火号(rt-spark)· RT-Thread 4.0.3 + +## 一、实现了什么 + +1. **AHT10 采温湿度** → 每 2 秒通过 **MQTT 上传到 EMQX**; +2. **挂载 W25Q64 的 font 分区** → 数据同步追加写入 `Data.txt`; +3. **云端发指令** → 翻转板载红色 LED; +4. 顺带梳理了软件包索引机制、文件系统启动流程和 shell 命令。 + +## 二、软件包选型 + +| 软件包 | 版本 | 用途 | +|--------|------|------| +| aht10 | v2.1.0 | 温湿度 | +| rw007 | v2.1.0 | SPI WiFi | +| umqtt | v1.0.0 | MQTT(官方轻量级,自带示例线程) | +| cJSON | v1.7.15 | JSON 组包 | +| fal | v1.0.0 | Flash 分区管理 | +| rt_vsnprintf_full | latest | %f 浮点打印 | + +## 三、硬件要点(星火号) + +- W25Q64 与 RW007 **共用 SPI2**,分别用 PB12 / PF10 片选; +- AHT10 挂软 I2C3(PE0/PE1); +- 板载红 LED = PF12。 + +## 四、几个关键坑 + +1. **Flash/WiFi 共用 SPI**:探测 W25Q64 前必须先拉高 RW007 的 CS(PF10),否则总线冲突读不到 Flash。 +2. **RW007 默认引脚不符**:软件包默认 CS=102 等是别的板子的,星火号要改成 90/29/90/107/111。 +3. **AHT10 缺 sensor.h**:4.0.3 的 `rtdevice.h` 不自动包含 sensor 框架头文件,`sensor_asair_aht10.c` 要手动 `#include `,否则 conflicting types。 +4. **SPI2 时钟域**:F407 的 SPI2/3 在 APB1,芯片工程 `drv_spi.c` 默认全按 APB2 算,需修正。 +5. **rt_vsnprintf_full**:内核 <4.1.0 要手动屏蔽 `kservice.c` 里的 `rt_vsnprintf`,否则重复定义。 + +## 五、文件系统挂载流程 + +``` +rt_components_init() + └─ INIT_COMPONENT_EXPORT SFUD 探测 W25Q64 → 注册 "norflash0" + └─ INIT_ENV_EXPORT fal_init → fal_blk_device_create("font") + → dfs_mount("font","/font","elm") // 首次 dfs_mkfs +``` + +分区表(8MB):`easyflash(512K) | download(1M) | wifi_image(512K) | font(3M) | filesystem(3M)` + +## 六、效果 + +- MQTT 发布主题 `rsoc26/xuweiwei/aht10`,JSON:`{"temp":28.3,"humi":55.6,"count":12}` +- 订阅 `rsoc26/xuweiwei/led`,`on`/`off`/其它消息分别亮/灭/翻转 +- Data.txt 内容: + +``` +Temp: 28.3 ; Humi: 55.6 ; Count: 1 +Temp: 28.4 ; Humi: 55.7 ; Count: 2 +``` + +编译产物:`text 368796 data 3224 bss 63544` + +完整笔记与代码见 Gitee 仓库(rsoc-rtt / 第3组 / 许伟威)。欢迎交流指正~ diff --git "a/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\347\254\224\350\256\260/day5_learning_notes.md" "b/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\347\254\224\350\256\260/day5_learning_notes.md" new file mode 100644 index 0000000000000000000000000000000000000000..012fe5bcd9add8fbf94929507ca8110011052369 --- /dev/null +++ "b/2026/\347\254\2543\347\273\204/\350\256\270\344\274\237\345\250\201/\347\254\224\350\256\260/day5_learning_notes.md" @@ -0,0 +1,266 @@ +# 【DAY5】RT-Thread 软件包生态、文件系统与 MQTT 上云 + +> 目标平台:STM32F407 星火号(rt-spark),W25Q64 + RW007 + AHT10 +> 内核:RT-Thread 4.0.3(RT-Thread Studio 芯片工程) +> 作者:`许伟威` · 第3组 + +--- + +## 一、软件包生态与索引机制 + +### 1.1 软件包从哪来 + +RT-Thread 的软件包**源码和索引是分离的**: + +| 内容 | 位置 | 说明 | +|------|------|------| +| 软件包索引(Kconfig + package.json) | env 工具目录 `packages/packages/` | 描述每个包的名字、版本、Kconfig 依赖、下载地址 | +| 软件包源码 | 各包的 GitHub 仓库 | menuconfig 使能后自动拉取到工程 `packages/` 目录 | +| 包版本选择 | 每个包 Kconfig 里的 `choice "Version"` | 通过 `PKG_XXX_VER` 决定拉取哪个 tag | + +以 aht10 为例,索引里 `aht10/package.json` 记录了 v1.0.0 / v2.0.0 / v2.1.0 / latest 四个版本,Kconfig 里 `PKG_USING_AHT10_V210` 选择 v2.1.0 后,`PKG_AHT10_VER="v2.1.0"`,env/Studio 据此去 `github.com/RT-Thread-packages/aht10` 拉取 `2.1.0` 这个 tag。 + +### 1.2 关键结论:源码里没有 Kconfig + +软件包仓库里**一般不含 Kconfig 文件**,Kconfig 只存在于索引仓库。所以在芯片工程里要自己把索引里的 Kconfig 拷进 `packages/-/Kconfig`,否则 menuconfig 无法识别该包。 + +### 1.3 依赖要手动开 + +很多包依赖底层框架,Kconfig 里用 `select` / `depends on` 表达,但**不一定会自动勾全**: + +- AHT10 的 `sensor_asair_aht10.c` 依赖 **Sensor 框架**(`RT_USING_SENSOR`),不开启会编译报错(rt_sensor 类型未定义)。 +- RW007 依赖 **SAL + lwIP + WiFi 框架**(`RT_USING_SAL`/`RT_USING_LWIP`/`RT_USING_WIFI`)。 +- 4.0.3 内核默认 `rt_vsnprintf` 不支持 `%f`,要打印浮点需开 `rt_vsnprintf_full` 软件包,并**手动注释内核 `kservice.c` 里的 `rt_vsnprintf`**(仅 <4.1.0 需要)。 + +### 1.4 本项目用到的软件包 + +| 软件包 | 版本 | 作用 | 依赖 | +|--------|------|------|------| +| aht10 | v2.1.0 | AHT10 温湿度驱动(raw + sensor 框架两套 API) | RT_USING_SENSOR, RT_USING_I2C | +| rw007 | v2.1.0 | RW007 SPI WiFi 驱动 | SAL + lwIP + WiFi 框架 | +| umqtt | v1.0.0 | 轻量级 MQTT 客户端 | SAL | +| cJSON | v1.7.15 | JSON 编解码 | — | +| fal | v1.0.0 | Flash 抽象层(分区管理) | SFUD | +| rt_vsnprintf_full | latest | 完整版 rt_vsnprintf(支持 %f) | 屏蔽内核 rt_vsnprintf | + +--- + +## 二、传感器驱动(AHT10) + +### 2.1 软 I2C 总线 + +星火号的 AHT10 挂在 **软 I2C3** 上(PE0=SCL、PE1=SDA),芯片工程的软 I2C 驱动是 `drv_soft_i2c.c`(bit-ops 模拟),在 `board.h` 里定义: + +```c +#define BSP_USING_I2C3 +#define BSP_I2C3_SCL_PIN GET_PIN(E, 0) +#define BSP_I2C3_SDA_PIN GET_PIN(E, 1) +``` + +`drv_soft_i2c.h` 里的 `I2C3_BUS_CONFIG` 把总线命名为 `"i2c3"`,`rt_hw_i2c_init()` 在 `INIT_BOARD_EXPORT` 阶段自动注册。 + +### 2.2 读取温湿度 + +AHT10 包提供两套 API,本作业用最直接的 raw API: + +```c +aht10_device_t dev = aht10_init("i2c3"); // 传入 I2C 总线名 +float temp = aht10_read_temperature(dev); // 摄氏度 +float humi = aht10_read_humidity(dev); // 相对湿度 % +``` + +--- + +## 三、WiFi(RW007)与 SPI 总线共享 + +### 3.1 硬件拓扑 + +W25Q64(Flash)和 RW007(WiFi)**共用一条 SPI2 总线**,靠不同片选区分: + +| 设备 | CS | 说明 | +|------|-----|------| +| W25Q64 | PB12 | SPI2 硬件 NSS 引脚 | +| RW007 | PF10 | 独立 GPIO 片选 | + +RW007 其余引脚(星火号原理图):BOOT0=PB13(与 SPI_CLK 同脚)、BOOT1=PF10、INT/BUSY=PG11、RST=PG15,对应 pin 索引 `90/29/90/107/111`。 + +### 3.2 引脚修正 + +RW007 软件包默认的 STM32 示例引脚是给别的板子用的(CS=102 等),必须按原理图改 Kconfig 里的: + +``` +RW007_SPI_BUS_NAME = "spi2" +RW007_CS_PIN = 90 RW007_BOOT0_PIN = 29 RW007_BOOT1_PIN = 90 +RW007_INT_BUSY_PIN = 107 RW007_RST_PIN = 111 +``` + +### 3.3 SPI 总线冲突(重点坑) + +WiFi 和 Flash 共用 SPI2,**在初始化 Flash 前必须把 RW007 的片选 PF10 拉高**,否则 RW007 可能误驱动 MISO,导致 W25Q64 读写失败: + +```c +rt_pin_mode(90, PIN_MODE_INPUT_PULLUP); // 90 = PF10 = RW007 CS +``` + +我的 `drivers/spi_flash_init.c` 在 `INIT_COMPONENT_EXPORT`(比 RW007 的 `INIT_APP_EXPORT` 早)阶段先拉高 PF10 再探测 W25Q64。 + +### 3.4 SPI2 时钟坑 + +F407 的 SPI1 在 APB2(84MHz)、**SPI2/SPI3 在 APB1(42MHz)**。芯片工程默认 `drv_spi.c` 全部按 PCLK2 算波特率分频,会导致 SPI2 实际速率减半,我修正了这段: + +```c +#elif defined(SOC_SERIES_STM32F4) + if (spi_handle->Instance == SPI1) SPI_APB_CLOCK = HAL_RCC_GetPCLK2Freq(); + else SPI_APB_CLOCK = HAL_RCC_GetPCLK1Freq(); +#endif +``` + +--- + +## 四、MQTT 上云(EMQX) + +### 4.1 选型 + +会议里提到的 cai_mqtt 不在官方索引中,改用官方轻量级 **umqtt**(自带示例、自动建接收线程),配合 **cJSON** 组 JSON。 + +### 4.2 基本流程 + +```c +struct umqtt_info info = {0}; +info.uri = "tcp://broker.emqx.io:1883"; // EMQX 公共测试服务器 +info.client_id = "rsoc26-xuweiwei-spark"; + +g_mqtt = umqtt_create(&info); // 建客户端 +umqtt_control(g_mqtt, UMQTT_CMD_EVT_CB, mqtt_event_cb); // 事件回调 +umqtt_start(g_mqtt); // 启动(自动连接+接收线程) +umqtt_publish(g_mqtt, UMQTT_QOS0, topic, payload, len, 100); // 发布 +umqtt_subscribe(g_mqtt, topic, UMQTT_QOS0, mqtt_sub_cb); // 订阅 +``` + +订阅回调收到的是 `struct umqtt_pkgs_publish *`,`payload` / `payload_len` 取正文。 + +### 4.3 数据格式 + +发布主题 `rsoc26/xuweiwei/aht10`,JSON:`{"temp":28.3,"humi":55.6,"count":12}`;订阅主题 `rsoc26/xuweiwei/led` 收控制指令。 + +--- + +## 五、文件系统启动流程与 font 分区挂载 + +### 5.1 DFS 启动流程(本次作业重点) + +``` +上电 + │ + ▼ +rtthread_startup() + └─ rt_components_init() ← 按 INIT_*_EXPORT 等级依次执行 + │ ① INIT_BOARD_EXPORT 板级(引脚/I2C/SPI) + │ ② INIT_DEVICE_EXPORT 设备驱动 + │ ③ INIT_COMPONENT_EXPORT SFUD 探测 W25Q64 → 注册 sfud 设备"norflash0" + │ ④ INIT_ENV_EXPORT FAL 初始化 + 分区块设备 + 挂载(本次) + │ ⑤ INIT_APP_EXPORT RW007 wifi 初始化 + ▼ +main() 线程 → 业务逻辑(读写文件) +``` + +我实现的挂载链: + +```c +static int filesystem_mount(void) // INIT_ENV_EXPORT +{ + fal_init(); // 1. FAL 初始化,读取分区表 + mount_elm_on_partition("font", "/font"); // 2. font 分区 → /font + mount_elm_on_partition("filesystem", "/flash"); // 3. filesystem 分区 → /flash +} + +static int mount_elm_on_partition(part, dir) +{ + blk = fal_blk_device_create(part); // 分区 → 块设备 + if (dfs_mount(blk->parent.name, dir, "elm", 0, 0) != RT_EOK) { + dfs_mkfs("elm", blk->parent.name); // 首次无文件系统 → 先格式化 + dfs_mount(blk->parent.name, dir, "elm", 0, 0); + } +} +``` + +### 5.2 分区表 + +W25Q64(8MB)分区(与 rt-spark BSP 一致): + +| 分区 | 偏移 | 大小 | 用途 | +|------|------|------|------| +| easyflash | 0 | 512KB | KV 库 | +| download | 512KB | 1MB | 下载区 | +| wifi_image | 1536KB | 512KB | RW007 OTA | +| **font** | 2048KB | 3MB | 字库(本作业挂载对象) | +| filesystem | 5120KB | 3MB | 文件系统 | + +`drivers/include/fal_cfg.h` 里用 `FAL_PART_TABLE` 宏定义,`FAL_PART_HAS_TABLE_CFG` 打开后 FAL 直接读这张静态表。 + +### 5.3 数据落盘 + +用 POSIX 接口(`open/write/close`)追加写 `/font/Data.txt`: + +```c +fd = open("/font/Data.txt", O_WRONLY | O_CREAT, 0); +lseek(fd, 0, SEEK_END); +snprintf(line, sizeof(line), "Temp: %.1f ; Humi: %.1f ; Count: %d\n", temp, humi, count); +write(fd, line, strlen(line)); +close(fd); +``` + +--- + +## 六、Shell 命令 + +### 6.1 常用命令速查 + +| 命令 | 作用 | +|------|------| +| `ls` / `cat` / `cd` | 文件浏览 | +| `mkfs -t elm <块设备>` | 格式化 FAT | +| `mount <设备> <目录> elm 0 0` | 手动挂载 | +| `df` / `free` | 空间/内存 | +| `list_device` | 查看已注册设备 | +| `fal probe` | FAL 探针(查看分区) | +| `wifi scan / join / status` | WiFi 调试 | +| `ifconfig` / `ping` | 网络调试 | +| `ps` / `list_thread` | 线程状态 | + +### 6.2 自定义命令 + +用 `MSH_CMD_EXPORT` 导出,`argc/argv` 解析参数。本作业导出 3 个: + +```c +MSH_CMD_EXPORT(led_toggle_cmd, toggle the onboard red led); // led_toggle +MSH_CMD_EXPORT(temp_read_cmd, read aht10 temperature and humidity); // temp_read +MSH_CMD_EXPORT(data_file_show, dump /font/Data.txt content); // data_file_show +``` + +--- + +## 七、其他软件包梳理 + +在索引里按分类浏览,常用/值得关注: + +- **系统**:EasyFlash(KV 库)、CmBacktrace(HardFault 回溯)、ulog_file(日志落盘) +- **网络**:pahomqtt(标准 MQTT)、WebClient(HTTP)、AT_Device(AT 模组)、netutils(TFTP 等) +- **多媒体**:LVGL(GUI)、u8g2(点阵屏)、MultiButton(按键) +- **外设**:ds18b20、bh1750、mpu6xxx 等传感器包(都遵循 Sensor 框架) +- **工具**:optparse(命令行解析)、cJSON/jsmn(JSON) + +通用规律:**传感器包走 Sensor 框架、网络包走 SAL、存储包走 FAL/DFS**,掌握了这三条主线,90% 的软件包都能快速上手。 + +--- + +## 八、踩坑记录 + +1. **软件包源码无 Kconfig** —— 需从索引手动拷贝。 +2. **AHT10 缺 Sensor 框架** —— 4.0.3 的 `rtdevice.h` 不包含 `sensor.h`,`sensor_asair_aht10.c` 需手动 `#include ` 且声明与定义类型一致(否则 conflicting types)。 +3. **RW007 默认引脚不符** —— 按原理图改 CS/BOOT0/1/INT/RST。 +4. **Flash/WiFi 共用 SPI** —— 探测 Flash 前拉高 RW007 CS。 +5. **SPI2 时钟域** —— F4 的 SPI2/3 在 APB1,修正 drv_spi.c。 +6. **串口驱动版本** —— 4.0.3 用 `drv_usart.c`(V1),不编 `drv_usart_v2.c`;且 V1 里的 flowcontrol 代码在 4.0.3 无对应字段,需去掉。 +7. **newlib `_sbrk` 未定义** —— cJSON 引入 malloc 后链接报 `_sbrk`,补一个返回 `-1` 的 stub(RT-Thread 堆由 rt_malloc 管理)。 +8. **rt_vsnprintf_full** —— <4.1.0 需手动屏蔽内核 `rt_vsnprintf`。