U8G2自定义 屏幕
u8g2_d_setup.c //仅保留使用 驱动函数
u8g2-master\csrc //仅保留使用 驱动文件 u8x8_d_xxxxxxxx.c
https://zhuanlan.zhihu.com/p/489251923
- u8g2_Setup_ssd1306_128x64_noname_1
- u8g2_Setup_ssd1306_128x64_noname_2
- u8g2_Setup_ssd1306_128x64_noname_f
- u8g2_Setup_ssd1306_i2c_128x64_noname_1
- u8g2_Setup_ssd1306_i2c_128x64_noname_2
- u8g2_Setup_ssd1306_i2c_128x64_noname_f
其中,前面3个,是给SPI接口的OLED用的,函数最后的数字或字母,代表显示时的buf大小:
- 1:128字节
- 2:256字节
- f:1024字节
2.1.3 精简u8g2_d_memory.c
由于用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f函数中,只调用了u8g2_m_16_8_f这个函数,所以留下这个函数,其它的函数一定要删掉或注释掉,否则编译时很可能会提示内存不足!!!
#include "u8g2.h" uint8_t *u8g2_m_16_8_f(uint8_t *page_cnt) { #ifdef U8G2_USE_DYNAMIC_ALLOC *page_cnt = 8; return 0; #else static uint8_t buf[1024]; *page_cnt = 8; return buf; #endif }
2.2 编写移植函数
精简源码之后,还需要编写如下的配置函数。
2.2.1 GPIO初始化
对OLED用到的IIC接口进行GPIO的初始化配置:
#define SCL_Pin GPIO_Pin_6 #define SDA_Pin GPIO_Pin_7 #define IIC_GPIO_Port GPIOB void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); GPIO_InitStructure.GPIO_Pin = SCL_Pin|SDA_Pin; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(IIC_GPIO_Port, &GPIO_InitStructure); }
如果是SPI接口,则初始化对应的SPI接口即可。
2.2.2 u8x8_gpio_and_delay
这个函数也需要自己写,主要的修改包括:
- 赋予U8g2相应的延时函数,比如下面的delay_ms和delay_us
- 为U8g2提供IIC接口的高低电平调用:
- U8X8_MSG_GPIO_I2C_CLOCK:IIC的SCL
- U8X8_MSG_GPIO_I2C_DATA:IIC的SDA
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch (msg) { case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds __NOP(); break; case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds for (uint16_t n = 0; n < 320; n++) { __NOP(); } break; case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second delay_ms(1); break; case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz delay_us(5); break; // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin if(arg_int == 1) { GPIO_SetBits(IIC_GPIO_Port, SCL_Pin); } else if(arg_int == 0) { GPIO_ResetBits(IIC_GPIO_Port, SCL_Pin); } break; // arg_int=1: Input dir with pullup high for I2C clock pin case U8X8_MSG_GPIO_I2C_DATA: // arg_int=0: Output low at I2C data pin if(arg_int == 1) { GPIO_SetBits(IIC_GPIO_Port, SDA_Pin); } else if(arg_int == 0) { GPIO_ResetBits(IIC_GPIO_Port, SDA_Pin); } break; // arg_int=1: Input dir with pullup high for I2C data pin case U8X8_MSG_GPIO_MENU_SELECT: u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0); break; case U8X8_MSG_GPIO_MENU_NEXT: u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0); break; case U8X8_MSG_GPIO_MENU_PREV: u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0); break; case U8X8_MSG_GPIO_MENU_HOME: u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0); break; default: u8x8_SetGPIOResult(u8x8, 1); // default return value break; } return 1; }
如果是SPI接口,可以参考如下写法:
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch (msg) { case U8X8_MSG_GPIO_SPI_DATA: lcd_sdin((uint8_t)arg_int); //SPI - MOSI break; case U8X8_MSG_GPIO_SPI_CLOCK: //SPI - CLK lcd_sclk(arg_int); break; case U8X8_MSG_GPIO_AND_DELAY_INIT: oled_init(); //OLED初始化 Delay(1); break; case U8X8_MSG_DELAY_MILLI: Delay(arg_int); //延时 break; case U8X8_MSG_GPIO_CS: //SPI - CS lcd_cs((uint8_t)arg_int); case U8X8_MSG_GPIO_DC: lcd_dc((uint8_t)arg_int); //SPI - MISO break; case U8X8_MSG_GPIO_RESET: break; } return 1; }
可以看出,对于IIC与SPI接口,只有分别进行对应的配置即可。
2.2.3 u8g2Init
U8g2的初始化,需要调用下面这个u8g2_Setup_ssd1306_128x64_noname_f函数,该函数的4个参数含义:
- u8g2:传入的U8g2结构体
- U8G2_R0:默认使用U8G2_R0即可(用于配置屏幕是否要旋转)
- u8x8_byte_sw_i2c:使用软件IIC驱动,该函数由U8g2源码提供
- u8x8_gpio_and_delay:就是上面我们写的配置函数
void u8g2Init(u8g2_t *u8g2) { u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay); // 初始化 u8g2 结构体 u8g2_InitDisplay(u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态 u8g2_SetPowerSave(u8g2, 0); // 打开显示器 u8g2_ClearBuffer(u8g2); }
2.2.4 显示测试函数
使用U8g2提供的测试函数,用于查看显示效果
void draw(u8g2_t *u8g2) { u8g2_SetFontMode(u8g2, 1); /*字体模式选择*/ u8g2_SetFontDirection(u8g2, 0); /*字体方向选择*/ u8g2_SetFont(u8g2, u8g2_font_inb24_mf); /*字库选择*/ u8g2_DrawStr(u8g2, 0, 20, "U"); u8g2_SetFontDirection(u8g2, 1); u8g2_SetFont(u8g2, u8g2_font_inb30_mn); u8g2_DrawStr(u8g2, 21,8,"8"); u8g2_SetFontDirection(u8g2, 0); u8g2_SetFont(u8g2, u8g2_font_inb24_mf); u8g2_DrawStr(u8g2, 51,30,"g"); u8g2_DrawStr(u8g2, 67,30,"\xb2"); u8g2_DrawHLine(u8g2, 2, 35, 47); u8g2_DrawHLine(u8g2, 3, 36, 47); u8g2_DrawVLine(u8g2, 45, 32, 12); u8g2_DrawVLine(u8g2, 46, 33, 12); u8g2_SetFont(u8g2, u8g2_font_4x6_tr); u8g2_DrawStr(u8g2, 1,54,"github.com/olikraus/u8g2"); }
U8g2库支持的屏幕类型总表以及构造器选择
介绍
要设置u8g2,请为显示选择正确的构造函数。构造函数名内包含:定义了显示类型、控制器、RAM缓冲区大小和通信协议。如果显示不完全,请尝试使用相同显示控制器名称的构造函数。
构造函数的参数定义了显示的旋转以及显示如何连接到Arduino板。这是Arduino c++接口的“Hello World”示例:
SSD1305 128X32_NONAME
...
A2PRINTER 384X240...
U8g2 支持3中不同的绘图模式
- 全屏缓存模式
- 页面模式(U8glib 图片轮询)
- U8c8,纯字符模式
全屏缓存模式(full buffer)
- 快速
- 可使用所有的图形程序
- 需要大量的内存(RAM)
- 从上面支持的类型中选择一个
U8g2的构造器,全屏缓存模式的构造器包含了“F”,比如:
U8G2_ST7920_128X64_ F _SW_SPI(rotation, clock, data, cs [, reset])
使用方法
- 清除缓存内容:u8g2.clearBuffer()。
- 使用绘图指令绘制图形。
- 发送缓存数据给显示器以显示:u8g2.sendBuffer()。
void setup(void) {
u8g2.begin();
}
void loop(void) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0,20,"Hello World!");
u8g2.sendBuffer();
}
页面模式(U8glib 图片轮询)
- 所有的图形程序可用
- 要求少量的内存(RAM)
- 速度慢
- 从上面支持的类型中,选择一个
U8g2的构造器,全屏缓存模式的构造器包含了“1”,或者“2,”比如:
U8G2_ST7920_128X64_ 1 _SW_SPI(rotation, clock, data, cs [, reset])
使用方法
- 调用 u8g2.firstPage()
- 运行一个 do-while 循环
- 在循环体内绘制一些图形
- 循环到U8g2.nextPage()返回真结束
void setup(void) {
u8g2.begin();
}
void loop(void) {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0,24,"Hello World!");
} while ( u8g2.nextPage() );
}
U8x8 字符模式
- 快速
- 不需要内存(RAM)
- 不能绘制图形
- 不支持所有显示控制器
- 从上面支持的类型中,选择一个
U8g2的构造器,全屏缓存模式的构造器包含了“1”,或者“2,”比如:
U8G2_ST7920_128X64_ 1 _SW_SPI(rotation, clock, data, cs [, reset])
使用方法
- 所有的绘制指令直接写入显示器
void setup(void) {
u8x8.begin();
}
void loop(void) {
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.drawString(0,1,"Hello World!");
}
Arduino c++构造函数的名称有以下几个部分:
这些部分与
_相连。示例的完整构造函数名是U8G2_UC1701_DOGS102_1_4W_SW_SPI。上面列出了所有可用的构造函数名.
- Buffer Size(缓冲区大小)
```c
#include <Arduino.h>
#include <SPI.h>
#include <U8g2lib.h>
/* Constructor */
U8G2_UC1701_DOGS102_1_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
/* u8g2.begin() is required and will sent the setup/init sequence to the display */
void setup(void) {
u8g2.begin();
}
/* draw something on the display with the `firstPage()`/`nextPage()` loop*/
void loop(void) {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0,20,"Hello World!");
} while ( u8g2.nextPage() );
delay(1000);
}
The full buffer F option can be used only, if there is sufficient RAM available in the microcontroller. Use option 1 or 2 on a microcontroller with a small amount of RAM. The u8x8 API can be used if there is not even RAM for one page.(翻译:只有当微控制器中有足够的RAM可用时,才可以使用全缓冲区F选项。在带有少量RAM的微控制器上使用选项1或2。如果一个页面甚至没有RAM,也可以使用u8x8 API。)
通信协议是构造函数名称的一部分(参见上面)。支持以下通信协议:
硬件I2C允许某些控制器类型的引脚重新映射。复位引脚后列出可选引脚编号:
..._HW_I2C([reset [, clock, data]]). UseU8X8_PIN_NONEif the reset input of the display is not connected.( 译文:如果显示的复位输入没有连接,使用U8X8_PIN_NONE)
This might be confusing, because the software emulated I2C constructor lists the required clock and data line before the reset pin:..._SW_I2C(clock, data [, reset])
当使用动态内存分配时,函数*U8G2::begin()将被禁用。
出于这个原因,setup()函数必须在*U8G2::begin()函数的位置上有以下内容:
uint8_t *buf;
void setup(void) {
buf = (uint8_t *)malloc(u8g2.getBufferSize());
u8g2.setBufferPtr(buf);
u8g2.initDisplay();
u8g2.clearDisplay();
u8g2.setPowerSave(0);
}
Rotation(用于屏幕显示的翻转角度)
软件模拟的显示旋转是构造函数的第一个参数。
If supported U8G2_R2 is identical to u8g2::setFlipMode(1).(译文:如果支持U8G2_R2与u8g2::setFlipMode(1)相同。)
构造函数引用
- SH1106 128X64_NONAME
Controller “sh1106”, Display “128x64_noname” [Description]
U8G2_SH1106_128X64_NONAME_1_SW_I2C(rotation, clock, data [, reset]) [page buffer, size = 128 bytes]
U8G2_SH1106_128X64_NONAME_2_SW_I2C(rotation, clock, data [, reset]) [page buffer, size = 256 bytes]
U8G2_SH1106_128X64_NONAME_F_SW_I2C(rotation, clock, data [, reset]) [full framebuffer, size = 1024 bytes]
U8G2_SH1106_128X64_NONAME_1_HW_I2C(rotation, [reset [, clock, data]]) [page buffer, size = 128 bytes]
U8G2_SH1106_128X64_NONAME_2_HW_I2C(rotation, [reset [, clock, data]]) [page buffer, size = 256 bytes]
U8G2_SH1106_128X64_NONAME_F_HW_I2C(rotation, [reset [, clock, data]]) [full framebuffer, size = 1024 bytes]
U8G2_SH1106_128X64_NONAME_1_2ND_HW_I2C(rotation, [reset]) [page buffer, size = 128 bytes]
U8G2_SH1106_128X64_NONAME_2_2ND_HW_I2C(rotation, [reset]) [page buffer, size = 256 bytes]
U8G2_SH1106_128X64_NONAME_F_2ND_HW_I2C(rotation, [reset]) [full framebuffer, size = 1024 bytes]
- SSD1306 128X64_NONAME
Controller “ssd1306”, Display “128x64_noname” [Description]
U8G2_SSD1306_128X64_NONAME_1_SW_I2C(rotation, clock, data [, reset]) [page buffer, size = 128 bytes]
U8G2_SSD1306_128X64_NONAME_2_SW_I2C(rotation, clock, data [, reset]) [page buffer, size = 256 bytes]
U8G2_SSD1306_128X64_NONAME_F_SW_I2C(rotation, clock, data [, reset]) [full framebuffer, size = 1024 bytes]
U8G2_SSD1306_128X64_NONAME_1_HW_I2C(rotation, [reset [, clock, data]]) [page buffer, size = 128 bytes]
U8G2_SSD1306_128X64_NONAME_2_HW_I2C(rotation, [reset [, clock, data]]) [page buffer, size = 256 bytes]
U8G2_SSD1306_128X64_NONAME_F_HW_I2C(rotation, [reset [, clock, data]]) [full framebuffer, size = 1024 bytes]
U8G2_SSD1306_128X64_NONAME_1_2ND_HW_I2C(rotation, [reset]) [page buffer, size = 128 bytes]
U8G2_SSD1306_128X64_NONAME_2_2ND_HW_I2C(rotation, [reset]) [page buffer, size = 256 bytes]
U8G2_SSD1306_128X64_NONAME_F_2ND_HW_I2C(rotation, [reset]) [full framebuffer, size = 1024 bytes]
5. 自定义屏幕驱动(高级)
如果屏幕未被 U8G2 支持,需手动添加驱动:
- 获取初始化序列:查阅屏幕控制器的数据手册。
- 修改 U8G2 源码:
- 在
u8g2.h中定义新控制器。 - 在
u8g2_d_memory.c或u8g2_d_custom.c中添加初始化代码。
- 在
- 示例自定义初始化:
cpp
uint8_t u8g2_d_mydisplay(u8g2_t *u8g2, register uint8_t msg, register uint8_t arg_val, union u8g2_dev_arg_val *arg_ptr) { switch(msg) { case U8G2_MSG_INIT: u8g2_InitDisplay(u8g2); u8g2_SetPowerSave(u8g2, 0); // 添加自定义初始化命令 u8g2_SendByte(u8g2, 0xAE); // 关闭显示 break; case U8G2_MSG_CONTRAST: u8g2_SendByte(u8g2, 0x81); u8g2_SendByte(u8g2, arg_val); // 设置对比度 break; } return 0; }
6. 调试技巧
- 检查 I²C 地址:使用 I²C 扫描工具(如
Wire库示例)确认屏幕地址。 - 电源问题:确保屏幕供电稳定,避免电压不匹配。
- 时序问题:SPI/I²C 速率过高可能导致通信失败,尝试降低频率。
- 逻辑分析仪:检查通信信号是否符合时序要求。
7. 注意事项
- 内存占用:U8G2 使用全屏缓冲区,128x64 像素需要 1KB 内存(Arduino Uno 可用 SRAM 有限)。
- 旋转方向:通过
U8G2_R0/U8G2_R1调整屏幕方向。 - 多屏幕支持:U8G2 不支持多屏同时显示,需使用其他库(如 Adafruit GFX)。
通过以上步骤,您应该能够成功为 U8G2 添加新屏幕。如果遇到问题,请提供屏幕型号、错误现象和代码片段,以便进一步排查!
https://gitee.com/powes/,作者:前沿风暴,转载请注明原文链接:https://www.cnblogs.com/Kreos/p/19405345

浙公网安备 33010602011771号