抑制u8g2库到stm32(标准库)

移植u8g2库到stm32(标准库)

实验硬件:

  • STM32F103ZE
  • OLED:0.96寸OLED,IIC接口

U8g2 是一个用于嵌入式设备的单色图形库。U8g2支持单色OLED和LCD,并支持如SSD1306等多种类型的OLED驱动。

U8g2源码的开源库地址:https://github.com/olikraus/u8g2

移植步骤

  1. 从官网下载 u8g2 的源码;
  2. 只移植c语言版本,因而只需要将用到 csrc文件夹内的文件
  3. 找到需要对应的驱动文件(u8x8_d_xxx.c),只需要添加一个型号即可,这里我用到的是 u8x8_d_ssd1306_128x64_noname.c 文件,然后其余的型号可以删除掉(格式一般都为 u8x8_d_xxx.c)
  4. 修改 u8g2_d_setup.c 文件,找到对应 OLED型号的函数,这里用到的是u8g2_Setup_ssd1306_i2c_128x64_noname_f ,保留这个函数,其他删除掉或者注释掉。
1
2
3
4
5
6
7
8
9
10
11
#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_i2c_128x64_noname_1

  • u8g2_Setup_ssd1306_i2c_128x64_noname_2

  • u8g2_Setup_ssd1306_i2c_128x64_noname_f

函数后面的数字或字母表示显示时 buf 的大小:

  • 1:128字节
  • 2:256字节
  • f:1024字节
  1. 修改 u8g2_d_memory.c 文件,查找 u8g2_d_setup.c 文件内保留的函数(u8g2_Setup_ssd1306_i2c_128x64_noname_f) 中 赋值 buf 调用到的函数(u8g2_m_16_8_f),然后将 u8g2_d_memory.c 文件中的其他函数都删除或注释掉,只保留赋值 buf 调用到的函数(u8g2_m_16_8_f)。
  2. 将修改完的 csrc文件夹添加到工程中。
  3. 将编译器的优化级别调整为 -O2或-O3,否则还是会因为字体的原因出现链接时空间不足的问题。

测试 u8g2库移植是否成功

创建一个头文件

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef __U8G2_OLED_H
#define __U8G2_OLED_H

#include "stm32f10x.h" // Device header
#include "u8g2.h"
void IIC_Init(void);
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
void u8g2Init(u8g2_t *u8g2);
void draw(u8g2_t *u8g2);

#endif

GPIO初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "u8g2_oled.h"


#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);
}

u8x8_gpio_and_delay

在 u8g2 的构造函数中,需要应用程序传入一个函数指针,u8g2 库将通过这个函数指针来调用这个函数,这个函数用来现延时,IO口的控制等等。

赋予U8g2相应的延时函数,比如下面的delay_ms和delay_us

为U8g2提供IIC接口的高低电平调用:

  • U8x8_MSG_GPIO_I2C_CLOCK:IIC 的 SCL

  • U8x8_MSG_GPIO_I2C_DATA:IIC 的 SDA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
__NOP();
break;
case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
for (uint16_t n = 0; n < 320; n++)
{
__NOP();
}
break;
case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
delay_ms(1);
break;
case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
delay_us(5);
break; // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
if(arg_int == 1)
{
GPIO_SetBits(IIC_GPIO_Port, SCL_Pin);
}
else if(arg_int == 0)
{
GPIO_ResetBits(IIC_GPIO_Port, SCL_Pin);
}
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
if(arg_int == 1)
{
GPIO_SetBits(IIC_GPIO_Port, SDA_Pin);
}
else if(arg_int == 0)
{
GPIO_ResetBits(IIC_GPIO_Port, SDA_Pin);
}
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 */ 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;
}

u8g2Init

U8g2的初始化,根据上面用到的函数进行调用,这里我需要调用下面这个 u8g2_Setup_ssd1306_128x64_noname_f函数,该函数的4个参数含义:

  • u8g2:传入的U8g2结构体

  • U8G2_R0:默认使用U8G2_R0即可(用于配置屏幕是否要旋转)

  • u8x8_byte_sw_i2c:使用软件IIC驱动,该函数由U8g2源码提供

  • u8x8_gpio_and_delay:就是上面我们写的配置函数

1
2
3
4
5
6
7
void u8g2Init(u8g2_t *u8g2)
{
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay); // 初始化 u8g2 结构体
u8g2_InitDisplay(u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); // 打开显示器
u8g2_ClearBuffer(u8g2);
}

显示测试函数 draw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);![image-20230802221013234](./C:/Users/Obito/AppData/Roaming/Typora/typora-user-images/image-20230802221013234.png)
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");
}

显示效果

最后编译完成烧录到 stm32即可,效果如下:

U8g2

STM32移植U8g2图形库——玩转OLED显示 - 哔哩哔哩 (bilibili.com)

stm32 u8g2移植笔记 - 代码天地 (codetd.com)