拓冰建站拓冰建站
首页 / 资讯中心 / 正文

RIOT OS 中集成 Ucglib 彩色图形库:SPI 显示驱动接入与虚拟屏调试实践

RIOT OS 中集成 Ucglib 彩色图形库SPI 显示驱动接入与虚拟屏调试实践【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOTUcglib 是一款面向 LCD 与 OLED 的彩色图形库同时提供底层显示驱动与高层绘图例程。本文以 RIOT OS 仓库中的 pkg/ucglib/README.md 为主体结合 适配层源码、构建配置 与 官方测试应用系统讲解如何在 RIOT OS 中启用 Ucglib、通过ucg_riotos_t结构体把 SPI 外设与引脚映射到该库、以及使用 Dummy/SDL 虚拟屏在无 SPI 硬件环境下调试绘图代码。读完本文你将掌握在 RIOT OS 应用里从零点亮一块 SPI 彩色屏并绘制文字与像素的完整流程。Ucglib 是什么为什么可以跑在 RIOT OS 上Ucglib 最初为 Arduino 开发板编写核心能力是驱动 绘图二合一它自带多种常见彩色屏控制器的设备驱动如 SSD1331、SSD1351、ILI9341 等同时提供ucg_DrawString、ucg_DrawPixel、ucg_ClearScreen这类与具体硬件解耦的高层绘图接口。正因如此只要目标平台能提供合适的底层通信回调它并不限于 Arduino。RIOT OS 官方包仓库在pkg/ucglib目录下维护了这个第三方库的集成其上游版本固定在提交230f15e3bcd3c84977780e84bd855ac89c1959ee见 pkg/ucglib/Makefile许可证为 BSD-2-Clause。集成方式不是简单搬运而是通过补丁patch在原库基础上增加一层 RIOT-OS 接口把 Ucglib 的通信回调映射到 RIOT 的periph_spi与periph_gpio抽象之上。这套集成在 RIOT 的包分类里属于图形库见 pkg/ucglib/doc.txt 的defgroup pkg_ucglib并在 makefiles 等构建体系中与显示设备驱动disp_dev模块配套使用。三步接入USEPKG、头文件、初始化在 RIOT OS 应用中使用 Ucglib 只需三个动作在应用Makefile中加入USEPKG ucglib在源码中#include ucg.h初始化一个ucg_t实例并调用ucg_Init完成配置。其中USEPKG ucglib会触发 pkg/ucglib/Makefile.dep 中的依赖声明自动补全以下模块与特性USEMODULE ztimer USEMODULE ztimer_usec FEATURES_REQUIRED periph_gpio USEMODULE ucglib_riot USEMODULE ucglib_csrc也就是说接入 Ucglib 会自动带上 ztimer 定时器用于延迟处理和periph_gpio引脚能力并编译两个子模块ucglib_csrcUcglib 的核心图形引擎与驱动源码ucglib_riotRIOT 适配层即pkg/ucglib/contrib目录下的代码。同时在编译期pkg/ucglib/Makefile.include 会INCLUDES -I$(PKGDIRBASE)/ucglib/csrc INCLUDES -I$(RIOTBASE)/pkg/ucglib/contrib # The RIOT-OS interface needs this to store peripheral information. CFLAGS -DWITH_USER_PTR把csrc与contrib两个头文件目录加入搜索路径定义WITH_USER_PTR宏——这是关键开关它让 Ucglib 原库编译出ucg_SetUserPtr/ucg_GetUserPtr这一对用户指针接口供 RIOT 适配层存放外设信息。RIOT-OS 接口ucg_com_hw_spi_riotos 回调剖析原版 Ucglib 通过设备回调 通信回调的组合驱动屏幕ucg_Init(ucg, dev_cb, ext_cb, com_cb)的最后一个参数就是通信回调负责把库内部的画点、发字节等消息翻译成真实的硬件操作。RIOT 集成目前只支持 SPI 外设的通信回调ucg_com_hw_spi_riotos它需要用户在初始化前通过ucg_SetUserPtr设置一个ucg_riotos_t结构体该结构体承载外设编号与引脚映射。其完整定义见 pkg/ucglib/contrib/ucg_riotos.htypedef struct { void *user_ptr; /** Pointer to optionally store any additional user-data */ unsigned int device_index; /** Index of the SPI device */ gpio_t pin_cs; /** Pin for SPI CS, GPIO_UNDEF if not used */ gpio_t pin_cd; /** Pin for SPI CD, GPIO_UNDEF if not used */ gpio_t pin_reset; /** Pin for RESET, GPIO_UNDEF if not used */ } ucg_riotos_t;各字段说明字段含义说明user_ptr附加用户数据指针可选用于扩展存放任意自定义数据不影响适配层工作device_indexSPI 设备编号传入 SPI 总线索引回调内通过SPI_DEV(index)转换为spi_tpin_cs片选 CS 引脚不使用时可置GPIO_UNDEFpin_cd命令/数据 CD 引脚不使用时可置GPIO_UNDEF如屏上已硬件接线或驱动不需要pin_reset复位 RESET 引脚不使用时可置GPIO_UNDEF从 适配层实现 可以看到该结构体头文件注释还特别说明结构体可以在不破坏 RIOT 适配的前提下按需扩展例如增加背光引脚这就是user_ptr保留扩展位的原因。回调如何处理 Ucglib 消息ucg_com_hw_spi_riotos本质是一个消息分发器switch (msg)在 pkg/ucglib/contrib/ucg_riotos.c 中对各类消息做了映射Ucglib 消息RIOT 对应实现UCG_COM_MSG_POWER_UP_enable_pins()初始化 CS/CD/RESET 引脚为输出spi_init_pins()初始化 SPI 引脚根据serial_clk_speed换算时钟并spi_acquire()占用总线UCG_COM_MSG_POWER_DOWNspi_release()释放 SPI 总线UCG_COM_MSG_DELAYztimer_sleep(ZTIMER_USEC, arg)微秒级延时UCG_COM_MSG_CHANGE_RESET_LINEgpio_write(pin_reset, arg)拉高/拉低复位线UCG_COM_MSG_CHANGE_CS_LINEgpio_write(pin_cs, arg)控制片选UCG_COM_MSG_CHANGE_CD_LINEgpio_write(pin_cd, arg)切换命令/数据线UCG_COM_MSG_SEND_BYTEspi_transfer_byte(dev, GPIO_UNDEF, true, arg)UCG_COM_MSG_REPEAT_1_BYTE循环spi_transfer_byte重复arg次UCG_COM_MSG_REPEAT_2_BYTES/REPEAT_3_BYTES循环spi_transfer_bytes连续发送 2 / 3 字节UCG_COM_MSG_SEND_STRspi_transfer_bytes一次发送arg字节UCG_COM_MSG_SEND_CD_DATA_SEQUENCE交替写 CD 线与数据字节见下文说明其中值得注意的两个实现细节时钟自动换算适配层内置ucg_serial_clk_speed_to_spi_speed()源码位置把 Ucglib 的serial_clk_speed以 kHz 为单位的数值映射为 RIOT 的spi_clk_t档位100 → SPI_CLK_10MHZ、200 → SPI_CLK_5MHZ、1000 → SPI_CLK_1MHZ、2500 → SPI_CLK_400KHZ、否则回退到SPI_CLK_100KHZ。数值越小对应时钟越快这个换算不需要用户干预。SPI 模式固定为 SPI_MODE_0且spi_acquire时 CS 参数传GPIO_UNDEF——因为片选完全由 Ucglib 通过pin_cs引脚自行控制这与许多 OLED 屏的 4 线 SPIDC CS SCLK SDA时序要求一致。UCG_COM_MSG_SEND_CD_DATA_SEQUENCE的处理展示了 Ucglib 驱动协议每个命令/数据对中第一个字节若非 0 则先写 CD 线电平区分命令还是数据随后通过 SPI 发送下一个字节。这种交替写 CD 的方式正是 SSD1331 等控制器命令/数据总线协议的标准做法。引脚复用约定ucg_riotos_t的三个引脚字段都支持GPIO_UNDEF适配层的_enable_pins()源码位置会先用gpio_is_valid()逐个检查只有有效引脚才会执行gpio_init(pin, GPIO_OUT)初始化为输出各消息处理分支中也同样先判有效性再gpio_write。因此对于没有单独复位脚或已在硬件上固定电平的引脚直接传GPIO_UNDEF即可安全运行。最小示例SSD1331 屏的完整初始化README 给出了一个可直接套用的最小初始化示例这里结合字段含义补充注释#include ucg.h #include ucg_riotos.h ucg_t ucg; ucg_riotos_t user_data { .device_index SPI_DEV(0), /* 使用 SPI 总线 0 */ .pin_cs GPIO_PIN(PA, 0), /* CS 片选 → PA0 */ .pin_cd GPIO_PIN(PA, 1), /* CD 命令/数据 → PA1 */ .pin_reset GPIO_PIN(PA, 2) /* RESET 复位 → PA2 */ }; ucg_SetUserPtr(ucg, user_data); /* 必须在 ucg_Init 之前调用 */ ucg_Init(ucg, ucg_dev_ssd1331_18x96x64_univision, ucg_ext_ssd1331_18, ucg_com_hw_spi_riotos);ucg_Init四个参数的含义ucg图形上下文实例ucg_dev_ssd1331_18x96x64_univision设备驱动回调指明屏幕是 SSD1331 控制器、1.8 寸、96×64 分辨率ucg_ext_ssd1331_18扩展回调补充该型号屏初始化所需的额外命令序列ucg_com_hw_spi_riotos通信回调即上文分析的 RIOT SPI 适配层。顺序上有一个硬性要求ucg_SetUserPtr必须先于ucg_Init因为ucg_com_hw_spi_riotos在收到UCG_COM_MSG_POWER_UP时会执行assert(ucg_GetUserPtr(ucg) ! NULL)见 适配层源码未设置用户指针将直接触发断言失败。此外示例中所有引脚都来自GPIO_PIN(PORT, NUM)宏这是 RIOT 板级定义的引脚写法具体可用引脚以你所用开发板的board.h为准。如果默认接口不够用如果现有回调无法满足需求例如屏幕需要额外的 GPIO、改用并行接口、或接入其他总线README 明确说明可以自行重新实现ucg_com_hw_spi_riotos这样的回调函数只要遵循 Ucglib 的消息协议即可。RIOT 适配层之所以采用补丁 回调而非直接修改库核心正是为了让这种自定义成为可能——你可以完全复用 Ucglib 的绘图引擎只替换自己需要的通信层。没有 SPI 硬件用虚拟屏调试对于无 SPI 外设的目标尤其是native仿真目标Ucglib 包还提供了虚拟显示支持。虚拟屏默认不编译需显式启用USEMODULE ucglib_sdl启用后pkg/ucglib/Makefile 中的ucglib_sdl目标会额外编译$(PKG_SOURCE_DIR)/sys/sdl/dev下的 SDL 虚拟设备pkg/ucglib/Makefile.include 则通过sdl2-config --libs把 SDL2 链接进最终镜像ifneq (,$(filter ucglib_sdl,$(USEMODULE))) LINKFLAGS $(shell sdl2-config --libs) endif使用 SDL 虚拟屏有两个前提条件务必注意仅限 native 目标虚拟屏只适用于 RIOT 的native移植需要 SDL2 且必须是 32 位版本RIOT OS 构建的是 32 位二进制因此需要 32 位的 SDL2 库头文件与库的查找依赖sdl2-config。初始化方式与 SPI 屏完全不同——不需要外设结构体通信回调传NULL即可ucg_t ucg; ucg_Init(ucg, ucg_sdl_dev_cb, ucg_ext_none, NULL);这里ucg_sdl_dev_cb是 SDL 虚拟显示设备回调ucg_ext_none表示无额外扩展命令NULL表示不需要通信回调虚拟设备直接以 SDL 窗口作为画布。测试应用中的第三种选择Dummy 虚拟屏除了 SDLRIOT 测试应用还提供了一种Dummy空输出。在 tests/pkg/ucglib/main.c 中TEST_OUTPUT1时使用ucg_dev_dummy_riotos初始化ucg_Init(ucg, ucg_dev_dummy_riotos, ucg_ext_none, NULL);ucg_dev_dummy_riotos实现在 适配层源码它不产生任何真实像素输出而是把UCG_MSG_DEV_POWER_UP、UCG_MSG_DEV_POWER_DOWN、UCG_MSG_GET_DIMENSION、UCG_MSG_DRAW_PIXEL等内部消息打印到串口例如每累计 128 个像素点打印一次计数其余消息交给ucg_dev_default_cb默认处理。它上报的虚拟分辨率为 128×128适合在完全无硬件的情况下验证绘图 API 调用链是否正确。官方测试应用三种输出模式一次跑通仓库在 tests/pkg/ucglib/ 提供了完整的 Ucglib 包测试应用其 Makefile 用TEST_OUTPUT变量切换三种输出模式TEST_OUTPUT输出目标适用平台关键参数1Dummy 空输出任意无2SDL 虚拟屏仅 native需 32 位 SDL2无3真实 SPI 屏带periph_spi/periph_gpio的板子TEST_SPI、TEST_PIN_CS、TEST_PIN_CD、TEST_PIN_RESET、TEST_DISPLAY、TEST_DISPLAY_EXT测试应用在main.c中为每种模式定义了对应的初始化路径模式 1Dummyucg_Init(ucg, ucg_dev_dummy_riotos, ucg_ext_none, NULL)模式 2SDLucg_Init(ucg, ucg_sdl_dev_cb, ucg_ext_none, NULL)并在主循环中调用ucg_sdl_get_key()处理 SDL 窗口事件模式 3SPI把TEST_SPI等宏直接填入ucg_riotos_t再ucg_SetUserPtrucg_Initucg_riotos_t user_data { .device_index TEST_SPI, .pin_cs TEST_PIN_CS, .pin_cd TEST_PIN_CD, .pin_reset TEST_PIN_RESET, }; ucg_SetUserPtr(ucg, user_data); ucg_Init(ucg, TEST_DISPLAY, TEST_DISPLAY_EXT, ucg_com_hw_spi_riotos);模式 3 还通过 Makefile 的FEATURES_REQUIRED periph_gpio periph_spi做编译期硬件能力检查并在main.c中用#error强制要求TEST_SPI、TEST_DISPLAY、TEST_DISPLAY_EXT、TEST_PIN_CS、TEST_PIN_CD、TEST_PIN_RESET全部显式定义避免漏配。同时tests/pkg/ucglib/README.md 提醒切换不同输出模式之间建议执行make clean因为模式 2 会引入-DTHREAD_STACKSIZE_MAIN48*1024见 Makefile 中TEST_OUTPUT2分支和 SDL 链接等不同的编译参数。各模式运行时参数一览均在 make 命令行传入TEST_SPISPI 设备编号如0TEST_PIN_CS/TEST_PIN_CD/TEST_PIN_RESETCS、命令/数据、复位引脚不使用可传GPIO_UNDEFTEST_DISPLAY显示驱动回调如ucg_dev_ssd1331_18x96x64_univision必须选 SPI 兼容型号TEST_DISPLAY_EXT对应的扩展命令回调如ucg_ext_ssd1331_18。一个典型的 SPI 模式构建命令示例make BOARDyour-board TEST_OUTPUT3 \ TEST_SPI0 \ TEST_PIN_CSGPIO_PIN\(PA,0\) \ TEST_PIN_CDGPIO_PIN\(PA,1\) \ TEST_PIN_RESETGPIO_PIN\(PA,2\) \ TEST_DISPLAYucg_dev_ssd1331_18x96x64_univision \ TEST_DISPLAY_EXTucg_ext_ssd1331_18 \ flash term绘图逻辑测试应用怎么用 Ucglib API无论哪种输出模式初始化完成后测试应用都会走同一套高层绘图流程见 tests/pkg/ucglib/main.c这是 Ucglib 绘图 API 的直观演示ucg_SetFontMode(ucg, UCG_FONT_MODE_TRANSPARENT); ucg_SetFont(ucg, ucg_font_helvB12_tf); while (1) { ucg_ClearScreen(ucg); switch (screen) { case 0: ucg_SetColor(ucg, 0, 189, 32, 43); /* 设置前景色 RGB */ ucg_DrawString(ucg, 0, 20, 0, THIS); /* 在 (0,20) 绘制字符串 */ break; case 1: ucg_SetColor(ucg, 0, 63, 167, 136); ucg_DrawString(ucg, 0, 20, 0, IS); break; case 2: /* 逐像素绘制 logo 位图96×48 RGB */ for (int y 0; y 48; y) { for (int x 0; x 96; x) { uint32_t offset (x (y * 96)) * 3; ucg_SetColor(ucg, 0, logo[offset 0], logo[offset 1], logo[offset 2]); ucg_DrawPixel(ucg, x, y); } } break; } screen (screen 1) % 3; ztimer_sleep(ZTIMER_USEC, US_PER_SEC); /* 每秒切换一屏 */ }涉及的核心 APIucg_SetFontMode(ucg, UCG_FONT_MODE_TRANSPARENT)透明字体模式ucg_SetFont(ucg, ucg_font_helvB12_tf)选择内嵌的 Helvetica 粗体 12 号字体ucg_SetColor(ucg, 0, r, g, b)设置当前颜色索引 0 为前景色后三参数为 RGB 分量ucg_DrawString(ucg, x, y, dir, str)在指定坐标绘制文本ucg_DrawPixel(ucg, x, y)绘制单像素用于逐点渲染位图ucg_ClearScreen(ucg)清屏。预期运行结果源自 tests/pkg/ucglib/README.md如果一切正常应用会循环切换三屏依次显示 THIS、IS 与一张内嵌 logo 图像组合起来即 This is RIOT-OS。在 SDL 模式下你能在本地弹出的 SDL 窗口中直接看到这三屏的彩色渲染效果。在应用中使用时的要点小结依赖自动补齐USEPKG ucglib后无需手动添加 ztimer / gpio / csrc 等依赖Makefile.dep 已处理顺序不能错ucg_SetUserPtr(ucg, user_data)必须在ucg_Init之前否则适配层assert会失败引脚可选pin_cs/pin_cd/pin_reset均可传GPIO_UNDEF适配层会按有效性自动跳过初始化与写入时钟自动换算SPI 时钟由 Ucglib 的serial_clk_speed自动映射到SPI_CLK_*档位SPI 模式固定为SPI_MODE_0CS 由库管理spi_acquire不传 CS 引脚片选时序完全由pin_cs通过 GPIO 控制虚拟屏需显式开启USEMODULE ucglib_sdl且仅限带 32 位 SDL2 的 native 目标自定义回调可行默认 SPI 回调不满足需求时可参照 ucg_riotos.h 重新实现通信回调绘图引擎可完全复用。总结Ucglib 包为 RIOT OS 提供了上层绘图 API 底层设备驱动的完整彩色屏解决方案。其集成核心在于 contrib 适配层通过ucg_riotos_t承载 SPI 设备与引脚映射以ucg_com_hw_spi_riotos回调把 Ucglib 的消息协议翻译为 RIOT 的periph_spi/periph_gpio调用并自动处理时钟换算、延时ztimer与引脚初始化。配合 Dummy / SDL 两种虚拟屏输出开发者可以在没有真实屏幕的情况下先行验证绘图逻辑再无缝切换到实体 SPI 屏。结合 tests/pkg/ucglib/ 测试应用你可以完整走通虚拟屏调试 → 实体屏点亮 → 绘制文字与位图的整条开发链路。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门