新建   展示   相册  列表  网摘
 

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);
}
View Code

 

如果是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;
}
View Code

 

如果是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;
}
View Code

 

可以看出,对于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);
}
View Code

 

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");
}
View Code

 



U8g2库支持的屏幕类型总表以及构造器选择

U8g2库为Arduino提供了广泛的屏幕支持,包括SSD1305、SSD1306、SH1106等众多型号。构造函数的选择至关重要,它定义了屏幕类型、控制器、通信协议和旋转。全屏缓存模式(如U8G2_UC1701_DOGS102_1_4W_SW_SPI)适用于需要大量内存的图形操作,而页面模式和U8x8字符模式则节省内存。通信协议包括SW_SPI、HW_SPI和I2C等,且支持屏幕旋转。了解并正确选择构造函数是高效使用U8g2库的关键。

U8g2库支持的屏幕类型总表以及构造器选择

介绍

要设置u8g2,请为显示选择正确的构造函数。构造函数名内包含:定义了显示类型、控制器、RAM缓冲区大小和通信协议。如果显示不完全,请尝试使用相同显示控制器名称的构造函数。

构造函数的参数定义了显示的旋转以及显示如何连接到Arduino板。这是Arduino c++接口的“Hello World”示例:

所有构造函数名列表
SSD1305 128X32_NONAME
...
A2PRINTER 384X240...
bash

U8g2 支持3中不同的绘图模式

  • 全屏缓存模式
  • 页面模式(U8glib 图片轮询)
  • U8c8,纯字符模式
全屏缓存模式(full buffer)
  • 快速
  • 可使用所有的图形程序
  • 需要大量的内存(RAM)
  • 从上面支持的类型中选择一个 U8g2 的构造器,全屏缓存模式的构造器包含了“F”,比如:
U8G2_ST7920_128X64_ F _SW_SPI(rotation, clock, data, cs [, reset])
bash
使用方法
  • 清除缓存内容: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();
}

c运行
页面模式(U8glib 图片轮询)
  • 所有的图形程序可用
  • 要求少量的内存(RAM)
  • 速度慢
  • 从上面支持的类型中,选择一个 U8g2 的构造器,全屏缓存模式的构造器包含了“1”,或者“2,”比如:
U8G2_ST7920_128X64_ 1 _SW_SPI(rotation, clock, data, cs [, reset])
bash
使用方法
  • 调用 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() );
}

c运行
U8x8 字符模式
  • 快速
  • 不需要内存(RAM)
  • 不能绘制图形
  • 不支持所有显示控制器
  • 从上面支持的类型中,选择一个 U8g2 的构造器,全屏缓存模式的构造器包含了“1”,或者“2,”比如:
U8G2_ST7920_128X64_ 1 _SW_SPI(rotation, clock, data, cs [, reset])
bash
使用方法
  • 所有的绘制指令直接写入显示器
void setup(void) {
  u8x8.begin();
}

void loop(void) {
  u8x8.setFont(u8x8_font_chroma48medium8_r);
  u8x8.drawString(0,1,"Hello World!");
}

c运行

构造函数的名字

Arduino c++构造函数的名称有以下几个部分:

序号描述示例
1 前缀 U8G2
2 显示控制器 UC1701
3 显示名称 DOGS102
4 Buffer Size 1, 2 or F (full frame buffer)
5 描述 4W_SW_SPI

这些部分与_相连。示例的完整构造函数名是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);
}

c运行
BufferDescription
1 Only one page of the display memory is stored in the microcontroller RAM. Use a firstPage()/nextPage() loop for drawing on the display.
2 Same as 1, but maintains two pages in the microcontroller RAM. This will be up to two times faster than 1.
F Keep a copy of the full display frame buffer in the microcontroller RAM. Use clearBuffer() to clear the RAM and sendBuffer() to transfer the microcontroller RAM to the display.

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。)

通信协议是构造函数名称的一部分(参见上面)。支持以下通信协议:
CommunicationDescription
4W_SW_SPI 4-wire (clock, data, cs and dc) software emulated SPI
4W_HW_SPI 4-wire (clock, data, cs and dc) hardware SPI (based on Arduino SPI library)
2ND_4W_HW_SPI If supported, second 4-wire hardware SPI (based on Arduino SPI library)
3W_SW_SPI 3-wire (clock, data and cs) software emulated SPI
SW_I2C Software emulated I2C/TWI
HW_I2C Hardware I2C based on the Arduino Wire library
2ND_HW_I2C If supported, use second hardware I2C (Arduino Wire lib)
6800 8-bit parallel interface, 6800 protocol
8080 8-bit parallel interface, 8080 protocol

硬件I2C允许某些控制器类型的引脚重新映射。复位引脚后列出可选引脚编号:
..._HW_I2C([reset [, clock, data]]). Use U8X8_PIN_NONE if 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);
}

c运行

Rotation(用于屏幕显示的翻转角度)

软件模拟的显示旋转是构造函数的第一个参数。

Rotation/MirrorDescription
U8G2_R0 No rotation, landscape
U8G2_R1 90 degree clockwise rotation
U8G2_R2 180 degree clockwise rotation
U8G2_R3 270 degree clockwise rotation
U8G2_MIRROR No rotation, landscape, display content is mirrored (v2.6.x)

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]
c运行
  • 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]
c运行
 
 


5. 自定义屏幕驱动(高级)

如果屏幕未被 U8G2 支持,需手动添加驱动:

  1. 获取初始化序列:查阅屏幕控制器的数据手册。
  2. 修改 U8G2 源码:
    • 在 u8g2.h 中定义新控制器。
    • 在 u8g2_d_memory.c 或 u8g2_d_custom.c 中添加初始化代码。
  3. 示例自定义初始化:
    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 添加新屏幕。如果遇到问题,请提供屏幕型号、错误现象和代码片段,以便进一步排查!

posted @ 2025-12-26 15:59  前沿风暴  阅读(2)  评论(0)    收藏  举报

校内网 © 2004-2025

京公网安备 33010602011771号 浙ICP备2021040463号-3