U8g2图形库移植
前言
在最早接触屏幕的时候用到的都是最常见的0.96寸的单色OLED屏幕,当时主要用于智能车PID调参,所以基本上都是使用网上大家通用的代码搞的,后面熟悉了之后用这个屏幕做了个简易的示波器,当时画波形图是自己通过芯片手册上说明自己写的一个GUI接口。后面接触到的屏幕逐步迈向彩屏了,所以对这种单色的OLED屏幕就没太关注过了。最近发现了一个U8g2的图形库正好适用于这种OLED屏幕上,且之前也了解过LVGL的图形库,所以这里就简单移植一下,为以后屏幕使用就不需要自己造轮子了。
U8g2简介
U8g2 是一个用于嵌入式设备的简易图形库,可以在多种 OLED 和 LCD 屏幕上,支持包括 SSD1306 等多种类型的底层驱动,并可以很方便地移植到 Arduino 、树莓派、NodeMCU 和 ARM 上。
U8g2 库同时包含了 U8x8 绘图库,两者的区别为:
- U8g2 包含各种简单及复杂图形的绘制,并支持各种形式的字体,但需要占用一定单片机的内存作为绘图缓存
- U8x8 只包含简单的显示文本功能,且只支持简单、定宽的字体。它直接绘制图形,没有缓存功能
U8g2 库的 GitHub 地址为:https://github.com/olikraus/u8g2 ,可以从中获取到源码与文档帮助。
U8g2的移植
本次以将 U8g2 移植到 STM32 单片机与 SSD1306 通过 SPI 驱动的 128x64 OLED 为例,介绍移植的方法。不同单片机和驱动的移植可以参考这一过程,也可以参考 U8g2 的官方移植教程 https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform
首先下载或克隆 U8g2 的源码,这里主要是使用 C 语言编写,所以只需要用到 csrc
目录下的文件。
下载完成后,将 csrc
目录拷贝或移动到工程目录里,并重命名为合适的目录名例如 u8g2
。
接下来,需要删除一些无用的代码,并添加底层驱动的代码。
① 删除无用内容
1.去到无用驱动文件
U8g2 的源码为了支持多种设备驱动,包含了许多兼容性的代码。首先,类似 u8x8_d_xxx.c
命名的文件中包含 U8x8 的驱动兼容,文件名包括驱动的型号和屏幕分辨率,因此需要删除无用的驱动文件,只保留当前设备的驱动。
例如:本次使用的是 128x64 的 SSD1306 屏幕,那么只需要保留 u8x8_d_ssd1306_128x64_noname.c
文件,删除其它类似的文件即可。U8g2 支持的所有屏幕驱动可以在 https://github.com/olikraus/u8g2/wiki/u8g2setupc 找到。
2.精简u8g2_d_setup.c
在 u8g2_d_setup.c
中,只需要保留 u8g2_Setup_ssd1306_128x64_noname_f()
这一个函数即可。注意,该文件内有几个命名类似的函数:
- 命名中无
i2c
的是 SPI 接口驱动的函数,含有i2c
的是I2C接口驱动函数,需要根据接口选择; - 以 1 结尾的函数代表使用的缓存空间为 128 字节,以 2 结尾的函数代表使用的缓存为 256字节,类似以 f 结尾的函数代表使用的缓存为 1024 字节。
3.精简u8g2_d_memory.c
它需要根据 u8g2_d_setup.c
中的调用情况决定用到哪些函数。由于 u8g2_Setup_ssd1306_i2c_128x64_noname_f()
函数只用到 u8g2_m_16_8_f()
这一个函数,因此只需要保留它,其余函数全部删除即可。
其它的函数一定要删掉或注释掉,否则编译时很可能会提示内存不足!!
4.精简字体文件
还有一处必要的精简是字体文件 u8x8_fonts.c
和 u8g2_fonts.c
,尤其是 u8g2_fonts.c
,该文件提供了包括汉字在内的几万个文字的多种字体,仅源文件就有 30MB ,编译后占据的内存非常大。
字体类型的变量非常多,建议先复制一个备份后将所有变量删除,之后视情况再添加字体。字体变量的命名大致遵循以下规则:
'' ' '
其中:
<prefix>
前缀基本上以 u8g2 开头;<name>
字体名,其中可能包含字符大小- 各种
<purpose>
含义如下表所示:
名称 | 描述 |
---|---|
t | 透明字体形式 |
h | 所有字符等高 |
m | monospace 字体(等宽字体) |
8 | 每一个字符都是 8x8 大小的 |
<charset>
是字体支持的字符集,如下表所示:
名称 | 描述 |
---|---|
f | 只包含单字节字符 |
r | 只包含 ASCII 范围为 32~127 的字符 |
u | 只包含 ASCII 范围为 32~95 的字符,即不包括小写英文 |
n | 只包含数字及一些特殊用途字符 |
... | 还包括许多自定义的字符集,例如有一些结尾带 gb2312 或 Chinese 的字体名就包括中文 |
一般建议只保留需要的字体即可。
② 将修改完的代码添加至工程中
这里我是采用gcc+make,这里展示makefile需要添加的。keil添加方式这里不在展示。
③ 增加回调函数
U8g2 已经包含了 SSD1306 的驱动,只需要添加一个函数 u8x8_gpio_and_delay()
用于模拟时序即可。这里给出软件4-SPI编写的回调函数。
uint8_t u8x8_stm32_gpio_and_delay(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_prt)
{
switch (msg)
{
case U8X8_MSG_GPIO_AND_DELAY_INIT:
HAL_Delay(200);
break;
case U8X8_MSG_DELAY_MILLI:
HAL_Delay(arg_int);
break;
case U8X8_MSG_DELAY_NANO:
break;
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
case U8X8_MSG_GPIO_SPI_DATA:
(arg_int) ? OLED_SDA_SET : OLED_SDA_CLR;
break;
case U8X8_MSG_GPIO_SPI_CLOCK:
(arg_int) ? OLED_CLK_SET : OLED_CLK_CLR;
break;
case U8X8_MSG_GPIO_CS:
(arg_int) ? OLED_CS_SET : OLED_CS_CLR;
break;
case U8X8_MSG_GPIO_DC:
(arg_int) ? OLED_DC_SET : OLED_DC_CLR;
break;
case U8X8_MSG_GPIO_RESET:
(arg_int) ? OLED_RES_SET : OLED_RES_CLR;
break;
default:
return 0;//A message was received which is not implemented, return 0 to indicate an error
}
return 1;
}
在.h文件中定义好引脚:
#define OLED_CLK_PIN GPIO_PIN_9
#define OLED_SDA_PIN GPIO_PIN_8
#define OLED_RES_PIN GPIO_PIN_7
#define OLED_DC_PIN GPIO_PIN_6
#define OLED_CS_PIN GPIO_PIN_5
#define OLED_CLK_CLR HAL_GPIO_WritePin(GPIOB, OLED_CLK_PIN, GPIO_PIN_RESET)
#define OLED_CLK_SET HAL_GPIO_WritePin(GPIOB, OLED_CLK_PIN, GPIO_PIN_SET)
#define OLED_SDA_CLR HAL_GPIO_WritePin(GPIOB, OLED_SDA_PIN, GPIO_PIN_RESET)
#define OLED_SDA_SET HAL_GPIO_WritePin(GPIOB, OLED_SDA_PIN, GPIO_PIN_SET)
#define OLED_RES_CLR HAL_GPIO_WritePin(GPIOB, OLED_RES_PIN, GPIO_PIN_RESET)
#define OLED_RES_SET HAL_GPIO_WritePin(GPIOB, OLED_RES_PIN, GPIO_PIN_SET)
#define OLED_DC_CLR HAL_GPIO_WritePin(GPIOB, OLED_DC_PIN, GPIO_PIN_RESET)
#define OLED_DC_SET HAL_GPIO_WritePin(GPIOB, OLED_DC_PIN, GPIO_PIN_SET)
#define OLED_CS_CLR HAL_GPIO_WritePin(GPIOB, OLED_CS_PIN, GPIO_PIN_RESET)
#define OLED_CS_SET HAL_GPIO_WritePin(GPIOB, OLED_CS_PIN, GPIO_PIN_SET)
④ U8g2简单使用
U8g2 的初始化可以参考如下步骤:
void u8g2_init(u8g2_t* u8g2)
{
u8g2_Setup_ssd1306_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_stm32_gpio_and_delay);// 初始化 u8g2 结构体
u8g2_InitDisplay(u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); // 打开显示器
u8g2_ClearBuffer(u8g2);
}
这里给出可以显示出官方logo代码,移植成功后调用这个函数既可以在屏幕中显示出官方logo图案:
void u8g2_test_demo(u8g2_t* u8g2)
{
u8g2_FirstPage(u8g2);
do {
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");
} while (u8g2_NextPage(u8g2));
}
官方logo图案:
后记
U8g2移植还是比较容易,后续学习下相关库资源,制作一些比较好看的动画效果。
参考链接:
本文来自博客园,作者:一月一星辰,转载请注明原文链接:https://www.cnblogs.com/tangwc/p/17300439.html