IIC通信协议详解与STM32实战指南
IIC通信协议详解与STM32实战指南
引言
IIC(Inter-Integrated Circuit)是Philips公司开发的串行通信协议,广泛应用于传感器、EEPROM、RTC等低速外设的连接。本文深入解析IIC协议原理,并提供基于STM32的GPIO模拟实现方案,包含完整的代码解析和实战应用示例。
一、IIC协议核心原理
1. 物理层特性
特性 | 参数说明 |
---|---|
总线构成 | SCL(时钟线)+ SDA(数据线) |
传输模式 | 半双工 |
最大设备数 | 112(7位地址) |
传输速率 | 标准模式100kbps,快速模式400kbps |
电平特性 | 开漏输出+上拉电阻(通常4.7KΩ) |
核心优势:
- 仅需两根线即可实现多设备通信
- 内置冲突检测和仲裁机制
- 支持热插拔(需设备具备总线释放功能)
2. 协议层详解
数据帧结构
[Start] + [Device Address + R/W] + [ACK] + [Data] + [ACK] + ... + [Stop]
└─7位地址─┘ └─0:写 1:读─┘
关键时序节点
- 起始条件:SCL高电平时,SDA从高→低跳变
- 停止条件:SCL高电平时,SDA从低→高跳变
- 数据有效性:SCL高电平期间必须保持SDA稳定
- 应答机制:每字节传输后接收方必须拉低SDA
二、IIC通信代码实现(详细注释版)
1. GPIO模拟IIC初始化
// IIC引脚定义(以STM32F103 PA6-SCL, PA7-SDA为例)
#define IIC_SCL_PIN GPIO_Pin_6
#define IIC_SDA_PIN GPIO_Pin_7
#define IIC_PORT GPIOA
void IIC_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
/* 开启GPIO时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* SCL和SDA配置为开漏输出模式 */
GPIO_InitStruct.GPIO_Pin = IIC_SCL_PIN | IIC_SDA_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
/* 为什么用开漏模式?
允许总线"线与"特性,配合上拉电阻实现电平控制 */
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_PORT, &GPIO_InitStruct);
/* 拉高总线(空闲状态) */
IIC_SCL_H();
IIC_SDA_H();
}
2. 基础信号函数解析
/* 产生IIC起始信号 */
void IIC_Start(void) {
SDA_OUT(); // 设置SDA为输出模式
IIC_SDA_H();
IIC_SCL_H();
delay_us(5); // 保持时间>4.7us
IIC_SDA_L(); // 下降沿触发起始条件
delay_us(5);
IIC_SCL_L(); // 钳住总线,准备发送数据
}
/* 产生IIC停止信号 */
void IIC_Stop(void) {
SDA_OUT();
IIC_SDA_L();
IIC_SCL_H(); // 停止条件:SCL高时SDA上升
delay_us(5);
IIC_SDA_H();
delay_us(5);
}
/* 发送一个字节(MSB First)*/
uint8_t IIC_SendByte(uint8_t byte) {
uint8_t ack;
SDA_OUT();
for(int i=0; i<8; i++) {
IIC_SCL_L();
delay_us(2);
(byte & 0x80) ? IIC_SDA_H() : IIC_SDA_L();
byte <<= 1;
delay_us(3);
IIC_SCL_H(); // 上升沿锁存数据
delay_us(5);
}
/* 等待从机应答 */
IIC_SCL_L();
SDA_IN(); // 切换SDA为输入模式
delay_us(2);
IIC_SCL_H();
ack = IIC_READ_SDA(); // 读取ACK信号(0-应答,1-无应答)
delay_us(5);
IIC_SCL_L();
return ack;
}
/* 接收一个字节(带应答控制) */
uint8_t IIC_RecvByte(uint8_t ack) {
uint8_t data = 0;
SDA_IN();
for(int i=0; i<8; i++) {
IIC_SCL_L();
delay_us(5);
IIC_SCL_H(); // 从机在SCL低电平时更新数据
data <<= 1;
data |= IIC_READ_SDA();
delay_us(5);
}
/* 发送应答信号 */
SDA_OUT();
ack ? IIC_SDA_L() : IIC_SDA_H(); // 0-应答,1-非应答
delay_us(2);
IIC_SCL_H();
delay_us(5);
IIC_SCL_L();
return data;
}
3. EEPROM读写示例(AT24C02)
/* 写单字节到EEPROM */
void EEPROM_Write(uint8_t addr, uint8_t data) {
IIC_Start();
IIC_SendByte(0xA0); // 设备地址 + 写操作(0)
IIC_SendByte(addr); // 内存地址
IIC_SendByte(data); // 写入数据
IIC_Stop();
delay_ms(10); // 等待EEPROM内部写入完成
}
/* 从EEPROM读取单字节 */
uint8_t EEPROM_Read(uint8_t addr) {
uint8_t data;
IIC_Start();
IIC_SendByte(0xA0); // 设备地址 + 写操作
IIC_SendByte(addr); // 设置读地址
IIC_Start(); // 重复起始条件
IIC_SendByte(0xA1); // 设备地址 + 读操作(1)
data = IIC_RecvByte(0); // 读取数据(发送非应答)
IIC_Stop();
return data;
}
关键代码原理说明
1. IIC总线特性
- 开漏输出:必须外接上拉电阻(通常4.7KΩ),避免总线电平冲突
- 地址格式:7位设备地址 + 1位读写方向位(0-写,1-读)
2. 时序控制要点
- 起始条件:SCL高电平时,SDA从高→低跳变
- 停止条件:SCL高电平时,SDA从低→高跳变
- 数据有效性:SCL高电平期间,SDA必须保持稳定
- 应答机制:每字节传输后接收方必须拉低SDA
3. EEPROM操作流程
-
写操作:
- 发送设备地址(写模式)
- 发送内存地址
- 发送数据
- 等待内部编程完成(典型5ms)
-
读操作:
- 发送设备地址(写模式)→设置内存地址
- 重复起始条件
- 发送设备地址(读模式)→读取数据
常见疑问解答
Q1: 为什么设备地址是0xA0?
A1: AT24C02的7位地址为1010000(A0-A2接地),左移后加写操作位(0)得到0xA0。
Q2: 如何调整通信速率?
A2: 修改延时函数参数,标准模式(100kbps)要求SCL高低电平各≥4.7us,快速模式(400kbps)≥0.6us。
Q3: 何时需要加上拉电阻?
A3: 当总线电容较大或设备较多时,建议在SCL和SDA线上加4.7KΩ上拉电阻至3.3V/5V。
Q4: 如何处理多设备冲突?
A4: 每个IIC设备有唯一地址,总线仲裁机制会自动解决冲突,软件需检测ACK信号判断是否成功。
三、进阶开发技巧
1. 错误处理机制
#define IIC_TIMEOUT 1000 // 超时阈值(单位:us)
uint8_t IIC_WaitAck(void) {
uint32_t time = 0;
SDA_IN();
IIC_SCL_H();
while(GPIO_ReadInputDataBit(IIC_PORT, IIC_SDA_PIN)) {
if(++time > IIC_TIMEOUT) {
IIC_Stop();
return 1; // 超时错误
}
delay_us(1);
}
IIC_SCL_L();
return 0;
}
2. 总线扫描工具
void IIC_Scanner(void) {
printf("Scanning IIC devices...\n");
for(uint8_t addr=0x08; addr<0x78; addr++) {
IIC_Start();
uint8_t ack = IIC_SendByte(addr << 1);
IIC_Stop();
if(!ack) {
printf("Device found at 0x%02X\n", addr);
}
delay_ms(10);
}
}
四、常见问题排查指南
1. 典型故障现象
- 总线锁死:检查SCL/SDA是否被意外拉低,尝试发送虚假时钟
- 地址无响应:确认设备地址是否正确(含地址引脚电平)
- 数据错位:检查时序延时是否符合设备要求
2. 调试建议
- 用示波器捕获总线波形,验证时序参数
- 在起始信号后添加LED指示,确认通信触发
- 逐步提升速率测试(从10kHz开始)
- 检查上拉电阻值(计算公式:Rp < (Vdd - Vol)/Iol)
结语
IIC协议凭借其简洁的硬件设计和灵活的多设备管理能力,在嵌入式领域占据重要地位。通过GPIO模拟实现,开发者可以深入理解协议细节,但在量产项目中建议使用硬件IIC外设以获得更好的稳定性。实际开发中需特别注意总线负载能力和时序参数的匹配。
延伸学习建议:
- 研究IIC总线仲裁机制
- 探索DMA在高速模式下的应用
- 了解SMBus协议扩展特性