I2C 通信过程代码实现

笔记 · 2024-08-25 · 155 人浏览
I2C 通信过程代码实现

  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)
    {

    }
}

学习链接

C IIC
Theme Jasmine by Kent Liao

本网站由 又拍云 提供CDN加速/云存储服务

鄂ICP备2023005457号    鄂公网安备 42011302000815号