资讯动态

RT-Thread W60X SDK实战:从Env配置到Wi-Fi与LVGL界面开发

发布时间:2026/10/17 14:56:15 来源:尧图企业网站定制
简介面向 W60X 系列无线微控制器的 RT-Thread 软件开发套件适合从事 Wi-Fi IoT 产品开发的嵌入式工程师与物联网学习者。SDK 基于轻量级、可裁剪的 RT-Thread 实时操作系统并集成 LVGL 图形库覆盖 BSP 板级支持、中间件、RTOS 核心组件、库文件、示例工程与编译烧录工具可支撑从底层驱动到图形界面的完整应用开发。压缩包约 228.22MB共 0 个文件目前暂无文件类型明细描述中涉及的 BSP、Middleware、Libraries、Examples、Tools 等模块便于开发者按需查阅与快速上手。已有 1321 人学习浏览。对希望基于 W60X 芯片快速搭建低功耗、高性能物联网应用并实现美观交互界面的开发者来说这套 SDK 提供了较为完整的软件基础和参考代码能够有效缩短项目评估与原型开发周期。1. 拿到 RT-Thread_W60X_SDK 后先别急着编译这个压缩包解出来不是一份源代码而是一整套基于 RT-Thread 的 Wi-Fi SoC 嵌入式工程。里面装着内核、外设驱动、网络协议栈、LVGL 图形库和一堆官方示例第一次打开的人很容易被目录结构劝退。这篇文章按我实际拆机的顺序走一遍先讲清 SDK 里每个目录管什么再用 Env 工具做配置裁剪、编译烧录、跑通外设和 Wi-Fi 配网最后把手里的 LVGL 界面从官方 Demo 换成自己的。适合刚拿到开发板的开发者也适合想在 RT-Thread 上同时调 Wi-Fi 和 GUI 的老手。2. 拆开 SDK目录结构、Env 工具与第一版工程生成2.1 解压后先看这几个目录bsp 与 components 的分工解压完成后不要急着进某个目录找 main 函数先把顶层结构扫一遍。这个 SDK 的目录组织方式和绝大多数 RT-Thread 工程一致核心就几个固定角色目录职责需要关注的文件rt-thread/RT-Thread 内核本体包含调度、信号量、消息队列、设备框架rtconfig.h 由 menuconfig 生成bsp/板级支持包存放具体芯片的启动代码、时钟、外设驱动初始化board.c、linker script、SConscriptcomponents/组件层包括 FinSH 控制台、Wi-Fi 驱动框架、LVGL 移植层drv_wifi.c、drv_lcd.c 等packages/软件包目录LVGL、网络库等以软件包形式挂载用 pkgs --update 拉取tools/构建辅助脚本env 工具依赖的脚本docs/编译说明、硬件设计参考拿到板子先读这份我一般习惯先打开 bsp 下对应板卡文件夹里的 readme 或 docs确认这款板子的 Flash 分区、默认波特率和烧录方式再碰代码。分区表通常在 linker script 里烧录地址填错会直接翻车。2.2 Env 与 menuconfig把裁剪做在编译之前RT-Thread 的配置不走 Keil 的图形界面而是用 env 工具配合 menuconfig 完成。env 本质是一套命令行工具集Windows 下打开 env.bat 后会进入一个定制的控制台环境里面带了 scons、python 和交叉编译工具链的路径。# 进入板卡对应的 bsp 目录 cd bsp/w60x # 打开图形化配置界面 menuconfig # 保存退出后生成 rtconfig.h # 如果需要拉取软件包比如 LVGL 依赖执行 pkgs --updatemenuconfig 界面里按空格键切换选中状态方向键导航斜杠可以搜索组件名称。保存退出后配置会自动写进 rtconfig.h后续编译时所有源码都依据这个头文件做条件编译。这里要留意pkgs --update 必须联网执行它会从包仓库拉取 components 和 packages 里声明过的软件包源码。如果这一步报网络错误先检查代理或 DNS。2.3 生成工程从空目录到可编译的 applicationsmenuconfig 只能管配置项源码文件的新增和删除由 SConscript 脚本控制。applications 目录是用户代码的主战场main.c 在这里主入口可以自己写也可以沿用 SDK 默认的示例入口。from building import * cwd GetCurrentDir() src Glob(*.c) Glob(*.cpp) CPPPATH [cwd] group DefineGroup(Applications, src, depend [], CPPPATH CPPPATH) Return(group)这段脚本的作用是把当前目录下所有 .c 和 .cpp 文件加入编译列表CPPPATH 把当前目录加到头文件搜索路径里。depend 字段控制这个组是否参与编译依赖了某个未打开的宏的话整个目录都会被跳过。常见做法是在 main.c 里通过宏控制示例代码的开关比如 Kconfig 里打开了 LVGL_DEMO示例代码才被编译这样既保留官方示例又能随时切回自己的逻辑。3. 编译、烧录与跑通第一个外设3.1 用 scons 编译命令、产物与烧录配置完成并且 applications 目录里的 SConscript 没问题之后编译只是几行命令的事。这个 SDK 用 scons 作为构建系统它读 SConstruct 和 SConscript 脚本组织整个构建过程。# 在 bsp 目录下执行全量编译-j 参数控制并行任务数 scons -j16 # 如果只想编译增量内容直接 scons # 编译产物在 bsp 目录下的 build 文件夹里 ls build/编译完成后build 目录里会出现 rtthread.bin、rtthread.elf。bin 是裸二进制固件elf 带符号表调试时用 elf。烧录时用官方烧录工具选择 bin 文件不需要手动指定地址——工具会读取 linker script 里的 FLASH 起始地址和长度。但要注意部分版本烧录工具默认擦除整片 Flash如果你之前烧过其他固件得先备份原厂固件没有后悔药。3.2 点灯与串口日志确认内核和驱动都活着固件烧进去之后第一件事不是跑网络而是验证最基本的 GPIO 输出和串口打印。这两个能通说明内核调度、时钟初始化、引脚复用都正常。W60x 这类 Wi-Fi SoC 的 GPIO 操作一般通过 RT-Thread 的 pin 设备框架不直接操作寄存器。#include rtthread.h #include drv_gpio.h #include rtdevice.h #define LED_PIN GET_PIN(C, 5) // 根据板卡原理图确认引脚 void led_thread_entry(void *parameter) { rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT); while (1) { rt_pin_write(LED_PIN, PIN_LOW); rt_kprintf(LED ON\n); rt_thread_mdelay(500); rt_pin_write(LED_PIN, PIN_HIGH); rt_kprintf(LED OFF\n); rt_thread_mdelay(500); } }rt_pin_mode 设置引脚方向PIN_MODE_OUTPUT 表示普通推挽输出rt_pin_write 的第二个参数填 PIN_LOW 或 PIN_HIGH对应的电气电平跟板卡设计有关有些板子 LED 是低电平点亮写反了灯不亮但代码没报错。GET_PIN 宏的用法是先看板卡原理图确认引脚编号这里 C 表示端口5 表示引脚号具体宏在 drv_gpio.h 里定义。串口上用 rt_kprintf 打印默认走调试串口。3.3 按键输入与中断回调点灯能跑通后建议再接一个按键输入熟悉中断上下文的处理方式。RT-Thread 的 pin 驱动支持中断回调但回调函数跑在中断上下文不能调用会阻塞的 API。void btn_callback(void *args) { rt_kprintf(bt pressed\n); rt_sem_release(btn_sem); } void btn_thread_entry(void *parameter) { rt_pin_mode(BTN_PIN, PIN_MODE_INPUT_PULLUP); rt_pin_attach_irq(BTN_PIN, PIN_IRQ_MODE_FALLING, btn_callback, RT_NULL); rt_pin_irq_enable(BTN_PIN, PIN_IRQ_ENABLE); while (1) { rt_sem_take(btn_sem, RT_WAITING_FOREVER); rt_kprintf(handle btn event\n); } }回调里只做标记和资源释放具体业务逻辑放到线程里这是中断编程的基本纪律。PIN_IRQ_MODE_FALLING 表示下降沿触发如果你的板子按键按下时引脚被拉高就改成 RISING。按键消抖没写在中断里因为机械开关抖动会连续触发多次常见做法是加一个 20ms 左右的滤波或者记录最后一次触发时间时间差小于 30ms 直接忽略。4. Wi-Fi 配网与网络通信实战4.1 配网方式怎么选AP 配网与 SmartConfigWi-Fi 芯片调试绕不开配网问题。这个 SDK 里提供了至少两条路径一种是设备自己开热点手机连上去后把路由器 SSID 和密码发给设备另一种是 SmartConfig手机 App 把 Wi-Fi 密码编码进广播包设备监听空中报文解出来。配网方式适用场景优点缺点AP 配网量产前调试可靠不依赖路由器特性需要切换热点交互多SmartConfig带 App 的消费产品用户体验好一键配网对路由器组播隔离敏感成功率看环境默认 SSID 配置开发板验证最快不适合量产每台设备都要改代码开发阶段我通常直接烧一个固定 SSID 和密码进代码先把链路调通再去做配网逻辑。配网逻辑本身也是一段值得反复测试的代码——它跟运行时的网络状态强相关光看代码很难发现问题。4.2 连接回调与断线重连Wi-Fi 连接不是一个同步返回结果的函数SDK 一般通过事件回调把结果通知给应用层连接、断线、IP 获取成功是三个最常监听的事件。#include rtdevice.h #include wlan_work.h static void wifi_event_handler(int event, struct rt_wlan_buff *buff, void *parameter) { if (event RT_WLAN_EVT_READY) { rt_kprintf(wifi connected, ip ready\n); rt_sem_release(conn_sem); } else if (event RT_WLAN_EVT_DISCONNECTED) { rt_kprintf(link lost\n); reconnect_flag 1; } } int wifi_connect_with_module(const char *ssid, const char *password) { rt_wlan_register_event_handler(RT_WLAN_EVT_READY, wifi_event_handler, RT_NULL); rt_wlan_register_event_handler(RT_WLAN_EVT_DISCONNECTED, wifi_event_handler, RT_NULL); rt_wlan_connect(ssid, strlen(ssid), password, strlen(password)); if (rt_sem_take(conn_sem, rt_tick_from_millisecond(10000)) ! RT_EOK) { rt_kprintf(connect timeout\n); return -1; } return 0; }rt_wlan_connect 只负责发出连接请求真正连上是在 READY 事件里所以用信号量把连接线程阻塞最多 10 秒。超过这个时间还没拿到 IP大概率是密码错误或者路由器处在 5G-only 模式——很多 Wi-Fi 模块只支持 2.4G 频段。断线重连不能直接在事件回调里调用 rt_wlan_connect 再连回调上下文里做耗时操作容易触发断言我习惯的做法是置一个重连标志让主循环或独立线程去处理。4.3 Socket 客户端上报一个加密数据包Wi-Fi 通了之后网络编程基本走 BSD socket 接口跟 PC 上的习惯没有太大差异。上面板跑的第一步通常是连接 TCP 服务器把设备状态实时上报。#include arpa/inet.h #include netdb.h #include string.h int tcp_client_send(const char *host, int port, uint8_t *payload, int len) { int sock socket(AF_INET, SOCK_STREAM, 0); if (sock 0) return -1; struct addrinfo hints {0}; struct addrinfo *res RT_NULL; hints.ai_family AF_INET; hints.ai_socktype SOCK_STREAM; if (getaddrinfo(host, RT_NULL, hints, res) ! 0) { closesocket(sock); return -2; } struct sockaddr_in server_addr; server_addr.sin_family AF_INET; server_addr.sin_port htons(port); server_addr.sin_addr.s_addr ((struct sockaddr_in *)res-ai_addr)-sin_addr.s_addr; if (connect(sock, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) { closesocket(sock); return -3; } send(sock, payload, len, 0); closesocket(sock); return 0; }socket 和 connect 是标准接口有两个坑值得单独提。第一getaddrinfo 在嵌入式平台比较容易踩 DNS 超时的坑域名解析失败时 connect 永远到不了建议先在代码里直连 IP 验证链路是否通第二send 返回的字节数可能小于 len不能只发一次就当完成TCP 协议栈在内存紧张时会缓存部分数据。量产代码里通常还会给 connect 加一个超时时间默认的阻塞时间可能长达几十秒不能接受。LwIP 的配置在 menuconfig 的 Network 菜单里把 TCP_SND_BUF 和 TCP_WND 调到合适的大小太小会影响吞吐太大占内存按设备实际 RAM 余量决定。5. 避坑与常见问题排查5.1 编译阶段env 命令不识别现象打开 env.bat 后输入 scons 提示不是内部或外部命令或者执行 menuconfig 直接报错。 原因常见有两种。一是 env.bat 里预设的工具链路径不匹配当前 SDK 版本二是把 SDK 解压到了带空格或中文的路径下脚本解析出错。 解决先确认在 env 的定制控制台里执行python --version能打印版本说明基础环境没问题然后把整个 SDK 挪到纯英文路径下比如 D:\w60x_sdk重新打开 env.bat。还不行就检查环境变量 PATH 里是否包含%ENV_PATH%\tools这类配置。5.2 烧录后串口没有任何输出现象固件烧录成功但串口工具里看不到任何打印复位也没用。 原因大概率是串口波特率不对或者芯片根本没有从 Flash 启动。W60X 默认调试波特率一般在文档里写明如果按照通用 115200 配置收不到数据先试 921600 或 460800。另一类是烧录工具勾选了整体擦除把 bootloader 也擦掉了没有引导程序自然跑不起来。 解决先在设备管理器里确认串口号没选错再看波特率。firmware 包里有原厂烧录脚本的话直接重新烧全量镜像不要只烧 app 分区。如果之前改过 linker script检查 FLASH 起始地址是不是被覆盖了。5.3 Wi-Fi 连接成功但每隔几秒掉线现象Wi-Fi 能连上路由器能收发包但不定时断开事件回调频繁触发 DISCONNECTED。 原因这种问题一半在供电一半在内存。Wi-Fi 发射瞬间电流很大如果板卡的 LDO 余量不足电压跌落会导致射频前端复位内存不足时 LwIP 的缓冲区申请失败协议栈主动断开连接日志里能看到 no mem 字样。 解决用稳压电源直接给板卡供电排除 USB 供电压降。软件上把 LwIP 的 MEM_SIZE 和 PBUF_POOL_SIZE 调大同时减少没必要的线程栈占用。调完之后连续跑一晚上 pings 看丢包率玄学问题必须先排除硬件。5.4 LVGL 界面花屏、刷新错位现象LVGL Demo 能跑起来但屏幕显示内容出现撕裂、颜色不对或者刷新只更新半个屏。 原因典型的帧缓冲配置错误。LCD 驱动里颜色深度设置成 RGB888但 LVGL 的 LV_COLOR_DEPTH 还是 16数据格式对不上或者是帧缓冲地址没有按 LCD 控制器要求对齐。 解决先把颜色深度统一LCD 是 RGB565 就把 LV_COLOR_DEPTH 改成 16并在 lv_conf.h 里确认 LV_COLOR_16_SWAP 的开关跟 LCD 控制器的字节序匹配。帧缓冲地址尽量定义到 4 字节对齐的静态数组不要用 malloc 随意分配。6. LVGL 接入把官方 Demo 换成你自己的界面6.1 显示驱动与帧缓冲的对接细节SDK 里集成 LVGL 之后跑官方 Demo 不难真正值钱的是把显示驱动换成你的屏。接线方式不同驱动代码必须跟着改。LVGL 只关心两个接口flush 回调负责把缓冲区内容送到屏幕tick 负责提供时间基准。static lv_disp_buf_t disp_buf; static lv_color_t buf_1[LV_HOR_RES_MAX * 40]; // 单缓冲按行分配 static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { // 把 color_p 里的像素数据按 area 的坐标写入 LCD lcd_draw_bitmap(area-x1, area-y1, area-x2, area-y2, (uint8_t *)color_p); lv_disp_flush_ready(disp_drv); // 通知 LVGL 可以继续绘制 } void lvgl_port_init(void) { lv_init(); lv_disp_buf_init(disp_buf, buf_1, RT_NULL, LV_HOR_RES_MAX * 40); lv_disp_drv_t disp_drv; lv_disp_drv_init(disp_drv); disp_drv.buffer disp_buf; disp_drv.flush_cb disp_flush; lv_disp_drv_register(disp_drv); }area 参数告诉驱动这次刷新的范围color_p 指向 LVGL 渲染好的像素数据。驱动层只需要按坐标把数据拷贝到 LCD 的显存或 SPI 发送缓冲区。缓冲区大小直接影响刷新体验单缓冲 10 行撕裂感会明显双缓冲能缓解撕裂但 RAM 占用直接翻倍。这个屏分辨率不高的话用 40 行单缓冲也能接受。6.2 内存与刷新率调优LVGL 的内存配置在 lv_conf.h 里关键宏就几个调参要有依据配置项建议值影响LV_MEM_SIZE8KB 起复杂界面 16KB 以上太小则控件一多就分配失败LV_HOR_RES_MAX与屏幕分辨率一致超过实际分辨率会浪费内存LV_VER_RES_MAX与屏幕分辨率一致同上LV_COLOR_DEPTH16跟 LCD 控制器保持一致LV_TICK_CUSTOM1用 RT-Thread 的 tick 喂 LVGL主循环里要周期性调用 lv_task_handler()在 RT-Thread 上一般放到一个单独线程里用 rt_thread_mdelay(20) 控制节奏。lv_tick_inc(1) 放在 SysTick 钩子里保证 LVGL 内部动画计时准确。6.3 从官方界面到自己界面的修改路径不要直接删官方 Demo 重写。先把官方 Demo 的顶层界面跑起来找到它初始化 login 或 main screen 的函数确认控件树的创建位置。改自己的界面时保留底层的 lvgl_port_init替换上层界面代码。新手容易在自定义字体和图片资源上卡住LVGL 的图片转换工具会生成 C 数组注意把数组放到内部 Flash不要让运行时再加载。我最初调这块屏幕时直接拿了官方 Demo 的初始化函数没改动结果只是换了个分辨率就花屏了一整天。后来强制要求自己每次接新屏幕都先看时序图、再确认颜色深度和字节序最后才碰代码。从那以后凡是遇到显示异常我第一反应都是去查硬件参数而不是改软件算法排查速度快了很多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑