第一次在STM32F103C8T6上看到OLED屏幕亮起"你好世界"四个汉字时,那种成就感至今难忘。但紧接着出现的region 'rom' overflowed报错又让人瞬间跌入谷底——这几乎是每个使用U8g2库显示中文的开发者都会经历的过山车体验。本文将带你深入分析问题根源,并提供三种经过实测的解决方案。
U8g2库之所以能成为OLED显示的瑞士军刀,关键在于其独特的字体处理机制。当我们调用u8g2_font_unifont_t_chinese2这类中文字体时,库实际上是将Unicode字符映射到内置的位图数据。每个16x16像素的汉字需要32字节存储空间,而常用汉字库轻松就能占用50KB以上的Flash。
资源占用对比实验(基于STM32F103C8T6 64KB Flash):
cpp复制// 测试用例1:英文显示
u8g2.setFont(u8g2_font_6x13_tf); // 占用约1.2KB
u8g2.print("Hello World");
// 测试用例2:中文显示
u8g2.setFont(u8g2_font_unifont_t_chinese2); // 占用约52KB
u8g2.print("你好世界");
通过串口输出编译后的内存分析报告可以看到:
code复制Sketch uses 23456 bytes (36%) of program storage space. Maximum is 65536 bytes.
Global variables use 3520 bytes (17%) of dynamic memory.
切换到中文字体后:
code复制Sketch uses 72104 bytes (110%) of program storage space.
region 'rom' overflowed by 6576 bytes
U8g2库提供print()和firstPage()/nextPage()两种显示模式,它们的资源占用有显著区别:
cpp复制void loop() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_unifont_t_chinese2);
u8g2.setCursor(0, 20);
u8g2.print("温度:25℃");
u8g2.sendBuffer();
delay(1000);
}
特点:
cpp复制void loop() {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_unifont_t_chinese2);
u8g2.drawUTF8(10, 30, "湿度:65%");
} while(u8g2.nextPage());
delay(1000);
}
优势:
实际测试发现:在STM32F103C8T6上,分页模式可以将rodata段大小从52KB降至37KB
找到Arduino安装目录下的:
code复制hardware/Arduino_STM32/STM32F1/boards.txt
搜索Generic STM32F103C6,修改以下参数:
code复制genericSTM32F103C6.menu.upload_method.STLinkMethod.upload.maximum_size=65536
genericSTM32F103C6.menu.upload_method.STLinkMethod.build.flash_size=64K
关键点:
使用U8g2提供的字体工具生成自定义字体:
bash复制# 安装字体工具
pip install u8g2_font_tools
# 生成仅包含特定汉字的字体
u8g2_font_conv --font unifont.ttf --chars "你好世界温度湿度" --output myfont.c
生成的字体文件体积可从52KB降至3KB左右。
在platform.local.txt中添加:
code复制compiler.c.extra_flags=-DLTO_ENABLED
compiler.cpp.extra_flags=-DLTO_ENABLED
实测可减少约15%的rodata占用,但可能增加编译时间。
经过优化的稳定版本:
cpp复制#include <U8g2lib.h>
#include <Wire.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, PB6, PB7);
void setup() {
u8g2.begin();
u8g2.enableUTF8Print();
}
void loop() {
static uint8_t page = 0;
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_unifont_t_chinese2);
switch(page % 3) {
case 0:
u8g2.drawUTF8(10, 20, "系统状态");
u8g2.drawUTF8(10, 40, "温度:25℃");
break;
case 1:
u8g2.drawUTF8(10, 20, "传感器数据");
u8g2.drawUTF8(10, 40, "湿度:65%");
break;
case 2:
u8g2.drawUTF8(10, 20, "设备信息");
u8g2.drawUTF8(10, 40, "STM32F103C8T6");
}
} while(u8g2.nextPage());
page++;
delay(2000);
}
连线参考:
| OLED引脚 | STM32引脚 |
|---|---|
| GND | GND |
| VCC | 3.3V |
| SCL | PB6 |
| SDA | PB7 |
在项目后期,我发现结合分页渲染和自定义字体是最稳定的方案。特别是在需要显示动态数据(如传感器数值)时,分页模式能保持流畅的刷新率。而通过精确控制使用的汉字数量,可以确保即使添加更多功能也不会再次触发rom溢出错误。