ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

在 Raspberry Pi Pico 上移植 u8g2:RP2040/RP2350 HAL 参考实现全解析

在 Raspberry Pi Pico 上移植 u8g2:RP2040/RP2350 HAL 参考实现全解析 嵌入式嵌入式GUI驱动开发【免费下载链接】u8g2U8glib library for monochrome displays, version 2项目地址https://gitcode.com/gh_mirrors/u8/u8g2点击查看免费下载导读本指南围绕 u8g2 仓库中面向 Raspberry Pi Pico 系列RP2040 / RP2350提供的 HAL 参考实现展开介绍如何借助官方 Pico SDK 将 u8g2 的 GPIO、延时与 SPI 通信回调接入 Pico 硬件从而驱动基于 ST7305 等控制器的单色屏。读完本文你将掌握u8g2_pico_init的完整调用流程、硬件配置结构体的每个字段、底层字节与 GPIO 消息回调的语义以及如何把这份参考实现改造成自己项目里可复用的显示驱动层。一、这份 HAL 的定位参考实现而非通用移植在 sys/arm/rpi-pico/README.md 中作者明确给出了这份代码的定位与边界目的演示如何将 u8g2 与 Raspberry Pi Pico SDK 集成内容提供一套最小化的 HAL 层覆盖 GPIO、延时和通信回调性质一份参考实现reference implementation不是完全通用、开箱即用的完整移植用法按需复制并适配到具体项目。这份示例源于 u8g2 issue #2159 中欢迎提供示例 HAL 实现的请求README 原文注明。虽然示例所用的显示控制器ST7305与该 issue 中讨论的型号不同但整体结构与集成方式完全一致可以平移到其他基于 RP2040 的系统上。另外两点重要承诺不改动仓库中任何已有的 u8g2 HAL 实现示例完全自包含不影响其他平台的构建。实测硬件覆盖两代 Pico平台主控备注Raspberry Pi PicoRP2040同一 HAL 实现直接可用Raspberry Pi Pico 2RP2350无需任何修改即可运行二、目录结构与文件职责整个参考实现只有三个文件结构非常克制sys/arm/rpi-pico/ ├── README.md # 定位、范围与测试说明 ├── examples/ │ └── pico_u8g2_st7305_Hello-world.c # 可运行的最小示例 └── hal/ ├── u8g2_pico_hal.h # 公共 API 与配置结构体 └── u8g2_pico_hal.c # HAL 实现字节/GPIO 回调hal/u8g2_pico_hal.h 定义对外接口和全部配置结构体hal/u8g2_pico_hal.c 实现硬件初始化和两个 u8x8 回调examples/pico_u8g2_st7305_Hello-world.c 给出最小可运行程序驱动一块 168×384 的 ST7305 单色屏。三、公共 API 与配置结构体3.1 接口类型枚举u8g2_pico_interface_t目前只定义了硬件 SPI 一种可用接口typedef enum { U8G2_PICO_IF_HW_SPI 0, U8G2_PICO_IF_SW_SPI /* reserved for future */ } u8g2_pico_interface_t;U8G2_PICO_IF_SW_SPI被注释为为将来保留当前若传入该值u8g2_pico_init会直接返回、不执行任何初始化这一点在实现中可以看到显式的守卫判断。3.2 硬件 SPI 配置typedef struct { spi_inst_t *spi; /* spi0 或 spi1 */ uint32_t baudrate; uint8_t pin_sck; uint8_t pin_mosi; uint8_t pin_cs; uint8_t pin_dc; uint8_t pin_rst; } u8g2_pico_spi_hw_config_t;字段含义与注意事项字段含义说明spiPico SDK 的 SPI 实例只能取spi0或spi1baudrateSPI 时钟频率Hz示例取 4 MHz需根据屏规格调整pin_sckSPI 时钟引脚通过gpio_set_function(..., GPIO_FUNC_SPI)复用pin_mosiSPI 数据输出引脚同上复用为 SPI 功能pin_cs片选引脚普通 GPIO 输出由回调手动控制电平pin_dc数据/命令选择引脚普通 GPIO 输出pin_rst复位引脚普通 GPIO 输出注意一个容易忽略的设计点SCK 与 MOSI 走 SPI 硬件复用而 CS / DC / RST 全部用普通 GPIO 手动控制这与 u8g2 的U8X8_MSG_GPIO_*消息机制天然契合见下文第五节。3.3 统一配置对象与 setup 函数类型typedef struct { u8g2_pico_interface_t interface; u8g2_pico_spi_hw_config_t hw_spi; } u8g2_pico_config_t; typedef void (*u8g2_pico_setup_fn)( u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_cb );u8g2_pico_setup_fn的函数指针签名与 u8g2 的u8g2_Setup_xxx()系列函数签名完全一致头文件注释专门强调这一点这是整个 HAL 能复用任意u8g2_Setup_xxx的关键——用户只需要把对应屏的 setup 函数地址传进来即可。3.4 唯一的公共入口u8g2_pico_initvoid u8g2_pico_init( u8g2_t *u8g2, u8g2_pico_setup_fn setup_fn, const u8g2_pico_config_t *cfg );按头文件注释该函数的职责与调用后状态为配置 SPI 与 GPIO安装 u8g2 回调初始化并唤醒显示调用返回后u8g2完全就绪、可直接开始绘图无需再做任何硬件设置。四、初始化流程源码级拆解hal/u8g2_pico_hal.c 中的u8g2_pico_init实现如下关键行:void u8g2_pico_init(u8g2_t *u8g2, u8g2_pico_setup_fn setup_fn, const u8g2_pico_config_t *cfg) { if (cfg-interface ! U8G2_PICO_IF_HW_SPI) return; g_hw_cfg cfg-hw_spi; setup_fn(u8g2, U8G2_R3, u8x8_byte_pico_hw_spi, u8x8_gpio_and_delay_pico); u8g2-u8x8.user_ptr g_hw_cfg; u8g2_InitDisplay(u8g2); u8g2_SetPowerSave(u8g2, 0); }四个步骤逐一说明接口守卫非U8G2_PICO_IF_HW_SPI直接返回避免误用保存配置把调用方的配置拷贝到静态变量g_hw_cfg并通过u8g2-u8x8.user_ptr挂到 u8g2 实例上——两个回调都从user_ptr取配置这是典型的 u8g2 回调间共享上下文的做法调用 setup 函数这里固定传入U8G2_R3旋转参数、u8x8_byte_pico_hw_spi字节回调和u8x8_gpio_and_delay_picoGPIO/延时回调。setup_fn内部会完成 display、CAD命令地址解码、buffer 三层装配初始化显示并解除省电u8g2_InitDisplay(u8g2)是宏展开为u8x8_InitDisplay(u8g2_GetU8x8(u8g2))见 csrc/u8g2.hu8g2_SetPowerSave(u8g2, 0)让屏退出掉电模式开始显示。以示例使用的u8g2_Setup_st7305_168x384_f为例它定义在 csrc/u8g2_d_setup.cvoid u8g2_Setup_st7305_168x384_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) { uint8_t tile_buf_height; uint8_t *buf; u8g2_SetupDisplay(u8g2, u8x8_d_st7305_168x384, u8x8_cad_011, byte_cb, gpio_and_delay_cb); buf u8g2_m_21_48_f(tile_buf_height); u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_horizontal_right_lsb, rotation); }其中u8x8_d_st7305_168x384是显示驱动回调位于 csrc/u8x8_d_st7305.c负责初始化序列、清屏、翻转、省电等显示级消息u8x8_cad_011是命令地址解码器CAD即命令字节 / 参数字节的 0-1-1 编码模式u8g2_m_21_48_f位于 csrc/u8g2_d_memory.c为全缓冲模式分配内存后缀f表示 full buffer21是宽度 tile 数21×8 168 像素48是高度 tile 数48×8 384 像素静态数组大小为8064 字节。_f后缀意味着整帧缓冲常驻 RAM8064 字节绘制完成后一次性发送如果内存紧张可改用_1/_2分页缓冲版本u8g2_m_21_48_1、u8g2_m_21_48_2牺牲一点绘制流程换取内存占用大幅下降。README 中参考实现的定位在这里得到印证HAL 层与显示驱动解耦换屏只需换 setup 函数。五、两个核心回调字节传输与 GPIO/延时5.1 消息机制背景u8g2 底层u8x8 层通过消息msg驱动一切硬件操作消息常量定义在 csrc/u8x8.h#define U8X8_MSG_GPIO_AND_DELAY_INIT 40 /* 初始化 GPIO 方向与通信外设 */ #define U8X8_MSG_DELAY_MILLI 41 /* 参数: 毫秒 */ #define U8X8_MSG_DELAY_10MICRO 42 /* 参数: 10 微秒 */ #define U8X8_MSG_DELAY_100NANO 43 /* 参数: 100 纳秒 */ #define U8X8_MSG_DELAY_NANO 44 /* 参数: 纳秒 */ #define U8X8_MSG_GPIO(x) (64(x)) /* 通用 GPIO 消息按引脚编号偏移 */ #define U8X8_MSG_GPIO_CS U8X8_MSG_GPIO(U8X8_PIN_CS) #define U8X8_MSG_GPIO_DC U8X8_MSG_GPIO(U8X8_PIN_DC) #define U8X8_MSG_GPIO_RESET U8X8_MSG_GPIO(U8X8_PIN_RESET)头文件中还有配套的便捷宏u8x8_gpio_SetCS、u8x8_gpio_SetDC、u8x8_gpio_Init等它们会把消息转发给gpio_and_delay_cb。理解这套消息体系就理解了 Pico HAL 为什么是这种写法。5.2 GPIO 与延时回调static uint8_t u8x8_gpio_and_delay_pico(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { const u8g2_pico_spi_hw_config_t *cfg (const u8g2_pico_spi_hw_config_t *)u8x8-user_ptr; switch (msg) { case U8X8_MSG_GPIO_AND_DELAY_INIT: spi_init(cfg-spi, cfg-baudrate); spi_set_format(cfg-spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); gpio_set_function(cfg-pin_sck, GPIO_FUNC_SPI); gpio_set_function(cfg-pin_mosi, GPIO_FUNC_SPI); gpio_init(cfg-pin_cs); gpio_init(cfg-pin_dc); gpio_init(cfg-pin_rst); gpio_set_dir(cfg-pin_cs, GPIO_OUT); gpio_set_dir(cfg-pin_dc, GPIO_OUT); gpio_set_dir(cfg-pin_rst, GPIO_OUT); gpio_put(cfg-pin_cs, 1); gpio_put(cfg-pin_rst, 1); break; ...初始化消息一次性完成SPI 外设初始化8 位、CPOL0、CPHA0、MSB first——这是绝大多数单色屏的 4-wire SPI 标准极性、SCK/MOSI 复用为 SPI 功能、CS/DC/RST 设为推挽输出并置于高电平CS 高 非选中RST 高 不复位。延时消息的实现非常直白且有值得一提的工程取舍case U8X8_MSG_DELAY_NANO: // 请求 arg_int 纳秒 sleep_us(arg_int); // 用微秒近似慢约 1000 倍 break; case U8X8_MSG_DELAY_100NANO: sleep_us(arg_int); // 同样用微秒近似 break; case U8X8_MSG_DELAY_10MICRO: sleep_us(arg_int * 10); break; case U8X8_MSG_DELAY_MILLI: sleep_ms(arg_int); break;源码注释明确承认RP2040 SDK 没有sleep_ns()因此U8X8_MSG_DELAY_NANO用sleep_us(arg_int)近似实际延时比请求值慢约 1000 倍。作者评估在实践中通常没问题因为这类纳秒级延时多用于满足显示器的建立/保持时间时序余量本就宽松。如果你要适配时序极其敏感的控制器这里需要按你的屏数据手册重新核算。GPIO 输出消息则是一对一的电平映射case U8X8_MSG_GPIO_CS: gpio_put(cfg-pin_cs, arg_int); break; case U8X8_MSG_GPIO_DC: gpio_put(cfg-pin_dc, arg_int); break; case U8X8_MSG_GPIO_RESET: gpio_put(cfg-pin_rst, arg_int); break; default: u8x8_SetGPIOResult(u8x8, 1); // 默认返回 1 break;最后return 1表示消息已被处理。5.3 字节传输回调static uint8_t u8x8_byte_pico_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch (msg) { case U8X8_MSG_BYTE_INIT: u8x8_gpio_SetCS(u8x8, u8x8-display_info-chip_disable_level); break; case U8X8_MSG_BYTE_START_TRANSFER: u8x8_gpio_SetCS(u8x8, u8x8-display_info-chip_enable_level); u8x8-gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8-display_info-post_chip_enable_wait_ns, NULL); break; case U8X8_MSG_BYTE_SEND: spi_write_blocking(cfg-spi, (const uint8_t *)arg_ptr, arg_int); break; case U8X8_MSG_BYTE_END_TRANSFER: u8x8-gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8-display_info-pre_chip_disable_wait_ns, NULL); u8x8_gpio_SetCS(u8x8, u8x8-display_info-chip_disable_level); break; case U8X8_MSG_BYTE_SET_DC: u8x8_gpio_SetDC(u8x8, arg_int); break; default: return 0; } return 1; }这段代码展示了 u8g2 通信时序模型的精髓——CS 的拉高拉低与延时都不是硬编码而是取自display_info里的控制器参数chip_enable_level/chip_disable_level该控制器片选的有效/无效电平post_chip_enable_wait_nsCS 拉低选中后到数据发送前的建立时间pre_chip_disable_wait_ns数据发送完到 CS 释放前的保持时间。因此换用不同控制器时只要 u8g2 的驱动表u8x8_d_xxx.c中的u8x8_display_info_t数据正确这份字节回调无需修改就能适配不同的片选极性与时序要求。数据通路本身只有一行核心代码spi_write_blocking(cfg-spi, arg_ptr, arg_int)即 Pico SDK 的阻塞式 SPI 发送。六、最小可运行示例examples/pico_u8g2_st7305_Hello-world.c 是完整的最小程序#include stdio.h #include pico/stdlib.h #include u8g2.h #include u8g2_pico_hal.h u8g2_t u8g2; /* 静态生命周期配置 */ static const u8g2_pico_config_t display_cfg { .interface U8G2_PICO_IF_HW_SPI, .hw_spi { .spi spi0, .baudrate 4000000, .pin_sck 2, .pin_mosi 3, .pin_cs 5, .pin_dc 4, .pin_rst 11, }, }; int main(void) { stdio_init_all(); u8g2_pico_init(u8g2, u8g2_Setup_st7305_168x384_f, display_cfg); u8g2_ClearBuffer(u8g2); u8g2_SetFont(u8g2, u8g2_font_ncenB24_tr); u8g2_DrawStr(u8g2, 100, 100, Hello World); u8g2_SendBuffer(u8g2); sleep_ms(100); while (true) { sleep_ms(1000); } }示例接线对应配置结构体SCKGP2、MOSIGP3、DCGP4、CSGP5、RSTGP11SPI0 总线4 MHz。绘制流程与 u8g2 其他平台的 full buffer 用法完全一致u8g2_pico_init完成全部硬件初始化u8g2_ClearBuffer清空整帧缓冲u8g2_SetFontu8g2_DrawStr设置字体u8g2_font_ncenB24_tr并绘制文本u8g2_SendBuffer把 8064 字节帧缓冲一次性刷到屏幕。从源码结构看该示例也可视为一个模板把u8g2_Setup_st7305_168x384_f换成仓库中 csrc/u8g2_d_setup.c 提供的任意u8g2_Setup_xxxSSD1306、ST7565、UC1608 等并同步调整分辨率与引脚即可驱动其他屏幕——这正是 README 所说的复制并适配。七、适配到自有项目的检查清单基于以上源码分析把这份 HAL 用进自己的项目时按顺序核对以下事项接线与配置确认spi0/spi1实例、引脚号、波特率与实际 PCB 一致SCK/MOSI 必须是该 SPI 实例支持的功能引脚屏幕驱动选择从 csrc/u8g2_d_setup.c 选对u8g2_Setup_控制器_分辨率_f|1|2函数_f全缓冲内存占用最大但绘制最简单_1/_2分页缓冲省 RAM 但需要配合页循环RAM 预算以 ST7305 168×384 全缓冲为例约 8 KBRP2040 的 264 KB SRAM 完全无压力小内存场景改用分页缓冲时序核验U8X8_MSG_DELAY_NANO/100NANO用sleep_us近似若目标控制器对纳秒级时序敏感按数据手册修正旋转方向HAL 硬编码U8G2_R3需要其他方向U8G2_R0/R1/R2时直接修改u8g2_pico_init中的旋转参数软件 SPI 预留当前仅U8G2_PICO_IF_HW_SPI可用软 SPI 枚举值已预留但未实现如需位带模拟 SPI 需自行扩展字节回调。结语这份 Pico HAL 是理解 u8g2 硬件抽象层设计的绝佳样例它用不到 200 行 C 代码把 u8g2 的消息机制、显示驱动装配和 Pico SDK 的 SPI/GPIO API 无缝衔接既保持了自包含、不影响其他平台的干净边界又通过user_ptr与display_info参数化留足了适配空间。无论你的目标是快速点亮一块屏幕还是为自有 RP2040 板卡搭建可复用的显示层都可以从 sys/arm/rpi-pico/hal/u8g2_pico_hal.c 出发复制、裁剪、扩展。赞分享嵌入式嵌入式GUI驱动开发【免费下载链接】u8g2U8glib library for monochrome displays, version 2项目地址https://gitcode.com/gh_mirrors/u8/u8g2点击查看免费下载相关推荐FreeRTOS-Kernel 在 Raspberry Pi Pico RP2040 上的 SMP 移植使用指南与源码解析FreeRTOS Kernel 在 Raspberry Pi Pico RP2040 上的 SMP 移植使用指南与源码解析 本指南以 portable/Thi操作系统嵌入式RTOS嵌入式OSMicroPython RP2 端口快速参考Raspberry Pi PicoRP2040/RP2350外设编程速查手册MicroPython RP2 端口快速参考Raspberry Pi PicoRP2040/RP2350外设编程速查手册 本篇技术指南以 MicroPyt嵌入式语言运行时编程语言解释器编译器物联网系统编程FluidNC 的 RP2040 移植版在 Raspberry Pi Pico / Pico W 上构建低成本多轴运动控制固件FluidNC 的 RP2040 移植版在 Raspberry Pi Pico / Pico W 上构建低成本多轴运动控制固件 导读 本文以 FluidNC/嵌入式固件硬件开发智能硬件上一篇快速解决ViGEmBus虚拟游戏控制器安装与使用问题的完整指南下一篇小红书内容保存终极指南3种简单方法帮你优雅收藏喜欢的内容创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表