STM32F103C8T6上玩转U8G2库手把手教你驱动0.96寸OLED显示中文和动画在嵌入式开发中OLED显示屏因其高对比度、低功耗和快速响应等特性成为许多项目的首选显示方案。而U8G2库作为一款功能强大的图形库能够为这些小型显示屏带来丰富的图形和文本显示能力。本文将深入探讨如何在STM32F103C8T6这款经典的Cortex-M3微控制器上通过U8G2库实现0.96寸OLED屏的中文显示和动画效果。1. 环境准备与基础配置在开始之前我们需要确保开发环境已经准备就绪。对于STM32F103C8T6这款芯片Keil MDK或者STM32CubeIDE都是不错的选择。这里我们以Keil MDK为例介绍必要的配置步骤。首先创建一个新的工程选择STM32F103C8T6作为目标设备。在工程设置中有几个关键点需要注意确保勾选C99 Mode选项这是U8G2库正常运行的前提设置合适的优化等级通常选择-O2可以获得较好的性能与代码大小的平衡在Include Paths中添加U8G2库的路径接下来是硬件连接部分。0.96寸OLED屏通常采用I2C接口接线方式如下OLED引脚STM32引脚功能GNDGND地线VCC3.3V电源SCLPB8时钟线SDAPB9数据线2. U8G2库的裁剪与优化U8G2库功能强大但体积较大直接使用可能会导致编译错误或占用过多Flash空间。因此我们需要对库进行适当的裁剪。首先从GitHub获取U8G2源码git clone https://github.com/olikraus/u8g2.git在csrc目录中我们只需要保留以下几个关键文件u8x8_d_ssd1306_128x64_noname.c (OLED驱动)u8g2_d_setup.c (保留i2c相关函数)u8g2_d_memory.c (保留u8g2_m_16_8_f函数)对于中文显示还需要特别注意字库的处理。U8G2提供了多种中文字体但全量包含会导致编译失败。推荐只保留需要的字体例如// 在u8g2_fonts.c中只保留以下字体 const uint8_t u8g2_font_wqy16_t_chinese2[] U8G2_FONT_SECTION(u8g2_font_wqy16_t_chinese2);3. I2C接口驱动实现U8G2库需要开发者提供底层的GPIO和延时函数。下面是一个完整的I2C接口实现示例#include u8g2.h #include stm32f10x.h #define OLED_I2C_SCL_PIN GPIO_Pin_8 #define OLED_I2C_SCL_PORT GPIOB #define OLED_I2C_SDA_PIN GPIO_Pin_9 #define OLED_I2C_SDA_PORT GPIOB void Delay_us(uint32_t us) { // 实现微秒级延时函数 us * 72 / 5; while(us--) { __NOP(); } } void Delay_ms(uint32_t ms) { while(ms--) { Delay_us(1000); } } uint8_t u8g2_gpio_and_delay_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch(msg) { case U8X8_MSG_DELAY_MILLI: Delay_ms(arg_int); break; case U8X8_MSG_GPIO_I2C_CLOCK: if(arg_int) GPIO_SetBits(OLED_I2C_SCL_PORT, OLED_I2C_SCL_PIN); else GPIO_ResetBits(OLED_I2C_SCL_PORT, OLED_I2C_SCL_PIN); break; case U8X8_MSG_GPIO_I2C_DATA: if(arg_int) GPIO_SetBits(OLED_I2C_SDA_PORT, OLED_I2C_SDA_PIN); else GPIO_ResetBits(OLED_I2C_SDA_PORT, OLED_I2C_SDA_PIN); break; default: return 0; } return 1; }4. 中文显示与动画效果实现4.1 中文显示基础要在OLED上显示中文首先需要选择合适的字体。U8G2提供了多种中文字体如u8g2_font_wqy16_t_chinese2。使用前需要确保字体已包含在工程中源代码文件保存为UTF-8编码Keil中设置了正确的编译选项基本的中文显示代码如下void ShowChinese(u8g2_t *u8g2) { u8g2_ClearBuffer(u8g2); u8g2_SetFont(u8g2, u8g2_font_wqy16_t_chinese2); u8g2_DrawUTF8(u8g2, 0, 16, 嵌入式开发); u8g2_DrawUTF8(u8g2, 0, 32, 中文显示测试); u8g2_SendBuffer(u8g2); }4.2 文字动画效果U8G2库支持多种文字动画效果下面实现一个简单的文字滚动效果void ScrollText(u8g2_t *u8g2, const char *text) { int width u8g2_GetUTF8Width(u8g2, text); for(int x 128; x -width; x--) { u8g2_ClearBuffer(u8g2); u8g2_SetFont(u8g2, u8g2_font_wqy16_t_chinese2); u8g2_DrawUTF8(u8g2, x, 32, text); u8g2_SendBuffer(u8g2); Delay_ms(30); } }4.3 图形动画实现除了文字我们还可以创建简单的图形动画。下面是一个跳动的小球示例void BouncingBall(u8g2_t *u8g2) { int x 64, y 32; int dx 2, dy 2; while(1) { u8g2_ClearBuffer(u8g2); u8g2_DrawDisc(u8g2, x, y, 5, U8G2_DRAW_ALL); u8g2_SendBuffer(u8g2); x dx; y dy; if(x 5 || x 123) dx -dx; if(y 5 || y 59) dy -dy; Delay_ms(20); } }5. 性能优化与常见问题解决5.1 显示刷新优化OLED的刷新速度直接影响动画的流畅度。以下是几个优化建议局部刷新只更新变化的部分而不是整个屏幕双缓冲使用U8G2的双缓冲功能减少闪烁简化图形减少复杂图形的使用5.2 常见问题及解决方案问题1中文显示乱码确保源代码文件保存为UTF-8编码在Keil的Misc Controls中添加--no-multibyte-chars检查是否包含了正确的中文字体问题2编译时Flash空间不足裁剪不需要的字体和功能提高优化等级使用更小的字体问题3动画卡顿减少每帧的绘制内容优化延时函数精度检查I2C时钟速度是否设置合理6. 高级应用示例6.1 多页面菜单系统利用U8G2可以构建简单的菜单界面。下面是一个基础实现框架typedef struct { const char *title; void (*action)(u8g2_t*); } MenuItem; void ShowMenu(u8g2_t *u8g2, MenuItem *items, int count, int selected) { u8g2_ClearBuffer(u8g2); u8g2_SetFont(u8g2, u8g2_font_wqy16_t_chinese2); for(int i 0; i count; i) { if(i selected) { u8g2_DrawBox(u8g2, 0, i*16, 128, 16); u8g2_SetDrawColor(u8g2, 0); } u8g2_DrawUTF8(u8g2, 10, i*16 13, items[i].title); if(i selected) { u8g2_SetDrawColor(u8g2, 1); } } u8g2_SendBuffer(u8g2); }6.2 实时数据可视化对于需要显示传感器数据的应用可以创建简单的图表void DrawGraph(u8g2_t *u8g2, int *data, int count) { u8g2_ClearBuffer(u8g2); // 绘制坐标轴 u8g2_DrawHLine(u8g2, 10, 60, 100); u8g2_DrawVLine(u8g2, 10, 10, 50); // 绘制数据点 for(int i 0; i count-1; i) { int y1 60 - data[i]/2; int y2 60 - data[i1]/2; u8g2_DrawLine(u8g2, 15i*5, y1, 15(i1)*5, y2); } u8g2_SendBuffer(u8g2); }在实际项目中我发现合理使用U8G2的绘图函数可以大大提升用户体验。例如通过组合基本图形创建自定义图标或者利用帧动画原理实现复杂的界面过渡效果。对于资源有限的STM32F103C8T6关键在于找到功能丰富性和性能消耗之间的平衡点。