STM32F103C8T6上跑u8g2图形库?手把手教你用HAL库+模拟IIC点亮OLED屏

张开发
2026/4/16 23:32:24 15 分钟阅读

分享文章

STM32F103C8T6上跑u8g2图形库?手把手教你用HAL库+模拟IIC点亮OLED屏
STM32F103C8T6上跑u8g2图形库手把手教你用HAL库模拟IIC点亮OLED屏在嵌入式开发领域STM32F103C8T6这款被称为蓝桥杯神器的MCU因其性价比和易用性广受欢迎。而u8g2作为一款功能强大的嵌入式图形库能够为各种OLED显示屏提供丰富的图形界面支持。本文将带你从零开始在STM32F103C8T6上使用HAL库和模拟IIC接口驱动0.96寸OLED显示屏SSD1306控制器特别针对这款MCU的RAM资源有限的特点进行优化。1. 开发环境准备与硬件连接1.1 所需硬件清单在开始之前确保你已准备好以下硬件组件STM32F103C8T6最小系统板核心资源72MHz主频64KB Flash20KB RAM0.96寸OLED显示屏SSD1306驱动芯片128×64分辨率杜邦线若干建议使用优质线材减少干扰USB转TTL模块用于程序下载和调试1.2 硬件连接方式OLED与STM32的连接非常简单只需要4根线OLED引脚STM32引脚功能说明VCC3.3V电源正极GNDGND电源地SCLPB6时钟线SDAPB7数据线提示虽然理论上可以任意选择GPIO引脚但建议优先使用PB6/PB7因为这两个引脚在硬件I2C模式下也有对应功能方便后续扩展。1.3 开发环境配置推荐使用以下开发工具组合STM32CubeMX用于初始化代码生成版本建议≥6.0Keil MDK-ARM作为主要开发IDE建议使用V5.25以上版本ST-Link Utility用于程序烧录和调试在CubeMX中创建新工程时关键配置步骤如下/* System Core配置 */ RCC- High Speed Clock (HSE): Crystal/Ceramic Resonator SYS- Debug: Serial Wire GPIO- PB6: GPIO_Output (重命名为OLED_SCL) GPIO- PB7: GPIO_Output (重命名为OLED_SDA)2. u8g2库的移植与优化2.1 获取u8g2源码u8g2的最新源码可以从GitHub获取git clone https://github.com/olikraus/u8g2.git或者直接下载zip压缩包。我们主要需要的是csrc目录下的核心源文件。2.2 精简u8g2库以节省内存STM32F103C8T6仅有20KB RAM而完整版u8g2会占用较多内存。我们需要进行以下优化删除不必要的显示驱动 在u8g2_d_setup.c中只保留与SSD1306相关的代码其他全部注释掉。选择合适的内存模式 u8g2提供多种内存模式针对小内存MCU推荐使用page buffer模式// 使用页缓冲模式相比全缓冲节省约1KB内存 u8g2_Setup_ssd1306_i2c_128x64_noname_1(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_stm32);修改u8g2_d_memory.c 注释掉所有未使用的全局变量定义仅保留SSD1306相关部分。2.3 关键回调函数实现u8g2需要通过回调函数与硬件交互以下是针对STM32HAL库的实现uint8_t u8x8_gpio_and_delay_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch(msg) { case U8X8_MSG_DELAY_MILLI: HAL_Delay(arg_int); break; case U8X8_MSG_GPIO_I2C_CLOCK: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET); break; case U8X8_MSG_GPIO_I2C_DATA: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET); break; default: return 0; } return 1; }3. 模拟I2C驱动实现3.1 I2C时序控制模拟I2C需要精确控制时序以下是关键时序参数时序参数典型值说明起始条件保持时间4.7μsSDA下降沿到SCL下降沿数据保持时间4.0μsSCL高电平期间数据稳定停止条件建立时间4.0μsSCL上升沿到SDA上升沿对应的延时函数实现void I2C_Delay(void) { volatile uint8_t i 5; while(i--); } void I2C_Start(void) { SDA_HIGH(); SCL_HIGH(); I2C_Delay(); SDA_LOW(); I2C_Delay(); SCL_LOW(); } void I2C_Stop(void) { SDA_LOW(); SCL_HIGH(); I2C_Delay(); SDA_HIGH(); }3.2 完整数据传输函数以下是带ACK检查的字节发送函数uint8_t I2C_WriteByte(uint8_t byte) { uint8_t i, ack; for(i0; i8; i) { if(byte 0x80) SDA_HIGH(); else SDA_LOW(); SCL_HIGH(); I2C_Delay(); SCL_LOW(); byte 1; } // 读取ACK SDA_HIGH(); SCL_HIGH(); ack HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7); SCL_LOW(); return ack; // 0表示ACK, 1表示NACK }4. 实际应用与性能优化4.1 显示性能测试在72MHz主频下不同刷新模式的性能对比刷新模式全屏刷新时间内存占用适用场景全缓冲模式15ms1KB复杂动画页缓冲模式25ms128B文本显示直接写入模式120ms0B极低内存环境4.2 常用显示功能实现显示文本u8g2_SetFont(u8g2, u8g2_font_6x10_tf); u8g2_DrawStr(u8g2, 0, 10, Hello STM32!);绘制图形// 画线 u8g2_DrawLine(u8g2, 0, 0, 127, 63); // 画矩形 u8g2_DrawFrame(u8g2, 10, 10, 50, 20); // 画圆 u8g2_DrawCircle(u8g2, 64, 32, 20, U8G2_DRAW_ALL);显示位图static const uint8_t logo_bits[] { 0x00, 0x00, 0x1F, 0x80, 0x20, 0x40, 0x40, 0x20, // 更多位图数据... }; u8g2_DrawXBM(u8g2, 30, 20, 32, 32, logo_bits);4.3 内存优化技巧使用PROGMEM存储字体 将不常用的字体存储在Flash而非RAM中static const uint8_t font_data[] U8G2_FONT_SECTION(font_data) {...};动态加载字体 只在需要时加载特定字体使用后立即释放u8g2_SetFont(u8g2, u8g2_font_6x10_tf); // 显示操作... u8g2_SetFont(u8g2, NULL); // 释放字体使用u8x8模式 如果只需要显示文本可以使用更轻量的u8x8模式u8x8_Setup(u8x8, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_i2c, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_stm32);在实际项目中我发现合理组合这些优化技巧可以在STM32F103C8T6上实现相当流畅的图形界面效果。特别是在使用页缓冲模式时配合精心设计的局部刷新策略完全可以满足大多数嵌入式GUI的需求。

更多文章