4、接口协议详解(SPI与I2C)

各位工程师朋友,今天我们来聊聊显示驱动芯片的“嘴巴”——接口协议。说白了,芯片怎么跟主控沟通,全靠这两个协议:SPI和I2C。我做了这么多年硬件,发现很多新手在这上面栽跟头。选错了接口,轻则显示卡顿,重则整个系统跑不起来。

嗯,咱们直接进入正题。

SPI接口的时序与配置

SPI,全称Serial Peripheral Interface,摩托罗拉发明的。它是个“四线制”的家伙:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。

为什么SPI快?因为它全双工。发送和接收可以同时进行。我在项目中遇到过用SPI驱动128x64的OLED屏,刷新率轻松上60fps。

时序要点

SPI的时序,核心就两个参数:CPOL(时钟极性)和CPHA(时钟相位)。

  • CPOL=0:空闲时时钟为低电平
  • CPOL=1:空闲时时钟为高电平
  • CPHA=0:在第一个时钟边沿采样数据
  • CPHA=1:在第二个时钟边沿采样数据

组合起来就是4种模式。大部分显示驱动芯片用模式0(CPOL=0, CPHA=0)或模式3(CPOL=1, CPHA=1)。

避坑指南:我曾经遇到过一块驱动IC,手册上写支持模式0,结果实际测试必须用模式3才能稳定通信。后来发现是芯片内部采样逻辑有bug。所以,拿到新芯片,我建议你先用逻辑分析仪抓一下波形,确认实际时序。

配置示例

以STM32为例,配置SPI驱动一个常见的SSD1306 OLED:

// SPI配置代码片段
SPI_HandleTypeDef hspi1;

hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;   // CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;       // CPHA=0
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&hspi1);

这里有个细节:波特率预分频器。SPI时钟不能超过从设备支持的最大频率。我一般留30%的余量。比如芯片标称20MHz,我实际跑14MHz左右。

I2C接口的时序与配置

I2C,飞利浦发明的。它只有两根线:SCL(时钟)和SDA(数据)。

你想想看,两根线就能挂一堆设备,是不是很省IO?但代价是速度慢。标准模式100kHz,快速模式400kHz,高速模式3.4MHz。跟SPI动辄几十MHz比,差远了。

时序要点

I2C的时序,核心是起始条件停止条件

  • 起始条件:SCL高电平时,SDA从高变低
  • 停止条件:SCL高电平时,SDA从低变高

每个字节后面跟一个ACK(应答位)。从设备拉低SDA表示应答。

注意:I2C总线上的上拉电阻很关键。我见过有人用4.7kΩ上拉,结果400kHz模式下波形都变形了。对于快速模式,我建议用2.2kΩ或1.5kΩ。具体看总线电容。

配置示例

同样以STM32驱动SSD1306为例(I2C版本):

// I2C配置代码片段
I2C_HandleTypeDef hi2c1;

hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;          // 400kHz快速模式
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);

写数据时,记得先发设备地址(7位地址左移1位,最后一位是R/W位)。比如SSD1306的I2C地址通常是0x3C,写操作就是0x78。

如何根据带宽需求选择接口

这是最实际的问题。我直接给结论:

显示场景 推荐接口 理由
静态文本/图标 I2C 连线少,够用就行
动态图形/动画 SPI 带宽需求大,I2C扛不住
高分辨率视频 RGB/MIPI SPI也不够,得上并行接口

怎么算带宽?我教你一个简单公式:

带宽 = 分辨率宽度 × 分辨率高度 × 色彩深度 × 刷新率

举个例子:128x64的单色屏,1bit色彩,60fps刷新。

带宽 = 128 × 64 × 1 × 60 = 491,520 bps ≈ 0.5 Mbps

I2C快速模式400kHz,理论带宽400kbps。嗯,刚好够用,但没余量。所以我建议用SPI,哪怕只跑10MHz,也有10Mbps,绰绰有余。

我的经验:选接口时,别只看理论带宽。还要考虑协议开销。I2C每个字节有ACK位,地址字节,还有起始停止条件。实际有效带宽只有理论值的60%-70%。SPI开销小,有效带宽能到90%以上。

再比如,一个320x240的彩色屏(16bit色深),60fps:

带宽 = 320 × 240 × 16 × 60 = 73,728,000 bps ≈ 73.7 Mbps

这个量级,SPI就算跑40MHz也才40Mbps,不够。必须上RGB接口或MIPI DSI。

知识体系总览

下面这张图,是我梳理的接口选型决策流程:

显示驱动芯片接口选型决策树 显示需求分析 带宽 < 5Mbps? I2C接口 SPI接口 静态显示? 动态显示? I2C足够 考虑SPI SPI首选 需RGB/MIPI

这张图很直观。先算带宽,再根据显示内容类型做选择。我个人习惯是:能用SPI就不用I2C。除非IO口实在不够,或者对速度没要求。

最后说一句:接口选型没有绝对的对错。我见过有人用I2C驱动320x240的屏,刷新率降到10fps,也能用。关键是你的产品需求是什么。成本?功耗?性能?想清楚再选。


公众号:蓝海资料掘金营,微信deep3321