This post was last edited by dirty on 2024-7-19 22:34
This article describes how to use the u8g2 graphics library to drive the oled display. The advantages of using this graphics library are that it is powerful and has a font library that includes Chinese GB2312. It is very convenient and comprehensive to display Chinese characters. There is no need for a modulus tool to get the Chinese character dot matrix. In addition, there are many API functions that can be designed for the interface. It is highly recommended for use in actual engineering projects.
1. Preparation
This time, ssd1306 is used to drive the OLED screen, with a pixel resolution of 128*32 and an I2C interface. u8g2 is the second version of the monochrome display library, which is open source. u8g2 supports LCD and OLED, and supports many driver chips, including SSD1306. The specific supported drivers can be viewed in the resource library.
The hardware connections are as follows:
Development Board OLED
PB9 SDA
PB8 SCL
3V3 VCC
GND GND
2. Code Preparation
This project supports ssd1306 128*64/128*32 resolution, which can be selected through the following macros. During the migration, please trim as needed and delete as much as possible to avoid occupying resources.
#define SSD1306_128x64 1
#define SSD1306_128x32 2
#define SSD1306_DEVICE SSD1306_128x32//SSD1306_128x64//
1. After cloning the u8g2 resources, use the resources in the scrc file
2. Delete u8x8_d_xxx.c non-related driver source files. Select ssd1306 128x64/128x32 (select one)
3. Select the driver chip initialization function used in the u8g2_d_setup.c source file and delete the others. Here, keep u8g2_Setup_ssd1306_i2c_128x64_noname_f/u8g2_Setup_ssd1306_i2c_128x32_univision_f
4. Modify the "u8g2_d_memory.c" file. This file actually contains the buffer corresponding to the "u8g2_d_setup.c" file. As above, block the unused ones and keep the used ones.
5. About fonts. Various fonts are defined in the "u8g2_fonts.c" file. These fonts take up a lot of space, and the unused ones are blocked according to the usage. To use the GB2312 font library, you need to enable the macro U8G2_USE_LARGE_FONTS. The specific steps are as follows:
Figure 1: Enabling the GB2312 font library
6. Two callback functions
void u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); Parameters byte_cb and gpio_and_delay_cb are two callback functions that need to be written. byte_cb: is a communication-related function, such as i2c write data, gpio_and_delay_cb: is a delay-related function. Communication functions are divided into hardware interfaces and software simulation methods. The official software simulation method is basically written, and you only need to simply specify the io port. The hardware method is used here. The implementation is as follows:
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
static uint8_t buffer[32]; /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
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 */
break;
case U8X8_MSG_BYTE_START_TRANSFER:
buf_idx = 0;
break;
case U8X8_MSG_BYTE_END_TRANSFER:
HW_I2cWrite(buffer, buf_idx); //硬件I2C写字节
break;
default:
return 0;
}
return 1;
}
uint8_t u8g2_gpio_and_delay(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, U8X8_UNUSED void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_GPIO_AND_DELAY_INIT:
OLED_I2C_Init(); //初始化
break;
case U8X8_MSG_DELAY_MILLI:
Delay_Ms(arg_int); //延时
break;
case U8X8_MSG_GPIO_I2C_CLOCK:
break;
case U8X8_MSG_GPIO_I2C_DATA:
break;
default:
return 0;
}
return 1; // command processed successfully.
}
7. u8g2 initialization
void u8g2_Init(u8g2_t *u8g2)
{
#if(SSD1306_DEVICE==SSD1306_128x64)
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8g2_gpio_and_delay); // 初始化 u8g2,硬件I2C
#elif(SSD1306_DEVICE==SSD1306_128x32)
u8g2_Setup_ssd1306_i2c_128x32_univision_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8g2_gpio_and_delay);
#endif
u8g2_InitDisplay(u8g2); //根据所选芯片初始化,完成后显示器处于关闭状态 // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); //唤醒显示器
u8g2_SetContrast(u8g2, 88); //设置对比度
u8g2_ClearBuffer(u8g2); //清除显示缓存
}
8. Design the interface
(1) Draw diagonal lines
#if(SSD1306_DEVICE==SSD1306_128x64)
u8g2_DrawLine(&u8g2, 0, 0, 127, 63); // 画一条线,起始坐标(0,0),终点坐标(127,63)
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
u8g2_DrawLine(&u8g2, 127, 0, 0, 63);
u8g2_SendBuffer(&u8g2);
#elif(SSD1306_DEVICE==SSD1306_128x32)
u8g2_DrawLine(&u8g2, 0, 0, 127, 31); // 画一条线,起始坐标(0,0),终点坐标(127,63)
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
u8g2_DrawLine(&u8g2, 127, 0, 0, 31);
u8g2_SendBuffer(&u8g2);
#endif
(2) Draw the U8g2 logo in general, placing it horizontally, vertically, etc.
void draw(u8g2_t *u8g2)
{
#if(SSD1306_DEVICE==SSD1306_128x64)
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");
#elif(SSD1306_DEVICE==SSD1306_128x32)
u8g2_SetFontMode(u8g2, 1); /*字体模式选择*/
u8g2_SetFontDirection(u8g2, 0); /*字体方向选择*/
u8g2_SetFont(u8g2, u8g2_font_inb24_mf); /*字库选择*/
u8g2_DrawStr(u8g2, 0, 24, "U");
u8g2_SetFontDirection(u8g2, 1);
u8g2_SetFont(u8g2, u8g2_font_inb30_mn);
u8g2_DrawStr(u8g2, 24, 8, "8");
u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb24_mf);
u8g2_DrawStr(u8g2, 64, 24, "g");
u8g2_DrawStr(u8g2, 96, 32, "\xb2");
#endif
}
(3) Display Chinese and English. Here we display the information related to EEWorld and Qinheng.
#if(SSD1306_DEVICE==SSD1306_128x64)
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); //选择字库
u8g2_DrawStr(&u8g2, 0, 15, "Hello World!");
u8g2_SetFont(&u8g2, u8g2_font_wqy16_t_chinese2);
u8g2_DrawUTF8(&u8g2, 0, 30, "H你好世界");
u8g2_SetFont(&u8g2, u8g2_font_wqy12_t_chinese2);
u8g2_DrawUTF8(&u8g2, 0, 43, "H你好世界");
u8g2_SetFont(&u8g2, u8g2_font_fur11_tr);
u8g2_DrawUTF8(&u8g2, 0, 59, "blog.zeruns.tech");
#elif(SSD1306_DEVICE==SSD1306_128x32)
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); //选择字库
u8g2_DrawStr(&u8g2, 0, 14, "EEWorld");
u8g2_SetFont(&u8g2, u8g2_font_wqy13_t_gb2312);
u8g2_DrawUTF8(&u8g2, 0, 30, "沁恒");
u8g2_SetFont(&u8g2, u8g2_font_ncenB08_tr); //选择字库
u8g2_DrawStr(&u8g2, 26, 30, "CH32V208");
u8g2_SetFont(&u8g2, u8g2_font_wqy13_t_gb2312);
u8g2_DrawUTF8(&u8g2, 80, 30, "开发板");
#endif
(4) Display concentric circles with gradient changes in a loop.
while(1)
{
Delay_Ms(100);
u8g2_ClearBuffer(&u8g2);//清除缓冲区数据
#if(SSD1306_DEVICE==SSD1306_128x64)
if (++t >= 32)
t = 1;
u8g2_DrawCircle(&u8g2, 64, 32, t, U8G2_DRAW_ALL); //画圆
u8g2_DrawCircle(&u8g2, 32, 32, t, U8G2_DRAW_ALL);
u8g2_DrawCircle(&u8g2, 96, 32, t, U8G2_DRAW_ALL);
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
#elif(SSD1306_DEVICE==SSD1306_128x32)
if (++t >= 16)
t = 1;
u8g2_DrawCircle(&u8g2, 64, 16, t, U8G2_DRAW_ALL); //画圆
u8g2_DrawCircle(&u8g2, 32, 16, t, U8G2_DRAW_ALL);
u8g2_DrawCircle(&u8g2, 96, 16, t, U8G2_DRAW_ALL);
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
#endif
}
9. The main function is as follows. I will not go into details about adding source files of project u8g2.
int main(void)
{
uint8_t t = 0;
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
printf( "SystemClk:%d\r\n", SystemCoreClock );
printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
OLED_I2C_Init();
u8g2_Init(&u8g2); //初始化U8g2
Delay_Ms(100);
#if(SSD1306_DEVICE==SSD1306_128x64)
u8g2_DrawLine(&u8g2, 0, 0, 127, 63); // 画一条线,起始坐标(0,0),终点坐标(127,63)
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
u8g2_DrawLine(&u8g2, 127, 0, 0, 63);
u8g2_SendBuffer(&u8g2);
#elif(SSD1306_DEVICE==SSD1306_128x32)
u8g2_DrawLine(&u8g2, 0, 0, 127, 31); // 画一条线,起始坐标(0,0),终点坐标(127,63)
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
u8g2_DrawLine(&u8g2, 127, 0, 0, 31);
u8g2_SendBuffer(&u8g2);
#endif
Delay_Ms(300);
u8g2_ClearBuffer(&u8g2); //清除缓冲区数据
draw(&u8g2);
u8g2_SendBuffer(&u8g2);
Delay_Ms(1000);
#if(SSD1306_DEVICE==SSD1306_128x64)
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); //选择字库
u8g2_DrawStr(&u8g2, 0, 15, "Hello World!");
u8g2_SetFont(&u8g2, u8g2_font_wqy16_t_chinese2);
u8g2_DrawUTF8(&u8g2, 0, 30, "H你好世界");
u8g2_SetFont(&u8g2, u8g2_font_wqy12_t_chinese2);
u8g2_DrawUTF8(&u8g2, 0, 43, "H你好世界");
#elif(SSD1306_DEVICE==SSD1306_128x32)
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); //选择字库
u8g2_DrawStr(&u8g2, 0, 14, "EEWorld");
u8g2_SetFont(&u8g2, u8g2_font_wqy13_t_gb2312);
u8g2_DrawUTF8(&u8g2, 0, 30, "沁恒");
u8g2_SetFont(&u8g2, u8g2_font_ncenB08_tr); //选择字库
u8g2_DrawStr(&u8g2, 26, 30, "CH32V208");
u8g2_SetFont(&u8g2, u8g2_font_wqy13_t_gb2312);
u8g2_DrawUTF8(&u8g2, 80, 30, "开发板");
#endif
u8g2_SendBuffer(&u8g2);
Delay_Ms(1300);
while(1)
{
Delay_Ms(100);
u8g2_ClearBuffer(&u8g2);//清除缓冲区数据
#if(SSD1306_DEVICE==SSD1306_128x64)
if (++t >= 32)
t = 1;
u8g2_DrawCircle(&u8g2, 64, 32, t, U8G2_DRAW_ALL); //画圆
u8g2_DrawCircle(&u8g2, 32, 32, t, U8G2_DRAW_ALL);
u8g2_DrawCircle(&u8g2, 96, 32, t, U8G2_DRAW_ALL);
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
#elif(SSD1306_DEVICE==SSD1306_128x32)
if (++t >= 16)
t = 1;
u8g2_DrawCircle(&u8g2, 64, 16, t, U8G2_DRAW_ALL); //画圆
u8g2_DrawCircle(&u8g2, 32, 16, t, U8G2_DRAW_ALL);
u8g2_DrawCircle(&u8g2, 96, 16, t, U8G2_DRAW_ALL);
u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
#endif
}
}
3. Test
After compiling and burning, you can see the amazing display effect as follows. Using u8g2 to do oled display design is really rich and flexible.
The effect video is as follows:
u8g2_oled_show
The source code is pinned to the top of this article, please pay attention.