OLED 这个词,如果你只是把它当成“一块会发光的屏幕”,那基本就错过了它最值钱的部分。我从最早用 0.96 寸单色 OLED 做温湿度显示,到后来折腾 1.3 寸、1.5 寸甚至彩色 SPI OLED,踩过的坑从“点不亮”到“花屏”再到“刷新慢得像幻灯片”,几乎每一类都经历过。这篇内容不打算写成数据手册的翻译,而是把 OLED 显示模块从选型、接线、驱动到代码落地这一整条链路,按一个实际做过项目的人的角度讲清楚。不管你是刚拿到一块 OLED 模块不知道怎么下手,还是已经在用 HAL 库驱动 OLED 但总遇到显示异常,这里应该都能找到对你有用的东西。
1. 先搞清楚你手里那块 OLED 到底是什么
1.1 从“能显示”到“显示得好”,中间差了什么
很多人拿到 OLED 模块的第一反应是找一份例程,烧进去,屏幕亮了,任务就算完成了。但真正做产品或者做稍微复杂一点的界面时,你会发现“能显示”和“显示得好”完全是两码事。OLED 的显示质量取决于几个核心因素:驱动芯片型号、通信接口类型、分辨率与像素排列方式、以及你用的驱动库对显存的管理策略。
我见过太多人拿着一块标注“0.96 寸 OLED”的模块,直接套用 SSD1306 的例程,结果要么完全不亮,要么显示错位。原因很简单,市面上常见的 0.96 寸 OLED 确实大多用 SSD1306,但 1.3 寸的模块很多用的是 SH1106,两者的显存寻址方式有细微差别。SSD1306 是 128x64 的显存按页寻址,SH1106 虽然也是 128x64,但它的列地址是从 2 开始的,如果你直接用 SSD1306 的初始化代码去驱动 SH1106,画面会整体偏移两列,看起来就是右边少了一块或者左边多了一条黑边。
所以第一步不是写代码,而是确认你手里模块的驱动芯片。最可靠的方法是看模块背面的丝印或者购买页面上的参数说明。如果实在找不到,可以看分辨率:128x64 的模块 SSD1306 和 SH1106 都有可能,128x32 的基本是 SSD1306,而 72x40 这种非标准分辨率通常是 SSD1315 或者 SH1107。确认芯片型号之后,再去对应的驱动库或者自己写初始化序列,能省掉后面大量的调试时间。
1.2 I2C 和 SPI 的选择不是随便定的
OLED 模块常见的通信接口有 I2C 和 SPI 两种,少数还有并口。I2C 的优势是接线少,两根线就能搞定,适合引脚资源紧张的场景。但它的缺点也很明显:速率上限低。标准模式 100kHz,快速模式 400kHz,即使你拉到 1MHz 左右,对于 128x64 的单色屏来说,刷一整屏的数据量是 1024 字节,加上命令和地址开销,实际刷新率也就十几帧到二十几帧。如果你只是显示一些静态文字和图标,完全够用;但如果你要做滚动字幕或者简单动画,I2C 就会显得吃力。
SPI 接口就不一样了,速率可以轻松跑到 10MHz 甚至更高,刷屏速度是 I2C 的好几倍。代价是多占几根线:SCLK、MOSI、CS、DC,有的还有 RESET。对于引脚充裕的 MCU 来说,SPI 是更好的选择。我自己的习惯是,如果项目里 OLED 只是显示状态信息,用 I2C 省事;如果要做波形显示或者菜单动画,果断上 SPI。
还有一个容易被忽略的点:I2C 的从机地址。SSD1306 的 I2C 地址通常是 0x78 或 0x7A,取决于模块上 SA0 引脚的电平。有些模块背面有电阻可以切换,有些则固定死了。如果你用 HAL 库的 I2C 函数死活读不到应答,先拿逻辑分析仪或者简单的扫描程序确认地址,别急着怀疑代码。
1.3 显存映射方式决定了你的绘图逻辑
OLED 的显存组织方式和常见的 LCD 不太一样。以 SSD1306 为例,128x64 的显存被分成 8 页,每页 8 行,每行 128 个像素。也就是说,显存是按页组织的,而不是按行。你在屏幕上画一个点,需要先算出它在哪一页的哪一个字节的哪一位,然后去修改那个字节。这种组织方式对于写字符和图标其实挺方便,因为一个 8x16 的字符正好占两页,每页 8 行,直接按字节写入就行。但如果你要做任意位置的像素级绘图,比如画斜线或者圆弧,就需要一个显存缓冲区,先在内存里把整个画面画好,再一次性刷到屏幕上。
我强烈建议在 RAM 允许的情况下,始终维护一个显存缓冲区。128x64 单色屏的缓冲区大小是 1024 字节,对于大多数 MCU 来说完全可以接受。有了缓冲区之后,你的绘图操作都是在内存里进行,速度快,而且可以做局部刷新——只把变化的部分写到屏幕,而不是每次全屏刷新。这在 I2C 接口下尤其重要,能显著提升响应速度。
2. HAL 库驱动 OLED 的完整落地路径
2.1 初始化序列里那些不能省的步骤
用 HAL 库驱动 OLED,第一步是初始化 I2C 或者 SPI 外设,第二步是给 OLED 发送初始化命令序列。很多人直接从网上抄一段初始化代码,能用就不管了,但如果你想知道为什么有时候会花屏或者不亮,就得理解这些命令在干什么。
以 SSD1306 的 I2C 初始化为例,典型的命令序列包括:关闭显示、设置显示时钟分频、设置多路复用率、设置显示偏移、设置起始行、设置电荷泵、设置内存寻址模式、设置段重映射、设置 COM 扫描方向、设置对比度、设置预充电周期、设置 COM 引脚配置、设置对比度微调、开启电荷泵、开启显示。这一长串命令里,有几个是绝对不能省的。
电荷泵命令是第一个关键点。SSD1306 需要内部电荷泵产生 7.5V 左右的驱动电压,如果你不开启电荷泵,屏幕要么完全不亮,要么亮度极低。很多例程里写的是 0x8D, 0x14,这两个字节就是开启电荷泵。第二个关键是内存寻址模式,通常设置为 0x20, 0x00 表示水平寻址,或者 0x20, 0x02 表示页寻址。如果你用页寻址模式,每次写数据前都要设置页地址和列地址,比较麻烦;水平寻址模式则可以在写完一个字节后自动递增列地址,跨页时自动换行,更适合全屏刷新。
还有一个容易踩的坑是显示偏移和起始行设置。有些模块的玻璃面板和驱动芯片之间的连接方式不同,导致默认的显示起始行不是 0。如果你发现屏幕内容整体上移或下移了几行,就要调整这两个参数。我遇到过一块 1.3 寸的 SH1106 模块,默认起始行是 0,但显示内容总是偏下两行,后来把显示偏移设为 0x02 才对齐。
2.2 写命令和写数据的时序差异
I2C 接口下,SSD1306 的写操作分为写命令和写数据两种。区别在于控制字节:写命令时控制字节是 0x00,写数据时是 0x40。很多初学者直接用 HAL_I2C_Mem_Write 函数,把寄存器地址设为 0x00 或 0x40,然后写数据,这样也能工作。但更标准的做法是用 HAL_I2C_Master_Transmit,手动构造一个缓冲区,第一个字节放控制字节,后面跟命令或数据。
这里有一个实际经验:如果你用 HAL_I2C_Mem_Write,每次调用都会产生一个起始条件、设备地址、寄存器地址、数据、停止条件的完整时序。对于连续写多个字节的情况,这种方式效率很低。更好的做法是把要写的数据打包成一个缓冲区,一次传输完成。比如你要写 128 个字节的显存数据,可以构造一个 129 字节的缓冲区,第一个字节是 0x40,后面 128 个字节是显存数据,然后一次 HAL_I2C_Master_Transmit 发出去。这样比循环调用 128 次单字节写要快得多。
SPI 接口下就更简单了,DC 引脚决定当前传输的是命令还是数据。写命令时拉低 DC,写数据时拉高 DC,然后通过 HAL_SPI_Transmit 发送即可。SPI 没有地址开销,所以刷屏速度比 I2C 快很多。但要注意 SPI 的时钟极性和相位设置,SSD1306 通常支持模式 0 和模式 3,如果你发现数据错乱,先检查 CPOL 和 CPHA 配置。
2.3 显存缓冲区的管理与局部刷新
前面提到过,维护一个显存缓冲区是提升显示质量的关键。具体做法是定义一个 uint8_t 数组,大小为 128 * 64 / 8 = 1024 字节。所有的绘图函数,包括画点、画线、写字符、显示图片,都只操作这个缓冲区,不直接写屏幕。等一帧画面在缓冲区里画好之后,再调用一个刷新函数,把整个缓冲区或者变化区域写到 OLED。
全屏刷新很简单,设置好起始页和列地址,然后连续写入 1024 字节即可。局部刷新的实现稍微复杂一点,你需要记录哪些区域发生了变化。一种简单的做法是维护一个“脏矩形”区域,每次绘图操作更新这个矩形的边界,刷新时只写这个矩形范围内的显存。另一种做法是分页刷新,每次只刷新一页或者几页,适合那些只有局部变化的界面。
我在一个项目里做过一个简单的菜单系统,菜单项切换时只有中间几行文字变化,如果每次全屏刷新,I2C 下会有明显的闪烁感。后来改成只刷新变化的那两页,切换就非常流畅了。具体实现是:在写字符函数里记录当前操作的页范围,刷新时只发送这些页的数据。代码不复杂,但效果提升很明显。
3. 那些让你调试到怀疑人生的常见故障
3.1 屏幕完全不亮的第一轮排查
屏幕不亮是最常见也最让人抓狂的问题。我的排查顺序是这样的:先确认供电,再确认接线,然后确认地址,最后确认初始化序列。
供电方面,OLED 模块通常需要 3.3V 或者 5V,具体看模块上的稳压芯片。有些模块标称 3.3V 到 5V 宽电压,但如果你用 3.3V 供电,而模块上的电平转换电路是按 5V 设计的,I2C 信号可能无法被正确识别。我遇到过一块模块,3.3V 供电时完全不亮,换成 5V 就正常了。所以先看模块背面的丝印,确认供电范围。
接线方面,I2C 的 SDA 和 SCL 不要接反,虽然有些 MCU 的 I2C 外设可以配置引脚交换,但大多数情况下接反了就是没反应。另外,I2C 需要上拉电阻,很多 OLED 模块自带上拉,但如果你用的模块没有,或者总线上挂了多个设备导致等效上拉电阻变小,就需要外接。上拉电阻一般用 4.7k 到 10k 之间,太小了增加功耗,太大了上升沿变缓,高速通信时容易出错。
地址确认可以用一个简单的 I2C 扫描程序,遍历 0x00 到 0x7F 的地址,看哪个地址有应答。如果扫描不到任何设备,那基本是硬件连接问题;如果扫描到了但地址和你预期的不一样,就改代码里的地址。
初始化序列的问题通常表现为屏幕亮但无显示,或者显示乱码。这时候可以先用一个最简单的测试:只发送开启显示的命令,看屏幕是否整体点亮。如果整体点亮了,说明电荷泵和显示开关正常,问题出在显存数据或者寻址模式上。如果连整体点亮都做不到,那就是初始化序列的前半部分有问题,重点检查电荷泵和对比度设置。
3.2 花屏、错位和闪烁的根因分析
花屏通常有两种表现:一种是随机噪点,一种是规律性的错位。随机噪点大多是通信错误导致的,可能是 I2C 速率太高、上拉电阻不合适、或者电源纹波太大。我遇到过一次,I2C 速率设到 400kHz 时屏幕偶尔出现噪点,降到 200kHz 就稳定了。后来在电源引脚旁边加了一个 100nF 的退耦电容,400kHz 也没问题了。所以如果你遇到随机花屏,先降速,再加电容,基本能解决大部分问题。
规律性错位通常是寻址模式或者驱动芯片型号不匹配导致的。比如用 SSD1306 的代码驱动 SH1106,画面会整体偏移两列。这时候要么换驱动芯片对应的初始化代码,要么在设置列地址时手动加 2 的偏移。还有一种情况是显示内容上下颠倒或者左右镜像,这是段重映射和 COM 扫描方向设置的问题,调整 0xA0/0xA1 和 0xC0/0xC8 这两组命令即可。
闪烁问题大多和刷新策略有关。如果你在主循环里不停地全屏刷新,而 I2C 速率又不够高,屏幕就会看起来在闪。解决办法是降低刷新频率,或者改用局部刷新。另外,如果你在刷新过程中被中断打断,导致 I2C 传输不完整,也可能出现闪烁。这种情况下可以在刷新函数前后关中断,或者用 DMA 传输来避免 CPU 干预。
3.3 显示内容偏移一两列或一两行的处理
偏移问题我在前面提过,这里展开说一下具体怎么调。SSD1306 的列地址设置命令是 0x21 后面跟起始列和结束列,页地址设置命令是 0x22 后面跟起始页和结束页。如果你发现显示内容整体右移了两列,可以把起始列从 0 改成 2,结束列从 127 改成 129。但注意 SSD1306 的列地址范围是 0 到 127,改成 129 可能超出范围,所以更稳妥的做法是在发送显存数据时,前面补两个字节的 0x00,相当于把内容往左推两列。
行偏移的调整类似,通过设置显示起始行命令 0x40 到 0x7F 来改变。这个命令的值直接对应起始行号,比如 0x40 表示从第 0 行开始,0x42 表示从第 2 行开始。如果你发现内容整体下移了两行,就把起始行设为 2,这样显示内容会往上移两行。
还有一种偏移是字符本身的偏移,比如你用的字库取模方式是从上到下、从左到右,但你的写字符函数是按页写入的,导致字符显示出来是旋转了 90 度或者错位的。这种情况需要检查字库的取模方向和写字符函数的实现是否匹配。常见的取模方式有纵向取模、横向取模、字节倒序等,不同的取模方式对应不同的写入逻辑,不能混用。
4. 从点亮到好用:显示效果与性能优化
4.1 字库选择与自定义图标的取舍
OLED 显示文字离不开字库。常见的字库有 6x8、8x16、16x16 等尺寸,6x8 适合显示密集信息,8x16 适合正常阅读,16x16 适合标题或者大号数字。字库的存储方式有两种:一种是直接放在代码里,用 const 数组存储,占用 Flash 空间;另一种是放在外部 Flash 或者 SD 卡里,运行时读取,节省内部 Flash。
如果你的 MCU Flash 足够大,比如 STM32F103C8T6 有 64KB Flash,放一个 8x16 的 ASCII 字库大概 1KB 左右,完全没问题。但如果要显示中文,一个 16x16 的汉字需要 32 字节,一千个汉字就是 32KB,这时候就要考虑外部存储了。我一般建议,如果只是显示少量固定汉字,比如“温度”“湿度”“设置”这些,直接取模放在代码里;如果要显示任意汉字,还是外挂一个 SPI Flash 存字库比较合适。
自定义图标也是类似,你可以用取模软件把图标转成字节数组,然后在代码里调用显示函数。取模的时候要注意设置正确的尺寸和取模方式,和你的显示函数匹配。我习惯用 PCtoLCD2002 这个工具,设置成阴码、逐列式、顺向取模,这样生成的数组可以直接按页写入,不需要额外的转换。
4.2 刷新率与 I2C 速率的平衡
刷新率是 OLED 显示效果的一个关键指标。对于 I2C 接口,刷新率受限于总线速率和传输数据量。假设 I2C 速率是 400kHz,传输 1024 字节的显存数据,加上控制字节和地址开销,大概需要 1024 * 9 / 400000 ≈ 23ms,也就是大约 43 帧每秒。这是理论值,实际因为 HAL 库的函数调用开销和中断影响,可能只有 20 到 30 帧。对于静态显示,这个刷新率绰绰有余;对于滚动文字,也能接受;但对于动画,就有点勉强了。
如果你需要更高的刷新率,有几个方向可以优化。第一是提高 I2C 速率,很多 MCU 支持 1MHz 的快速模式,但要注意 OLED 模块是否支持。第二是减少传输数据量,比如只刷新变化区域,而不是全屏。第三是改用 SPI 接口,SPI 在 10MHz 下传输 1024 字节只需要不到 1ms,刷新率可以轻松上百帧。
还有一个容易被忽略的点是 HAL 库的 I2C 传输函数本身有开销。HAL_I2C_Master_Transmit 是阻塞式的,传输期间 CPU 一直在等待。如果你用 DMA 传输,CPU 可以在传输期间处理其他任务,整体效率更高。不过 DMA 传输需要配置好 DMA 通道和中断,稍微复杂一点,但对于需要高刷新率的场景是值得的。
4.3 低功耗场景下的 OLED 控制策略
如果你的项目是电池供电的,OLED 的功耗就不能不考虑。OLED 的功耗和显示内容有关,亮的像素越多,功耗越大。全屏点亮时,0.96 寸 OLED 的电流大概在 20mA 左右,全屏熄灭时只有几毫安。所以如果你做的是低功耗设备,可以通过以下策略降低功耗。
第一是降低亮度。SSD1306 支持对比度调节,通过 0x81 命令设置对比度值,范围是 0 到 255。默认通常是 0x7F 或者 0xCF,你可以根据实际需要调低。第二是缩短显示时间,比如按键唤醒后显示几秒钟,然后自动关闭显示。关闭显示用 0xAE 命令,开启用 0xAF。第三是减少刷新频率,静态画面不需要一直刷新,画好之后就可以停止传输数据。
我在一个便携式传感器项目里用过这些策略,OLED 平时处于关闭状态,按键唤醒后显示 5 秒,然后自动关闭。整体功耗从原来的 25mA 降到了平均 3mA 左右,电池续航从几个小时提升到了几天。所以如果你做的是低功耗设备,千万别让 OLED 一直亮着。
5. 进阶玩法:让 OLED 显示更有价值
5.1 多级菜单与交互界面的实现思路
OLED 虽然小,但做多级菜单完全可行。核心思路是维护一个菜单状态机,每个菜单项对应一个函数指针或者一个结构体,包含显示内容和按键处理逻辑。按键按下时,根据当前菜单层级和按键类型,切换到下一个状态。
我实现过一个三级菜单,第一级是主菜单,有“数据显示”“参数设置”“关于”三个选项;第二级是子菜单,比如“参数设置”下面有“亮度”“对比度”“刷新率”;第三级是具体的参数调节界面。整个菜单系统用一个数组存储菜单项,每个菜单项包含显示文本、子菜单索引、处理函数指针。按键处理用一个简单的状态机,支持上下选择、确认进入、返回上一级。
显示方面,为了减少闪烁,我只在菜单切换时刷新屏幕,而不是每帧都刷。具体做法是:菜单状态变化时,先在显存缓冲区里画好新界面,然后一次性刷新。这样切换非常流畅,没有闪烁感。
5.2 用 OLED 做实时数据可视化的注意事项
OLED 做实时数据可视化,比如显示传感器波形或者系统状态曲线,有几个注意点。第一是数据缓冲,你需要一个环形缓冲区来存储最近的数据点,比如最近 128 个采样值。第二是坐标映射,把数据值映射到屏幕的像素坐标,注意处理超出范围的值。第三是刷新策略,波形显示通常需要较高的刷新率,建议用 SPI 接口或者局部刷新。
我做过一个简单的示波器功能,用 ADC 采集信号,然后在 OLED 上显示波形。采样率是 1kHz,每 10ms 采集一个点,缓冲区存 128 个点,也就是 1.28 秒的数据。显示时把 128 个点映射到 128 列,每列的高度对应数据值。刷新率是 20 帧每秒,用 SPI 接口完全没问题。如果换成 I2C,刷新率降到 10 帧左右,波形看起来就有点卡顿。
还有一个细节是波形清除。如果你每次刷新都先清屏再画波形,屏幕会闪。更好的做法是用一个“背景缓冲区”保存坐标轴和网格,每次刷新时先把背景复制到显存缓冲区,再画波形,这样就不会闪了。
5.3 从单色到彩色:SSD1331 等彩色 OLED 的驱动差异
如果你用腻了单色 OLED,想试试彩色 OLED,常见的驱动芯片有 SSD1331、SSD1351 等。彩色 OLED 的驱动逻辑和单色有相似之处,但有几个关键差异。
第一是数据格式。单色 OLED 每个像素用 1 位表示,彩色 OLED 通常用 16 位 RGB565 格式,每个像素两个字节。所以显存缓冲区的大小是 128 * 64 * 2 = 16384 字节,对于 RAM 较小的 MCU 来说是个挑战。第二是初始化序列不同,彩色 OLED 需要设置颜色模式、伽马校正等参数。第三是通信接口,彩色 OLED 因为数据量大,基本都用 SPI 接口,I2C 的速率根本不够。
我用过一块 0.96 寸的 SSD1331 彩色 OLED,分辨率是 96x64,SPI 接口。刷一屏的数据量是 96 * 64 * 2 = 12288 字节,在 10MHz SPI 下大概需要 10ms 左右,刷新率能到 50 帧以上。但 RAM 占用确实大,我用的是 STM32F407,192KB RAM 才勉强够用。如果你用 RAM 较小的 MCU,建议不要维护全屏缓冲区,而是直接写屏幕,或者只维护局部缓冲区。
彩色 OLED 的显示效果确实比单色好很多,可以显示彩色图标、渐变、甚至简单的图片。但功耗也比单色高不少,全屏点亮时电流可能超过 50mA。所以如果你的项目是电池供电,还是要权衡一下。
6. 我在实际项目中积累的几个关键经验
6.1 初始化代码不要照抄,要理解每一行的作用
网上能找到的 OLED 初始化代码大多长得差不多,但如果你只是复制粘贴,遇到问题就无从下手。我的建议是,至少把初始化序列里的每一条命令都查一遍数据手册,知道它在设置什么。比如 0xD5 是设置时钟分频,0xA8 是设置多路复用率,0xD3 是设置显示偏移,0x40 是设置起始行。理解了这些,你就能根据实际模块调整参数,而不是盲目试错。
我遇到过一块模块,用标准初始化代码显示正常,但亮度偏低。查了数据手册后发现,对比度设置命令 0x81 后面的值默认是 0xCF,而这块模块的最佳对比度是 0xFF。改成 0xFF 之后,亮度明显提升。如果我不理解这条命令的作用,可能就一直以为模块本身亮度不够。
6.2 显存缓冲区是提升显示质量的关键
再强调一次显存缓冲区的重要性。没有缓冲区,你的所有绘图操作都要直接写屏幕,速度慢不说,还容易因为通信中断导致画面撕裂。有了缓冲区,你可以先在内存里完成所有绘图,然后一次性刷新,画面完整且流畅。而且缓冲区让你可以实现一些高级功能,比如双缓冲、局部刷新、画面叠加等。
缓冲区的代价是占用 RAM。128x64 单色屏需要 1024 字节,对于大多数 MCU 来说不算什么。但如果你用的是 RAM 很小的 MCU,比如 2KB RAM 的 8 位机,可能就要斟酌一下了。这种情况下可以只维护一页的缓冲区,画完一页刷一页,虽然效率低一点,但也能工作。
6.3 调试工具的选择:逻辑分析仪比示波器更实用
调试 OLED 通信问题,逻辑分析仪比示波器好用得多。示波器只能看模拟波形,而逻辑分析仪可以直接解码 I2C 或 SPI 协议,告诉你发了什么命令、什么数据、有没有应答。我用的是一款几十块钱的 8 通道逻辑分析仪,配合开源软件,能直接看到 I2C 的起始条件、地址、数据、停止条件,排查问题非常方便。
比如你怀疑初始化序列有问题,可以用逻辑分析仪抓一下上电后的通信数据,看看实际发送的命令和你代码里写的是否一致。如果发现某条命令没发出去,或者数据错了,就能快速定位到代码问题。如果没有逻辑分析仪,也可以用 MCU 的串口打印调试信息,但效率低很多。
6.4 模块选型时容易忽略的细节
最后说几个选型时容易忽略的细节。第一是模块的尺寸和分辨率,0.96 寸 128x64 是最常见的,但如果你需要显示更多内容,可以考虑 1.3 寸 128x64 或者 1.5 寸 128x128。第二是模块的接口类型,I2C 模块通常只有四个引脚,SPI 模块有七个或更多,买之前确认你的 MCU 引脚够不够。第三是模块的供电电压,有些模块标称 3.3V 到 5V,但实际 3.3V 下亮度会降低,如果你对亮度有要求,最好选 5V 供电或者带升压电路的模块。
还有一个细节是模块的视角。OLED 的视角通常很好,但不同厂家的面板视角有差异。如果你做的是需要从侧面观看的设备,比如戴在手腕上的手环,就要注意选视角大的模块。这个参数在购买页面上通常不会写,只能看评价或者买样品测试。
我个人在实际操作中的体会是,OLED 这个看似简单的东西,真正要做好并不容易。从选型到驱动,从显示效果到功耗控制,每一个环节都有可以优化的空间。上面这些经验都是我在一个个项目中踩坑踩出来的,希望能帮你少走一些弯路。如果你刚开始接触 OLED,建议先从 I2C 接口的 0.96 寸模块入手,把基本的显示和刷新跑通,再逐步尝试 SPI、彩色屏和更复杂的界面。