本人是初学者,非常希望与大家交流。
想象一下你是个芯片老大,要指挥手下的小弟(其他芯片)干活。怎么给小弟们发命令、收情报呢?这时就需要通信协议—— IIC 和 SPI。
1. IIC(也叫 I²C):两线制的“电话会议系统”
-
核心特点: 只用两根线!
-
SDA (数据线): 像电话听筒,既能说又能听,但不能同时(半双工)。
-
SCL (时钟线): 像电话铃声/节拍器,老大负责摇铃铛📢,告诉大家“注意!现在开始传消息了!1,2,1,2...”
-
-
怎么工作?
-
老大点名: 老大(主设备)先摇铃铛(SCL),然后在SDA线上喊:“喂喂喂!地址是0x68的小弟(从设备)在吗?我要跟你说话啦!”(广播一个唯一的设备地址)。
-
小弟应答: 地址匹配的小弟会回一声:“在呢老大!”(发送一个应答信号)。
-
传纸条/听汇报: 然后老大就可以通过这根SDA线,按照SCL的节奏,要么给小弟发命令(写数据),要么听小弟汇报情况(读数据)。一次只能一个人说话。
-
多个小弟: 所有小弟都挂在同一对 SDA 和 SCL 线上。老大靠点名(设备地址)找到特定小弟。就像开电话会议,大家都能听到铃响和点名,但只有被点到名的才能说话。
-
-
优点:
-
省线省钱! 接再多的设备也只需要两根线,电路板走线清爽。
-
有地址,好管理。 理论上可以挂很多设备(地址不冲突就行)。
-
-
缺点:
-
速度慢点。 因为要等点名、应答,而且一根线来回传数据。
-
容易“串线”。 一根线上挂太多设备或者线路太长,信号可能变差。
-
老大累。 时钟SCL必须由老大(主设备)产生,小弟不能主动摇铃铛。
-
-
适合场景: 速度要求不高,设备不太多,成本敏感的地方。比如:读取温度传感器、光照传感器、EEPROM(小存储器)的数据。
形象比喻:IIC 就像一个“电话会议群聊”
-
只有一个人(主设备)能主持(发起通话、控制时钟)。
-
主持人点名(发地址)呼叫特定参会者(从设备)。
-
大家共用一条语音线路(SDA),同一时间只能一个人说话或听(半双工)。
-
主持人用另一个铃声(SCL)统一节奏。
2. SPI:四线制的“点对点高速专线”
-
核心特点: 一般用四根线!(有时三根)
-
MOSI (Master Out Slave In): 老大说,小弟听 的专属通道。
-
MISO (Master In Slave Out): 小弟说,老大听 的专属通道。
-
SCLK (时钟 Clock): 老大负责摇铃铛📢,统一数据节奏(和IIC的SCL作用一样)。
-
CS/SS (片选 Chip Select / Slave Select): 点名专用线! 老大想和哪个小弟说话,就单独拉一下(通常是拉低)连到那个小弟的这根线,意思是:“喂!你!就你!注意听/说!”
-
-
怎么工作?
-
老大指人: 老大决定要和哪个小弟A说话,就拉低小弟A的CS线(其他小弟的CS保持高电平,让它们“睡觉”别听)。
-
同时开聊! 老大一边在MOSI线上给小弟A发命令(说),一边在MISO线上听小弟A的回复(听),两边同时进行(全双工)! SCLK线一直在摇,同步两边数据的收发节奏。
-
换人: 老大说完A,把A的CS线拉高(“好了你歇着吧”),再拉低小弟B的CS线,开始和小弟B说话。
-
-
优点:
-
速度飞快! 有独立通道,能同时收发,时钟频率通常比IIC高很多。
-
连接简单直接。 点对点连接,信号干扰小,更稳定。
-
全双工,效率高。 能一边下命令一边收结果。
-
-
缺点:
-
费线! 每增加一个设备,老大就需要多一根CS线来控制它!挂4个设备就需要4根CS线 + 3根公共线(MOSI, MISO, SCLK)= 7根线!布线可能变复杂。
-
没地址。 全靠CS线物理点名。
-
标准稍松散。 具体怎么传数据(比如数据位顺序、时钟极性等)细节上可能略有不同,需要看芯片手册配置。
-
-
适合场景: 对速度要求高,数据量大,设备相对较少的地方。比如:驱动显示屏、读写SD卡、连接高速ADC/DAC(模数/数模转换器)、连接无线模块(WIFI, 蓝牙芯片)。
形象比喻:SPI 就像“老大和小弟开私聊小灶”
-
老大(主设备)是中心。
-
每个小弟(从设备)都有自己专属的“收命令通道”(MOSI)、“汇报通道”(MISO)和“点名铃”(CS)。
-
老大想和谁私聊,就按谁的专属点名铃(拉低CS)。
-
私聊时,老大可以对着小弟的耳朵(MOSI)快速下命令,同时小弟也能对着老大的耳朵(MISO)快速汇报,两边同时说同时听(全双工)! 老大还用另一个铃铛(SCLK)指挥两人说话的节奏。
-
老大一次只和一个小弟私聊(通过CS选择)。
总结:关键区别一目了然
特点 | IIC (电话会议) | SPI (私聊小灶) |
---|---|---|
线数 | 少 (2根:SDA, SCL) | 多 (通常4根:MOSI, MISO, SCLK, CS/SS) |
点名方式 | 喊地址 (广播+应答) | 拉专线 (CS/SS 物理选择) |
数据通道 | 1根线来回用 (半双工) | 2根独立线 (全双工,可同时收/发) |
速度 | 较慢 | 很快 |
设备扩展 | 容易 (地址区分),但总线负载有限 | 费线 (每加一设备多一根CS线) |
复杂度 | 协议稍复杂 (地址、应答) | 硬件连线稍多,协议相对直接 |
典型应用 | 传感器、小存储器、低速控制 | 显示屏、存储器卡、高速ADC、无线模块 |
再浓缩成一句话:
IIC 是“省线省钱开大会,点名叫号慢慢聊”;
SPI 是“多线高速开小灶,指谁跟谁同时唠”!
现在是不是感觉它们就像两个性格迥异的通信小能手啦?下次在电路图上看到它们,想想谁是开大会的,谁是开小灶的,就明白啦!
示例1:IIC通信 - 控制OLED显示屏
#include <Wire.h> // IIC通信必备库(电话会议系统)
#include <Adafruit_SSD1306.h> // OLED显示屏库
// 设置OLED参数(128x64分辨率)
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C // OLED地址(相当于电话号码)
// 创建OLED对象(初始化电话会议系统)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(9600);
// 尝试启动IIC通信(拨打电话)
if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("IIC连接失败!检查接线");
while(1); // 卡死在这里
}
Serial.println("IIC连接成功!开始电话会议...");
// 清屏(挂断之前的通话)
display.clearDisplay();
// 设置字体(选择说话语气)
display.setTextSize(2); // 字体大小
display.setTextColor(WHITE); // 字体颜色
// 设置光标位置(指定说话位置)
display.setCursor(10, 20);
// 发送数据(开始说话)
display.println("Hello IIC!"); // 说"Hello IIC!"
// 显示内容(挂上听筒结束通话)
display.display();
}
void loop() {
// 这里不需要重复操作
}
代码解释(IIC版):
-
包含库:
Wire.h
是Arduino的IIC库(电话会议系统) -
地址设置:
0x3C
是OLED的固定地址(相当于电话号码) -
初始化:
display.begin()
启动IIC通信(拨打电话) -
数据传输:
-
setCursor()
:设置文本位置(指定说话位置) -
println()
:发送数据(说话内容) -
display()
:结束传输(挂断电话)
-
-
特点体现:
-
只用了SDA(A4)和SCL(A5)两根线
-
通过地址
0x3C
选择设备 -
每次只能发送或接收(半双工)
-
Esp32 -> OLED
GND -> GND
5V -> VCC
A4 -> SDA (数据线)
A5 -> SCL (时钟线)
示例2:SPI通信 - 控制MAX7219 LED点阵
#include <SPI.h> // SPI通信必备库(私聊系统)
// 定义SPI引脚(Esp32 固定引脚)
#define DATA_PIN 11 // MOSI(老大发令通道)
#define CLK_PIN 13 // SCK (摇铃铛节奏线)
#define CS_PIN 10 // CS (点名专线)
// LED点阵命令(MAX7219专用指令)
const byte DECODE_MODE = 0x09; // 解码模式
const byte INTENSITY = 0x0A; // 亮度设置
const byte SCAN_LIMIT = 0x0B; // 扫描限制
const byte SHUTDOWN = 0x0C; // 关机模式
const byte DISPLAY_TEST = 0x0F; // 测试模式
void setup() {
// 设置CS引脚为输出(点名专线控制权)
pinMode(CS_PIN, OUTPUT);
// 启动SPI通信(开启私聊系统)
SPI.begin();
// 初始化LED点阵(唤醒小弟)
sendCommand(SHUTDOWN, 1); // 退出关机模式
sendCommand(DISPLAY_TEST, 0); // 关闭测试模式
sendCommand(DECODE_MODE, 0); // 禁用BCD解码
sendCommand(SCAN_LIMIT, 7); // 扫描所有8位数码管
sendCommand(INTENSITY, 8); // 设置中等亮度
}
void loop() {
// 显示"SPI"字母动画
showS(); delay(500);
showP(); delay(500);
showI(); delay(500);
clearDisplay(); delay(300);
}
// ======== 核心函数:发送SPI命令 ========
void sendCommand(byte command, byte data) {
digitalWrite(CS_PIN, LOW); // 拉低CS(点名:小弟注意!)
SPI.transfer(command); // 发送命令(说:要做什么)
SPI.transfer(data); // 发送数据(说:具体怎么做)
digitalWrite(CS_PIN, HIGH); // 拉高CS(结束通话)
}
// ======== 显示字母S ========
void showS() {
// 点亮构成"S"的LED(同时发送行列数据)
for(int col=1; col<=8; col++) {
byte pattern = 0;
if(col==1) pattern = B00111100;
if(col==2) pattern = B01000010;
// ...其他行类似
sendCommand(col, pattern);
}
}
// ======== 清屏函数 ========
void clearDisplay() {
for(int i=1; i<=8; i++) {
sendCommand(i, 0x00); // 所有LED关闭
}
}
代码解释(SPI版):
-
SPI引脚:Esp32的固定SPI引脚(MOSI-11, MISO-12, SCK-13)
-
CS引脚:自定义的片选引脚(这里是D10,点名专线)
-
通信流程:
-
digitalWrite(CS_PIN, LOW)
:选中设备(点名叫小弟) -
SPI.transfer()
:发送数据(同时收/发) -
digitalWrite(CS_PIN, HIGH)
:释放设备(结束通话)
-
-
全双工体现:发送命令同时可接收点阵状态
-
MAX7219控制:
-
通过寄存器命令控制(如SHUTDOWN/INTENSITY)
-
每列单独控制(8x8点阵)
-
Esp32 -> MAX7219模块
GND -> GND
5V -> VCC
D11 (MOSI) -> DIN (数据输入)
D13 (SCK) -> CLK (时钟)
D10 (自定义CS) -> CS (片选)
对比总结:
特性 | IIC示例 (OLED) | SPI示例 (LED点阵) |
---|---|---|
线数 | 2根 (SDA+SCL) | 3根 (MOSI+SCK+CS) |
库文件 | Wire.h | SPI.h |
设备选择 | 通过地址(0x3C) | 通过CS引脚(物理拉低) |
数据传输 | display.print() (单向发送) | SPI.transfer() (双向传输) |
速度 | 较慢(100kHz-400kHz) | 快速(默认4MHz) |
典型应用 | 传感器/小屏幕 | 高速外设(LED/存储器卡) |
建议:
-
先尝试IIC示例(接线简单)
-
用串口监视器观察调试信息
-
成功后尝试SPI示例(注意CS引脚接线)
-
修改显示内容(IIC改文本,SPI改图案)
-
进阶:同时使用IIC和SPI(体验多协议协作)
这两个示例展示了:
-
IIC像「电话会议」:两根线搞定,靠地址呼叫
-
SPI像「私聊专线」:多线高速,CS点名指谁跟谁聊
实际接线时,记得给OLED和MAX7219模块都供电哦!遇到问题先检查地址和CS引脚设置~