一、OLED基础与驱动原理

做嵌入式显示这么多年,我接触过LCD、TFT、电子墨水屏,但要说上手最快、效果最直观的,还得是OLED。尤其是0.96寸128×64的单色屏,几块钱一片,I2C四根线一接,就能点亮。今天咱们就从最底层开始,把OLED的驱动原理彻底讲透。

1.1 OLED显示技术概述

OLED,全称Organic Light Emitting Diode,有机发光二极管。它跟LCD最大的区别是——每个像素自己发光,不需要背光。这意味着什么?

  • 对比度极高:黑色就是纯黑,因为像素不发光
  • 响应速度快:微秒级,LCD还在那扭液晶呢,OLED已经切换完了
  • 视角宽:170度以上,侧面看也不变色
  • 功耗低:显示黑色几乎不耗电

我个人习惯把OLED分成两类:无源矩阵(PMOLED)和有源矩阵(AMOLED)。咱们课程里用的SSD1306驱动的小尺寸屏,属于PMOLED。结构简单,驱动芯片直接控制行列扫描。

关键指标:我们常用的0.96寸OLED,分辨率128×64,每个像素只有1bit——要么亮要么灭。但别小看它,配合灰度控制,照样能做出漂亮的UI。

1.2 SSD1306 / SH1106 驱动芯片详解

市面上最常见的两款驱动芯片就是SSD1306和SH1106。我踩过它们的坑,这里直接给你对比清楚。

特性 SSD1306 SH1106
分辨率 128×64 132×64
显存大小 128×64 = 1024字节 132×64 = 1056字节
通信接口 I2C / SPI / 6800 / 8080 I2C / SPI / 6800 / 8080
内部振荡器 有 有
兼容性 最广泛,库最多 与SSD1306基本兼容,但显存偏移不同

避坑指南:我曾经在项目里直接用SSD1306的库驱动SH1106,结果屏幕最左边多出一列黑线。后来才发现SH1106的显存起始列是2,不是0。驱动初始化时记得加一句偏移设置。

SSD1306内部集成了DC-DC升压电路,能自己产生7V左右的驱动电压。你只需要给它3.3V供电就行。芯片内部还有振荡器,不需要外部晶振——当然,如果你对时序要求高,也可以外接。

1.3 I2C / SPI 通信协议对比

驱动OLED,最常用的就是I2C和SPI。我个人的建议是:追求简单用I2C,追求速度用SPI。

对比项 I2C SPI
引脚数 2根(SDA + SCL) 4根(MOSI + MISO + SCK + CS)
速度 标准100kHz / 快速400kHz 可达10MHz以上
多设备支持 地址区分,可挂多个 片选区分,每个设备一根CS
全双工 否 是
适合场景 引脚紧张、刷新率要求不高 需要快速刷新、动画效果

I2C的OLED模块,默认地址一般是0x3C(写)或0x3D(读)。你想想看,两根线就能控制128×64个像素,是不是很划算?

SPI虽然线多,但刷屏速度确实快。我做过测试,同样刷一帧全白,I2C大概要30ms,SPI只要3ms。如果你要做动态UI,SPI是更好的选择。

小技巧:I2C模式下,SSD1306的D/C引脚接地,命令和数据通过控制字节区分。SPI模式下,D/C引脚单独控制——拉低发命令,拉高发数据。这个细节容易搞混,我刚开始就栽过。

1.4 像素与显存映射关系

这是驱动OLED最核心的部分。搞懂了显存映射,你就掌握了屏幕的“寻址方式”。

SSD1306的显存是1024字节,对应128×64个像素。但它的组织方式不是按行排列的,而是按页(Page)排列。

  • 一共8页,每页8行
  • 每页128列,每列对应一个字节的8个bit
  • 一个字节的LSB对应页内最上面一行,MSB对应最下面一行

说白了,就是:显存[page][column] = 8个像素的垂直条。

// 显存映射示例:在(5, 10)位置画一个点
// 5是列,10是行
uint8_t page = 10 / 8;      // 页号 = 1
uint8_t bit_pos = 10 % 8;   // 页内行偏移 = 2
buffer[page * 128 + 5] |= (1 << bit_pos);

嗯,这里要注意:列地址和页地址是分开设置的。发送数据前,要先通过命令设置当前列地址和页地址,然后连续发送数据,列地址会自动递增。

核心理解:显存映射决定了你写代码的方式。如果你要画一个水平线,需要逐列设置每个字节的对应bit位。如果你要画一个垂直线,只需要在一个字节内设置多个bit位。理解了这一点,后面做图形库就顺了。

下面这张图,把整个OLED驱动体系串起来了:

OLED驱动体系结构图 应用层(UI / 图形 / 文字) 显存管理(1024字节Buffer) 驱动层(SSD1306 / SH1106) 硬件层(I2C / SPI 物理接口) I2C协议 SPI协议 显存映射:8页 × 128列,每页8行,1字节 = 8个垂直像素 列地址自动递增,页地址需手动切换

从这张图能看出来,整个驱动体系是分层的。上层只管往显存里写数据,底层负责通过I2C或SPI把数据刷到屏幕上。中间那层1024字节的Buffer,就是你的画板。

个人经验:我建议你永远保留一个完整的显存Buffer在MCU内存里。需要更新UI时,先改Buffer,然后一次性刷到OLED。这样能避免屏幕闪烁,也方便做局部刷新优化。

好了,第一章的内容就到这里。OLED的基础原理、驱动芯片差异、通信协议选择、显存映射关系——这些是后面所有实战内容的地基。地基打牢了,后面做灰度控制、动态UI才不会跑偏。