51 单片机是不支持硬件 I2C 的,所以我们要驱动 I2C 设备,只能通过代码实现软件 I2C,简单来说就是通过 IO 口模拟 I2C 的时序。
代码实现:
#include <reg52.h>
sbit sda = P0 ^ 1;
sbit scl = P0 ^ 2;
/* 50kbit/s = 1s 50k周期 = 1周期 = 1/50ks = 20us
10us发送1bit,1ms=100bit 1s = 100kbit
*/
void delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (-- i);
}
void i2c_start()
{
scl = 1;
sda = 1;
delay10us(); //起始信号建立时间,高电平时间持续大于4.7us
sda = 0; //SDA拉低,下降沿
delay10us(); //起始信号保持时间
}
void i2c_stop()
{
scl = 1;
sda = 0;
delay10us(); //停止信号建立时间,SDA高电平时间持续大于4.7us
sda = 1; //SDA拉高,上升沿
delay10us(); //总线空闲时间保持
}
void i2c_write_bit(unsigned char databit)
{
scl = 0; //开始发送前,设置scl为0
if (databit == 1)
{
//设置需要发送的数据
sda = 1;
}
else
{
sda = 0;
}
delay10us();
scl = 1;
delay10us();
}
void i2c_write_byte(unsigned char datasend)
{
/* 0x20 = 0b0010 0000
& 0b1000 0000 --> 检测第一位
0x20 = 0b0010 0000
<< 1
0b0100 0000 --> 检测下一位
*/
int i = 0;
for (i = 0; i < 8; i ++)
{
if (datasend & 0x80) // 判断最高位是否为 1
{
i2c_write_bit(1); // 输出高电平
}
else
{
i2c_write_bit(0); // 输出低电平
}
datasend <<= 1; //左移一位,for循环8次,直到数据发送完
}
}
unsigned char i2c_read_bit()
{
unsigned char databit = 0;
scl = 0; //每bit接收前,先设置scl为0
delay10us(); //scl为低电平的时间
scl = 1; //拉高scl开始接收数据
delay10us();//等待数据稳定的时间
if (sda)
{
databit = 1;
}
else
{
databit = 0;
}
return databit;
}
unsigned char i2c_read_byte()
{
unsigned char value = 0;
unsigned char i = 0;
sda = 1; // 处于空闲状态
for (i = 0; i < 8; i ++)
{
if (i2c_read_bit() == 1)
{
value |= 0x01;
}
else
{
value |= 0x00;
}
if (i < 7) // 防止数据丢失
/* 每次向左移动一位以后,如果sda=1的时候就把最后一位置1,
sda=0的时候则不用置,因为向左移动就有一个0了 */
value <<= 1;
}
scl = 0; //接收完成后,设置scl为0
delay10us();
return value;
}
// 应答信号
void i2c_ack()
{
scl = 0;
sda = 0; //SDA拉低,发出应答信号
delay10us();;
scl = 1;
delay10us();
scl = 0;
}
// 等待应答信号
void i2c_wait_ack()
{
unsigned char time = 0;
scl = 0;
sda = 1; //sda为高电平释放总线,然后检测应答信号是否被拉低
delay10us();
scl = 1;
delay10us();
while (sda) //sda为高电平,就表示没有检查到ACK,
{
delay10us();
time ++;
if (time > 100) break;
}
scl = 0;
delay10us();
}
void i2c_nack()
{
scl = 0;
sda = 1; //SDA拉高,发出非应答信号
delay10us();
scl = 1;
delay10us();
scl = 0;
}
void main()
{
i2c_start(); // 开始
i2c_write_byte(0x55); // 发送数据
i2c_wait_ack(); // 等待应答
i2c_stop(); // 结束
while (1)
{
}
}