电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
STM32移植U8g2图形库——玩转OLED显示
分 享
扫描二维码分享
STM32移植U8g2图形库——玩转OLED显示
STM32
U8g2
oled
码农爱学习
关注
发布时间: 2022-04-06
丨
阅读: 793
之前的文章,介绍过ESP8266在Arduino IDE环境中使用U8g2库,实现OLED上的各种图形显示。 本篇,介绍一下U8g2库如何移植到STM32上,进行OLED的图形显示。 本次的实验硬件为: - STM32:型号为最常见的**STM32F103C8T6** - OLED:0.96寸OLED,**IIC接口**(如果是**SPI接口**,文中也有对应的修改介绍) # 1 U8g2简介 U8g2 是一个用于嵌入式设备的单色图形库。U8g2支持单色OLED和LCD,并支持如SSD1306等多种类型的OLED驱动。 U8g2源码的开源库地址:
![](https://cf02.ickimg.com/bbsimages/202204/a2b5fcbb60e4af8a7cc3e35781d87a07.png) # 2 移植步骤 首先下载U8g2的源码,因为STM32主要是使用C语言编程,所以只需关注源码中的C源码部分,即csrc文件夹下的文件。 ## 2.1 精简c源码 U8g2支持多种显示驱动的屏幕,因为源码中也包含了各个驱动对应的文件,为了减小整个工程的代码体积,在移植U8g2时,可以删除一些无用的文件。 ### 2.1.1 去掉无用的驱动文件 这些驱动文件通常是u8x8_d_xxx.c,xxx包括驱动的型号和屏幕分辨率。ssd1306驱动芯片的OLED,使用**u8x8_ssd1306_128x64_noname.c**这个文件,其它的屏幕驱动和分辨率的文件可以删掉。 ![](https://cf02.ickimg.com/bbsimages/202204/458da0ee987a1f8f4aa2f5b604ad9046.png) ### 2.1.2 精简u8g2_d_setup.c 由于我的OLED是IIC接口,只留一个本次要用到的**u8g2_Setup_ssd1306_i2c_128x64_noname_f**就好(如果是SPI接口,需要使用**u8g2_Setup_ssd1306_128x64_noname_f**这个函数),其它的可以删掉或注释掉。 ```c #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字节** ### 2.1.3 精简u8g2_d_memory.c 由于用到的**u8g2_Setup_ssd1306_i2c_128x64_noname_f**函数中,只调用了**u8g2_m_16_8_f**这个函数,所以留下这个函数,**其它的函数一定要删掉或注释掉,否则编译时很可能会提示内存不足!!!** ```c #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 } ``` ## 2.2 编写移植函数 精简源码之后,还需要编写如下的配置函数。 ### 2.2.1 GPIO初始化 对OLED用到的IIC接口进行GPIO的初始化配置: ```c #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接口即可。 ### 2.2.2 u8x8_gpio_and_delay 这个函数也需要自己写,主要的修改包括: - 赋予U8g2相应的**延时函数**,比如下面的delay_ms和delay_us - 为U8g2提供IIC接口的高低电平调用: - **U8X8_MSG_GPIO_I2C_CLOCK**:IIC的SCL - **U8X8_MSG_GPIO_I2C_DATA**:IIC的SDA ```c 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; } ``` 如果是SPI接口,可以参考如下写法: ```c 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个参数含义: - **u8g2**:传入的U8g2结构体 - **U8G2_R0**:默认使用U8G2_R0即可(用于配置屏幕是否要旋转) - **u8x8_byte_sw_i2c**:使用软件IIC驱动,该函数由U8g2源码提供 - **u8x8_gpio_and_delay**:就是上面我们写的配置函数 ```c 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); } ``` ### 2.2.4 显示测试函数 使用U8g2提供的测试函数,用于查看显示效果 ```c 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"); } ``` ## 2.3 源码加入到MDK编译 在一个STM32的基础例程上进行修改。 ### 2.3.1添加u8g2源码到工程 左侧工程目录添加U8g2源码,然后再添加U8g2的头文件搜寻目录,如下: ![](https://cf02.ickimg.com/bbsimages/202204/a8acf61fdadf599f29158d8423626af8.png) ### 2.3.2 主函数 主函数中,首先是IIC的初始化和U8g2的初始化,然后就可以测试U8g2的图形显示功能了: ```c #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)); } } ``` # 3 测试效果 ![](https://cf02.ickimg.com/bbsimages/202204/8e15260c9b2dd168863643511f90c3a2.png) # 4 总结 本篇介绍了如何将U8g2图形库移植到STM32中,其中主要的修改包括: - 精简源码中的u8g2_d_setup.c和u8g2_d_memory.c - OLED所用IIC接口的GPIO初始化 - 编写u8x8_gpio_and_delay和u8g2Init 其中,u8g2_d_memory.c文件一定要去掉无用的函数,否则编译时会提示内存不足;对于SPI接口的OLED,参考IIC接口进行类似的修改即可。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
码农爱学习
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交