概念

        IIC,或者I2C,年“I方C”,集成电路总线,是同步串行半双工通信总线方式。该总线允许同时连接多个设备(芯片),每个芯片在总线上有特定的地址

        SDA:数据线

        SCL:时钟线

        两个信号线都是双向的,在硬件电路设计上,是必须要接上拉电阻,因为这是一种双开漏的通信模式,引脚只能主动拉低电平,无法主动输出高电平,需要上拉电阻来提供稳定的高电平,同时实现多设备 “线与” 通信逻辑

开漏和推挽

        GPIO通过两个MOS管来控制引脚的高低电平

        开漏模式:当GPIO处于该模式下,只允许出现2,3情景,高阻态下,相当于断路

        推挽模式:当GPIO处于该模式下,只允许出现1,2情景

时序

        总线处于空闲时,不管是SCL,还是SDA线,均处于高电平状态,所以总线上的设备都处于开漏模式中的高阻态。当某个设备想要通信时,就先在总线上发送start起始信号,把SDA拉低,此时会在SCL上产生时钟信号,是高低电平交错的脉冲信号

        1,start信号:在总线空闲时(高电平),主机在时钟线SCL为高电平时,在数据线SDA上产生一个下降沿,称之为起始信号

        2,发送数据时

                1)遵循高位优先原则MSB

                2)在SCL为低电平时,只允许发送方改变SDA数据线(高1低0),接收方不得采样SDA

                3)在SCL为高电平时,只允许接收方采样SDA数据线,此时发送方需保证数据线稳定

        3,应答位(9clock第九个bit)

                1)ACK:应答,类似真人回复

                2)NACK:非应答,类似自动回复

        4,stop型号:在主机不需要再通信时,主机在SCL为高电平时,在数据线SDA上产生一个上升沿,称之为停止信号

从机地址

        为了确定接收的从机,规定发送的第一个字节必须具有特定含义,前七个bit位为从机地址,第八个为数据流向位,指的是通信的过程的收发方,0为主机发送从机接收,1则相反

线与特性

chipA chipB 实际电平
1 0 0
0 1 0
1 1 1
0 0 0

        当一个芯片输出高电平,真实的引脚电平由外接的芯片、上下拉电路决定

PSR引脚状态寄存器

所需寄存器

写函数

void i2c_write(I2C_Type *base, unsigned char dev_addr, unsigned short reg_addr, int reg_len, unsigned char *data, unsigned int len)
{
    int stat = 0;
    //0. 清除IAL、IIF, 
    base->I2SR &= ~(I2SR_IAL | I2SR_IIF);
    //1. 设置为发送模式
    base->I2CR |= I2CR_MTX;
    //2.start信号
    base->I2CR |= I2CR_MSTA;

    //3.发送从机地址及数据流向位
    base->I2DR = ((dev_addr << 1) | 0);
    stat = wait_i2c_iif(base);
    if (stat != 0) goto stop;

    //4.发送从机寄存器地址
    int i = reg_len - 1;
    for (; i >=0; i--)
    {
        base->I2DR = (reg_addr >> (i * 8)) & 0xFF;
        stat = wait_i2c_iif(base);
        if (stat != 0) goto stop;  
    }

    for (i = 0; i < len; i++)
    {
        base->I2DR = data[i];
        stat = wait_i2c_iif(base);
#if AT24C02
        if (((reg_addr + (i + 1)) % 8 == 0) && ((i + 1) < len))
        {
            //发送stop信号
            base->I2CR &= ~I2CR_MSTA; 
            delay_ms(5);

            //2.start信号
            base->I2CR |= I2CR_MSTA;

            //3.发送从机地址及数据流向位
            base->I2DR = ((dev_addr << 1) | 0);
            stat = wait_i2c_iif(base);
            if (stat != 0) goto stop;

            //4.发送从机寄存器地址
            int j = reg_len - 1;
            for (; j >= 0; j--)
            {
                base->I2DR = ((reg_addr + (i + 1)) >> (j * 8)) & 0xFF;
                stat = wait_i2c_iif(base);
                if (stat != 0) goto stop;  
            }
        }
#endif
        if (stat != 0) goto stop;
    }

stop:
    //发送stop信号
    base->I2CR &= ~I2CR_MSTA;
    while ((base->I2SR & I2SR_IBB) != 0)
    {
        //设计超时机制
    } 
#if AT24C02
    delay_ms(5);
#endif
}

读函数

void i2c_read(I2C_Type *base, unsigned char dev_addr,unsigned short reg_addr, int reg_len, unsigned char *data, unsigned int len)
{
    int stat = 0;
    //0. 清除IAL、IIF, 
    base->I2SR &= ~(I2SR_IAL | I2SR_IIF);
    //1. 设置为发送模式
    base->I2CR |= I2CR_MTX;
    //2.start信号
    base->I2CR |= I2CR_MSTA;

    //3.发送从机地址及数据流向位
    base->I2DR = ((dev_addr << 1) | 0);
    stat = wait_i2c_iif(base);
    if (stat != 0) goto stop;

    //4.发送从机寄存器地址
    int i = reg_len - 1;
    for (; i >= 0; i--)
    {
        base->I2DR = (reg_addr >> (i * 8)) & 0xFF;
        stat = wait_i2c_iif(base);
        if (stat != 0) goto stop;  
    }

    //5.重发start
    base->I2CR |= I2CR_RSTA;

    //6.发送从机地址及数据流向位
    base->I2DR = ((dev_addr << 1) | 1);
    stat = wait_i2c_iif(base);
    if (stat != 0) goto stop;

    //7. 设置为接收模式
    base->I2CR &= ~I2CR_MTX;
    //8. 设置应答类型
    if (len > 1)
    {
        base->I2CR &= ~I2CR_TXAK;       //ACK
    } else {
        base->I2CR |= I2CR_TXAK;       //NACK
    }
    // 9.触发一次伪读;
    data[0] = base->I2DR;

    for (i = 0; i < len; i++)
    {
        stat = wait_i2c_iif(base);
        if (i == len - 2){
            base->I2CR |= I2CR_TXAK;       //倒数第二个字节读数据前,设置应答类型为NACK,开始触发接收 倒数第一个字节。
        }
        if (i == len - 1){
           base->I2CR |= I2CR_MTX;          //倒数第一个字节读数据前 修改为发送模式(避免触发新的读取操作)
        }
        data[i] = base->I2DR;
        if (stat != 0) goto stop;
    }

stop:
    //发送stop信号
    base->I2CR &= ~I2CR_MSTA;
    while ((base->I2SR & I2SR_IBB) != 0)
    {
        //设计超时机制
    } 
}

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐