好问题,你看见这个标题就觉得有意思是吧?我经常就想着能不能系统里面可以方便的添加硬件模块,然后系统马上就可以检测并使用。
PS:后面一时兴起以CW32的MCU为例详细的解读了IIC协议的种种,如果读懂,必定功力大增。然后又以ADS1115入手,检验了学习成果。(又加了好几个器件,更加的丰满)
IIC以独特的特性,在外设和外部器件上拥有着半壁江山,在面试和工作中也是经常要打交道的东西,我说要学到精通也不为过。
IIC最早是06年飞利浦搞出来的,后来NXP买了飞利浦。然后各家鬼迷日眼的想摆脱这个名字,什么TWI,这里说的就是你ESP32!
好吧,要知道它是一种串行协议。半双工通信,一次只有一个控制器或目标设备在总线上发送数据。半的意思就是,一个时间上只有一个任务。
IIC 控制器设备启动和停止通信,从而消除了潜在的总线争用问题。与目标设备的通信通过总线上的唯一地址发送。这允许 IIC 总线上同时存在多个控制器和多个目标设备。
通讯的时候使用的是帧:地址帧后面是一个或多个数据帧,每个数据帧由一个字节组成。每个帧还有一个确认位,用于提醒控制器目标设备或控制器设备已收到通信。
书接上文继续:
热插拔是指在不暂停、重新启动系统或关闭系统电源并保持正常运行的情况下为已加电的系统更换或添加元件。保持 I2C 总线的正常运行包括不因向总线添加负载或破坏正在进行的位流而影响通信。当 I2C 节点或器件首次连接到系统时,没有电源将内部 I2C 输出 FET 的栅极接地,如果这些引脚漏极上的上电瞬态足够快,则可能会耦合 到这些输出 FET 的栅极中,从而将栅极电压提升得足够高,瞬间使开关导通。
但是抱歉,在软件上并没有这个功能。
I2C只能做到硬件准许热插拔,但软件加载无法自动实现,I2C标准中没有相关内容,需要在底层引入直接控制才行。
这里面的问题是,让主设备知道某个从设备是否加载了。
如果使用硬件I2C跟它通信。当遇到通信不正常的情况下,就复位I2C外设。
而且就是这个插拔动作也进而可能使总线电压电平降至足以超越不同器件的高/低阈值的水平,从而导致总线上出现通信错误或数据损坏。
我搜索了一下这个怎么搞?这肯定是隔离啊!
没错,就是这样
它还是一个扩展器
三个硬件引脚(A0、A1 和 A2)用于对 I2C 地址进行编程,该地址允许高达八个 TCA9555 器件共享同一个 I2C 总 线。
挂上了
中间做缓存
电脑里面的电源管理是IIC的
电脑里面也是啥也有,在PCIE上面
不过SPI协议也不强制要求热插拔。是否支持热插拔同样取决于具体的设备实现。一般来说,SPI设备的热插拔相对IIC来说更复杂,因为SPI协议没有明确的总线仲裁机制,容易出现冲突。
看着有两个特色功能,在参考手册里面说这个IIC说的比较细致
这些是速度
I2C 通信采用主从结构,并由主机发起和结束通信。
你需要知道的是,其实目标就是要传输0,1数字,使用高低电平来表示。然后我们就在时钟的节奏下改变电平。
主机通过发送 START 起始信号来发起通信,之后发送 SLA+W/R 共 8bit 数据(其中,SLA 为 7bit 从机地址,W/R 为读写位),并在第 9 个 SCL 时钟释放 SDA 总线(就是主机这个时候不发东西了,这个电平变化的权利给了从机), 对应的从机在第 9 个 SCL 时钟占用 SDA 总线并输出 ACK 应答信号,完成从机寻址。(从机这个时候回答,我知道啦!)
此后根据主机发送的第 1 字 节的 W/R 位来决定数据通信的发端和收端(这个时候还不知道要自己输出还是接受数据),发端每发送 1 个字节数据,收端必须回应 1 个 ACK 应答信号(就是句句有回应,是主机说的)。数据传输完成后,主机发送 STOP 信号结束本次通信。
SDA上面拉低,读写在第一次就说了,第二个绿框就是咱们的从机发数据了。
所以通讯最小单位是字节,就是01 八个。
都高是空闲,数据拉低是要说话了
然后主机先说话。
停止后是SDA拉高,就像用完把东西放好的感觉呢
总结一下
如果是继续传输呢?
当一个起始信号后未出现停止信号之前,出现了新的起始信号,新的起始信号被定义为重复起始信号。在主机发送停止信号前,SDA 总线一直处于占用状态,其它主机无法占用总线。
也就是说,一个开始和一个停止,如果有一个开始,但是没结束,而且又是开始,那就主机还是会等STOP信号,很好理解吧?
妈的,STOP信号快来,占着茅坑不拉s
可以看到在时钟低,数据允许变换,这也是时序图的看法
写的真好,言简意赅呢!
其实是说的,在时钟的高来判断SDA上面的0,1,这个时候是在组装字节,但是在SCL的低的时候,开始了01变换,如果我们把一高一低的时钟变化看做一组信号的话,那么就是传输了一位,就是一个0,1的信号。
时钟高,稳定取样,低的时候变换
在多主机通信系统中,总线上的每个节点都有从机地址。每个节点可以作为从节点被其它节点访问,也可以作为主节点向其它的节点发送控制字节和传送数据。
还有就是应答呢?
在总线上传输数据时,发端每传输完 1 个字节数据,在第 9 个 SCL 时钟周期发端放弃对 SDA 的控制,收端须在第 9 个 SCL 时钟周期回复 1 个应答位:接收成功,发送 ACK 应答,接收异常发送 NACK 应答。
这个简单吧?前8个bit是主机发出的,7位地址和一个读写标志,9bit让从机回应。1就是ACK,没反应就是应答失败。
读写是字节的,看图说话
如果有两个或两个以上的节点同时向总线发出起始信号并开始传输数据,就会造成总线冲突。I2C 控制器内置一个仲裁器,可对 I2C 总线冲突进行检测和仲裁,以保证数据通信 的可靠性和完整性。
物理上面:现代医学电子仪器原理与设计实验.电阻电容电感 我这文章里面有说这个,可以读读。
开漏的意思就是器件里面有个MOS管,漏极没有接东西
当 NMOS 开启时,设备会将电流通过电阻拉至地。这会将开漏线拉至低电平。
通常,由于 NMOS 会将 SDA 或 SCL 拉低,因此 IIC 从高电平到低电平的转换非常快速。转换速度由 NMOS 驱动强度和 SDA 或 SCL 上的所有总线电容决定。
MOS管停止工作,上拉电阻会把漏极拉到高电位
就是这样的情况
在物理实现上,SDA 和 SCL 引脚电路结构相同,引脚的输出驱动与输入缓冲连在一起。输出结构为漏极开路的场效应管、输入结构为高输入阻抗的同相器。
基于该结构:
1. 由于 SDA、SCL 为漏极开路结构,借助于外部的上拉电阻实现了信号的“线与”逻辑;
2. 设备向总线写数据的同时读取数据,可用来检测总线冲突,实现 “时钟同步”和“总线仲裁”。
根据“线与”逻辑,如果 2 个主机同时发送逻辑 1 或逻辑 0,则 2 个主机都检测不到冲突(就好像千手观音一样,没有办法分辨后面有几个人是吧?),需要等到下一位数据发送再继续检测冲突;如果 2 个主机一个发送逻辑 1,一个发送逻辑 0,此时总线上为逻辑 0,发送逻辑 1 的主机检测到冲突,发送逻辑 0 的主机没有检测到冲突。
为什么呢?因为是0是IIC的开始信号,拉低了,其实就是一个茅坑,先拉低就先检测,那另外一个肯定就是落选了。
当主机检测到总线冲突后,该主机丢失仲裁,退出主机发送模式,进入未寻址从机模式,释放 SDA 数据线, 并回到地址侦测状态。就不发了,现处理吧!
之后根据接收到的 SLA+W/R 进入相应的从机模式 (SLA 地址匹配进入已寻址从机模式, SLA 地址不匹配则进入未寻址从机模式 )。仲裁失败的主机,仍会发送 SCL 串行时钟,直到当前字节传输结束。当主机没有检测到总线冲突,该主机赢得仲裁,继续主导本次数据传输,直到通信完成。
1是两个设备同步,检测不出来。2的时候,两个设备都在发,那就是发1的错误,很简单的规则,因为拉低才是开始信号。
A比如说发1,现在就是失败了,没有发出启动信号。这个时候就退出竞争模式。
B发0,赢得本次机会,继续本次的数据传输。
在3的时候,A还是没被寻址的从机模式,但是还有时钟信号,直到当前字节传输结束。
此后 A 主机 I2C 控制器不再发送时钟信号,B 主机由于赢得仲裁,SCL 和 SDA 都由 B 主机来主导控制传输。
就是两个主机强一个从机。
SDA 仲裁一般发生在主机发送 SLA+W/R 数据阶段,如果两个主机同时向一个从机发送数据,即两个主机发送的从机地址相同,则仲裁会在第二个字节持续。
记得拉低
意犹未尽?不确定,你要是把上面的理解了,学会了,单片机的半壁江山就拿到了。
这个框图还是挺简单的
都在APB上面,时钟模块是单独的,你看一个时钟高低就得一个数据。
这样就适合走线,瞎几把走
PCLK是时钟,时钟是按照8位除的,可以得到我们经常使用的字节表示法
I2C 总线上各设备都有从机地址,且各从机地址均不同。主机根据从机地址寻址从机,从机通过地址比较器自动检测主机发送的 7bit 寻址地址与本机地址是否匹配,以确定是否与主机通信。从机是自己看是不是在喊自己。
I2C 控制器支持 3 个可编程的从机地址,具体地址信息通过从机地址寄存器 I2C_ADDR0 / I2C_ADDR1 / I2C_ADDR2 进行配置。
就像这种便宜的MCU,很多时候就是做粘合剂,那么就可以做从机来模拟一个器件。
从机的地址比较器将接收到的 7bit 寻址地址和 3 个从机地址以及广播地址(0x00)相比较。
如果符合 4 个地址中的任何一个,则认为地址匹配,同时 I2C 中断标志位 I2C_CR.SI 会被置 1,并产生一个中断请求。先自己看,如何和主机说,有人喊我。
应用程序可通过查询从机地址匹配寄存器 I2C_MATCH 获取匹配成功的地址序号。如果地址匹配到 I2C_ADD0 / 1 / 2,则从机进入相应的已寻址从机接收模式(接收到 SLA+W)或者已寻址从机发 送模式(接收到 SLA+R);如果地址匹配到广播地址 0x00(接收到 SLA+W),则从机进入广播接收模式。
这个功能有意思哈!
I2C 支持时钟同步(时钟延展)功能,SCL 时钟低电平的时间由 SCL 时钟低电平宽度最长的器件决定,而 SCL 时高电平时间由时钟高电平宽度最短的器件决定。
如果从机希望主机降低传输速度,可以在收到数据并回应 ACK 后,保持 I2C_CR.SI 为 1,则从机I2C 控制器将保持 SCL 为低电平状态,以此来通知主机。
也就是说,其实是使用了连续发送的模式。
当主机准备下一个字节数据传输(发送或者接收)时检测到 SCL 的电平被拉低,则进行等待(等这个从机的stop信号),直到从机完成操作并将 I2C_CR.SI 清 0,从机控制器释放 SCL 的拉低控制,主机检测到 SCL 为高电平后继续下一个字节数据的传输。
就是想靠这样的方式,主机一边等这个停止信号,从机这里自己赶紧处理。这个功能好,在MCU做从机的时候可以加多一些处理时间。
I2C 数据传输的收端必须在每个字节的第 9 个 SCL 时钟周期给发端进行 ACK 或者 NACK 应答,发端通过该应答位来获取收端当前状态:回应 ACK 应答,则表明收端已正确接收该字节数据,可以继续接收下一字节数据;回应 NACK 一般表示收端已不再接收任何数据。
收端发送 ACK 还是 NACK,由收端的 I2C 控制寄存器 I2C_CR 的 AA 位域来控制。
当设置 I2C_CR.AA 为 1 时,I2C 模块每收到 1字节数据后会回应 ACK应答,当设置I2C_CR.AA为 0时,I2C模块每收到 1字节数据后回应 NACK应答。
这个就是当从机的时候来控制收发的,需要做大量的数据校验。
在主机接收数据过程中,主机作为通信发起方,控制着收发字节个数,主机(收端)在最后一个字节数据接收完成后回应 NACK 应答给从机(发端),从机收到 NACK 应答后将切换为未寻址从机接收模式,并释放 SDA 总线, 以便主机发送 STOP 停止信号或 Repeated START 重复起始信号。
主机给从机说,没收到,从机收到这个NACK以后就变成没寻址的接受模式,重新走一遍IIC的发送流程。同时拉高SDA,就是结束。
在从机发送数据过程中,如果自身的 I2C_CR.AA 应答控制位被应用程序清零,则从机在发送完最后 1 字节有效数据后,将自身切换为未寻址从机接收模式,并释放 SDA 总线,主机从总线上读数据将得到 0xFF。
这里就是发送的时候,9位应该是主机来控制,如果给了0,就是清空,那从机就知道了,不发了,继续等着被翻牌子。
此时主机应能判断从机处于无响应状态,并发送 STOP 停止信号或 Repeated START 重复起始信号。
在从机接收数据过程中,如果回应 NACK 给主机(发端),则表示从机(收端)主动结束本次通信,不再接收主机发送的数据,且将自身切换为未寻址从机接收模式,并释放 SDA 总线,此时主机应发送 STOP 停止信号或 Repeated START 重复起始信号。
当SDA和SCL切换到VC的时候可以电平转换
主机发送模式下,主机主动发送多个字节到从机。
SCL 串行时钟由主机控制产生,因此需要先根据传输波特率设置 I2C_BRR 寄存器并设置 I2C_BRREN 寄存器的 EN 位域为 1,然后启动传输。
时钟安排好了!启动!
启动了
主机设置 I2C_CR.STA 为 1,通知控制器发送 START 起始信号,控制器收到通知后检测总线是否空闲,当总线空闲时,主机控制器向总线发送一个 START 起始信号。一切的起点,拉低SDA。
如果发送成功,状态码 I2C_STAT 变为 0x08,中断标志位 I2C_CR.SI 被置 1。 也就是这个时候查询,这个位置是1.
STA位置
主机发送完 START 起始信号后,需要软件设置 I2C_CR.STA 为 0(清0,发完就清0了),然后将从机地址和写标志位(SLA+W)写入到 I2C 数据寄存器 I2C_DR;
数据就在这里这种地方
清0就是没中断
清零 I2C_CR.SI 位,主机控制器将发送 SLA+W 到 I2C 总线上;当主机发送完 SLA+W 并收到从机 ACK 应答信号后,状态码 I2C_STAT 变为 0x18,中断标志位 I2C_CR.SI 被置 1。 也就是说这次的发送很圆满,这个中断就是发送成功的中断信号。
此后主机根据应用需要,发送多个字节的用户自定义数据并检测 ACK 应答信号,每个字节的发送过程和发送 SLA+W 数据帧类似。
主机在发送数据过程中,如果收到 NACK 应答信号,表明从机不再接收主机发送的数据(从机的 I2C_CR.AA 被清零),主机应发送 STOP 信号结束本次数据传输,或者发送 Repeated START 重复起始信号开启新一轮传输。这正常, 从机不要了,主机就停止了。
也就是发送STOP信号,拉高
当主机发送完成所有数据后,设置 I2C_CR.STO 为 1,通知控制器数据已发送完成,待发送 STOP 停止信号。
清零 I2C_CR.SI 位,主机控制器将发送 STOP 停止信号到 I2C 总线上,完成本次数据传输,释放总线。
当主机发送完成所有数据后,也可以不发送 STOP 停止信号,而是直接发送 Repeated START 重复起始信号, 续占用总线进行新一轮数据传输。 当主机由于总线冲突丢失仲裁时,会进入未寻址从机接收模式(状态码 I2C_STAT = 0x38)。
可以查询总线上面的状态,知道IIC现在的情况。SDA拉低开始工作,发送8位,回应一位。
I2C 控制寄存器 I2C_CR 的 SI 位域为中断标志位。当 I2C 状态寄存器 I2C_STAT 的 STAT 位域值发生改变(变成 0xF8 除外)时,I2C_CR.SI 标志位就会被置位,同时产生中断请求。
我觉得看懂这种图要求对协议特别的熟悉
这就是状态的流程图,通过对从机的回应来决定怎么做。先启动发送条件,接下来就是地址+RW 等从机回应。
第一次收到ACK的时候就考虑是一个字节发送还是多个字节,后者就是不发送STOP,直接拉低,继续发送,不过还是要判断从机的ACK情况,
这里就不说打架的情况了。
主机接收模式用于接收从机发送的多个数据,主机每接收到 1 字节数据后会回应 ACK 应答信号。
SCL 串行时钟由主机控制产生,因此需要先根据传输波特率设置主机的 I2C_BRR 寄存器并设置 I2C_BRREN 寄存器的 EN 位域为 1,然后启动传输。SDA拉低。
主机设置 I2C_CR.STA 为 1,通知控制器发送 START 起始信号,控制器收到通知后检测总线是否空闲,当总线空闲时,主机控制器向总线发送一个 START 起始信号。
如果发送成功,状态码 I2C_STAT 变为 0x08,中断标志位 I2C_CR.SI 被置 1。 这就是完成了发送前的准备。
主机发送完 START 起始信号后,需要软件设置 I2C_CR.STA 为 0,然后将从机地址和读标志位(SLA+R)写入到 I2C 数据寄存器 I2C_DR;清零 I2C_CR.SI 标志位,主机控制器将发送 SLA+R 到 I2C 总线上;当主机发送完 SLA+R 并收到从机 ACK 应答信号后,状态码 I2C_STAT 变为 0x40,中断标志位 I2C_CR.SI 被置 1。
这段简单,清楚状态,每干一件事情都汇报。接着就是发送地址和RW,继续改变标志位。等着从机的ACK,状态继续改变,而且中断写1.有数据了
此后,主机设置 I2C_CR.AA 为 1,并清零 I2C_CR.SI 位,开始接收从机发送的数据,每接收完 1 字节数据后,都要回复一个ACK 应答信号。
在主机接收过程中,应注意:
1. 为保证每接收到 1 字节数据后都能正确产生 I2C_CR.SI 中断信号,需要在收到 1 字节数据后及时将 I2C_ CR.SI 位清除。
2. 在接收最后一个字节前需要将 I2C_CR.AA 清零,即主机在接收到最后一个字节时不产生 ACK 应答信号, 以此来通知从机停止数据发送(0位)。
主机在接收数据过程中,如果从机由于某种原因不再发送主机所需要的数据(从机的 I2C_CR.AA 被清零),主机后续将收到全 1 信号(没有启动信号了),此时主机需要在应用层对数据进行判决,判定从机为无响应状态,应发送 STOP 停止信号 来结束本次传输,或发送 Repeated START 重复起始信号开启新一轮传输。
当主机接收完成所有数据后,设置 I2C_CR.STO 为 1,通知控制器数据已接收完成,待发送 STOP 停止信号。
清零 I2C_CR.SI 位,主机控制器发送 STOP 停止信号到 I2C 总线上,完成本次数据传输,释放总线。当
主机接收完成所有数据后,也可以不发送 STOP 停止信号,而是直接发送 Repeated START 重复起始信号(连续接收),继续占用总线进行新一轮数据传输。当主机由于总线冲突丢失仲裁时,会进入未寻址从机接收模式(状态码 I2C_STAT = 0x38)。
简单
流程图不放了,很简单。
状态码真好,可以方便的知道接下来如何处理
很简单,就是先配置引脚,然后就是把IIC模块开启,复位,以便在确定的状态下运行。接下来就是设置,等待外部或者内部信号,来决定下一步的动作。只是为了准确发送数据。
寄存器就这么几个,不过IIC的状态有点多,要好好的理解协议
接下来看一个IIC的ADC,检验学习成果
IIC的
读
我们不看别的东西,直接看时序。主机开始启动信号,如果是地址信号和读写,接着就是ADS的回应,接着开始读。MSB靠前的,接着是8位,9的时候ACK,继续拉低,把地位的发完。16个bit,需要拼两个字节。
写,地址+RW,等ACK。接着就是16bit数据发两次,简单死了
The ADS111x provide 16 bits of data in binary two's complement format. 16bit的。
学的不错,比心。
第一字节就是控制器件的状态,是要配置还是要读取数据
结果就在这里
官方也是使用的ADS1115说的
输出 16 位数据转换。
控制器设备从转换寄存器读取以获取 ADC 转换数据。转换寄存器地址指针为 00h。转换数据以二进制补码的形式显示为 16 位结果。
正满量程输入产生输出代码 7FFFh,负满量程输入产生输出代码 8000h。
因为是高位在前,所以15bit是首先配置的,符合。
PGA的是可以放大的倍数
这个是连续转换
这个转换率是非常的低了
有一个小应用,在低侧的电流感知
Layout的时候,这个IIC不咋要求信号完整性,因为就是有时钟就行
电源是上面垮了一个电容,直接滤波,电容是横跨上面的。
再看看有什么?
这个吧,看完DNA就动了
这就很简单了,SDA拉低,开始,后面就是SCL的高位读取,SCL低位变化,Sr是持续拉低,连续读写,后面就是拉高,结束,再往后就是继续。
这个是读取,7位地址和读写,ADC回复ACK,接着就是读,在SCL的高读取,低变化,9bit的时候又是回复,拉高就是NACK,结束。
写也很简单,还是老样子
再看一个
也是积分的
需要注意的是,在第一帧回复了以后,马上就开始了写入,在别的数据手册这里是没有体现的。9bit的时候是0,NACK。停止了通讯,但是ADC已经写入了数据。
这个是连续的读,帧写的比较有特点
这个地方文档错误,时序少了T
还给了羊肉串的器件
这个器件给了很多的应用,可以学习。
再看一个
有特色的地方是,这个IIC改名字了,俩线串口,还在SCL低的时候变换
ADS1115-16Bit ADC 调试.上
MCP3421-18bit ADC 调试
Ti.ADS1115-15Bit差分ADC
IIC协议长文详解-解惑版
51单片机模拟IIC以及引脚模式
这是以前写的,怎么说呢,这些东西过几天就会忘。
IIC是比较难的,在下篇文章会有SPI终结版,那个时候你会在低速的世界游刃有余。
https://www.ti.com.cn/document-viewer/cn/lit/html/sbaa565
https://www.ti.com.cn/cn/lit/an/sbaa565/sbaa565.pdf?ts=1735865538163&ref_url=https%253A%252F%252Fwww.ti.com.cn%252Fproduct%252Fcn%252FDAC53401