【STM32 + SSD1306 OLED】U8G2图形库移植
2026/5/26 16:15:12 网站建设 项目流程

【STM32 + SSD1306 OLED】U8G2图形库移植

  • 一、前言
  • 二、认识U8G2图形库
  • 三、移植流程
    • 1.硬件组成
    • 2.移植前准备工作
    • 3.U8G2源文件精简与优化
    • 4.编写代码
  • 四、问题优化方案
    • 1.内存不足报错
    • 2.中文显示乱码、缺失或不显示
  • 五、总结

一、前言


基于STM32平台移植U8G2图形库,实现OLED多种单色图形界面的显示。本文提供详细的移植教程,并汇总了开发过程中常见问题的解决策略,助力开发者快速上手。



二、认识U8G2图形库


使用U8G2库驱动OLED相比于直接用IIC/SPI等通信方式更具有优势,主要体现在如下方面:

(1)跨平台且移植性强:同一套U8G2代码可在ESP32、STM32、Arduino等不同平台运行,仅需要调整初始化参数,极大提升了项目的移植效率。

(2)降低开发复杂度:如IIC通信控制需要处理时序、发送命令或数据等代码,涉及不少寄存器操作或运算,而U8G2封装了这些复杂逻辑,且提供不同驱动芯片(如SH1107、UC1701等)的绘图函数(如u8g2_DrawStr、u8g2_DrawGlyph等)和多种字体(中、英文等)。

(3)提供多种缓冲模式:全款冲、页缓冲和U8X8纯文本模式,可根据单片机的RAM大小选择合适模式。



三、移植流程


1.硬件组成


(1)主控芯片:使用的是STM32F103RCT6。

(2)OLED显示屏:本模块搭载的是SSD1306驱动芯片,分辨率是128*64,默认采用4线制SPI总线接口。实际应用切换为IIC接口,需进行如下硬件调整:将模块上的电阻R3移至R1位置,R8位置使用0欧电阻短路,同时将CS片选引脚、DC数据/命令引脚接地,RES复位引脚接电源拉高。


STM32与OLED的引脚连接方式如下。


2.移植前准备工作


(1)在GitHub平台搜索U8G2并下载最新源码。



(2)准备需要移植的基于STM32的工程源码。


3.U8G2源文件精简与优化


通过移除未引用的文件及死代码,消除无效编译代码,从而减小构建体积并提升编译效率。

(1)STM32使用的是C语言编程,则只需要csrc文件夹内的文件。鉴于OLED屏幕采用SSD1306驱动且分辨率为128x64,应仅保留u8x8_d_ssd1306_128x64_noname.c文件,其余所有不匹配的u8x8_d_*.c驱动文件均可删除。


(2)先确认OLED屏幕接口类型(IIC或SPI),随后在u8g2_d_setup.c文件中仅保留对应的函数(IIC接口保留u8g2_Setup_ssd1306_i2c_128x64_noname_f,SPI接口保留u8g2_Setup_ssd1306_128x64_noname_f),并将其余无关函数屏蔽或删除。



函数后缀含义及选型建议:
_f:对应1024字节缓冲区,适用于全缓冲模式;
_1:对应128字节缓冲区,适用于页缓冲模式;
_2:对应256字节缓冲区,刷新速度下降,但稳定性提升。

选型策略:STM32资源充裕时,推荐优先使用_f(1024字节)以获最佳性能;若资源受限,可根据实际需求选择_1或_2。

(3)在u8g2_d_memory.c中根据前步选择的缓冲区类型(_f/_1/2),仅保留对应的u8g2_m_16_8*(_f/_1/_2)系列函数,屏蔽或删除其余无关函数。



(4)添加以下函数完成硬件接口配置

//IIC接口#defineSCL_PinGPIO_Pin_1#defineSDA_PinGPIO_Pin_2#defineSCL_GPIO_PortGPIOA#defineSDA_GPIO_PortGPIOAuint8_tu8x8_gpio_and_delay(U8X8_UNUSEDu8x8_t*u8x8,U8X8_UNUSEDuint8_tmsg,U8X8_UNUSEDuint8_targ_int,U8X8_UNUSEDvoid*arg_ptr){switch(msg){caseU8X8_MSG_DELAY_MILLI://Function with a delay of 'arg_int' msdelay_ms(arg_int);break;caseU8X8_MSG_DELAY_10MICRO://Function with a delay of 10usdelay_us(10);break;caseU8X8_MSG_DELAY_100NANO://Function with a delay of 100ns__NOP();break;caseU8X8_MSG_GPIO_I2C_CLOCK:if(arg_int)GPIO_SetBits(SCL_GPIO_Port,SCL_Pin);//SCL=1elseGPIO_ResetBits(SCL_GPIO_Port,SCL_Pin);//SCL=0break;caseU8X8_MSG_GPIO_I2C_DATA:if(arg_int)GPIO_SetBits(SDA_GPIO_Port,SDA_Pin);//SDA=1elseGPIO_ResetBits(SDA_GPIO_Port,SDA_Pin);//SDA=0break;default:return0;// Received an unimplemented message. Returning 0 indicates an error.}return1;// The command has been successfully processed.}
//SPI接口uint8_tu8x8_gpio_and_delay(u8x8_t*u8x8,uint8_tmsg,uint8_targ_int,void*arg_ptr){switch(msg){caseU8X8_MSG_GPIO_SPI_DATA:lcd_sdin((uint8_t)arg_int);//SPI - MOSIbreak;caseU8X8_MSG_GPIO_SPI_CLOCK:lcd_sclk(arg_int);//SPI - CLKbreak;caseU8X8_MSG_GPIO_AND_DELAY_INIT:oled_init();//OLED初始化Delay(1);break;caseU8X8_MSG_DELAY_MILLI:Delay(arg_int);//延时break;caseU8X8_MSG_GPIO_CS:lcd_cs((uint8_t)arg_int);//SPI - CScaseU8X8_MSG_GPIO_DC:lcd_dc((uint8_t)arg_int);//SPI - MISObreak;caseU8X8_MSG_GPIO_RESET:break;default:return0;}return1;}

4.编写代码


按以下步骤配置函数。

(1)添加修改过的U8G2源码到工程并添加头文件路径。

(2)对硬件引脚初始化:对IIC接口进行GPIO口初始化配置,对应SPI接口也是如此。

voidIIC_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);}

(3)对U8G2初始化:调用 u8g2_Setup_ssd1306_128x64_noname_f 函数时,需根据需求配置旋转角度、通信接口等参数。其中参数U8G2_R0、R1、R2、R3分别对应0、90、180、270度;IIC 模式使用 u8x8_byte_sw_i2c,SPI 模式则需在底层驱动文件(u8x8_byte.c)中选取对应的回调函数参数。

voidu8g2Init(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);}

(4)可在此阶段添加显示测试功能,以下为参考示例的思路:初始化后配置字体格式,后续无需重复配置,调用U8G2函数绘制画面并写入缓存以完成显示开机画面;随后通过逐字显示与逐字清除字符串的循环操作,实现动画效果。

//主函数u8g2_tu8g2;//u8g2结构体intmain(void){//SystemInit(); //初始化RCC 设置系统主频为72MHZ SYSCLK_FREQ_72MHzdelay_init();//延时初始化IIC_Init();//液晶屏初始化u8g2Init(&u8g2);Dsp_Startup();//开机显示界面delay_ms(200);while(1){Dsp_Animation();}}//开机显示函数voidDsp_Startup(void){u8g2_ClearBuffer(&u8g2);//清缓冲//u8g2_SetFontMode(&u8g2, 1); //设置字体模式,1启用,0禁用(透明模式)//u8g2_SetFontDirection(&u8g2, 0); //设置字体方向u8g2_SetFont(&u8g2,u8g2_font_wqy12_t_gb2312);//设置字体格式u8g2_DrawUTF8(&u8g2,6,12,"你好,很高兴认识你!");//中文显示u8g2_DrawXBM(&u8g2,0,27,32,32,wechat);//图片显示u8g2_DrawXBM(&u8g2,32,30,24,24,search);//图片显示u8g2_DrawFrame(&u8g2,33,30,95,26);//方框显示 u8g2_DrawRFrame 带圆角矩形框u8g2_SendBuffer(&u8g2);//把要显示的信息写入缓冲}//逐字显示字符函数//x、y:字符首坐标地址//str:字符内容//weight:字符宽度(配置的字体格式)//delay:延时时间voidPrint_String(u8g2_t*u8g2,uint8_tx,uint8_ty,uint8_t*str,uint8_tweight,uint16_tdelay){uint8_tstr_len;str_len=strlen(str);for(uint8_ti=0;i<str_len;i++){u8g2_DrawGlyph(u8g2,x+weight*i,y,str[i]);u8g2_SendBuffer(u8g2);delay_ms(delay);}}//字符逐字清除//weight:绘制矩形宽度//delay:延时时间voidClear_String(u8g2_t*u8g2,uint8_tweight,uint16_tdelay){intclear_x=119;//绘制矩形首坐标x轴地址intclear_height=16;//绘制矩形高度intx;u8g2_SetDrawColor(u8g2,0);//设置绘图颜色为黑色for(x=clear_x;x>=54;x-=weight){u8g2_DrawBox(u8g2,x,32,weight,clear_height);u8g2_SendBuffer(u8g2);delay_ms(delay);}u8g2_SetDrawColor(u8g2,1);// 恢复默认绘图颜色}//动画显示函数voidDsp_Animation(void){u8g2_SetFont(&u8g2,u8g2_font_spleen8x16_mf);//设置字体格式(若更改字体格式需重新设置)Print_String(&u8g2,55,48,"YouziTech",8,50);//逐字打印字符Clear_String(&u8g2,8,50);//逐字从右向左清除字符}

图形显示的实现主要三个步骤:清空缓冲区;绘制内容到缓冲区;发送缓冲区数据。若不清除缓冲区,u8g2_SendBuffer会将上一帧的残留数据与当前内容叠加发送,导致图形重影、闪屏或显示异常。



四、问题优化方案


1.内存不足报错


经过精简U8G2源代码后还存在内存不足现象,还可以通过以下方法解决:

(1)提升Keil的优化等级,设为非0值以减少代码体积。


(2)屏蔽u8g2_fonts.c和u8g2_font.c文件中没有用到的字体,仅保留项目实际使用的字体(如u8g2_font_spleen8x16_mf)。



(3)使用更少字节的缓冲模式,如全缓冲改成页缓冲,初始化u8g2_m_16_8_f改成_1或_2后缀。


2.中文显示乱码、缺失或不显示


(1)在Keil软件打开Configuration窗口,在Editor页面将Encoding选项改为UTF-8,再将代码文件保存为UTF-8



(2)显示缺失可能没有使用全缓冲模式,可以改成全缓冲模式修改显示内容或位置

(3)检测使用的字体格式是否支持中文字或特殊字体,确保字体文件中包含UTF-8编码中文字符集



五、总结


U8G2图形库支持STM32等多平台移植,通过源文件中函数操作实现快速开发。使用前需确认屏幕为单色驱动芯片分辨率在库支持范围内,并核实通信接口。此外,精简和优化源码对解决内存不足及减小代码体积至关重要。





关注「YouziTech」 ,获取更多内容

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询