嵌入式嵌入式GUI驱动开发【免费下载链接】u8g2U8glib library for monochrome displays, version 2项目地址https://gitcode.com/gh_mirrors/u8/u8g2点击查看免费下载导读U8g2 是面向单色 OLED/LCD 显示器的通用图形库而本文讲解的是它在 ARM Linux树莓派、NanoPi、OrangePi 等 SBC上的移植分支该移植不再依赖过时的 sysfs GPIO 接口而是基于 c-periphery 用户态库直接操作/dev/gpiochip*字符设备同时支持 I2C、SPI 总线访问。读完本文你将掌握如何在 ARM Linux 上完成非 root 权限配置、修改并编译四种典型显示接口示例硬件 SPI、软件 SPI、硬件 I2C、软件 I2C以及如何使用 pthread 在同一进程内驱动多块显示屏。一、移植背景从 sysfs 到 GPIO 字符设备Linux 内核的 GPIO 访问历史上经历过两个阶段早期广泛使用的/sys/class/gpiosysfs 接口以及内核 4.8 之后引入的gpiochip字符设备/dev/gpiochip0、/dev/gpiochip1等。本文对应的 arm-linux 移植分支仓库中位于 sys/arm-linux/README.md选择了后者其核心变化如下不再使用 sysfs 操作 GPIO字符设备接口速度更快、稳定性更好但为了兼容旧内核仍保留 sysfs 编译选项GPIO、I2C、SPI 资源可关闭和释放程序退出时可以close并归还句柄避免资源泄漏整体性能更好字符设备 ioctl 调用比写 sysfs 伪文件系统效率更高可以非 root 用户运行配合 udev 规则或启动脚本授权设备节点即可线程安全、支持多屏每个显示屏拥有独立的用户数据结构与帧缓冲可多线程并行驱动。从源码结构看这套移植被组织在 sys/arm-linux/ 目录下分为port/U8g2 与 Linux 设备对接的移植层、examples/C/C 示例、drivers/c-periphery 的 GPIO/I2C/SPI 驱动封装以及tools/等部分。另外官方还提供了基于 Java 的 Java UIO U8g2 方案复用该 arm-linux 移植。二、环境准备下载 U8g2 源码在 ARM Linux 设备或开发机上先获取 U8g2 仓库。文档给出的步骤是cd ~/ git clone --depth 1 https://github.com/olikraus/u8g2.git--depth 1表示浅克隆只拉取最新提交可以显著减少下载时间。克隆后本文涉及的移植代码位于u8g2/sys/arm-linux/。如果你是在交叉编译环境中操作请确认已安装对应的交叉工具链详见下文“编译”一节。三、非 root 用户访问设备节点默认情况下/dev/gpiochip*、/dev/i2c-*、/dev/spidev*仅 root 可读写。若希望普通用户直接运行 U8g2 示例需要做两件事把用户加入专用组并让设备节点对该组开放读写权限。1. 创建用户组并加入用户sudo groupadd u8g2 sudo usermod -a -G u8g2 username # username 换成你的非 root 用户名2. 开机时设置设备节点权限写入/etc/rc.localchown -R root:u8g2 /dev/gpiochip* chmod -R ugrw /dev/gpiochip* chown -R root:u8g2 /dev/i2c* chmod -R ugrw /dev/i2c* chown -R root:u8g2 /dev/spidev* chmod -R ugrw /dev/spidev*3. 重启生效sudo reboot更规范的做法是编写 udev 规则代替rc.localudev 可以在设备节点创建时自动设置属主与权限适合长期部署。无论哪种方式核心目标都是让u8g2组成员对上述三类设备节点拥有读写权。四、修改示例以硬件 SPI 四线为例文档推荐从四线硬件 SPI 示例入手修改 sys/arm-linux/examples/c-examples/u8g2_4wire_hw_spi/u8g2_4wire_hw_spi.c。打开该文件后需要按你的硬件调整三组宏// GPIO chip number for character device #define GPIO_CHIP_NUM 0 // SPI bus uses upper 4 bits and lower 4 bits, so 0x10 will be /dev/spidev1.0 #define SPI_BUS 0x10 #define OLED_SPI_PIN_RES 199 #define OLED_SPI_PIN_DC 198 // CS pin is controlled by linux spi driver, thus not defined here, but need to be wired #define OLED_SPI_PIN_CS U8X8_PIN_NONE参数含义与取值规则宏含义取值说明GPIO_CHIP_NUMGPIO 字符设备编号0对应/dev/gpiochip0SPI_BUSSPI 总线与片选设备号高 4 位是总线号、低 4 位是设备号0x10表示/dev/spidev1.00x20表示/dev/spidev2.0OLED_SPI_PIN_RES显示屏 RESET 引脚使用该 GPIO chip 内的绝对引脚编号NanoPi Duo 上为 199OLED_SPI_PIN_DC显示屏 DC数据/命令引脚同样为 GPIO 绝对编号NanoPi Duo 上为 198OLED_SPI_PIN_CS片选引脚硬件 SPI 模式下由 Linux SPI 驱动接管故置为U8X8_PIN_NONE但电路上仍需接线接下来看main()中的初始化流程它体现了该移植的标准用法u8g2_t u8g2; // Initialization u8g2_Setup_ssd1306_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_arm_linux_hw_spi, u8x8_arm_linux_gpio_and_delay); init_spi_hw(u8g2, GPIO_CHIP_NUM, SPI_BUS, OLED_SPI_PIN_DC, OLED_SPI_PIN_RES, OLED_SPI_PIN_CS); u8g2_InitDisplay(u8g2); u8g2_ClearBuffer(u8g2); u8g2_SetPowerSave(u8g2, 0); u8g2_SetFont(u8g2, u8g2_font_ncenB08_tr); u8g2_DrawStr(u8g2, 1, 18, U8g2 HW SPI); u8g2_SetFont(u8g2, u8g2_font_unifont_t_symbols); u8g2_DrawGlyph(u8g2, 112, 56, 0x2603); u8g2_SendBuffer(u8g2); printf(Initialized ...\n); sleep_ms(5000); u8g2_SetPowerSave(u8g2, 1); // Close and deallocate SPI resources done_spi(); // Close and deallocate GPIO resources done_user_data(u8g2); printf(Done\n);几个关键点u8g2_Setup_ssd1306_128x64_noname_f选择 SSD1306 128×64 全帧缓冲_f后缀初始化函数如果你的屏是其他控制器替换为对应的u8g2_Setup_*即可字节回调u8x8_byte_arm_linux_hw_spi与 GPIO/延时回调u8x8_arm_linux_gpio_and_delay都是本移植在 sys/arm-linux/port/u8g2port.h 中导出的接口结束时调用done_spi()与done_user_data(u8g2)显式关闭并释放 SPI、GPIO 句柄这正是文档强调的“资源可关闭和释放”特性。五、编译构建字符设备与 sysfs 两种方式在 sys/arm-linux/ 目录下执行编译。Makefile 顶层会递归进入examples子目录examples/c-examples/Makefile 再通过通配符逐个构建u8g2*子目录产物统一输出到bin/。1. 使用 GPIO 字符设备默认推荐cd ~/u8g2/sys/arm-linux make clean make CPPFLAGS-DPERIPHERY_GPIO_CDEV_SUPPORT1 CCgcc CXXgPERIPHERY_GPIO_CDEV_SUPPORT1是关键宏它让 sys/arm-linux/port/u8g2port.c 中init_pin()走gpio_open()打开/dev/gpiochip%d字符设备的分支。2. 使用 GPIO sysfs兼容旧内核cd ~/u8g2/sys/arm-linux make clean make CCgcc CXXg不定义PERIPHERY_GPIO_CDEV_SUPPORT时init_pin()会调用gpio_open_sysfs()走/sys/class/gpio路径见 u8g2port.c 中#if PERIPHERY_GPIO_CDEV_SUPPORT的分支选择。老内核没有 gpiochip 字符设备时使用此方式。3. 交叉编译工具链选择如果直接在树莓派等设备本机编译CCgcc CXXg即可。交叉编译时需替换工具链前缀各示例子目录的 Makefile 顶部给出了对照表目标平台编译器前缀NanoPi、Raspberry Pi Zeroarm-linux-gnueabi-gccRaspberry Pi 2、OrangePi Zero、NanoPi NEO、NanoPi NEO Plus 2arm-linux-gnueabihf-gccC-SKY Linuxcsky-linux-gcc子目录 Makefile 还会把 csrc/ 下的全部.c编译进目标并链接 sys/arm-linux/port/u8g2port.o 与drivers/下的gpio.o、spi.o、i2c.o。构建完成后可执行文件位于sys/arm-linux/bin/。六、源码级原理port 移植层如何工作这一节深入 sys/arm-linux/port/u8g2port.c 与 sys/arm-linux/port/u8g2port.h理解移植层的内核。6.1 用户数据结构与多屏/线程安全移植层用user_data_struct保存每个显示屏的独立状态struct user_data_struct { uint8_t gpio_chip; // /dev/gpiochip%d gpio_t *pins[U8X8_PIN_CNT]; // c-periphery GPIO 句柄 uint8_t bus; // I2C /dev/i2c-%dSPI 高 4 位总线号 低 4 位设备号 uint8_t index; // 回调缓冲索引 uint8_t buffer[256]; // I2C 一次最多 32 字节、SPI 最多 128 字节 unsigned long delay; // I2C 延时纳秒 unsigned int spi_mode; // SPI 模式 uint32_t max_speed; // SPI 最大速率 uint8_t *int_buf; // 内部帧缓冲 };init_user_data()u8g2port.c会为每个显示实例动态分配独立的帧缓冲并调用u8g2_SetBufferPtr()绑定同时把整个结构挂到u8g2_SetUserPtr()。注释明确指出“每个显示屏需要独立的缓冲才能线程友好”——这是多屏示例能够并行渲染的基础。而 I2C、SPI 句柄是全局静态数组i2c_handles[8]、spi_handles[256]因为同一总线可被多个设备共享例如同一条 I2C 总线上地址不同的多块屏且init_i2c()/init_spi()只在句柄为空时打开一次天然实现“总线只开一次”。6.2 GPIO 字符设备打开与写引脚在PERIPHERY_GPIO_CDEV_SUPPORT开启时init_pin()拼接/dev/gpiochip%d文件名并用gpio_open()以GPIO_DIR_OUT_HIGH模式打开引脚未开启时退化为gpio_open_sysfs()。write_pin()对非U8X8_PIN_NONE的引脚执行gpio_write()。GPIO 与延时的统一回调u8x8_arm_linux_gpio_and_delay则把 U8x8 协议层的各类消息U8X8_MSG_GPIO_*、U8X8_MSG_DELAY_*映射到引脚写操作与nanosleep实现的毫秒/微秒/纳秒延时上。6.3 硬件 I2C 与硬件 SPI 回调u8x8_byte_arm_linux_hw_i2c把U8X8_MSG_BYTE_SEND的字节暂存进user_data-buffer在U8X8_MSG_BYTE_END_TRANSFER时组装成一条写消息i2c_transfer()发出地址由u8x8_GetI2CAddress(u8g2) 1得出u8x8_byte_arm_linux_hw_spi每次U8X8_MSG_BYTE_SEND直接调用spi_transfer()并通过U8X8_MSG_BYTE_SET_DC联动u8x8_gpio_SetDC()切换数据/命令线init_spi()按bus 4与bus 0x0f拆出/dev/spidevX.Y以spi_mode和max_speed打开设备。关于 SPI 速率init_spi_hw()调用init_spi_hw_advanced()时默认max_speed 500000500 kHz同时 SPI 模式取自display_info-spi_mode。如果你的屏需要更高速度可以改用init_spi_hw_advanced()自行指定模式与速率。七、多屏多线程示例三块屏并行渲染多屏能力是这套移植的突出卖点示例位于 sys/arm-linux/examples/c-examples/u8g2_sw_i2c_thread/u8g2_sw_i2c_thread.c。它的思路是为每块屏建立一个 pthread线程内各自初始化、绘制并刷新。struct display { uint8_t gpio_chip; uint8_t scl; uint8_t sda; uint8_t res; unsigned long delay; };main()中定义三个显示实例display_t display_1 { 0, 14, 13, U8X8_PIN_NONE, 0 }; display_t display_2 { 0, 15, 16, U8X8_PIN_NONE, 0 }; display_t display_3 { 0, 198, 199, U8X8_PIN_NONE, 0 };即三组gpio_chip / scl / sda / res / delay随后用pthread_create()为每块屏创建线程pthread_join()等待结束。每个线程执行do_display()u8g2_Setup_ssd1306_i2c_128x32_univision_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_arm_linux_gpio_and_delay); init_i2c_sw(u8g2, disp.gpio_chip, disp.scl, disp.sda, disp.res, disp.delay); u8g2_InitDisplay(u8g2); h u8g2_GetDisplayHeight(u8g2); w u8g2_GetDisplayWidth(u8g2); u8g2_SetPowerSave(u8g2, 0); for (int i 1; i 200; i) { u8g2_ClearBuffer(u8g2); y1 rand() % h; y2 rand() % h; u8g2_DrawLine(u8g2, 0, y1, w - 1, y2); u8g2_SendBuffer(u8g2); sleep_ms(50); }每 50 ms 画一条随机直线并刷新循环 200 次后关闭电源、释放 GPIO 资源退出。文档特别提醒修改display_1/2/3的引脚定义以匹配你的实际接线如果只有两块屏删掉对应的线程创建与 join 代码即可示例刻意使用软件 I2Cu8x8_byte_sw_i2c因为多数 SBC 没有三路硬件 I2C 控制器而靠修改显示屏 I2C 地址又往往需要焊接电阻——软件 I2C 只需任意 GPIO 即可扩展出多路总线。该移植也因此支持最多 3 块软件 I2C 屏并行工作见 u8g2port.c 头部注释。编译并运行cd ~/u8g2/sys/arm-linux make CPPFLAGS-DPERIPHERY_GPIO_CDEV_SUPPORT1 CCgcc CXXg cd bin ./u8g2_sw_i2c_thread八、其他接口模式与 C 封装除硬件 SPI 与多线程软件 I2C 外examples/c-examples/ 还提供四套等价示例覆盖主流连接方式示例目录接口说明u8g2_4wire_hw_spi四线硬件 SPI见上文CS 由内核 SPI 驱动接管u8g2_4wire_sw_spi四线软件 SPI通过init_spi_sw()用 GPIO 模拟 SPI 时钟与数据u8g2_hw_i2c硬件 I2C通过init_i2c_hw()指定/dev/i2c-%d总线号u8g2_sw_i2c软件 I2C通过init_i2c_sw()指定 SCL/SDA 两个 GPIOu8g2_sw_i2c_thread多线程软件 I2C三屏并行渲染示范以 u8g2_sw_i2c/u8g2_sw_i2c.c 为例软件 I2C 只需三个宏#define GPIO_CHIP_NUM 0 #define OLED_I2C_PIN_SCL 11 #define OLED_I2C_PIN_SDA 12 #define OLED_I2C_PIN_RESET U8X8_PIN_NONE初始化时使用init_i2c_sw(u8g2, GPIO_CHIP_NUM, SCL, SDA, RESET, 0)最后一个参数是 I2C 延时纳秒0表示不额外延时若你的屏需要更慢的时钟可传入非零值。C 用户可参考 examples/cpp-examples/ 下对应的*_cpp示例它们基于 sys/arm-linux/port/U8g2lib.h 的 C 封装接口风格与 Arduino 版 U8g2lib 一致。九、常见问题与注意事项gpio_open()/i2c_open()报权限错误多半是设备节点权限未配置回到第三节完成u8g2组授权或临时用 root 验证设备节点不存在确认内核已启用对应外设例如树莓派需在config.txt开启dtparamspion/dtparami2c_armon并重启SPI 速率不够init_spi_hw()默认 500 kHz可改用init_spi_hw_advanced()并传入更高max_speed与合适spi_mode多屏线程数示例默认三块屏少于三块时删减线程即可i2c_handles上限 8、spi_handles上限 256理论可扩展更多旧内核兼容内核不含/dev/gpiochip*时编译去掉PERIPHERY_GPIO_CDEV_SUPPORT1走 sysfs 路径即可。这套 arm-linux 移植让 U8g2 摆脱了对 Arduino/单片机环境的依赖直接在 Linux 用户态以标准 C 结构驱动单色屏配合 c-periphery 库在 GPIO 字符设备、I2C、SPI 三条通路上的封装做到了非 root 运行、资源可回收、线程安全与多屏并行适合在树莓派、NanoPi 等嵌入式 Linux 设备的仪表盘、信息屏类应用中落地。赞分享嵌入式嵌入式GUI驱动开发【免费下载链接】u8g2U8glib library for monochrome displays, version 2项目地址https://gitcode.com/gh_mirrors/u8/u8g2点击查看免费下载相关推荐Serial-Studio与嵌入式Linux在ARM设备上的移植指南Serial Studio与嵌入式Linux在ARM设备上的移植指南 引言嵌入式开发中的数据可视化痛点 你是否在ARM嵌入式项目中遇到过这些问题调试传感器桌面应用数据可视化物联网Arduino-ESP32外设驱动详解GPIO、ADC、I2C、SPI实战应用Arduino ESP32外设驱动详解GPIO、ADC、I2C、SPI实战应用 引言为什么选择Arduino ESP32进行嵌入式开发 还在为ESP32复嵌入式物联网驱动开发Gobot 驱动 FriendlyELEC NanoPiNEO开发板GPIO、PWM、I2C、SPI 完整实战指南Gobot 驱动 FriendlyELEC NanoPiNEO开发板GPIO、PWM、I2C、SPI 完整实战指南 导读 本文以 platforms/f物联网机器人嵌入式上一篇3步实现Word文档智能整合告别多文件管理混乱下一篇FlutterFire查询缓存策略指南文档Firebase缓存步骤说明创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?