IIC总线协议

——即便这类文章多如牛毛,也只有当自己写时才能发现不足——

IIC总线协议

1简介

IIC(Inter-Integrated Circuit,集成电路总线)是一种由 PHILIPS 公司开发的半双工、双向两线式同步串行总线,用于连接微控制器及其外围设备。

它是由双向数据线 SDA (serial data)和单向时钟 SCL (serial clock)构成的串行总线,可发送和接收数据,两条线可以挂多个设备,其接线图如下图所示:

SDA为双向是因为其可以被主设备或从设备控制,SCL为单向是因为其仅能被主设备控制。

在这里插入图片描述

图摘自网络。

I2C 和 SPI 一样以主从的方式工作,不同于 SPI 一主多从的结构,它允许同时有多个主设备存在,每个连接到总线上的器件都有唯一的固化地址,主设备启动数据传输并产生时钟信号,从设备被主设备寻址同一时刻只允许有一个主设备和一个从设备

总结:IIC允许多主从,但同一时刻只允许有一主一从。

I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
起始和结束信号由主设备产生,应答信号(ACK和NACK)由接受方产生。

开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号ACK(acknowledge)。当从设备为接受方时,从设备在接收到 8bit 数据后拉低SDA,表示已收到数据(ACK);如果它不拉低SDA线,就表示不响应(NACK,Notacknowledge)。
当从设备为发送方时,在从机发送完最后一个字节后,主设备必须产生一个NACK,用以通知从机(发送方)不再发送信息,则从机释放SDA来允许主机发出结束信号。

在以下情况下,ACK将缺失(即发送NACK信号):

  1. 当从机不能响应从机地址时(从机忙或该地址没有对应从机);
  2. 从机接收器在传输过程中不能接收更多的数据时(已满);
  3. 主机接收器在接收到最后一个字节后;

不发送ACK相当于发送了NACK。

部分参考: https://www.cnblogs.com/nufangrensheng/p/3652606.html
对IIC总线时序的一点理解以及ACK和NACK

2过程概述

主设备发送起始信号,在传送的第一个字节中指定从设备的地址并决定读或写操作,其后:

1.主设备进行写操作时,主设备向从设备发送一个字节后,等待从设备发出一个应答信号。从设备如果还有空间接受下一个字节应该回答“ACK”,主设备接收到应答信号后,根据实际情况作出是否继续传递信号的判断。;如果没有空间从设备应该回答“NACK”,若未收到应答信号,主机发出一个停止信号终止传输或者重新发出一个起始信号开始新的传输。

2.主设备进行读操作时,从设备向主设备发送完一个字节后,等待主设备发出一个应答信号,收到后从设备继续发送下一个数据;如果主设备接收到最后一个字节,应该发送“NACK”以提示从设备准备接收Stop信号。

写操作时,从设备无法拒绝主设备发送的数据直至从设备接收器满并发出NACK信号;
读操作时,从设备在收到主机应答信号后必须继续传输至最后一字节。

SDA 和 SCL 都是由主机控制,从设备只能够将SDA线拉低。

  1. 主机为发送方时,从机只是在SCL线为高的时候去被动读取SDA。
  2. 主机为接收方时,从机只是在SCL线为高的时候保证SDA的状态。

3起始、传输和停止

初始化:总线在空闲状态时,SCL和SDA都保持着高电平。
起始:当SCL为高电平而SDA由高到低跳变,表示产生一个起始条件,在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线。
数据传输:在给一个起始信号后开始传送一字节(8位)的数据,每一位的传送就是在SCL的一个周期期间完成,共需要8个SCL周期。具体请参考下一章节。
停止:当SCL为高而SDA由低到高的跳变,表示产生一个停止条件,在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。
重复起始: 在一次通信过程中,主机可能需要和不同的从机传输数据或者需要切换读写操作时,主机可以再发送一个起始条件。

(到处是转载,我找不到原始图作者 )
部分图片及内容引自https://blog.csdn.net/w89436838/article/details/38660631
和https://blog.csdn.net/Lingdongtianxia/article/details/81135456

4数据传输

数据传输以字节为单位。

SDA数据的改变
SDA上的数据只能在SCL为低电平期间翻转变化,在SCL为高电平期间必须保持稳定,IIC设备只在SCL为高电平期间采集SDA数据。SCL由主设备控制。

完成一个字节的传输
主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位。当一个字节按数据位从高位到低位(MSB到LSB,小端传输)的顺序传输完后,主设备SDA引脚将变为输入,由于SDA和SDL硬件设计时都有上拉电阻,此时SDA变成高电平。从设备若接受到信号,将在第9个周期把SDA拉低,回传给主设备一个应答位, 完成一个字节的传输。因此一次通信是9个CLK,即8个数据位加1个应答位。

在这里插入图片描述

数据传输格式

单次数据传输
主设备在传输有效数据之前要先指定从设备的地址
主机发送的第一个字节为从机地址,高 7 位为地址(默认为7 位地址模式,也有10位地址模式),最低位为 R/W 读写控制位,1 表示操作,0 表示操作。
下图中”N字节数据+ACK“是指每传输一个字节,接受方需产生一位应答信号。
在这里插入图片描述

图片摘自RT-THREAD 编程指南v2.0.0 I2C总线设备

重复起始

主设备往从设备中写(读)数据,然后重启起始条件,紧接着从从设备中读(写)取数据;
在这里插入图片描述

尾注
我重新对这篇文章进行了修订,对错误的地方进行了改正,对模糊的地方进行了补充。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 首先,你需要明确I2C总线协议的功能要求,并确定技术细节。其次,使用C语言编写I2C总线协议,可以使用标准的I2C库函数,以及特定于处理器的I2C驱动程序。最后,使用模拟或调试工具来验证I2C总线协议的正确性。 ### 回答2: I²C(Inter-Integrated Circuit)总线协议是一种用于在集成电路之间进行通信的串行总线协议。下面是使用C语言编写I²C总线协议的步骤: 首先,我们需要定义I²C总线的相关参数,包括时钟频率、数据位数和I²C地址等。可以使用宏定义或者全局变量来定义这些参数。 接下来,我们需要实现I²C总线的初始化函数,用于初始化I²C控制器和相关硬件。在初始化函数中,我们需要配置相关引脚的输入输出模式、上下拉电阻等。 然后,我们需要实现发送数据的函数。发送数据的过程包括发送起始位、I²C地址、数据、应答等步骤。通常情况下,我们可以使用位运算来控制每个步骤的时序和数据传输。 接着,我们需要实现接收数据的函数。接收数据的过程与发送类似,只是在发送结束后需要切换为接收模式,并且在接收数据时需要发送应答或非应答信号。 最后,我们可以实现其他辅助函数,例如读写寄存器、发送和接收多个字节的数据等。 在编写以上函数时,我们需要参考相关的I²C总线协议规范和硬件手册,以确保代码的正确性和可靠性。另外,为了提高代码的可读性和可维护性,可以使用适当的注释和模块化设计。 需要注意的是,具体的代码实现可能因硬件平台和具体需求而有所不同。以上只是基本的编写步骤和思路,实际编写过程中还需根据实际情况进行相应的优化和调整。 编写I²C总线协议的代码是一项复杂而精细的任务,需要对硬件和通信协议有深入的理解。因此,在实际应用中建议使用已经存在的I²C库或者驱动程序进行开发,以减少开发时间和复杂度。 ### 回答3: I2C(Inter-Integrated Circuit)总线协议是一种用于连接低速外设的串行通信协议,通过两根传输线SCL(时钟线)和SDA(数据线)进行通信。以下是使用C语言编写I2C总线协议的示例代码。 ```c #include <stdio.h> #include <stdint.h> #define I2C_ACK 0 #define I2C_NACK 1 // 初始化I2C总线 void i2c_init() { // TODO: 设置SCL和SDA引脚和配置相关寄存器 } // 发送一个起始信号 void i2c_start() { // TODO: 设置SDA和SCL引脚状态,产生起始信号 } // 发送一个停止信号 void i2c_stop() { // TODO: 设置SDA和SCL引脚状态,产生停止信号 } // 等待应答信号 int i2c_wait_ack() { // TODO: 读取SDA引脚状态,判断是否接收到应答信号 // 如果接收到应答信号,返回 I2C_ACK // 如果没有接收到应答信号,返回 I2C_NACK } // 发送一个字节 void i2c_write_byte(uint8_t data) { // TODO: 依次发送数据的每一位,同时检测应答信号 } // 读取一个字节 uint8_t i2c_read_byte() { // TODO: 依次读取数据的每一位,同时发送应答信号 // 返回读取的字节数据 } int main() { i2c_init(); // 初始化I2C总线 // 示例: 使用I2C总线向某个设备写入数据 i2c_start(); // 发送起始信号 i2c_write_byte(0xA0); // 发送设备地址和写入标志 i2c_wait_ack(); // 等待应答信号 i2c_write_byte(0x01); // 发送数据 i2c_wait_ack(); // 等待应答信号 i2c_write_byte(0x23); // 发送数据 i2c_wait_ack(); // 等待应答信号 i2c_stop(); // 发送停止信号 // 示例: 使用I2C总线从某个设备读取数据 i2c_start(); // 发送起始信号 i2c_write_byte(0xA1); // 发送设备地址和读取标志 i2c_wait_ack(); // 等待应答信号 uint8_t data = i2c_read_byte(); // 读取数据 i2c_send_ack(I2C_NACK); // 发送非应答信号 i2c_stop(); // 发送停止信号 printf("读取的数据是 %d\n", data); return 0; } ``` 以上示例是一个简单的I2C总线协议的实现。具体的操作需要根据硬件平台和具体的I2C总线驱动进行相应的设置和配置。可以根据实际需求对上述代码进行修改和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值