> For the complete documentation index, see [llms.txt](https://oceanaparts.gitbook.io/halnote/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://oceanaparts.gitbook.io/halnote/i2c-tong-xun.md).

# I2C通讯

### 原理

SCL,SDA挂载上拉电阻，保持空闲状态，有最大设备数限制，有传输速率分别

![image-20230408171531425](https://s2.loli.net/2023/04/08/KIn8VUsoMRCNQtk.png)

#### 软件层面

空闲状态:两个都处于高电平

应答信号：主机从机都可以发送，告诉发送者我收到数据了

停止信号：使通讯状态保持空闲

有效性：时钟线处于高电平期间，数据才有效

#### 信号分析

起始信号：时钟线处于高电平期间，数据线SDA从高电平往低电平跳变

停止信号：时钟线处于高电平期间，数据线从低电平往高电平跳变

应答信号：在发送8字节数据后，时钟线SCL处于第九个高电平期间，SDA检测到高电平就是NACK（默认上拉电阻下就是高定平），SDA检测到低电平就是ACK（从机若应答，发送低电平，拉低SDA电平）

应答完再停止

#### 代码

起始信号

```
void iic_start(void)
{
    IIC_SDA(1); 	//把sda拉高，后面才能拉低发出起始信号
    IIC_SCL(1);		//先把scl拉高，确保信号有效
    iic_delay();	//延迟时间与设备有关
    IIC_SDA(0);     /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    iic_delay();
    IIC_SCL(0);     /* 钳住I2C总线，准备发送或接收数据，在发送时再拉高 */
    iic_delay();
}
```

停止信号

```c
void iic_stop(void)
{
    IIC_SDA(0);     /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    iic_delay();
    IIC_SCL(1);		//SCL拉高确保数据有效性
    iic_delay();
    IIC_SDA(1);     /*SDA从低到高，实现结束信号的发送 发送I2C总线结束信号 */
    iic_delay();
}
```

检测应答信号

```c
uint8_t iic_wait_ack(void)
{
    uint8_t waittime = 0;
    uint8_t rack = 0;

    IIC_SDA(1);     /* 主机释放SDA线(此时外部器件可以拉低SDA线) */
    iic_delay();
    IIC_SCL(1);     /* SCL=1, 此时从机可以返回ACK */
    iic_delay();

    while (IIC_READ_SDA)    /* 高电平状态，等待应答，若变为低电平，完成应答 */
    {
        waittime++;

        if (waittime > 250) //超时
        {
            iic_stop();
            rack = 1;	//标志位置1，表示失败
            break;
        }
    }

    IIC_SCL(0);     /* SCL=0, 结束ACK检查 */
    iic_delay();
    return rack;	//返回标志位，若能直接从while出来，而不是超时break，则为0，代表应答成功
}
```

发送应答信号

```c
void iic_ack(void)
{
    IIC_SDA(0);     /* SCL 0 -> 1  时 SDA = 0,表示应答 */
    iic_delay();
    IIC_SCL(1);     /* 拉高数据有效，  产生一个时钟 */
    iic_delay();
    IIC_SCL(0);
    iic_delay();
    IIC_SDA(1);     /* 主机释放SDA线 */
    iic_delay();
}
```

发送非应答

```c
void iic_nack(void)
{
    IIC_SDA(1);     /* SCL 0 -> 1  时 SDA = 1,表示不应答 */
    iic_delay();
    IIC_SCL(1);     /* 产生一个时钟 */
    iic_delay();
    IIC_SCL(0);
    iic_delay();
}
```

发送一字节数据

```
void iic_send_byte(uint8_t data)
{
    uint8_t t;
    
    for (t = 0; t < 8; t++)
    {
        IIC_SDA((data & 0x80) >> 7);    /* 高位先发送 */
        iic_delay();
        IIC_SCL(1);		//SCL高，数据有效
        iic_delay();
        IIC_SCL(0);
        data <<= 1;     /* 左移1位,把此次发送的最高位丢掉，用于下一次发送 */
    }
    IIC_SDA(1);         /* 发送完成, 主机释放SDA线，恢复默认的 */
}
```

数据接收

```c
uint8_t iic_read_byte(uint8_t ack)
{
    uint8_t i, receive = 0;

    for (i = 0; i < 8; i++ )    /* 接收1个字节数据 */
    {
        receive <<= 1;  /* 高位先输出,所以先收到的数据位要左移 */
        IIC_SCL(1);
        iic_delay();

        if (IIC_READ_SDA) //读取SDA数据，为1就让receive+1
        {
            receive++;
        }
        
        IIC_SCL(0);//释放SCL
        iic_delay();
    }

    if (!ack)
    {
        iic_nack();     /* 发送nACK */
    }
    else
    {
        iic_ack();      /* 发送ACK */
    }

    return receive;
}
```

总结：

> 会发现每次操作完都把SCL拉低

### AT24C02实操

AT24C02是一款2K BIT(256Byte)的eeprom,有32页，每页8Byte，有WP写保护引脚，当该引脚置高电平，只可读不可写

* **地址方面**：前四位固定1010固定，中间三位由三个引脚A2 A1 A0决定，最后一位地址由数据传输方向决定，当读的时候最后一位为0，写的时候最后一位为1
  * 对本精英开发板来说 写地址为0XA0,读地址为0XA1

​ 前7位称设备地址，真正编程的时候操作的八位地址称作通讯地址

* \*\*写操作：\*\*AT24C02支持字节写模式和页写模式。
  * 字节写模式就是一个地址一个数据进行写。
  * 页写模式就是连续写入数据。只需要写一个地址，连续写入数据时地址会自增，但存在页的限制，超出一页时，超出数据**覆盖**原先写入的数据。但读会自动翻页。
* \*\*读操作：\*\*AT24C02支持当前地址读模式，随机地址读模式和顺序读模式

  * 当前读模式是基于上一次读/写操作的最后位置继续读出数据。
  * 随机地址读模式是指定地址读出数据，一个字节一个字节的读
  * 顺序读模式是连续读出数据，自动翻页

  #### IO口配置

  SCL推挽输出 SDA 开漏输出

  > 输出时：开漏输出的特点是能输出低电平，无法输出高电平，但可以被外部上拉至高电平，主机（MCU）输出0，可以拉低信号，来实现低电平发送，主机输出1（实际不起作用），由外部上拉电阻上拉，实现高电平发送。
  >
  > 输入时：当SDA作输入时，设置输出1状态，此时因为MCU无法输出1，相当于释放了SDA脚，此时外部器件可以主动拉低SDA脚/释放SDA脚（同样由上拉电阻提供“输出1的功能”），实现SDA脚的高低电平变化。由于开漏输出模式下，MCU还是可以读取IDR状态寄存器，来获取引脚高低电平，因此MCU读取IDR，即可获得SDA脚的高低电平状态，从而实现输入检测。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://oceanaparts.gitbook.io/halnote/i2c-tong-xun.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
