移植U8G2单色图形库

移植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);

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);             // 清除缓冲区
}

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

总结

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