基于 SSD1306 驱动芯片、I2C 总线接口的单色有机发光显示屏。
分辨率 128×64,可显示字符、汉字、图形与位图,是嵌入式系统最常用的输出模块之一。
0.96 寸 OLED12864 是一种采用有机自发光技术的小型显示模块,相比传统 LCD, 它无需背光、对比度更高、响应更快、视角更广。
每个像素独立发光,无背光,纯黑场景下像素可完全关闭,对比度可达 10000:1。
仅需 2 根信号线(SCL、SDA)即可与 MCU 通信,节省 GPIO 资源。
显示内容越简单越省电,关闭像素几乎不耗电,适合电池供电场景。
支持字符、汉字、位图、点线面等任意图形绘制。
PCB 尺寸仅 27.6 × 27.3 mm,可嵌入各种紧凑型设备。
常见有白色、蓝色、黄蓝双色等,本演示使用白光的单色屏。
实物外观、尺寸标注与背面的 I2C 地址选择电阻。
模块的关键电气与物理参数。
分辨率 128 × 64 像素
像素尺寸 0.15 × 0.15 mm
有效区域 21.744 × 10.864 mm
颜色 单色(白/蓝/黄蓝)
可视角度 > 160°
工作电压 3.3 V ~ 5 V
逻辑电平 3.3 V(与 STM32 兼容)
驱动 IC SSD1306
通信接口 I2C(默认 0x78)
工作温度 -40 ℃ ~ 85 ℃
从像素结构、显存组织到显示刷新的完整链路。
屏幕共有 128 列 × 64 行 = 8192 个像素点,每个像素对应 OLED_DisplayBuf 显存数组中的一位。 128 列分成 8 个 Page(页),每页 8 行;显存按"纵向 8 点、高位在下"组织:
OLED_DisplayBuf[8][128] 这个 RAM 数组;
调用 OLED_Update() 时,程序才把整个数组通过 I2C 推送到屏幕。
这种"先画后刷"的模式,可以避免显示过程出现半成品画面。
↑ 单字节点亮示意(8 个像素纵向排列)
通过 SCL(时钟)与 SDA(数据)两根线,实现主机(STM32)与从机(OLED)通信。
SCL 保持高电平期间,SDA 由高拉低,标志一次传输开始。
本模块地址为 0x78(写)或 0x79(读),由背面 R5/R6 决定。
从机在第 9 个时钟周期拉低 SDA 表示应答;本驱动未严格检查 ACK。
0x00(命令)或 0x40(数据)通知 OLED 接下来收到的是命令还是显示数据。
逐字节写入。数据通常连续发送以提高效率。
SCL 保持高电平期间,SDA 由低拉高,结束本次传输。
本模块为 4 针脚版本(I2C 接口),与 STM32F103C8T6 的引脚对应关系如下:
| 序号 | 模块引脚 | 说明 | STM32 连接 |
|---|---|---|---|
| 1 | GND | 电源地 | GND |
| 2 | VCC | 电源正极(3.3V ~ 5V) | 3V3 或 5V |
| 3 | SCL | I2C 时钟线 | PB15(开漏输出) |
| 4 | SDA | I2C 数据线 | PB14(开漏输出) |
原理图与 PCB 封装图,确认 OLED 与 STM32 的接线。
从 main() 进入:先初始化 OLED,再进入 1 秒周期的主循环刷新画面。
梳理驱动中最核心的几个函数,理解整个刷屏链路。
初始化 GPIO,再写入一长串 SSD1306 命令:关闭显示、设置时钟分频、多路复用率(0x3F = 64 行)、左右/上下方向、对比度、充电泵等。最后清屏并刷新一次。
遍历 8 个 Page,每页把 128 字节连续写入:
OLED_WriteData(OLED_DisplayBuf[j], 128),把整个 1 KB 显存推到屏幕。
遍历字符:ASCII 调用 ShowChar 查 OLED_F8x16/OLED_F6x8 字库;中文按 GB2312 编码在 OLED_CF16x16 中查找 16×16 点阵数据。
把图像数组的字节按位写入显存对应位置,跨页时通过移位 ( << Shift / >> (8-Shift) ) 分发到两页。
将 1024 字节显存全部清零。注意:调用后不会立即显示,必须配合 OLED_Update() 才生效。
循环 8 次逐位发送一个字节,MSB 在前。发送完毕后多给一个时钟(不检查 ACK),符合本模块的简化协议。
// 0xAE / 0xAF → 关闭 / 开启显示 // 0xA8 0x3F → 多路复用率 64 行 // 0xA1 → 段(列)方向正常 // 0xC8 → COM(行)方向正常 // 0xDA 0x12 → COM 引脚交替配置 // 0x81 0xCF → 设置对比度(0x00~0xFF) // 0x8D 0x14 → 启用电荷泵(必需!否则 3.3V 供电无显示) // 0xAF → 开启显示
0x8D 0x14 启用电荷泵是初学者最常见的问题——程序看起来都跑通了,但屏幕一片漆黑。
根据 demo 程序,模拟 STM32 在真实硬件上呈现的效果。 点击 ▶ 运行 启动自动刷新循环,或点击 方向 按钮手动切换。
// main.c 中的核心绘制逻辑(节选) if(HAL_GetTick() - Time_Now >= 1000) // 每 1 秒刷新一次 { OLED_Clear(); // ① 清空显存 (Arrow >= 3) ? (Arrow = 0) : (Arrow++); // ② 方向计数 0~3 循环 OLED_ShowNum(120, 0, Arrow, 1, OLED_8X16); // ③ 右上角显示数字 OLED_ShowString(0, 0, "Hello World!", OLED_6X8); // ④ 顶部英文 6×8 OLED_ShowString(0, 20, "Hello World!", OLED_8X16); // ⑤ 中部英文 8×16 OLED_ShowString(0, 48, "你好世界!", OLED_8X16); // ⑥ 中文 16×16 OLED_DrawRectangle(94, 30, 34, 34, OLED_UNFILLED); // ⑦ 画矩形框 switch(Arrow) { // ⑧ 在矩形内显示方向箭头位图 case 0: OLED_ShowImage(95, 31, 32, 32, UP); break; case 1: OLED_ShowImage(95, 31, 32, 32, RIGHT); break; case 2: OLED_ShowImage(95, 31, 32, 32, DOWN); break; case 3: OLED_ShowImage(95, 31, 32, 32, LEFT); break; } OLED_Update(); // ⑨ 推送到屏幕 Time_Now = HAL_GetTick(); }
从驱动中摘录最值得阅读的几段核心实现。
void OLED_I2C_SendByte(uint8_t Byte) { uint8_t i; for (i = 0; i < 8; i++) { // 循环 8 次发送 1 bit OLED_W_SDA(!!(Byte & (0x80 >> i))); // MSB 在前 OLED_W_SCL(1); // 从机在 SCL 高电平采样 OLED_W_SCL(0); // 准备下一位 } OLED_W_SCL(1); // 额外一个时钟(不处理 ACK) OLED_W_SCL(0); }
void OLED_WriteCommand(uint8_t Command) { OLED_I2C_Start(); OLED_I2C_SendByte(0x78); // 从机地址(写) OLED_I2C_SendByte(0x00); // 控制字节:写命令 OLED_I2C_SendByte(Command); OLED_I2C_Stop(); } void OLED_WriteData(uint8_t *Data, uint8_t Count) { OLED_I2C_Start(); OLED_I2C_SendByte(0x78); // 从机地址(写) OLED_I2C_SendByte(0x40); // 控制字节:写数据 for (uint8_t i = 0; i < Count; i++) OLED_I2C_SendByte(Data[i]); OLED_I2C_Stop(); }
void OLED_Update(void) { uint8_t j; for (j = 0; j < 8; j++) { // 8 个 Page OLED_SetCursor(j, 0); // 设置光标到本页第 0 列 OLED_WriteData(OLED_DisplayBuf[j], 128); // 连续写 128 字节 } }
• 分页(Page)寻址
• 高位在下的纵向字模
• 显存双缓冲
• 软件 I2C 时序
• 修改 SCL_Pin / SDA_Pin
• 改为硬件 I2C 提速
• 替换字库支持更多字符
• 滚动显示 / 帧动画
• 菜单 UI 框架
• 配合按键 / 传感器做交互