将I2C总线与51单片机结合使用,通常需要通过软件模拟I2C协议(因传统51单片机无硬件I2C模块),实现与外部I2C设备(如EEPROM、传感器等)的通信。以下是具体实现方法和示例:
### **一、硬件连接**
1. **电路设计**:
- **SDA**和**SCL**引脚:连接到51单片机的任意两个IO口(如P2.0和P2.1)。
- **上拉电阻**:在SDA和SCL线上各接一个4.7kΩ电阻到VCC,确保总线空闲时为高电平。
- **I2C设备**:例如AT24C02(EEPROM)、MPU6050(加速度计)等。
```plaintext
51单片机 I2C设备
P2.0 (SDA) ------> SDA
P2.1 (SCL) ------> SCL
VCC ------> VCC
GND ------> GND
### **二、软件模拟I2C协议**
#### **1. GPIO配置**
将SDA和SCL引脚配置为**准双向模式**(51单片机默认模式),支持输出和输入功能:
```c
sbit SDA = P2^0; // 定义SDA引脚
sbit SCL = P2^1; // 定义SCL引脚
```
#### **2. 基本时序函数**
需实现以下关键时序函数:
**起始条件**:SCL高电平时,SDA从高→低。
**停止条件**:SCL高电平时,SDA从低→高。
**发送ACK/NACK**:接收方在第9个时钟周期拉低SDA(ACK)或保持高电平(NACK)。
**读写字节**:逐位发送或接收8位数据。
**示例代码**:
```c
// 起始条件
void I2C_Start() {
SDA = 1;
SCL = 1;
Delay_us(5); // 延时保证稳定性
SDA = 0;
Delay_us(5);
SCL = 0;
}
// 停止条件
void I2C_Stop() {
SDA = 0;
SCL = 1;
Delay_us(5);
SDA = 1;
Delay_us(5);
}
// 发送ACK
void I2C_Ack() {
SDA = 0; // ACK:拉低SDA
SCL = 1;
Delay_us(2);
SCL = 0;
SDA = 1; // 释放SDA
}
// 发送NACK
void I2C_Nack() {
SDA = 1; // NACK:保持SDA高电平
SCL = 1;
Delay_us(2);
SCL = 0;
}
// 发送一个字节(MSB优先)
void I2C_SendByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
SDA = (dat & 0x80) ? 1 : 0; // 发送最高位
dat <<= 1;
SCL = 1;
Delay_us(2);
SCL = 0;
Delay_us(2);
}
// 等待从机ACK
SDA = 1; // 释放SDA,准备接收ACK
SCL = 1;
Delay_us(2);
if (SDA == 0) { /* ACK成功 */ }
SCL = 0;
}
// 接收一个字节
unsigned char I2C_ReadByte() {
unsigned char i, dat = 0;
SDA = 1; // 释放SDA,切换为输入模式
for (i = 0; i < 8; i++) {
SCL = 1;
Delay_us(2);
dat <<= 1;
dat |= SDA; // 读取当前位
SCL = 0;
Delay_us(2);
}
return dat;
}
### **三、实际应用示例:读写AT24C02 EEPROM**
#### **1. 写入数据到EEPROM**
```c
void EEPROM_Write(unsigned char addr, unsigned char dat) {
I2C_Start();
I2C_SendByte(0xA0); // 发送器件地址(写模式)
I2C_SendByte(addr); // 发送存储地址
I2C_SendByte(dat); // 发送数据
I2C_Stop();
Delay_ms(10); // 等待EEPROM写入完成
}
#### **2. 从EEPROM读取数据**
```c
unsigned char EEPROM_Read(unsigned char addr) {
unsigned char dat;
I2C_Start();
I2C_SendByte(0xA0); // 发送器件地址(写模式)
I2C_SendByte(addr); // 发送存储地址
I2C_Start(); // 重启总线
I2C_SendByte(0xA1); // 发送器件地址(读模式)
dat = I2C_ReadByte(); // 读取数据
I2C_Nack(); // 发送NACK结束读取
I2C_Stop();
return dat;
}
### **四、关键注意事项**
1. **时序精度**:
51单片机时钟频率较低(如12MHz),需调整延时函数以满足I2C速率(如100kHz标准模式)。
延时函数可通过循环空操作或定时器实现。
2. **总线冲突处理**:
多主模式下需实现仲裁机制,但51单片机资源有限,通常设计为单主模式。
3. **抗干扰设计**:
总线长度尽量短(<1米),避免信号反射。
若环境干扰大,可降低速率或使用屏蔽线。
4. **上拉电阻选择**:
根据总线上设备数量调整阻值,设备越多,阻值应越小(如3.3kΩ)。
### **五、调试技巧**
1. **逻辑分析仪**:观察SDA和SCL波形,检查起始/停止条件、ACK/NACK是否正常。
2. **串口打印**:在读写过程中通过串口输出调试信息(如发送的地址、数据)。
3. **简化代码**:先实现单字节读写,再扩展为多字节操作。