u8g2
一.u8g2是什么
u8g2是一个开源的C/C++库,用于在嵌入式系统中显示文本和图形。它支持多种显示器类型,只能用于单色显示器,包括OLED、LCD、LED矩阵等,并且可以在多种微控制器上运行,包括Arduino、ESP8266、ESP32等。
u8g2库的主要特点包括:
- 支持多种显示器类型和分辨率
- 支持多种字体和字符集
- 内置了各种型号的显示的初始化及驱动代码
- 提供了丰富的字体和字符集,无需自己取模.
- 移植非常简单,逻辑也很清晰.
二.u8g2如何移植?
- U8g2图形库与STM32移植
- 基于STM32移植U8g2图形库的OLED显示(HAL库)
- STM32移植U8g2图形库——玩转OLED显示
- 【开源】硬件/软件i2c两种方式移植u8g2单色图形库驱动0.96吋OLED
1. 下载源码?
https://github.com/olikraus/u8g2 ,在这个地址下载得到其中的源代码 ,我们只需其中的 csrc 目录里面的文件,其它的文件都不需要.
2. 清除不必要的驱动文件
u8g2中已经包含的所有可以支持的显示屏的驱动文件, 每一种显示屏都有对应的驱动文件.我们需要将不需要的驱动文件全部删除,以避免冲突. 这些驱动文件的文件名都有一个共同的特征.
- 如:
u8x8_d_ssd1305.c这个文件,所有的驱动文件都是以u8x8_d_开头的.后面就是芯片的名称. 或者u8x8_d_ssd1306_128x64_noname.c这个文件名就是指的ssd1306驱动芯片的,尺寸大小为128x64的 noname 表示不是专指的而是通用的驱动程序.由于ssd1306的显示屏非常多,所以名称就相对复杂一些. - 除了不需要的驱动文件外,其它文件一个
都不要删除.都是有用的. - 同时要注意的:
u8x8_d_setup.c文件以及u8g2_d_memory.c这两个文件是显示屏驱动的配置文件,这两个文件是我们需要的两个重要文件,不能删除.
最后是这个样子的 
3. 修改驱动配置文件u8g2_d_setup.c
假如我的屏幕是ssd1306芯片的,借助于i2c来驱动的 128*64分辨率的屏幕. 那么我们可以找到下面几个函数
// 未指定 i2c 的,是指的spi接口中的函数
void u8g2_Setup_ssd1306_128x64_noname_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
void u8g2_Setup_ssd1306_128x64_noname_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
void u8g2_Setup_ssd1306_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
// 指定了 i2c 的,是指的使用i2c接口的函数
void u8g2_Setup_ssd1306_i2c_128x64_noname_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
void u8g2_Setup_ssd1306_i2c_128x64_noname_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
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)对于ssd1306屏幕有8个page. 所以其刷新也有三种方式.
- 最后一个字符的
_1,表示:单缓冲,需要一页page画一页page, 对RAM要求最低 - 最后一个字符的
_2,表示:双缓冲模式. 两个缓冲转流工作, 刷新速度更快一些. - 最后一个字符的
_f,表示:全缓冲模式, 8个page全部缓冲,占RAM最大,但是刷新速度最快. - 建议直接选择
_f
需要根据名称必须判断,不要弄错了.
只需要保留一个函数,其它所有的代码全部删除.
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)
{
uint8_t tile_buf_height;
uint8_t *buf;
u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb);
buf = u8g2_m_16_8_f(&tile_buf_height);
u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}4. 修改u8g2_d_memory.c的内存分配文件
在上面的函数中,有一个函数 u8g2_m_16_8_f 这个函数位于 u8g2_d_memory.c 文件,我们需要修改这个文件.
方法也很简单,直接找到这个函数u8g2_m_16_8_f,其它所有的函数,全部删除.
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
}除了用得着的这个函数,其它的所有的函数全都删除. 通过以上配置,其源码的移植部分就算弄好了,但是要想让屏幕可以正常使用,在调用 u8g2_Setup_ssd1306_i2c_128x64_noname_f 这个函数时:
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)时,需要指定两个回调函数: u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb 所以这两个回调函数,需要写在自己的项目处
5. 编写回调函数
1. 编写函数名
在自己的项目目录中, 创建一个 oled.c 文件,我们要在这个函数中,编写三个函数.其它两个是上面的回调函数. 还有一个是void u8g2Init(u8g2_t *u8g2)这个函数用于调用 u8g2_Setup_ssd1306_i2c_128x64_noname_f 来将设置的回调函数配置上去.
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){
//配置回调函数
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_stm32_hw_i2c,u8g2_gpio_and_delay_stm32);
//设置I2C地址(8位地址格式)
u8g2_SetI2CAddress(u8g2, OLED_ADDRESS<<1);
u8g2_InitDisplay(u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); // 打开显示器
u8g2_ClearBuffer(u8g2); // 清除缓冲区
}我们来看一下uint8_t u8x8_byte_stm32_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr)这个函数中的具体内容
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[32];
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(); // 注释掉,因为已经在main中初始化
break;
case U8X8_MSG_BYTE_START_TRANSFER:// 开始传输
buf_idx = 0;
break;
case U8X8_MSG_BYTE_END_TRANSFER:// 结束传输
// HAL库使用7位地址,需要左移1位转换为8位地址
if (HAL_I2C_Master_Transmit(&hi2c1, OLED_ADDRESS<<1, buffer, buf_idx, 1000) != HAL_OK)
return 0;
break;
case U8X8_MSG_BYTE_SET_DC:// 设置DC引脚
// 对于I2C,DC状态已经在CAD层通过控制字节处理
// 这里不需要做任何操作
break;
default:
return 0;
}
return 1;
}2.分析一下 u8x8_byte_stm32_hw_i2c的回调函数
在这个函数中,实际上就是处理u8g2请求的各种消息.我们只需要根据这些消息,和对应的参数,来将数据处理好,就可以了. 结合我们的I2C硬件处理方式,我们只需要将这些消息处理好主可以了.包括:
case U8X8_MSG_BYTE_SEND// 当u8g2请求发送消息时. 其参数是要发送的消息arg_ptr和消息的长度arg_intcase U8X8_MSG_BYTE_INIT//初始化硬件环境,只需要初始i2c的硬件就可以了.MX_I2C1_Init();case U8X8_MSG_BYTE_START_TRANSFER// 开始传输进行数据传输case U8X8_MSG_BYTE_END_TRANSFER// 等待结束传输数据传输完成,return 0表示传输完成case U8X8_MSG_BYTE_SET_DC// 设置DC引脚,对于i2c来讲,并没有dc这个引脚,也不需要这个消息,所以直接break即可,但是对于spi接口屏幕就需要处理了. 根据这些消息处理好了,就可以完成了屏幕的驱动.
3. 不需要自己来进行屏幕的初始化序列
咱们都知道一个屏幕在使用之前,必须要进行一次初始化序列,屏幕才能正常工作.但是在这个u8g2中,并不需要我们自己来编写屏幕的初始化序列. 因为u8g2会根据 型号 自动去处理这个初始化序列.
3.完成u8g2_gpio_and_delay_stm32函数
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: // 延时100纳秒
__NOP();
break;
case U8X8_MSG_DELAY_10MICRO: // 延时10微秒
for (uint16_t n = 0; n < 320; n++) {
__NOP();
}
break;
case U8X8_MSG_DELAY_MILLI: // 延时arg_int毫秒
HAL_Delay(arg_int);
break;
// 硬件I2C不需要以下延时和GPIO控制
// case U8X8_MSG_DELAY_I2C: // 延时I2C时钟 - 硬件I2C不需要
// case U8X8_MSG_GPIO_I2C_CLOCK: // 输出I2C时钟 - 硬件I2C不需要
// case U8X8_MSG_GPIO_I2C_DATA: // 输出I2C数据 - 硬件I2C不需要
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;
}这个函数是为u8g2提供一些必要的延时函数.也是软件模拟i2c的引脚电平控制的地方. 我们这里只需要完成延时函数的代码就可以了.
case U8X8_MSG_DELAY_100NANO:延时100nscase U8X8_MSG_DELAY_10MICRO:延时10uscase U8X8_MSG_DELAY_MILLI:延长 x ms , x的值取值为arg_int的值U8X8_MSG_DELAY_I2C: I2C的操作延时,我们使用硬件控制,这里不需要处理case U8X8_MSG_GPIO_I2C_CLOCK: // 输出I2C时钟 - 硬件I2C不需要case U8X8_MSG_GPIO_I2C_DATA: // 输出I2C数据 - 硬件I2C不需要case U8X8_MSG_GPIO_MENU_*// 这几个是用于处理按键操作的,我们这里不需要,直接使用默认的函数即可.
6. 将u8g2的代码添加至系统中
要将全部的c代码文件和头文件目录都要编译到系统中去,我们使用cmakefile.txt来编写,就非常简单了
# 遍历u8g2目录,将所有源文件添加到项目中
file(GLOB_RECURSE u8g2_sources "u8g2/*.c")
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
${u8g2_sources}
Core/Src/oled.c
)
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
u8g2
)三.u8g2如何使用?
这里直接给出一个使用示例
void draw(u8g2_t *u8g2)
{
u8g2_ClearBuffer(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_SendBuffer(u8g2);
}
void main()
{
u8g2_t u8g2;
u8g2Init(&u8g2);
draw(&u8g2);
while(1);
}四.全部示例源代码
这里的代码是使用ssd1306芯片的的I2C的驱动 u8g2的主要源码?
- main.c
- oled.c(.h)(这是核心代码)
- u8g2_d_setup.c
- u8g2_d_memory.c
- CMakeList.txt
- 对于ssd1312屏幕的移植代码,基本上与 ssd1306完全一样.只是移植的文件有一点不一样
- 移植的文件名为:
u8x8_d_ssd1312.c - 以下是
NOKIA 5510屏幕的驱动代码,这个屏幕使用的芯片是PCD8544,u8g2是支持的 - 移植的文件名为:
u8x8_d_pcd8544_84x48.c - pcd8544_lcd.c (这个文件是使用NOKIA 5510屏幕核心代码)
- pcd8544_u8g2_d_setup.c
- pcd8544_u8g2_d_memory.c
