超简单的I2C流程讲解附录模拟I2C程序

一、I2C概述

I2C应该是各位i接触最多的单片机协议了,常见的使用场景有AT24Cxx系列的记忆芯片,时钟芯片等,主打的就是操作简单
模拟I2C是最常用的I2C操作方式,下面简单描述下I2C的流程和代码实现。

1.I2C简介

I2C有两条线在控制器和从机之间通信:SDA和SCL。总线空闲时候SDA和SCL都是高电平,标准模式下速度为100Kb/s,快速模式下为400Kb/s。
在这里插入图片描述

2.I2C起始位

在SCL为高电平时候,SDA切换为低电平(下出现降沿),表明I2C通信开始了。
在这里插入图片描述

3.I2C停止位

在SCL高电平的时候,SDA出现上升沿(和空闲时候状态一样)。
在这里插入图片描述

4.数据传输

I2C数据的传输要保证在SCL高电平时候,SDA上的电平要稳定,因此SDA的电平变化只允许发生在SCL为低电平时候。
**加粗样式**

5.应答信号

I2C的应答信号由从机主动产生。当I2C的主机发送完8位数据后会将主机置为输入状态,等待从机应答。从机通过将SDA拉低表示应答成功,否则应答失败。

6.I2C写时序(单字节)

在这里插入图片描述

1)、起始信号。
2)、发送 I2C 设备地址,每个 I2C 器件都有一个设备地址,通过发送具体的设备地址来决
定访问哪个 I2C 器件。这是一个 8 位的数据,其中高 7 位是设备地址,最后 1 位是读写位,为
1 的话表示这是一个读操作,为 0 的话表示这是一个写操作。
3)、 I2C 器件地址后面跟着一个读写位,为 0 表示写操作,为 1 表示读操作。
4)、从机发送的 ACK 应答信号。
5)、重新发送开始信号。
6)、发送要写写入数据的寄存器地址。
7)、从机发送的 ACK 应答信号。
8)、发送要写入寄存器的数据。
9)、从机发送的 ACK 应答信号。
10)、停止信号。

7.I2C读时序(单字节)

在这里插入图片描述

I2C 单字节读时序比写时序要复杂一点,读时序分为 4 大步,第一步是发送设备地址,第
二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是 I2C 从器件输出要
读取的寄存器值,我们具体来看一下这步。
1)、主机发送起始信号。
2)、主机发送要读取的 I2C 从设备地址。地址是1-7位,第8位是读写控制位
3)、读写控制位,因为是向 I2C 从设备发送数据,因此是写信号。
4)、从机发送的 ACK 应答信号。
5)、重新发送 START 信号
6)、主机发送要读取的寄存器地址。
7)、从机发送的 ACK 应答信号。
8)、重新发送 START 信号。
9)、重新发送要读取的 I2C 从设备地址。
10)、读写控制位,这里是读信号,表示接下来是从 I2C 从设备里面读取数据。
11)、从机发送的 ACK 应答信号。
12)、从 I2C 器件里面读取到的数据。
13)、主机发出 NO ACK 信号,表示读取完成,不需要从机再发送 ACK 信号了。
14)、主机发出 STOP 信号,停止 I2C 通信。

8.I2C仲裁

在多主的通信系统中。总线上有多个节点,它们都有自己的寻址地址,可以作为从节点被别的节点访问,同时它们都可以作为主节点向其他的节点发送控制字节和传送数据。如果有两个或两个以上的节点都向总线上发送启动信号并开始传送数据,这样就形成了冲突。要解决这种冲突,就要进行仲裁的判决,这就是I 2C总线上的仲裁。

二、 单字节读写代码

提示:代码真实可以运行,从本人项目上摘抄下来,使用前根据自己的芯片设置下宏定义

模拟I2C程序
//预先定义状态,这里是通过FPGA接口模拟的,实际单片机或者ARM通过IO口控制
#define SET_SDA_OUT(bus) FpgaWrite(EEPROM_IIC_SDA_SEL_REG, 0)  //设置SDA为输出
#define SET_SDA_IN(bus) FpgaWrite(EEPROM_IIC_SDA_SEL_REG, 1)   //设置SDA为输入
#define SET_SDA_HIGH(bus) FpgaWrite(EEPROM_IIC_SDA_REG, 1)     //设置SDA输出高电平
#define SET_SDA_LOW(bus) FpgaWrite(EEPROM_IIC_SDA_REG, 0)      //设置SDA输出低电平
#define GET_SDA_VAL(bus) FpgaRead(EEPROM_IIC_SDA_REG)          //获取SDA的值

#define SET_SCL_OUT(bus) FpgaWrite(EEPROM_IIC_SCL_REG 0)       //设置SCL为输出
#define SET_SCL_HIGH(bus) FpgaWrite(EEPROM_IIC_SCL_REG, 1)     //设置SCL输出高电平
#define SET_SCL_LOW(bus) FpgaWrite(EEPROM_IIC_SCL_REG, 0)      //设置SCL输出低电平
 
#define I2C_DELAY 4  //延迟时间
 
// i2c启动
int i2c_start(uint8_t iic_bus) 
{
//START:when CLK is high, SDA changes form high to low 


    SET_SDA_OUT(iic_bus);
    //SET_SCL_OUT(iic_bus);


    SET_SDA_HIGH(iic_bus);
    usleep(I2C_DELAY); //usleep是linux下的时间延迟,去执行其他进程了
    SET_SCL_HIGH(iic_bus);
    usleep(I2C_DELAY);


    SET_SDA_LOW(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_LOW(iic_bus);
    usleep(I2C_DELAY);
    return 0;
}

// i2c停止
int i2c_stop(uint8_t iic_bus)
{
//STOP:when CLK is high SDA changes form low to high


    SET_SDA_OUT(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_LOW(iic_bus);
    usleep(I2C_DELAY);


    SET_SDA_LOW(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_HIGH(iic_bus);
    usleep(I2C_DELAY);


    SET_SDA_HIGH(iic_bus);
    usleep(I2C_DELAY);
    return 0;
}

// 等待ack
static int i2c_wait_ack(uint8_t iic_bus)
{
    int ack_times = 0;
    int ret = 1;
    unsigned char buf = 0;


    SET_SDA_IN(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_HIGH(iic_bus);
    usleep(I2C_DELAY);
    ack_times = 0;
    buf = GET_SDA_VAL(iic_bus);
    usleep(I2C_DELAY);
    while (buf) {
        buf = GET_SDA_VAL(iic_bus);
        usleep(I2C_DELAY);
        ack_times++;
        if (ack_times == 2) {
            ret = 0;
            //printf("i2c ack error, no ack \n");
            break;
        }
    }
    SET_SCL_LOW(iic_bus);
    usleep(I2C_DELAY);
    return ret;
}

// 发送ACK
static int i2c_send_ack(uint8_t iic_bus)
{
    SET_SDA_OUT(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_LOW(iic_bus);
    usleep(I2C_DELAY);


    SET_SDA_LOW(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_HIGH(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_LOW(iic_bus);
    usleep(I2C_DELAY);


    return 0;
}


// 发送noack
static int i2c_send_noack(uint8_t iic_bus)
{
    SET_SDA_OUT(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_LOW(iic_bus);
    usleep(I2C_DELAY);


    SET_SDA_HIGH(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_HIGH(iic_bus);
    usleep(I2C_DELAY);


    SET_SCL_LOW(iic_bus);
    usleep(I2C_DELAY);
    return 0;
}

// 写一个字节
int i2c_write_byte(uint8_t iic_bus, char data)
{
    char i = 0;
    SET_SDA_OUT(iic_bus);
    usleep(I2C_DELAY);


    for (i = 0; i < 8; i++) 
    {
        if (data & 0x80)
        {
            SET_SDA_HIGH(iic_bus);
        }
        else
        {
            SET_SDA_LOW(iic_bus);
        }
        usleep(I2C_DELAY);


        SET_SCL_HIGH(iic_bus);
        usleep(I2C_DELAY);


        SET_SCL_LOW(iic_bus);
        usleep(I2C_DELAY);


        data <<= 0x1;
    }
    return 0;
}

// 读一个字节
int i2c_read_byte(uint8_t iic_bus, char *data)
{
    char ret = 0;
    char i = 0;
    char buf = 0;


    SET_SDA_IN(iic_bus);
    usleep(I2C_DELAY);


    for (i = 0; i < 8; i++) 
    {
        SET_SCL_HIGH(iic_bus);
        usleep(I2C_DELAY);
        ret <<= 1;
        //usleep(10000);
        buf = GET_SDA_VAL(iic_bus);
        if (buf)
        {
            ret |= 0x01;
        }


        SET_SCL_LOW(iic_bus);
        usleep(I2C_DELAY);
    }
    *data = ret;
    return 0;
}

三、小结

模拟I2C会比硬件自带的I2C模块要稳定的多,常见的STM32自带的I2C偶尔会出现失效的问题

  • 22
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c-ncap2024附录l是指中国新车评价计划(China-New Car Assessment Program)的2024版附录l。中国新车评价计划是由中国汽车技术研究中心和交通运输部联合发起的,目的是对汽车的安全性能进行评估和公开,提升消费者对汽车安全的认知和关注度。 附录l是指新车的被动安全性能评价标准。被动安全性能是指在车辆发生事故时,保护车内乘员免受伤害的能力。附录l主要包括以下几个方面的评价要点: 1.碰撞试验:该项试验是模拟车辆在前方碰撞事故中的情况。通过对车头、车身和车顶的刚度及变形程度进行评估,来判断车辆在碰撞时乘员是否会受到严重伤害。 2.侧面碰撞试验:该项试验是模拟车辆在侧面碰撞事故中的情况。通过评估车辆的门、车体结构、侧气囊等的保护性能,来判断车辆在侧面碰撞时乘员是否会受到严重伤害。 3.罗尔试验:该项试验是模拟车辆翻滚事故中的情况。通过评估车辆的抗翻滚稳定性和车顶的抗压能力,来判断车辆在翻滚事故中乘员是否能得到有效的保护。 4.附加项目:此外,附录l还包括了车辆的控制系统、灯光设备、车窗防护、儿童乘员保护等内容的评价要求。 c-ncap2024附录l的发布,意味着在2024年之后,新车的被动安全性能标准将更加严格,对车企来说是一项新的挑战。消费者将有更多的依据来选择更安全的汽车,提高整个汽车产业的安全水平。同时,车企也需要加大研发力度,不断提升车辆的安全性能,以满足市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值