【STM32 + SSD1306 OLED】U8G2图形库移植
【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。2OLED显示屏本模块搭载的是SSD1306驱动芯片分辨率是128*64默认采用4线制SPI总线接口。实际应用切换为IIC接口需进行如下硬件调整将模块上的电阻R3移至R1位置R8位置使用0欧电阻短路同时将CS片选引脚、DC数据/命令引脚接地RES复位引脚接电源拉高。STM32与OLED的引脚连接方式如下。2.移植前准备工作1在GitHub平台搜索U8G2并下载最新源码。2准备需要移植的基于STM32的工程源码。3.U8G2源文件精简与优化通过移除未引用的文件及死代码消除无效编译代码从而减小构建体积并提升编译效率。1STM32使用的是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_fSPI接口保留u8g2_Setup_ssd1306_128x64_noname_f并将其余无关函数屏蔽或删除。函数后缀含义及选型建议_f对应1024字节缓冲区适用于全缓冲模式_1对应128字节缓冲区适用于页缓冲模式_2对应256字节缓冲区刷新速度下降但稳定性提升。选型策略STM32资源充裕时推荐优先使用_f1024字节以获最佳性能若资源受限可根据实际需求选择_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);//SCL1elseGPIO_ResetBits(SCL_GPIO_Port,SCL_Pin);//SCL0break;caseU8X8_MSG_GPIO_I2C_DATA:if(arg_int)GPIO_SetBits(SDA_GPIO_Port,SDA_Pin);//SDA1elseGPIO_ResetBits(SDA_GPIO_Port,SDA_Pin);//SDA0break;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_PinGPIO_Pin_2|GPIO_Pin_1;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_SpeedGPIO_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_i2cSPI 模式则需在底层驱动文件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_lenstrlen(str);for(uint8_ti0;istr_len;i){u8g2_DrawGlyph(u8g2,xweight*i,y,str[i]);u8g2_SendBuffer(u8g2);delay_ms(delay);}}//字符逐字清除//weight绘制矩形宽度//delay延时时间voidClear_String(u8g2_t*u8g2,uint8_tweight,uint16_tdelay){intclear_x119;//绘制矩形首坐标x轴地址intclear_height16;//绘制矩形高度intx;u8g2_SetDrawColor(u8g2,0);//设置绘图颜色为黑色for(xclear_x;x54;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」 获取更多内容