一、硬件准备
使用的主控是:立创梁山派GD32F450ZGT6
使用的OLED是:0.96寸4针IIC通信的OLED屏

二、u8g2介绍
U8g2 是一个用于嵌入式设备的单色图形库。U8g2支持单色OLED和LCD,并支持如SSD1306等多种类型的OLED驱动,显示丝滑。
三、精简u8g2
首先去下载u8g2的源码
U8g2源码的开源库地址:https://github.com/olikraus/u8g2
(因为是外网,所以会有一点慢)

源码下载下来之后,我们只需要关注文件夹里面的【csrc】文件夹下的文件。

因为u8g2支持很多的驱动芯片,所以整个文件对我们的工程来说非常大。我们只需要我们屏幕的驱动文件,其他无用的文件可以删除。
3.1删除其他不需要的驱动文件
这些驱动文件通常是u8x8_d_xxx.c,xxx包括驱动的型号和屏幕分辨率。我们使用的是ssd1306驱动芯片的OLED,分辨率是128*64。在u8g2里使用的是u8x8_ssd1306_128x64_noname.c这个文件,其它的屏幕驱动和分辨率的文件可以删掉。(我这里已经把其他的驱动文件删除,只剩下需要的)

3.2 删除其他的驱动入口函数
在u8g2_d_setup.c里,因为我们的OLED是IIC的接口,所以只留一个我们要用到的【u8g2_Setup_ssd1306_i2c_128x64_noname_f】函数就好,其他的可以删除或者注释
在函数结尾加上
/* 注意要加上这一句 不然无法显示*/
u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
(如果你嫌弃删除麻烦,可以直接复制下面的代码)
/*该文件属于 u8g2_d_setup.c */
#include "u8g2.h"
/*
* 给IIC接口的OLED用的
* 函数最后的数字或字母,代表显示时的buf大小:
1:128字节
2:256字节
f:1024字节
*/
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);
}
3.3删除内存文件中的其他函数
由于用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f函数中,只调用了u8g2_m_16_8_f这个函数,这个函数在【u8g2_d_memory.c】里面,留下这个函数,其它的函数一定要删掉或注释掉,否则编译时很可能会提示内存不足。
#include "u8g2.h"
/*该文件属于 u8g2_d_memory.c */
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
}
//记得给新行,不然有警告
3.4 IIC初始化及回调函数编写
我们的屏幕是需要IIC进行通信的,这里选择使用的是软件IIC。因为u8g2里自带了软件IIC代码,我们只需要提供用的是哪一个引脚,还有提供IIC需要的延时函数即可。
/*该代码位于oled.c 和 oled.h */
//需要修改的引脚时钟
#define RCU_SCL RCU_GPIOF
#define RCU_SDA RCU_GPIOC
//需要修改的引脚 SCL SDA
#define GPIO_periph_SCL GPIOF
#define GPIO_pin_SCL GPIO_PIN_8
#define GPIO_periph_SDA GPIOC
#define GPIO_pin_SDA GPIO_PIN_3
void IIC_software_init(void)
{
rcu_periph_clock_enable(RCU_SCL);
rcu_periph_clock_enable(RCU_SDA);
//SCL
gpio_mode_set(GPIO_periph_SCL, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_pin_SCL);
gpio_output_options_set(GPIO_periph_SCL, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_pin_SCL);
//SDA
gpio_mode_set(GPIO_periph_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_pin_SDA);
gpio_output_options_set(GPIO_periph_SDA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_pin_SDA);
gpio_bit_write(GPIO_periph_SCL, GPIO_pin_SCL, SET);
gpio_bit_write(GPIO_periph_SDA, GPIO_pin_SDA, SET);
}
回调函数的主要功能有
1、赋予U8g2相应的延时函数,比如下面的delay_ms和delay_us
2、为U8g2提供IIC接口的高低电平调用:
U8X8_MSG_GPIO_I2C_CLOCK:IIC的SCL
U8X8_MSG_GPIO_I2C_DATA:IIC的SDA
回调函数:
/*
u8x8回调函数
/*该代码属于 u8g2_d_setup.c */
*/
uint8_t u8x8_gpio_and_delay_gd32f450(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
switch(msg)
{
case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8
IIC_software_init();
break; // can be used to setup pins
case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
delay_1ms(1);
break;
case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
if(arg_int == 1)delay_us(5);
else if(arg_int == 4){;}
break; // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
case U8X8_MSG_GPIO_RESET: // Reset pin: Output level in arg_int
break;
case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
//SCL输出
gpio_bit_write(GPIO_periph_SCL, GPIO_pin_SCL, (bit_status)arg_int);
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
//SDA输出
gpio_bit_write(GPIO_periph_SDA, GPIO_pin_SDA, (bit_status)arg_int);
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 */ key_state_get(BSP_KEY1_PIN));
// //printf("u8g2 get key sel\r\n");
// break;
// case U8X8_MSG_GPIO_MENU_NEXT:
// u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ key_state_get(BSP_KEY2_PIN));
// //printf("u8g2 get key next\r\n");
// break;
// case U8X8_MSG_GPIO_MENU_PREV:
// u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ key_state_get(BSP_KEY3_PIN));
// //printf("u8g2 get key prev\r\n");
// break;
// case U8X8_MSG_GPIO_MENU_HOME:
// u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ key_state_get(BSP_KEY4_PIN));
// //printf("u8g2 get key home\r\n");
break;
default:
u8x8_SetGPIOResult(u8x8, 1); // default return value
break;
}
return 1;
}
四、显示测试
将改好的文件复制到过程文件夹里。(我放在工程目录APP里的u8g2文件夹下)

打开工程导入u8g2的.c文件(创建一个新目录)


导入完成的样子

main.c
#include "gd32f4xx.h"
#include "systick.h"
#include "led.h"
#include "oled.h"
#include "u8g2.h"
#include "stdio.h"//请在魔法棒处,勾选 Use MicroLIB
u8g2_t u8g2;
void u8g2Init(u8g2_t *u8g2)
{
// 初始化 u8g2 结构体
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_gd32f450);
u8g2_InitDisplay(u8g2); //根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); //打开显示器
u8g2_SetFont(u8g2, u8g2_font_unifont_t_chinese2); //设置字体
u8g2_SetFontDirection(u8g2, 0); //字体方向向右
}
int main(void)
{
char temp[20], i = 0, j = 0, k = 0;
nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3); // 中断分组3
systick_config(); //系统滴答定时器 定时1us
LED_Init(); //LED初始化
IIC_software_init(); //IIC的GPIO初始化
u8g2Init(&u8g2); //u8g2初始化
while(1)
{
u8g2_ClearBuffer((u8g2_t*)&u8g2);//先清除缓冲区数据
u8g2_DrawRFrame(&u8g2, 5, 10, 128-(12*1),40, 5);//显示圆角空心矩形
sprintf(temp,"i = %d",i++);
u8g2_DrawStr(&u8g2,6,29,temp); //显示字符串
sprintf(temp,"j = %d",j+=2);
u8g2_DrawStr(&u8g2,62,29,temp); //显示字符串
sprintf(temp,"k = %d",k+=3);
u8g2_DrawStr(&u8g2,38,43,temp); //显示字符串
u8g2_SendBuffer((u8g2_t*)&u8g2);//发送缓冲区数据到屏幕
delay_1ms(50);
}
}
如果编译后发现有几百个的错误,像下面。

那么需要开启优化,最少开1级。

开启后重新编译即可。

实际效果:
代码链接:
链接:https://pan.baidu.com/s/1DU0uHcqeBtcHSRBLjOJLxQ?pwd=1234
提取码:1234