新建   文章列表   相册  后台  网摘
 

Kreos

Kreos 无所不能

U8G2自定义 新增屏幕适配

本质上是构造函数的适配


 

u8g2_d_setup.c       //仅保留使用 驱动函数

u8g2_d_memory.c   //仅保留使用 驱动函数

u8g2-master\csrc    //仅保留使用 驱动文件  u8x8_d_xxxxxxxx.c

 实现byte_cd()delay_cd()


 

image

 然后把其他函数给删除或者注释掉。

  

u8g2_d_memory

我们需要找到下面这个函数

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
}

把其他函数删除或注释掉.

修改屏幕分辨率

1.修改构造函数

image

 2.修改内存映射;大小


u8g2_t u8g2;

 

void Disp_Init(void) {
  u8g2Init(&u8g2);   // 初始化U8g2库,为OLED显示做准备
  OLED_SetFont(MENU_FONT);  // 设置默认使用的字体为MENU_FONT
  //u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_stm32_gpio_and_delay);
  u8g2_InitDisplay(&u8g2);
  u8g2_SetPowerSave(&u8g2,0);

}



一键获取完整项目代码
bash
1
使用方法
清除缓存内容: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();
}

一键获取完整项目代码


一键获取完整项目代码
bash
1
使用方法
调用 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() );
}

 

 

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

全屏缓存模式 全屏缓存模式(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 图片轮询) 页面模式(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() );
}

U8c8,纯字符模式

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

 

//x:0~127;
//y:1~63;//8
void setAddrPt(uint8_t x ,uint8_t y){
        lcdBuff[y/8][x] |= 1<<(y%8);
}
uint8_t getAddrPt(uint8_t x ,uint8_t y){
    if(lcdBuff[y/8][x] & 1<<(y%8)){
    return 1;
    }
    return 0;
}

extern void setGrayPot(uint16_t x,uint16_t y,uint8_t v);
void lcdMainLoop(void){

    uint16_t i,j;
    for(i=0; i<(240-1); i++){
        for(j=0; (j<80-1); j++){
            if(getAddrPt(i,j)){
                //TFT_Point(i,j);
                lcd_set_cursor(i, j);       /* 设置光标位置 */
                lcd_write_ram_prepare();    /* 开始写入GRAM */
                LCD->LCD_RAM = RED;
                setGrayPot(i,j,3);
            }else{
    
                lcd_set_cursor(i, j);       /* 设置光标位置 */
                lcd_write_ram_prepare();    /* 开始写入GRAM */
                LCD->LCD_RAM = YELLOW;
                setGrayPot(i,j,1);
            }
        }
    }
} 

 

3. 初始化u8g2结构体
在用户代码中初始化u8g2结构体,并分配足够大的缓冲区:


u8g2_t u8g2;  // u8g2对象
static uint8_t u8g2_buffer[12800];// 缓冲区大小:320x320单色屏幕需要320*320/8 = 12800字节

void init_u8g2(void) {
    // 设置屏幕尺寸、回调函数和旋转方向
    u8g2_SetupCustom(&u8g2, 320, 320, display_cb, hardware_cb);

    // 设置缓冲区
    u8g2_SetupBuffer(&u8g2, u8g2_buffer, sizeof(u8g2_buffer), U8G2_FLAG_1_BIT);

    // 初始化屏幕
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);  // 开启屏幕

    //u8g2_SetDisplayRotation(&u8g2, U8G2_R0);  // U8G2_R0 ~ U8G2_R3 屏幕旋转设置(可选)
    //void test_draw(void)  //测试代码示例
    {
        u8g2_ClearBuffer(&u8g2);
        u8g2_SetFont(&u8g2, u8g2_font_ncenB08_tr);  // 设置字体
        u8g2_DrawStr(&u8g2, 0, 10, "Hello 320x320");  // 绘制字符串
        u8g2_SendBuffer(&u8g2);  // 发送缓冲区到屏幕
    }
}

 

void Disp_Init(void)
{
    // 初始化U8g2库,为OLED显示做准备
    u8g2Init(&u8g2);
    // 设置默认使用的字体为MENU_FONT
    OLED_SetFont(MENU_FONT);
    
        
    //u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_stm32_gpio_and_delay);  
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2,0);
}

void u8g2Init(u8g2_t *u8g2)
{
//    #ifdef SOFTWARE_I2C
//    MyI2C_Init();
//    u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay); 
//    #endif 

//    #ifdef HARDWARE_I2C
//    HardWare_I2C2_GPIOInit();
//    u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay_hw);
//    #endif
    //TODO HardWare_I2C2_GPIOInit();
    //  u8x8_byte_4wire_sw_spi    //u8g2_gpio_and_delay_stm32
u8g2_Setup_st7567_jlx12864_f(u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8g2_gpio_and_delay_stm32);
     
    u8g2_InitDisplay(u8g2);                                                                       
    u8g2_SetPowerSave(u8g2, 0);                                                                   
    u8g2_ClearBuffer(u8g2);
}

 


 

 https://zhuanlan.zhihu.com/p/489251923

https://www.codeleading.com/article/94655971569/      //构造函数适配

移植U8G2单色图形库 - 韦东山嵌入式开发者社区  //百问移植示例


  • 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字节

 

 

在u8g2中新增一个320x320屏幕适配,需要结合你的底层绘图函数lcd_draw_point,并遵循u8g2的回调机制。以下是分步骤的实现方案,确保兼容性和性能。

1. 定义回调函数处理显示消息

u8g2通过回调函数处理底层显示操作。我们需要实现以下关键消息:

  • U8X8_MSG_DISPLAY_INIT:屏幕初始化
  • U8X8_MSG_DISPLAY_DRAW_TILE:将缓冲区的图块数据绘制到屏幕
c 
#include "u8g2.h"

// 假设颜色定义(单色)
#define BLACK 0
#define WHITE 1

// 用户提供的底层点绘制函数
extern void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color);

// 回调函数处理显示消息
uint8_t display_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_DISPLAY_INIT:
            // 调用用户提供的初始化函数
            init_my_screen();
            break;

        case U8X8_MSG_DISPLAY_DRAW_TILE:
        {
            // 获取当前行起始像素Y坐标
            uint16_t curr_row = u8x8->tile_current_row * 8;

            // 遍历每个tile
            for (uint8_t tile_col = 0; tile_col < arg_int; tile_col++) {
                // 遍历每个tile中的8行
                for (uint8_t y = 0; y < 8; y++) {
                    uint8_t byte = *(uint8_t *)arg_ptr++;
                    // 遍历每个bit(即像素)
                    for (uint8_t x = 0; x < 8; x++) {
                        // 提取bit值(高位在前)
                        uint8_t bit = (byte >> (7 - x)) & 0x01;
                        // 计算实际屏幕坐标
                        unsigned int draw_x = tile_col * 8 + x;
                        unsigned int draw_y = curr_row + y;
                        // 调用用户提供的点绘制函数
                        lcd_draw_point(draw_x, draw_y, bit ? WHITE : BLACK);
                    }
                }
            }
        }
        break;

        default:
            return 0;
    }
    return 1;
}

 

2. 定义硬件接口回调(可选)

如果你的lcd_draw_point函数已经处理了通信接口(如SPI或I2C),可以将硬件回调设为NULL。否则,需实现以下回调:

c
uint8_t hardware_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_BYTE_SendByte:
            // 发送一个字节到屏幕
            send_byte_to_lcd(*(uint8_t *)arg_ptr);
            break;

        case U8X8_MSG_BYTE_StartTransfer:
        case U8X8_MSG_BYTE_EndTransfer:
            // 启动或结束传输(如片选控制)
            break;

        case U8X8_MSG_GPIO_and_Delay:
            // 延时或控制GPIO(如复位、DC)
            if (arg_int == U8X8_MSG_GPIO_RESET) {
                if (*(uint8_t *)arg_ptr == 0)
                    set_reset_low();
                else
                    set_reset_high();
            }
            break;

        default:
            return 0;
    }
    return 1;
}

 

3. 初始化u8g2结构体

在用户代码中初始化u8g2结构体,并分配足够大的缓冲区:

c 
u8g2_t u8g2;  // u8g2对象

// 缓冲区大小:320x320单色屏幕需要320*320/8 = 12800字节
static uint8_t u8g2_buffer[12800];

void init_u8g2(void) {
    // 设置屏幕尺寸、回调函数和旋转方向
    u8g2_SetupCustom(&u8g2, 320, 320, display_cb, hardware_cb);

    // 设置缓冲区
    u8g2_SetupBuffer(&u8g2, u8g2_buffer, sizeof(u8g2_buffer), U8G2_FLAG_1_BIT);

    // 初始化屏幕
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);  // 开启屏幕
}

 

4. 屏幕旋转设置(可选)

如果屏幕方向不正确,可以通过以下函数调整:

c 
u8g2_SetDisplayRotation(&u8g2, U8G2_R0);  // U8G2_R0 ~ U8G2_R3

 

5. 测试代码示例

c 
void test_draw(void) {
    u8g2_ClearBuffer(&u8g2);
    u8g2_SetFont(&u8g2, u8g2_font_ncenB08_tr);  // 设置字体
    u8g2_DrawStr(&u8g2, 0, 10, "Hello 320x320");  // 绘制字符串
    u8g2_SendBuffer(&u8g2);  // 发送缓冲区到屏幕
}

 

关键注意事项
  1. 性能问题:lcd_draw_point逐像素绘制可能导致性能低下。对于大屏幕,建议使用DMA或批量传输优化。
  2. 颜色深度:示例中使用单色(1bit),若需彩色,需调整缓冲区大小和颜色处理逻辑。
  3. 坐标映射:确保lcd_draw_point的坐标原点与u8g2一致(通常为左上角)。
  4. 缓冲区大小:320x320单色屏幕需要12800字节缓冲区,确保MCU内存充足。

通过以上步骤,你可以在u8g2中成功适配320x320屏幕,并利用lcd_draw_point作为底层绘图函数。

 

 


移植U8G2指南

U8G2库简介

U8G2是嵌入式设备的单色图形库,主要应用于嵌入式设备,包括我们常见的单片机。

移植

打开项目链接 看到项目的 README 介绍如下:

U8g2是一个用于嵌入式设备的单色图形库。

U8g2支持单色OLED和LCD,其中包括以下控制器:SSD1305、SSD1306、SSD1309、SSD1312、SSD1316、SSD1318、SSD1320、SSD1322、SSD1325、SSD1327、SSD1329、SSD1606、SSD1607、SH1106、SH1107、SH1108、 SH1122、T6963、RA8835、LC7981、PCD8544、PCF8812、HX1230、UC1601、UC1604、UC1608、UC1610、UC1611、UC1617、UC1638、UC1701、ST7511、ST7528、 ST7565、ST7567、ST7571、ST7586、ST7588、ST75160、ST75256、ST75320、NT7534、ST7920、IST3020、IST3088、IST7920、LD7032、KS0108、KS0713、HD44102、T7932、 SED1520、SBN1661、IL3820、MAX7219、GP1287、GP1247、GU800(完整列表请参见此处)。

U8g2还包括U8x8库:

U8g2
包括所有图形程序(线/框/圆绘制)。
支持多种字体。 (几乎)对字体高度没有限制。
微控制器中需要一些内存来渲染显示。

U8x8
仅文本输出(字符)设备。
仅允许适合 8x8 像素网格的字体。
直接写入显示器。微控制器中不需要缓冲器。

点击 Setup Guide and Reference Manual

在此之前,可以先将项目源码下载到本地。将项目移动到我们的工程目录下,打开我们下载的u8g2源码,可以看到里面有许多文件夹。其他的文件夹里面都是一些说明文档,可以不用关注,注意红框标注的 csrc 文件夹,这里面存放的都是我们所需要的源码。

1

将 csrc 文件夹复制到新的目录下,我这里复制到了 ./U8g2 打开复制的 csrc 文件夹,可以看到里面有许多.c文件。注意到有许多U8x8_d_*****类型的c文件,这是针对不同的驱动芯片所写的驱动程序,我们只选择我们需要的就行,其他的在移植到自己的工程的时候,可以把删除。我们此次使用的是ssd1306,因此在移植的时候,只保留ssd1306相关的驱动程序就可以了(图中画红线的部分)。

删除掉不需要的文件,最终需要的文件列表如下图所示:

2

到这一步距离成功仅有一步之遥了,接下来我们需要实现接口即可。

驱动程序

driver_u8g2_interface.h


uint8_t u8x8_byte_stm32_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

uint8_t u8g2_gpio_and_delay_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

void u8g2Init(u8g2_t *u8g2);

 
C

driver_u8g2_interface.c


uint8_t u8x8_byte_stm32_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
     /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
 static uint8_t buffer[128];
 static uint8_t buf_idx;
 uint8_t *data;

 switch (msg)
 {
 case U8X8_MSG_BYTE_SEND:
  data = (uint8_t *)arg_ptr;
  while (arg_int > 0)
  {
   buffer[buf_idx++] = *data;
   data++;
   arg_int--;
  }
  break;
 case U8X8_MSG_BYTE_INIT:
  /* add your custom code to init i2c subsystem */
  MX_I2C1_Init();
  break;
 case U8X8_MSG_BYTE_START_TRANSFER:
  buf_idx = 0;
  break;
 case U8X8_MSG_BYTE_END_TRANSFER:
  if (HAL_I2C_Master_Transmit(&hi2c1, SSD1306_ADDR_SA0_0, buffer, buf_idx, 1000) != HAL_OK)
   return 0;
  break;
 case U8X8_MSG_BYTE_SET_DC:
        break;
 default:
  return 0;
 }
 return 1;
}

uint8_t u8g2_gpio_and_delay_stm32(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
        HAL_Delay(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
        arg_int ? HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET) : HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
  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
        arg_int ? HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET) : HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);  
   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;
}

void u8g2Init(u8g2_t *u8g2)
{
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_stm32_hw_i2c, u8g2_gpio_and_delay_stm32); // ssd1306 I2C初始化 u8g2 结构体
    //u8g2_Setup_ssd1306_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_gpio_and_delay); // SSD1306 SPI u8g2 结构体

    u8g2_InitDisplay(u8g2);             // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
    u8g2_SetPowerSave(u8g2, 0);         // 打开显示器
    u8g2_ClearBuffer(u8g2);             // 清除缓冲区
}
 
C

按照以上步骤进行移植,就可以使用U8g2库了。

总结

能力有限,有错误地方欢迎指正。

 

 

 


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 on 2025-12-26 15:59  前沿风暴  阅读(113)  评论(0)    收藏  举报

导航


校内网 © 2004-2026

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