ARM处理器外设驱动SPI与I2C传输驱动开发实战 - 第1节
1.1 SPI协议概述:四线制、主从模式、全双工
SPI,全称Serial Peripheral Interface,是Motorola公司在上世纪80年代推出的一种同步串行通信协议。说实话,这玩意儿虽然年纪不小了,但在嵌入式领域至今仍是主力。我最早接触SPI是在做一款工业数据采集卡的时候,当时需要在主控和ADC之间高速搬运数据,试了一圈,最后还是选了SPI。
1.1.1 四线制信号
SPI用四条线完成通信,每条线各司其职:
- SCLK(Serial Clock):串行时钟,由主设备产生。时钟频率决定了通信速率。
- MOSI(Master Out Slave In):主设备输出,从设备输入。数据从主设备流向从设备。
- MISO(Master In Slave Out):主设备输入,从设备输出。数据从从设备流向主设备。
- SS(Slave Select):从设备选择线,低电平有效。主设备通过拉低某个从设备的SS线来选中它。
你想想看,四条线就能实现全双工通信,这在硬件设计上是非常经济的。我在项目中见过有人把SS线叫做CS(Chip Select),一回事,别搞混就行。
1.1.2 主从模式
SPI总线上的设备分为主设备和从设备。主设备负责产生时钟信号,并控制所有通信的发起和结束。从设备只能被动响应,不能主动发起通信。
一个主设备可以挂多个从设备,每个从设备独占一条SS线。主设备想跟谁说话,就把谁的SS线拉低,其他从设备的SS线保持高电平,它们就会乖乖待在总线上不动。
1.1.3 全双工通信
SPI最让我欣赏的一点就是全双工。什么意思?主设备在发送数据的同时,从设备也在发送数据。一个时钟周期内,MOSI和MISO上各有一位数据在传输,互不干扰。
底层实现其实很简单:主设备和从设备各有一个移位寄存器,时钟的上升沿和下降沿分别负责数据的采样和移位。主设备把数据从MOSI推出去的同时,从设备把数据从MISO推回来。所以SPI的每一次传输,主设备都会收到从设备返回的数据——哪怕你不需要,也得读出来。
1.2 I2C协议概述:两线制、多主多从、半双工
I2C,Inter-Integrated Circuit,是Philips公司(现在的NXP)开发的。它的设计理念跟SPI完全不同——用最少的线完成最灵活的通信。我刚开始做驱动时,总觉得I2C比SPI复杂,后来用熟了才发现,它的优雅之处恰恰在于那套协议层的设计。
1.2.1 两线制信号
I2C只需要两条线:
- SCL(Serial Clock):串行时钟线,由主设备控制。
- SDA(Serial Data):串行数据线,双向传输数据。
这两条线都是开漏输出,外部需要上拉电阻。开漏的好处是多个设备可以共用总线,谁想拉低就拉低,不会发生短路。坏处嘛,就是速度上不去,因为上拉电阻的充放电时间限制了信号跳变的速度。
1.2.2 多主多从与仲裁
I2C支持多个主设备挂在同一条总线上。这听起来很美好,但实际用起来需要小心。多个主设备同时发起通信怎么办?I2C的解决方案是仲裁——谁先拉低SDA,谁就赢得总线控制权。
仲裁机制是这样的:每个主设备在发送数据的同时,会监测SDA上的电平。如果它想发送高电平,但SDA上实际是低电平,说明有其他主设备在拉低总线,它就自动退出,让出控制权。整个过程对上层软件是透明的。
1.2.3 半双工与地址寻址
I2C是半双工的,同一时刻只能有一个方向的数据传输。主设备发送时,从设备只能接收;主设备接收时,从设备才能发送。切换方向需要重新发送起始条件和从设备地址。
每个I2C从设备都有一个7位或10位的地址。主设备在通信开始时,先发送从设备地址和读写位(0表示写,1表示读),然后等待从设备的应答信号(ACK)。如果从设备应答了,说明它在线并且准备好了,后续就可以传输数据。
7位地址最多支持112个设备(保留地址除外),10位地址可以扩展到1024个。实际项目中,7位地址基本够用。
1.3 SPI与I2C对比分析
做项目选型时,SPI和I2C怎么选?我见过不少工程师在这个问题上纠结。下面这张表是我多年经验的总结,你直接拿去用。
| 对比维度 | SPI | I2C |
|---|---|---|
| 引脚数量 | 4线(SCLK, MOSI, MISO, SS) | 2线(SCL, SDA) |
| 通信模式 | 全双工 | 半双工 |
| 主从关系 | 单主多从 | 多主多从 |
| 速度范围 | 典型10MHz~50MHz,最高可达100MHz+ | 标准模式100kHz,快速模式400kHz,高速模式3.4MHz |
| 从设备选择 | 硬件片选线(每设备一根) | 软件地址寻址(7位或10位) |
| 协议复杂度 | 低,无应答机制 | 高,有起始/停止条件、应答、仲裁 |
| 硬件开销 | 引脚多,但逻辑简单 | 引脚少,但需要上拉电阻 |
| 典型应用 | ADC/DAC、LCD显示屏、SD卡、Flash存储器 | 温度传感器、EEPROM、RTC、IO扩展芯片 |
1.3.1 速度与引脚:鱼和熊掌
SPI的优势在速度。50MHz的SPI总线,每秒能传50Mbit的数据,换算成字节就是6.25MB/s。I2C的快速模式才400kHz,差了100多倍。所以需要高速数据搬运的场景,比如摄像头图像数据、音频流、Flash读写,SPI是唯一选择。
但SPI的代价是引脚多。每增加一个从设备,就要多一根SS线。如果你的主控GPIO本来就紧张,挂4个SPI从设备就得占用7个引脚(SCLK+MOSI+MISO+4×SS),而I2C只需要2个引脚。
我记得有一次做智能家居网关,主控引脚只剩3个了,但需要挂6个传感器。SPI肯定没戏,最后全部走I2C,用地址区分设备,2根线搞定6个传感器,省下来的引脚接了按键和LED。
1.3.2 复杂度:简单粗暴 vs 协议严谨
SPI的协议层几乎为零。主设备只管发时钟、推数据、收数据,没有应答、没有地址、没有起始停止条件。驱动写起来非常直接——配置好时钟极性和相位,往数据寄存器里写值,读回来就行。
I2C就复杂多了。你得处理起始条件、从设备地址、读写位、ACK/NACK、停止条件,还要考虑时钟拉伸(Clock Stretching)和仲裁。驱动代码量至少是SPI的两倍。
但复杂度换来了灵活性。I2C的地址机制让总线扩展变得非常容易,想加设备就加,只要地址不冲突就行。SPI加设备就得加SS线,硬件改动大。
1.3.3 应用场景:各有所长
选型时我一般遵循这个原则:
- 选SPI的场景:数据量大、速度要求高、引脚不紧张。比如SD卡读写、LCD刷屏、音频编解码器、高速ADC。
- 选I2C的场景:引脚受限、设备数量多、数据量小。比如温度传感器、光照传感器、EEPROM配置存储、RTC时钟芯片。
- 两者都行:有些设备同时支持SPI和I2C接口,比如某些加速度计和陀螺仪。这时候看你的主控资源,哪个方便用哪个。
另外,SPI没有标准的总线枚举机制,你没法像I2C那样扫描总线发现设备。所以SPI设备通常都是固定的,上电就知道挂在哪里。I2C支持热插拔和动态发现,适合需要灵活配置的系统。
1.4 知识体系总览
下面这张图把SPI和I2C的核心知识点串起来了。你可以把它当作本章的思维导图,后续章节会逐一展开每个模块。