2. 硬件接口基础:SPI与I2C协议对比、4线SPI与3线SPI区别、I2C地址配置

做OLED驱动,第一关就是选通信接口。说白了,你得决定用SPI还是I2C把数据灌进驱动芯片里。我见过不少新手上来就抄代码,结果屏幕死活不亮,最后发现是接口模式没配对。这一节咱们就把SPI和I2C的底裤扒干净。

2.1 SPI vs I2C:核心差异

先看一张对比表,心里有个谱。

对比项 SPI(4线) I2C
信号线数量 4根(SCLK, MOSI, MISO, CS) 2根(SCL, SDA)
通信模式 全双工 半双工
速率 典型10-80 MHz 标准100 kHz / 快速400 kHz / 高速3.4 MHz
从机选择 独立CS引脚,一对一或菊花链 7位/10位地址,总线仲裁
硬件复杂度 引脚多,但协议简单 引脚少,但协议复杂(ACK/NACK)
OLED适用场景 高刷新率、大分辨率(128×64以上) 低引脚数、小尺寸、低功耗

我个人习惯:如果MCU引脚够用,优先选SPI。为什么?因为刷屏快。你想想看,128×64的OLED,一帧数据是1024字节。用400 kHz的I2C传,光数据就得20多毫秒,加上命令和地址开销,30帧都跑不满。而SPI轻松上10 MHz,一帧1毫秒搞定。

但I2C也有它的主场。比如你手上只有一颗8引脚的MCU,或者需要挂多个传感器共享总线,那I2C就是天选之子。

关键结论:SPI快但费引脚,I2C省引脚但慢。选哪个,取决于你的刷新率要求和引脚预算。

2.2 4线SPI与3线SPI的区别

很多OLED驱动芯片(比如SSD1306、SH1106)都支持两种SPI模式:4线SPI和3线SPI。这里有个坑,我当年就踩过。

4线SPI:标准的四根线——SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。全双工,一发一收同时进行。

3线SPI:只有三根线——SCLK、MOSI(兼做MISO)、CS。半双工,数据线分时复用。发送时当MOSI用,接收时当MISO用。

等等,OLED驱动芯片需要接收数据吗?

嗯,这里要注意。大多数OLED驱动芯片是只写设备——你只管往它肚子里灌数据,它不会给你回传像素值。所以MISO这根线其实是浪费的。3线SPI就是把这个冗余砍掉了。

特性 4线SPI 3线SPI
数据线 MOSI + MISO 独立 SDATA 双向复用
通信方向 全双工 半双工
引脚数 4 3
OLED适用性 兼容所有SPI主机 节省1个引脚,适合引脚紧张场景
驱动配置 默认模式 需在初始化命令中切换

我曾经在一个项目中,MCU只剩3个空闲GPIO,硬着头皮上了3线SPI。结果发现,有些OLED模块的3线模式需要额外发一个控制字节来区分命令和数据,时序比4线复杂一点。所以我的建议是:除非引脚真的不够,否则老老实实用4线SPI。

避坑指南:我曾经买过一批号称“4线SPI”的OLED模块,结果板子上MISO引脚根本没焊。后来一查手册,芯片默认就是3线模式。所以买模块前,一定先看数据手册的接口配置表。

2.3 I2C地址配置

I2C的地址配置,是另一个容易翻车的地方。OLED驱动芯片的I2C地址通常是7位的,但不同芯片、不同模块的地址可能不一样。

以SSD1306为例,它的I2C地址由SA0引脚的电平决定:

  • SA0接GND:地址 0x3C(7位地址)
  • SA0接VCC:地址 0x3D(7位地址)

但注意,很多库函数里写的是8位地址(左移一位后的值)。比如0x3C左移一位变成0x78。你写代码时一定要搞清楚库用的是7位还是8位地址。我见过有人把0x3C直接当8位地址传进去,结果I2C总线一直NACK,屏幕死活不亮。

警告:I2C地址的7位/8位混淆是OLED驱动中最常见的bug之一。建议在代码开头用宏定义明确标注:#define OLED_I2C_ADDR_7BIT 0x3C,并在注释里写明是7位还是8位。

另外,有些OLED模块(比如0.96寸的常见款)会把SA0引脚通过电阻固定到GND,地址就是0x3C。但也有模块把SA0引出到焊盘,让你自己跳线。买模块时多看一眼原理图,能省去后面一小时的调试时间。

2.4 知识结构图:接口选择与配置流程

下面这张图把整个决策过程串起来了。你可以把它当作一个快速参考。

OLED通信接口选择与配置流程 选择通信接口 SPI I2C 4线SPI(全双工) 3线SPI(半双工) 配置I2C地址(SA0) 初始化驱动芯片 发送命令/数据 显示正常? 常见问题排查 • SPI模式不匹配(CPOL/CPHA) • I2C地址7位/8位混淆 • 3线SPI控制字节遗漏

2.5 实战要点总结

  • SPI选型:引脚够用就上4线SPI,省心。3线SPI只在你数着引脚过日子的时候用。
  • I2C地址:先查模块原理图或数据手册,确认SA0电平。代码里明确标注地址是7位还是8位。
  • 速率匹配:SPI时钟不要超过芯片手册标称最大值(常见10 MHz)。I2C用400 kHz通常没问题,但长线缆时降到100 kHz更稳。
  • 上拉电阻:I2C的SCL和SDA必须外接上拉电阻(4.7 kΩ典型值)。SPI的CS引脚建议接上拉,防止浮空误触发。

我的习惯:每次拿到新OLED模块,第一件事不是写代码,而是用逻辑分析仪抓一下初始化时序。看一眼波形,接口模式、地址、速率全清楚了。这比对着手册猜快得多。