概述
官方基础工程已预置 SSD1306 OLED 驱动(components/SSD1306,含中文字库适配)并在 ws2812_modeTask 任务中完成初始化——目前只显示"欢迎使用 九章开发板"欢迎页。官方工程每秒都在读取温湿度(sht3x_read_task),但只打印到串口、没有显示到屏幕。本章把温湿度数据送上屏幕:定义全局变量 → 状态页函数显示 → 每秒刷新,让屏幕实时显示环境温湿度。
OLED 使用 SPI1(PA5 SCK / PA7 MOSI),片选与 DC 用 GPIO(PA4 / PA1)。本项目
components/SSD1306/axk_ssd1306.c已适配完成,并带 GT20L16S 中文字库支持(通过 CS2=PB0 访问)。
官方基础工程已预置 OLED 驱动 components/SSD1306/axk_ssd1306.c(含中文字库适配),无需复制。SCBB 库中对应模块名为 OLED096(芯片同为 SSD1306 控制器),从零工程起步时可参考:
ls ~/AiPi-SCBB/OLED096/
# axk_oled096.c axk_oled096.h
驱动依赖的 SPI BSP(Bsp/spi/stm32f10x_bsp_spi.c 已实现):
void bsp_spi_init(void); // 初始化 SPI1 主机
void bsp_spi_cs1_reset(void); // OLED_CS1 (PA4) 拉低 → 选中 OLED
void bsp_spi_cs1_set(void); // 拉高 → 释放
void bsp_spi_cs2_reset(void); // CS2 (PB0) 选中字库芯片
void bsp_spi_transmit_dma(const uint8_t *data, uint16_t size); // DMA 发送
components/CMakeLists.txt 加入 SSD1306 目录(含 gt20l16s 子目录)。
| OLED 模块 | 接开发板 |
|---|---|
| DIN(数据) | PA7(SPI1_MOSI) |
| CLK(时钟) | PA5(SPI1_SCK) |
| CS(片选) | PA4 |
| DC(数据/命令) | PA1 |
| RES(复位) | 3.3V 或由驱动控制(本项目由驱动 GPIO 拉高) |
| VCC | 3.3V |
| GND | GND |
官方基础工程已在 ws2812_modeTask 任务中完成 OLED 初始化并显示欢迎页:
axk_ssd1306_init();
axk_ssd1306_set_color_turn(0);
axk_ssd1306_set_display_turn(0);
axk_ssd1306_clear_screen();
axk_ssd1306_show_utf8_str(32, 0, "欢迎使用");
axk_ssd1306_show_utf8_str(24, 3, "九章开发板");
核心 API(axk_ssd1306.h):
axk_ssd1306_init();
axk_ssd1306_set_color_turn(0); // 正常颜色(1 则反色)
axk_ssd1306_set_display_turn(0); // 正常方向(1 则翻转 180°)
axk_ssd1306_clear_screen();
核心 API 仅三个(axk_ssd1306.h):
void axk_ssd1306_clear_screen(void); // 清屏
void axk_ssd1306_show_utf8_str(x, y, str); // 显示字符串(UTF-8,自动使用字库)
// x: 0~15(每字符 8 像素列),y: 0~7(每行 8 像素高)→ 128x64 共 8 行
这一步做什么:官方工程每秒都在读取温湿度(sht3x_read_task 任务),但只在串口打印、没显示到屏幕。本章把它显示到 OLED:数据提升为全局变量 → 状态页函数显示 → 每秒刷新,屏幕实时显示环境温湿度。
文件路径:~/DOCS_TEST1/Core/Src/freertos.c,共 7 处改动
改动 1:添加 stdio.h 头文件(snprintf 函数需要)
位置:/* USER CODE BEGIN Includes */ 之后添加:
/* USER CODE BEGIN Includes */
#include // snprintf 需要
/* USER CODE END Includes */
改动 2:定义全局温湿度变量
位置:/* USER CODE BEGIN Variables */ 之后添加:
/* USER CODE BEGIN Variables */
double g_temp = 0.0; // 温度,供屏幕显示
double g_hum = 0.0; // 湿度,供屏幕显示
/* USER CODE END Variables */
改动 3:添加函数原型声明(ui_show_status 定义在调用之后,必须先声明)
位置:/* USER CODE BEGIN FunctionPrototypes */ 之后添加:
/* USER CODE BEGIN FunctionPrototypes */
void ui_show_status(void); // 屏幕状态页原型
/* USER CODE END FunctionPrototypes */
改动 4:读取后存入全局变量
位置:sht3x_read_task 函数,res = axk_sht3x_read(0x2c06, &temperature, &humidity); 之后、if (res != 0) { ... continue; } 块之后(读取成功才执行到这里),插入两行:
res = axk_sht3x_read(0x2c06, &temperature, &humidity); ← 现有代码
if (res != 0) { log_error("sht3x read error: %d", res); continue; } ← 现有代码
g_temp = temperature; // 保存到全局,供屏幕显示
g_hum = humidity;
改动 5:替换欢迎页显示代码
位置:ws2812_modeTask 函数内,官方欢迎页两行:
axk_ssd1306_init(); ← 现有代码
axk_ssd1306_show_utf8_str(32, 0, "欢迎使用"); ← 删除
axk_ssd1306_show_utf8_str(24, 3, "九章开发板"); ← 删除
ui_show_status(); // 调用状态页函数 ← 替换为
axk_ws2812_init(&ws2812); ← 现有代码
改动 6:新增状态页函数
位置:/* USER CODE BEGIN Application */ 之后添加(官方已在此区实现接收回调,加在它后面即可):
/* USER CODE BEGIN Application */
void ui_show_status(void) {
char buf[32];
uint8_t ti = (uint8_t)g_temp, td = ((uint8_t)(g_temp * 10)) % 10;
uint8_t hi = (uint8_t)g_hum, hd = ((uint8_t)(g_hum * 10)) % 10;
/* 第一行:系统名称 */
axk_ssd1306_show_utf8_str(0, 0, "九章开发板");
/* 第三行:实时温度(%2d.%d 整数拼接) */
snprintf(buf, sizeof(buf), " T:%2d.%d C ", ti, td);
axk_ssd1306_show_utf8_str(0, 2, buf);
/* 第五行:实时湿度 */
snprintf(buf, sizeof(buf), " H:%2d.%d %% ", hi, hd);
axk_ssd1306_show_utf8_str(0, 4, buf);
}
/* USER CODE END Application */
改动 7:周期刷新屏幕
位置:sht3x_read_task 函数循环内,(改动 4 的两行赋值)之后添加:
g_temp = temperature; ← 改动 4 添加的两行
g_hum = humidity;
ui_show_status(); // 每秒刷新屏幕(读取成功后)
⚠️ 这步不能漏:它是"屏幕持续更新"的关键。不添加的话,屏幕只在开机时显示一次(启动时的 0.0 值),之后数值再也不变。
-
按编译和下载工程的图形化方法编译烧录,上电后屏幕应显示:
九章开发板 T:29.2 C H:52.4 % -
温度湿度数值每秒刷新(可用手捂住传感器看数值变化)——说明"全局变量 → 显示函数 → 周期刷新"链路已打通。第 8 章温湿度查询工具将直接复用
g_temp/g_hum。

故障排查:
| 故障 | 排查 |
|---|---|
| 屏幕不亮 | 检查 SPI 接线(PA5/PA7/PA4/PA1)、供电(3.3V)、RES 引脚是否拉高 |
| 花屏/乱码 | 确认 SPI1_TX 的 DMA(DMA1_Channel3)已配置;检查 CubeMX .ioc |
| 中文显示异常 | 字库芯片 GT20L16S 的 CS2(PB0)需接好或保持拉高 |
常见问题与踩坑提示
🔧 屏幕完全不亮(黑屏)
原因:① 供电/接线 ② RES 悬空 ③ CS 选错
解决:① VCC 3.3V + GND 共地 ② RES 必须拉高(很多模块默认悬空,接 3.3V 即可,本项目由驱动 GPIO 拉高)③ 确认接的是 OLED 的 CS(PA4),不是字库的 CS2(PB0)
🔧 屏幕亮但只有一半/花屏
原因:SPI 线序错或 DMA 未配
解决:核对 DIN→PA7、CLK→PA5;确认 CubeMX 里 SPI1_TX 的 DMA(DMA1_Channel3)已配置
🔧 显示内容有残缺/乱码
原因:① 数据太快 ② 字库访问冲突
解决:① 检查 SPI 分频(128 足够)② 中文字库 GT20L16S 的 CS2(PB0)要接好或保持拉高,显示中文时才能正确读字库
🔧 中文显示成方框/问号
原因:字库芯片没接或字体缺失
解决:确认 GT20L16S 接线(SPI 共享总线 + CS2=PB0);该字库内置 GB2312 常用汉字
🔧 上电白屏后一直白屏
原因:初始化失败或复位引脚被拉低
解决:检查 axk_ssd1306_init() 是否被调用(应在任务里);RES 引脚不要接 GND
🔧 显示位置不对
原因:x/y 理解错误
解决:axk_ssd1306_show_utf8_str(x, y, str):x 是列(0~15,每字符 8 像素),y 是行(0~7),不是像素坐标
🔧 屏幕偶尔闪烁/撕裂
原因:刷新与 DMA 发送冲突
解决:显示数据刷新与 bsp_spi_transmit_dma 不要并发;本项目放在独立任务中用 osDelay 错开
🔧 温度湿度显示不更新
原因:定时刷新逻辑没跑
解决:确认 sht3x_read_task 在循环里调用刷新函数(本项目 1 秒刷新一次)
🔧 按键翻页没反应
原因:按键引脚/消抖
解决:本项目 PB4(上翻)/PB8(下翻);按键需长按触发(btn_pressed 做了 20ms 消抖)
黑屏排查顺序(按概率从高到低)
- RES 是否拉高 → 2. VCC/GND 是否接对 → 3. DIN/CLK 是否接对(PA7/PA5)→ 4. CS 是否选对(PA4)→ 5. SPI DMA 是否配置 → 6. 初始化函数是否被调用。九成黑屏是 1~3 的接线问题。

