STM32移植U8g2图形库实践指南
STM32移植U8g2图形库
目录标题
前言
一直想找一个可移值性强的显示驱动,偶然发现U8G2这个图像库,发现这个库能实现许多好玩有趣的动画,可以编一些界面,作为知识储备。
U8G2图形库介绍
U8g2 是一个用于嵌入式设备的简易图形库,可以在多种 OLED 和 LCD 屏幕上,支持包括 SSD1306 (新版型号为SSD1315)等多种类型的底层驱动,并可以很方便地移植到 Arduino 、树莓派、NodeMCU 和 ARM 上。
U8g2 库同时包含了 U8x8 绘图库,两者的区别为:
U8g2 包含各种简单及复杂图形的绘制,并支持各种形式的字体,但需要占用一定单片机的内存作为绘图缓存
U8x8 只包含简单的显示文本功能,且只支持简单、定宽的字体。它直接绘制图形,没有缓存功能
U8g2 库的 GitHub 地址为:https://github.com/olikraus/u8g2 ,可以从中获取到源码与文档帮助
移值
本次以将 U8g2 移植到 STM32 单片机与 SSD1306 通过 I2C 驱动的 128×64 OLED 为例,介绍移植的方法。不同单片机和驱动的移植可以参考这一过程,也可以参考 U8g2 的官方移植教程。
https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform 首先下载或克隆 U8g2 的源码,这里主要是使用 C 语言编写,所以只需要用到 csrc 目录下的文件。
下载完成后,将 csrc 目录拷贝或移动到工程目录里,并重命名为合适的目录名例如 u8g2lib 。
精简c源码
U8g2支持多种显示驱动的屏幕,因为源码中也包含了各个驱动对应的文件,为了减小整个工程的代码体积,在移植U8g2时,可以删除一些无用的文件。
去掉无用的驱动文件
精简u8g2_d_setup.c
由于我的OLED是IIC接口,只留一个本次要用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f就好(如果是SPI接口,需要使用u8g2_Setup_ssd1306_128x64_noname_f这个函数),其它的可以删掉或注释掉
#include "u8g2.h"
/* ssd1306 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);
}
注意,与这个函数看起来十分相似的函数的有:
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_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
}
编写移植函数
精简源码之后,还需要编写如下的配置函数。
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);
}
如果是SPI接口,则初始化对应的SPI接口即可。
u8x8_gpio_and_delay
这个函数也需要自己写,主要的修改包括:
赋予U8g2相应的延时函数,比如下面的delay_ms和delay_us
为U8g2提供IIC接口的高低电平调用:
uint8_t u8g2_gpio_and_delay_stm32(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_DELAY_MILLI:
delay_ms(arg_int);
break;
case U8X8_MSG_DELAY_10MICRO:
delay_us(10);
break;
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
case U8X8_MSG_GPIO_I2C_CLOCK:
if (arg_int) I2C_OLED_SCL_H();
else I2C_OLED_SCL_L();
break;
case U8X8_MSG_GPIO_I2C_DATA:
if (arg_int) I2C_OLED_SDA_H();
else I2C_OLED_SDA_L();
break;
default:
return 0;
}
return 1;
}`
如果是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;
}
可以看出,对于IIC与SPI接口,只有分别进行对应的配置即可。
2.2.3 u8g2Init
U8g2的初始化,需要调用下面这个u8g2_Setup_ssd1306_128x64_noname_f函数,该函数的4个参数含义:
void u8g2Init(u8g2_t *u8g2)
{
OLED12864_I2C_Init();
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8g2_gpio_and_delay_stm32);
u8g2_InitDisplay(u8g2); //初始化
u8g2_SetPowerSave(u8g2, 0);
u8g2_ClearBuffer(u8g2);//清屏
}
显示测试函数
使用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");
}
源码加入到MDK编译
主函数
主函数中,首先是IIC的初始化和U8g2的初始化,然后就可以测试U8g2的图形显示功能了:
#include "delay.h"
#include "sys.h"
#include "u8g2.h"
int main(void)
{
delay_init();
IIC_Init();
u8g2_t u8g2;
u8g2Init(&u8g2);
while(1)
{
u8g2_FirstPage(&u8g2);
do
{
draw(&u8g2);
} while (u8g2_NextPage(&u8g2));
}
}
测试效果
## 总结
本篇介绍了如何将U8g2图形库移植到STM32中,其中主要的修改包括:
作者:蒲公英666