一、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驱动体系串起来了:
从这张图能看出来,整个驱动体系是分层的。上层只管往显存里写数据,底层负责通过I2C或SPI把数据刷到屏幕上。中间那层1024字节的Buffer,就是你的画板。
个人经验:我建议你永远保留一个完整的显存Buffer在MCU内存里。需要更新UI时,先改Buffer,然后一次性刷到OLED。这样能避免屏幕闪烁,也方便做局部刷新优化。
好了,第一章的内容就到这里。OLED的基础原理、驱动芯片差异、通信协议选择、显存映射关系——这些是后面所有实战内容的地基。地基打牢了,后面做灰度控制、动态UI才不会跑偏。