STM32单片机入门学习——第31节: [10-1] I2C通信协议
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!
本文写于:2025.04.10
STM32开发板学习——第31节: [10-1] I2C通信协议
- 前言
- 开发板说明
- 引用
- 解答和科普
- 一、I2C硬件
- 二、I2C软件
- 问题
- 总结
前言
本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始32单片机的学习之路。
欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.
在这里会记录下江协科技32单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!
开发板说明
本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。另外我也购买了江科大的学习套间。
原理图如下
1、开发板原理图
2、STM32F103C6和51对比
3、STM32F103C6核心板
视频中的都用这个开发板来实现,如果有资源就利用起来。另外也计划实现江协科技的套件。
下图是实物图
引用
【STM32入门教程-2023版 细致讲解 中文字幕】
还参考了下图中的书籍:
STM32库开发实战指南:基于STM32F103(第2版)
数据手册
解答和科普
一、I2C硬件
1、 介绍协议规则,然后用软件模拟的形式来实现协议;因为I2C是同步时序,软件模拟协议也非常方便;
2、 介绍STM32的I2C外设,然后用硬件来实现协议;
51单片机使用的是AT24C02这个存储器模块来学习I2C的,这里会使用MPU6050这个陀螺仪,加速度传感器来学习I2C。
代码一个是:软件I2C读写MPU6050,另一个是硬件I2C读写MPU6050,两个代码实现的效果是一样的。
软件是通过软件I2C通信,对MPU6050芯片内部的寄存器进行读写,写入到配置寄存器,就可以对外挂的这个模块进行配置, 读出寄存器,就可以获取外挂模块的数据,这就是I2C通信的目的。最终,我们读取的数据会显示在这个OLED上,其中最上面的数据是,设备的ID号,MPU6050的ID号固定为0x68,一般我们可以读出这个ID号,看看是不是0x68,用来测试I2C读取数据的功能是不是正常,左边三个,是加速度传感器的输出数据,分别是X轴、Y轴、Z轴的加速度,右边3个,是陀螺仪传感器的输出数据,分别是X轴,Y轴,Z轴的角速度,我们可以改变MPU6050传感器的姿态,这6个数据就会对应变化。
串口通信:就是从TX引脚向RX引脚发送数据流,数据流以字节为单位,可以组合多个字节,变成多字节的数据包传输,设计为,一条发送线,一条接收线,可以直接通过内部的数据总线来实现,直接用指针操作就行,不需要我们操心,但是,现在这个模块的寄存器在单片机的外面,你要是直接把单片机内部的数据总线拽出来,把两个芯片合而为一体,那可能不太现实,所以设计一种通信协议,在单片机和外部模块连接少量的几根线,实现单片机读写外部模块寄存器的功能,这时你回想,这不太简单了,用这个串口的数据包通信就可以完成任务,比如就用这个HEX数据包,定义一个3个字节的数据包,从单片机向外挂模块发过去,第一个字节,表示读写,发送0,表示这是一个写数据包,发送1,表示这是一个读数据包,第二个字节,表示读写的地址,第三个字节表示写入的数据,比如发送数据包为:0x00,0x06,0Xaa,这就表示在0x06的地址下写入0Xaa,模块收到后,就执行这个写入操作,如果发送数据包为:0x01,0x06,0x 00;表示要读取0x06地址下的数据,注意,这个读的数据包第三个字节无效,模块收到之后,就要再给我发送一个字节,返回0x06地址下的数据,这样就行了,是不是完美完成任务;目前串口设计,需要两根通信线的全双工协议,我们这个流程是一种基于对话的形式来进行的,在整个工程中并不需要同事进行发送或接收,发送的时候不需要接收,接收的时候不需要发送,这样就会导致始终有一条线处于空闲状态,删掉一根线,只能在同一个根线上进行发送和接收,也就是把全双工变成半双工,我们这个协议并没有一个应答机制,也就是单片机发送了一个数据,对方有没有收到,单片机是完全不了解的,所以为了安全起见,要求增加应答机制,要求每发一个字节,对方要给我一个应答,每接收到一个字节,我也要对方一个应答;要求3线上可以接多个外挂设备,单片机可以指定,和任意一个模块通信,同时单片机在和某个模块进行通信时,其他模块不能对正常通信产生干扰,第四个要求:串口是异步的时序来通信,也就是发送发和接收方约定的传输速率是非常严格的,时钟不能有过大的偏差,也不能说是,在传输过程中,单片机有点事,进中断了,这个时序能不能暂停一下,对于异步时序来说,这时不行的,你单片机一个字节发一半暂停了,接收方可能是不知道的,它仍然会按照原来那个约定的速率读取,这就会导致传输出错,所以异步时序的缺点就是,非常依赖硬件外设的支持,必须要有USART电路才能方便地使用,如果没有USART硬件电路的支持,那么串口是很难用软件来模拟的,虽然说软件模拟串口通信也是行得通的,但是异步时序对时间要求很严格,一般我们很少使用软件来模拟串口通信,要把这个协议改成同步的协议,另外加一条时钟线来指导对方读写,由于存在时钟线,对传输的时间要求就不高了,单片机可以随时暂停处理其他事情,因为暂停的同时,时钟线也暂停了,所以传输双方都能定格在暂停的时刻,可以过一段时间再来继续,不会对传输造成影响,这就是同步时序的好处,使用同步时序就可以极大地降低单片机对硬件电路的依赖,即使没有硬件电路的支持,也可以很方便地使用软件手动翻转电平来实现通信,比如之前51单片机里,那个单片机就没有I2C的硬件外设,但是同样不影响51单片机进行软件模拟的I2C通信,异步时序的好处就是省一根时钟线,节省资源,缺点就是对时间要求严格,对硬件电路的依赖比较严重,同步时序的好处就是,反过来,对时间要求不严格,对硬件电路不怎么依赖,在一些低端单片机,没有硬件资源的情况下,也很容易使用软件来模拟时序,缺点就是多一根时钟线,这就是同步和异步的区别,所以需要一个同步的协议。
通过通信线,实现单片机读写外挂模块寄存器的功能,其中至少要实现,在指定的位置写寄存器,和在指定的位置读寄存器这两个功能。实现了读写寄存器,就是实现了对这个外挂模块的完全控制。
MPU6050、OLED、AT24C02、DS3231
同步的时序,降低对硬件的依赖,同时同步的时序稳定性也比异步时序更高,然后只有一根SDA数据线,满足了大公司提出的要求1,变全双工为半双工,一根线兼具发送和接收,最大化利用资源,带数据应答,支持挂载多个设备。
在总线冲突中,I2C协议会进行仲裁,仲裁胜利的一方取得总线控制权,失败的一方自动变为从机,当然时钟线也是有主机控制的,所以在多主机的模型下,还要进行时钟同步,多主机的情况下,协议是比较复杂的。
这是一个一主多从的模型,左边CPU就我我们的单片机,作为总线的主机,主机的权利很大,包括,对SCL线的完全控制,任何时候都是主机完全掌握SCL线,另外在空闲状态下,主机可以主动发起对SDA的控制, 只有在从机发送数据和从机应答的时候,主机才会转交SDA的控制权给从机,这就是主机的权利;
下面是一系列被控IC,也就是挂载I2C总线上了从机,这些从机可以是姿态传感器、OLED、存储器、时钟模块等等,从机的权利比较小,对于SCL时钟线,在任何时刻都只能被动的读取, 从机不允许控制SCL线, 对于SDA数据线,从机不允许主动发起对SDA的控制,只有在主机发送读取从机的命令后,或者从机应答的时候,从机才能短暂地取得SDA的控制权,这就是一主多从模型中协议的规定;
看接线,所有I2C设备的SCL连在一起,SDA连在一起;如何规定每个设备SCL和SDA的输入输出模式呢,SCL应该好规定,因为现在是一主多从,主机拥有SCL的绝对控制权,所以主机的SCL可以配置成推挽输出,所有从机的SCL都配置成浮空输入或者上拉输入,数据流向是,主机发送,所有从机接收,但是到SDA线这里,就比较麻烦了,因为这是半双工的协议,所以主机的SDA在发送的时候是输出,在接收的时候是输入,同样从机的SDA也会在输入和输出之间反复切换,如果你能协调好输入输出的切换时间,也没问题,不然极有可能发生两个引脚同时处于输出的状态,如果这时又刚好是一个输出高电平,一个输出低电平,那这个状态就是电源短路,这个状态是要极力避免的,所以为了避免总线协议没协调好导致电源短路这个问题,I2C的设计是,禁止所有设备输出强上拉的高电平,采用外置弱上拉电阻加开漏输出的电路结构。
首先,引脚进来,都可以通过一个数据缓冲器或者是施密特触发器,进行输入,因为输入对电路没有任何影响,所以任何设备在任何时刻都是可以输入的,但是在输出的这部分,采用的是开漏输出的配置;
正常的推挽输出是这样的,所以是强上拉和强下拉的模式:
开漏输出:就是去掉上面的开关管,输出低电平时,下管导通,是强下拉,输出高电平时,下管断开,但是没有上管了,此时引脚处于浮空啥的状态,这就是开漏输出。
输出低电平,这个开关管导通,引脚直接接地,是强下拉,输出高电平,这个开关管断开,引脚什么都不接,处于浮空状态,这样的话,所有的设备都只能输出低电平而不能输出高电平,为了避免高电平造成的引脚浮空,这时就需要在总线外面,SCL和SDA各外置一个上拉电阻,这时一个通过电阻拉高到高电平的,所以这是一个弱上拉,用我们之前讲的弹簧和杆子的模型来解释就是,SCL或SDA就是一根杆子,为了防止有人向上推杆子,有人向下拉杆子,造成冲突,我们就规定,所有的人,不准向上推杆子,只能选择向下拉或者放手,然后我们再外置一个弹簧向上拉,你要输出低电平,就往下拽,这根弹簧肯定拽不赢你,所以弹簧被拉伸,杆子处于低电平状态,你要输出高电平,就放手,杆子在弹簧的拉力下,回弹到高电平,这就是一个弱上拉的高电平,但是完全不影响数据传输,这样做有什么好处:第一,完全杜绝了电源短路现象,保证电路的安全,你看所有人无论怎么拉杆子或者放手,杆子都不会处于一个被同事强拉和强推的状态,即使有多个人同时向下拉杆子,也没问题,第二,避免了引脚模式的频繁切换,开漏加上弱上拉的模式,同时兼具了输入和输出的功能,你要是想输出,就去拉杆子或放手,操作杆子变化就行了,你要是相输入,就直接放手,然后观察杆子高低就行了,因为开漏模式下,输出高电平就相当于断开引脚,所以在输入之前,可以直接输出高电平,不需要再切换成输入模式了,第三就是,这个模式会有一个“线与”的现象,就是只要有任意一个或多个设备输出低电平,总线就处于低电平,只有所用设备都输出高电平,总线才是高电平,I2C可以利用这个点了特性,执行多主机模式下的时钟同步和总线仲裁,所以SCL虽然在一主多从模式下可以用推挽输出,但是他仍然使用了开漏加上上拉输出的模式,因为在多主机模式下会利用这个特性,以上就是I2C的硬件电路设计。
二、I2C软件
软件:时序
在I2C总线处于空闲状态时,SCL和SDA都处于高电平状态,也就是没有任何一个设备去碰SCL和SDA,SCL和SDA由外挂的上拉电阻拉高至高电平,总线处于平静的高电平状态,当主机需要进行数据收发时,首先就要打破总线的宁静,产生一个起始条件,这个起始条件就是,SCL处于高电平不去动它,然后SDA拽下来,产生一个下降沿,当从机捕获到这个SCL高电平,SDA下降沿信号时,就会进行自身的复位,等待主机的召唤,然后在SDA下降沿之后,主机要再把SCL拽下来,拽下来SCL,一方面是占用这个总线,另一方面也是为了方便我们这些基本单元的拼接,就是之后我们会保证,除了起始和终止条件,每个时序单元的SCL都是以低电平开始,低电平结束,这样这些单元拼接起来,SCL才能续得上;
终止条件是SCL高电平期间SDA从低电平切换到高电平:也就是SCL先放手,回弹到高电平,SDA再放手,回弹高电平,产生一个上升沿,这个上升沿触发终止条件,同时终止条件之后,SCL和SDA都是高电平,回归到最初的平静状态,这个起始条件和终止条件就类似串口时序里的起始位和停止位,一个完整的数据帧,总是以起始条件开始,终止条件结束,另外,其实和终止,都是由主机产生的,从机不允许产生起始和终止,所以在总线空闲状态时,从机必须始终双手放开,不允许主动跳出来,去碰总线,如果允许的话,那就是多主机模型了;
SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。起始条件后,第一个字节,也必须是主机发送的,主机如何发送呢,就是最开始,SCL低电平,主机如果想发送0,就拉低SDA到低电平,如果想发送1,就放手,SDA回弹到高电平,在SCL低电平期间允许改变SDA的电平,当这一位放好之后,主机就松手时钟线,SCL回弹到高电平,在高电平期间,是从机读取SDA的时候,所以高电平期间,SDA不允许变化,SCL处于高电平之后,从机需要尽快地读取SDA,一般都是在上升沿这个时刻,从机就已经读取完成了,因为时钟是主机控制的,从机并不知道什么时候就会产生下降沿了,你从机要是磨磨唧唧的,主机可不会等你的,所以从机在上升沿时,就会把数据读走,主机在放手SCL一段时间后,就可以继续拉低SCL,传输下一位了,主机也需要在SCL下降沿之后尽快把数据放在SDA上,但是主机有时钟的控制权,所以主机并不需要这么着急,只需要在低电平的任意时刻把数据放在SDA上就行了,晚点也没关系,数据放完之后,主机再松手SCL,SCL高电平,从机读取这一位;主机拉低SCL,把数据放在SDA上,主机松开SCL,从机读取SDA的数据,在SCL的同步下,依次进行主机发送和从机接收,循环8次,就发了8位数据,也就是一个字节,另外注意,这里是高位先行,所以第一位是一个字节的最高位B7(和串口不同);如果突然暂停,SCL和SDA电平都暂停变化,传输也完全暂停,等中断结束之后,主机回来继续操作,传输仍然不会出问题,这就是同步时序的好处,最后就是,由于这整个时序是主机发送一个字节, 所以在这个单元里,SCL和SDA全程都由主机掌控,从机只能被动读取,这就是发送一个字节的时序;
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)。
主机在接收之前,需要释放SDA,释放SDA起始就相当于切换成输入模式,或者这样来理解,所有设备包括主机都始终处于输入模式,当主机需要发送的时候,就可以主动去拉低SDA,而主机在被动接受的时候,就必须先释放SDA,不要去动它,以免影响别人发送,因为总线是线与的特征,任何一个设备拉低了,总线就是低电平,如果你接受的时候,还拽着SDA不放手,那别人无论发什么数据,总线都始终是低电平,你自己给它拽着不放,还让别人怎么发送呢,所以主机在接收之前,需要释放SDA,从流程上来看,接收一个字节和发送一个字节是非常相似的,区别就是发送一个字节是,低电平主机放数据,高电平从机读数据;而接受一个字节是:低电平从机放数据,高电平主机读数据;然后看一下下面的时序,和上面基本一样,区别就是SDA线,主机在接收之前要释放SDA,然后这时从机就取得了SDA的控制权,从机需要发送0,就把SDA拉低,从机需要发送1,就放手,SDA回弹高电平,然后同样的,低电平变换数据,高电平读取数据,这里实线部分便是主机控制的电平,虚线部分表示从机控制的电平,SCL由主机控制,SDA主机在接收前要释放,交由从机控制,之后还是一样,因为SCL时钟是由主机控制的,所以从机的数据变换基本上都是贴着SCL下降沿进行的,而主机可以在SCL高电平的任意时刻读取,这就是接收一个字节的时序。
首先应答机制分为发送应答和接受应答,它们的时序分别和发送一个字节、接收一个字节的其中一位是相同的,你可以理解为发送一位和接收一位,这一位就用来作为应答。
首先是发送应答,是主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答;
然后是接受应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA),我们在调用发送一个字节之后,就要紧跟着调用接收应答的时序,用来判断从机有没有收到刚才给它的数据,如果从机收到了,那在应答位这里,主机释放SDA的时候,从机就应该瞬间把SDA拉下来,然后在SCL高电平期间,主机读取应答位,如果应答位为0,就说明从机确实收到了,这个场景就是,主机发送一个字节,然后说,有没有人收到呀,我现在把SDA放手了,如果有人收到的话,你就把SDA拽下来,然后主机高电平读取数据,发现,确实有人给它拽下来了,那就说明有人收到了,如果主机发现,我松手了,结果这个SDA就跟着回弹到高电平了,那就说明没有人回应我,刚发的一个字节可能没人收到,或者它收到了,但是没给我回应,这就是发送一个字节,接收应答的流程,同理,在接收一个字节后,我们也要给从机发送一个应答位,发送应答的目的是告诉从机,你是不是还有继续发,如果从机发送一个数据后,得到了主机的应答,那从机就还会继续发送,如果从机没得到主机的应答,那从机就会认为,我发送了一个数据,但是主机不理我,可能主机不想要了吧,这时从机就会乖乖地释放SDA,交出SDA的控制权,防止干扰主机之后的操作,这就是应答位的执行逻辑,好,那到这里,我们I2C的6块拼图就已经集齐了,分别是,起始条件,终止条件,发送一个字节,接受一个字节,发送应答和接受应答。接下来,我们就来拼接这些基本单元,组成一个完整的数据帧,I2C的完整时序,主要有指定地址写,当前地址读和指定地址读这三种。
我们这个I2C是一主多从的模型,主机可以访问总线上的任何一个设备,那如何来发出指令,来确定要访问的是那个设备呢,这就需要把每个从设备都确定一个唯一的设备地址,从机设备地址就相当于每个设备的名字,主机在起始条件之后,要先发送一个字节叫一下从机名字,所有从机都回收到第一个字节,和自己的名字进行比较,如果不一样,则认为主机没有叫我,之后的时序我就不管了,如果一样,就说明,主机现在在叫我,那我就相应之后主机的读写操作,在同一条I2C总线里,挂载的每个设备地址必须不一样,否则,主机叫一个地址,有多个设备都响应,那不就乱套了,从机设备地址,在I2C协议标准里分为7位地址和10位地址,我们目前只讲了7位地址的模式,因为7位地址比较简单而且应用范围最广,
那在每个I2C设备出厂时,厂商都会为它分配一个7位的地址,这个地址具体是什么,可以在芯片手册里找到,比如我们MPU6050这个芯片的7位地址是1101 000,之前学习的AT24C02的7位地址是1010 000一般不同型号的芯片地址都是不同的,相同型号的芯片地址都是一样的,那如果有相同的芯片挂载在同一条总线上怎么办呢,这就需要用到地址中的可变部分了,一般器件地址的最后几位是可以在电路中改变的,比如MPU6050地址的最后一位,就可以由这个板子上的AD0引脚确定,这个引脚接低电平,那它的地址就是1101 000,如果接高电平,那他的地址就是1101 0001,比如AT24C02地址的最后三位,都可以分别由这个板子上的A0、A1、A2引脚确定,一般I2C的从机设备地址,高位都是由厂商确定的,低位可以由引脚来灵活切换,这样,即使相同型号的芯片,挂载在同一个总线上,也可以通过切换地址低位的方式,保证每个设备的地址都不一样,这就是I2C设备的从机地址,然后看一下I2C的时序:
指定地址写:完成的任务是,对于指定设备,指定设备,通过Slave Address,从机地址来确定,在指定地址下,这个指定地址就是某个设备内部的Reg Address,寄存器地址,写入指定数据,就是要在这个寄存器中写入Data数据,然后看一下下面的时序,这个时序是我在示波器下实际抓拍到的波形,大家也可以用逻辑分析仪抓这个波形,而且逻辑分析仪还自带协议解析的功能,还是非常方便的,在这里,上面的线是SCL,下面的线是SDA,空闲状态,它俩都死高电平,然后主机需要给从机写入数据的时候,首先,SCL高电平期间,拉低SDA,产生起始条件(Start,S),在起始条件之后,紧跟着的时序,必须是发送一个字节的时序,字节的内容,必须是从机地址+读写位,正好从机地址是7位,读写位是1位,加起来是一个字节8位,发送从机地址,就是确定通信的对象,发送读写位,就是确定我接下来要写入还是读出,具体发送的时候呢,在这里,低电平期间,SDA变换数据,高电平期间,从机读取SDA,这里我用绿色的线,来标明了从机读到的数据。第一个数据表示,高7位,表示从机地址,1101 000,最后一位0,表示是写,1表示,之后的时序主机要进行读出操作。然后跟着的单元,就得是接收从机的应答位(Receive Ack),在这个时刻,主机要释放SDA,所以单看主机的波形,应该是这样,释放SDA之后,引脚电平回弹到高电平,但是根据协议规定,从机要在这个位拉低SDA,所以单看从机的波形,应该是这样,该应答的时候,从机立刻拽住SDA,然后应答结束之后,从机再放开SDA,现在综合两者的波形,结合线与的特性,在主机释放SDA,由于SDA也被从机拽住了,所以主机松手后,SDA并没有回弹高电平,这个过程,就代表从机产生了应答,最终高电平期间,主机读取SDA,发现是0,就说明,我进行寻址,有人给我应答了,传输没问题,如果主机读取SDA,发现是1,就说明,我进行寻址,应答期间,我松手了,但是没人拽住它,也没用人给我应答,那就直接产生停止条件吧,并提示一些信息,这就是应答位,这个上升沿,就是应答位结束后,从机释放SDA产生的,从机交出了SDA的控制权,因为从机要在低电平尽快变换数据,所以这个上升沿和SCL下降沿,几乎是同时发生的,然后继续往后,由于之前我们读写位给了0,所以应答结束后,我们要继续发送一个字节,同样的时序再来一遍,第二个字节,就可以送到指定设备的内部了,就表示,我要在0x19地址下,写入0x AA,最后是接收应答位,如果主机不需要继续传输了,就可以产生停止条件,在停止条件之前,先拉低SDA,为后续SDA的上升沿做准备,然后释放SCL,再释放SDA,这样就产生了SCL高电平期间,SDA的上升沿,这样一个完整的数据帧就拼接完成了;
从机应答之后,从这里开始数据的传输方向就要反过来了,因为刚才主机发了读的指令,所以这之后,主机就不能继续发送了,要把SDA的控制权交给从机,主机调用接收一个字节的时序,进行接收操作,然后到这一块,从机就得到了主机的允许,可以在SCL低电平期间写入SDA,然后主机在SCL高电平期间读取SDA,那最终,主机在SCL高电平期间依次读取8位,就接受到了从机发送的一个字节数据,0000 1111,那这个0x 0F是从哪个寄存器色数据呢,可以看到,在读的时序中,I2C协议的规定是,主机进行寻址时,一但读写标志位给1了,下一个字节就要立马转为读的时序,所以主机还来不及指定,我想要读哪个寄存器,就得开始接收了,所以这里就没有指定地址这个环节,那主机并没有指定寄存器的地址。
从机到低该发那个寄存器的数据呢,这就需要用到我们上面说的当前地址指针了,在从机中,所有的寄存器被分配到了一个线性区域中, 并且有单独的指针变量,指示着其中一个寄存器,这个指针上电默认,一般指向0地址,并且,每写入一个字节和读取一个字节后,这个指针就会自动自增一次,移动到下一个位置,那么在调用当前地址读的时序时,主机没有指定要读那个地址,从机就会返回当前指针指向的寄存器的值,那假设,我刚刚调用了这个指定地址写的时序,在0x19的位置写入了0xAA,那么指针就会+1,移动到0x1A的位置,我再调用这个当前读的时序,返回的就是0x1A地址下的值,如果再调用一次呢,返回的就是0x1B地址下的值,这就是当前地址读时序的操作逻辑。由于当前地址读并不能指定读的地址,所以这个时序用的不是很多。
对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data),那这个时序为什么能指定读的地址呢,前面的部分是指定地址写,后面的部分是当前地址读,
因为刚才制定了地址,所以再调用当前地址读,两者加在一起就是指定地址读了,所以,指定地址读的时序会复杂一些;
首先是起始条件,然后发送一个字节,进行寻址,这里指定从机地址是1101 0000,读写标志位是0,代表我要进行写的操作,进过从机应答之后,再发送一个字节,第二个字节,用来指定地址,这个数据就写入到了从机的地址指针里了,也就是说,从机接收到这个数据之后,它的寄存器指针就指向了0x19这个位置,之后,我们要写入的数据,不给它发,而是直接再来一个起始条件,这个SR(Start Repeat)的意思就是重复起始条件,相当于另起一个时序,因为制定读写标志位只能是跟着起始条件的第一个字节,所以如果想切换读写方向,只能再来个起始条件,然后起始条件后,重新寻址并且指定读写标志位,此时读写标志位是1,代表我要开始读了,接着主机接收一个字节,这个字节是不是就是0x19,地址下的数据,这就是指定地址读,另外在这里,你也可以加一个停止条件,这样也行,这样的话,就是两个完整的时序了,先起始,写入地址,停止,因为写入的地址会存在地址指针里面,所以这个地址并不会因为时序的停止而消失,我们就可以再起始,读当前位置,停止,这样两条时序也可以完成任务,但是I2C协议规定的符合格式是一整个数据帧,就是先起始,再重复起始,再停止,相当于把两条时序拼接在一条了,这就是3个I2C完整时序的介绍了。
现在介绍的:指定地址写,只是写一个字节,当前地址读和指定地址读,也都是读一个字节,那进阶版本就是指定地址读多个字节,当前地址读多个字节,和指定地址读多个字节,时序上和这些都非常相似,只需要增加一些小细节就行:在这里,指定地址,然后写入一个字节,如果你只想写一个字节,那就停止,就行了,如果你想写多个字节,就可以把最后一部分,多重复几次,比如这里,重复三遍发送一个字节和接受应答, 这样第一个数据就写入到了指定地址0x19的位置,然后不要忘了刚才说的,写入一次数据后,地址指针会自动加1,变成0X1A,所以这第二个数据就写入到了0x1A的位置,同理第三个数据就写入的是0x1B的位置,这样这个时序就进阶为在指定的位置开始,按顺序连续写入多个字节,比如你需要连续写入多个寄存器,就可以考虑这样来操作,这样在一条数据帧里,就可以同时写入多个字节,执行效率比较高,然后同理,当前位置读和指定位置读,也可以多次执行这最后一部分时序,由于地址指针在读后也会自增,所以这样就可以连续读出一片区域的寄存器,效率也会非常高。
如果你只想读一个字节就停止的话,在读完一个字节之后,一定要给从机发个非应答,非应答就是该主机应答的时候,主机不把SDA拉低,从机读到SDA为1,就代表主机没有应答,从机收到非应答之后,就知道主机不想要继续了,从机就会释放总线,把SDA控制器交还给主机,如果主机读完仍然给从机应答了,从机就会认为主机还想要数据,就会继续发送下一个数据,而这时,主机如果想产生停止条件,SDA可能就会因为从机被拽住了,而不能正常弹回高电平,这个注意一下,如果主机想连续读多个字节,就需要在最后一个字节给非应答,而之前的所用字节都要给应答,简单来说就是主机给应答 ,从机就会继续发,主机给非应答了,从机就不会再发了,交出SDA的控制权,从机控制SDA发送一个字节的权利,开始于读写标志为去,结束于主机给应答为1,这就是主机给从机发送应答位的作用。
问题
总结
本节课主要是了I2C的协议内容,硬件的层面和软件的层面。
相关文章:
STM32单片机入门学习——第31节: [10-1] I2C通信协议
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.10 STM32开发板学习——第31节: [10-1] I2C通信协议 前言开发板说明引用解答和科普一…...
OpenCV 图形API(24)图像滤波-----双边滤波函数bilateralFilter()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 应用双边滤波到图像。 该函数对输入图像应用双边滤波,如 http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Fil…...
【图像处理基石】什么是影调?并用python实现一个哈苏色彩影调
影调是摄影语言的核心,通过控制明暗、虚实与色彩,可精准传达创作意图。实际选择需结合主题情感、光线条件及画面结构,灵活运用高调、低调或冷暖色调,以强化视觉表现力。 一、影调的定义 影调指画面中明暗、虚实、色彩的层次与对比…...
MySQL行列转换
创建一个sc表并插入数据 方法一: select distinct uid, (select score from sc where s.uiduid and course语文)语文, (select score from sc where s.uiduid and course数学)数学, (select score from sc where s.uiduid and course英语)英语 from sc s; 方法二: select * fro…...
cookie和session哪个生成的时间早
Cookie 和 Session 的出现时间都可以追溯到 Web 开发的早期阶段,但它们的生成顺序在实际应用中通常是先生成 Session,然后通过 Cookie 来存储 Session ID。 详细解释: Session 的生成过程 • 用户请求服务器: • 用户首次访问网…...
sh脚本删除指定后缀.txt的文件,保留6个月的数据
1、linux下脚本删除指定后缀.txt和.path的文件,保留6个月的数据: 下面代码内容: #!/bin/bash # 指定要删除文件的路径列表 paths("/data/fail")# 获取当前系统日期6个月之前的日期 six_months_ago$(date -d "-6 months"…...
嵌入式Linux按键监控模块详解:实现设备重启与长按检测
嵌入式Linux按键监控模块详解:实现设备重启与长按检测 在嵌入式Linux设备开发中,物理按键仍然是用户与设备交互的重要方式。本文将分享一个轻量级但功能完整的按键监控模块,它可以精确识别按键的短按和长按事件,并执行对应操作如…...
[错误经验 坑]关于UDP服务器和客户端通信使用的recvfrom的输出型参数len没有被初始化导致的问题
[错误经验 坑]关于UDP服务器和客户端通信使用的recvfrom的输出型参数len没有被初始化导致的问题 水墨不写bug 文章目录 一、困惑:二、解答:(1)函数原型1. int sockfd2. void *buf3. size_t len4. int flags5. struct sockaddr *sr…...
KaiwuDB:面向AIoT场景的多模融合数据库,赋能企业数字化转型
引言 在万物互联的AIoT时代,企业面临着海量时序数据处理、多模数据融合和实时分析等挑战。KaiwuDB应运而生,作为一款面向AIoT场景的分布式、多模融合、支持原生AI的数据库产品,为企业提供了一站式数据管理解决方案。 产品概述 KaiwuDB是一…...
Web3 的云基础设施正在成型,Polkadot 2.0 用三项技术改写“上链成本”
在Web3基础设施内卷加剧的今天,“如何以更低成本、更大灵活性部署一条高性能应用链”正成为开发者们最关心的问题。而刚刚走出“技术慢热”误区的Polkadot,正在用一套名为 Polkadot 2.0 的架构升级方案,重新定义这一问题的解法。 这套升级最…...
Elasticsearch 学习规划
Elasticsearch 学习规划 明确学习目标与动机 场景化需求分析 - **S**:掌握Elasticsearch架构体系,熟练使用Elasticsearch 进行数据分析,Elasticsearch结合java 项目落地案例 - **M**:搜索和Elasticsearch相关GitHub项目 - **A**:每…...
OpenHarmony如何编译安装系统应用(以settings设置为例)
开发环境 1.OpenHarmony 2.DevEco Studio 3 .Full Sdk 实现步骤 1.获取设置应用源码 https://gitee.com/openharmony/applications_settings/tree/OpenHarmony-v5.0.0-Release/ 2,使用 DevEco Studio 和 Full SDK对系统应用进行签名,默认工程是未配置签名的状态,所构建…...
手撕 STL 之—— list
目录 引言 1, list_node类及其构造函数 2, list类的创建 3, list基本功能函数 3_1, 构造函数 3_2,push_back 3_3,push_front 3_4, pop_back 3_5,pop_front 4,迭代器 (重点) 4_1,如何设…...
Med-R1论文阅读理解
论文介绍 这篇论文介绍了一个名为 Med-R1 的新方法,用于提升多模态视觉语言模型(VLM)在医学图像理解和推理任务中的泛化能力和可解释性。下面是对整篇论文的简洁总结: ⸻ 🧠 核心思想 • 当前医学 VLM 多依赖于监督…...
微服务相关
1.SpringCloud有哪些常用组件?分别是什么作用? 注册中心:nacos 负载均衡:rabbion/LoadBalancer 网关:gateway 服务熔断:sential 服务调用:Feign 2.服务注册发现的基本流程是怎样的&#x…...
Linux vagrant 导入Centos到virtualbox
前言 vagrant 导入centos 虚拟机 前提要求 安装 virtualbox 和vagrant<vagrant-disksize> (Linux 方式 Windows 方式) 创建一键部署centos 虚拟机 /opt/vagrant 安装目录/opt/VirtualBox 安装目录/opt/centos8/Vagrantfile (可配置网络IP,内存…...
Spring Boot MongoDB 分页工具类封装 (新手指南)
Spring Boot MongoDB 分页工具类封装 (新手指南) 目录 引言:为何需要分页工具类?工具类一:PaginationUtils - 简化 Pageable 创建 设计目标代码实现 (PaginationUtils.java)如何使用 PaginationUtils 工具类二:PageResponse<…...
第七章 指针
2024-04 2023-10 A 2023-04 2022-10 2022-04 2021-10 2021-04 2020-10 2020-04...
20年AB1解码java
P8706 [蓝桥杯 2020 省 AB1] 解码 - 洛谷 详细代码如下: import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner innew Scanner(System.in); // 接收输入的字符串char [] c in.next().toCharArray(); // 接收 还原的字符…...
《Java实战:密码加密算法实现与代码解析》
文章目录 一、需求背景二、代码逐模块解析1. 主程序入口2. 密码输入模块3. 加密处理模块4. 结果拼接模块 三、完整代码示例四、运行示例五、优化方向(下篇预告) 一、需求背景 实现一个4位数字密码的简单加密系统,规则如下: 输入…...
AllData数据中台升级发布 | 支持K8S数据平台2.0版本
🔥🔥 AllData大数据产品是可定义数据中台,以数据平台为底座,以数据中台为桥梁,以机器学习平台为中层框架,以大模型应用为上游产品,提供全链路数字化解决方案。 ✨杭州奥零数据科技官网…...
Jupyter notebook使用技巧
一、打开指定文件夹 在快捷方式目标中,使用如下代码 anaconda3\python.exe anaconda3\cwp.py anaconda3 anaconda3\python.exe anaconda3\Scripts\jupyter-notebook-script.py --notebook-dirD:\code\python...
6.3es新特性web worker
Web Worker 是 HTML5 提供的多线程技术,允许在浏览器后台创建独立线程执行 JavaScript 代码,解决主线程因耗时任务导致的 界面卡顿 问题。 核心特性 线程隔离:Worker 线程无法直接操作 DOM 或访问 window 对象通信机制:通过 pos…...
基于 OpenHarmony 5.0 的星闪轻量型设备应用开发——Ch3 设备驱动开发
写在前面:本篇是系列文章《基于 OpenHarmony 5.0 的星闪轻量型设备应用开发》的第 3 章。本篇从 GPIO、PWM、I2C、UART 以及 ADC 等方面对基于 OpenHarmony 5.0 的 WS63 设备驱动开发进行了详细的介绍。本篇的篇幅较长,建议先收藏再阅读。 3.1 OpenHarmo…...
iphone各个机型尺寸
以下是苹果(Apple)历代 iPhone 机型 的屏幕尺寸、分辨率及其他关键参数汇总(截至 2023年10月,数据基于官方发布信息): 一、标准屏 iPhone(非Pro系列) 机型屏幕尺寸(英寸…...
OfficePlus去掉PDF文件右键菜单里的PDF转换
今天在吾爱破解论坛看到一个求助帖,说是OfficePlus,安装后,PDF文件的右键菜单里多了PDF转换,想去掉,不知道怎么弄。底下的回复基本都是百度复制或者AI搜索出的答案,大致就是找注册表里CLASSID下的菜单栏相关…...
Linux驱动开发进阶(七)- DRM驱动程序设计
文章目录 1、前言2、DRAM(KMS、GEM)2.1、KMS2.2、GEM 3、DRM3.1、驱动结构体3.2、设备结构体3.3、DRM驱动注册3.4、DRM模式设置3.4.1、plane初始化3.4.2、crtc初始化3.4.3、encoder初始化3.4.4、connect初始化 4、示例说明5、DRM Simple Display框架6、DRM热插拔7、DRM中的plan…...
Parasoft C++Test软件单元测试_条件宏和断言宏使用方法的详细介绍
系列文章目录 Parasoft C++Test软件静态分析:操作指南(编码规范、质量度量)、常见问题及处理 Parasoft C++Test软件单元测试:操作指南、实例讲解、常见问题及处理 Parasoft C++Test软件集成测试:操作指南、实例讲解、常见问题及处理 进阶扩展:自动生成静态分析文档、自动…...
vue辅助工具(vue系列二)
目录 第一章、安装周边库1.1)状态管理:Pinia1.2)路由管理:Router1.3)HTTP 客户端:Axios1.4)UI 组件库:Element 第二章、下载Vue插件并安装2.1)安装开发者工具2.1.1&#…...
WPF 五子棋项目文档
WPF 五子棋项目文档 1. 项目概述 本项目是一个使用 Windows Presentation Foundation (WPF) 技术栈和 C# 语言实现的桌面版五子棋(Gomoku)游戏。它遵循 MVVM(Model-View-ViewModel)设计模式,旨在提供一个结构清晰、可…...
UniApp 实现兼容 H5 和小程序的拖拽排序组件
如何使用 UniApp 实现一个兼容 H5 和小程序的 九宫格拖拽排序组件,实现思路和关键步骤。 一、实现目标 支持拖动菜单项改变顺序拖拽过程实时预览移动位置拖拽松开后自动吸附回网格兼容 H5 和小程序平台 二、功能结构拆解以及完整代码 完整代码: <…...
谷歌推出统一安全平台-一个平台实现跨云网端主动防护
👋 今天要给大家带来一个超级棒的消息!谷歌云推出了全新的“谷歌统一安全平台”,感觉我们的网络安全问题有救啦!😄 随着企业基础设施变得越来越复杂,保护它们也变得越来越难。攻击面不断扩大,安…...
众趣科技丨沉浸式 VR 体验,助力酒店民宿数字化营销宣传
随着旅游季的到来,各地的旅游景区开始“摩拳擦掌”推出各种活动,吸引更多游客来此游玩。 自去年以来,冰雪游热度持续上升,尤其是对于满心期待的南方游客来说,哈尔滨仍是冰雪旅游的热门目的地。据美团数据显示ÿ…...
DAY05:【pytorch】图像预处理
1、torchvision 功能:计算视觉工具包 torchvision.transforms:常用的图像预处理方法torchvision.datasets:常用数据集的 dataset 实战,MINIST,CIFAR-10,ImageNet等torchvision.model:常用的模…...
真实企业级K8S故障案例:ETCD集群断电恢复与数据保障实践
背景描述 某跨境电商平台生产环境使用Kubernetes(v1.23.17)管理500微服务。某日机房突发市电中断,UPS未能及时接管导致: 3节点ETCD集群(v3.5.4)全部异常掉电 Control-Plane节点无法启动api-server 业务P…...
rbd块设备的id修改
背景 看到有这个需求,具体碰到什么场景了不太清楚,之前做过rbd的重构的研究,既然能重构,那么修改应该是比重构还要简单一点的,我们具体看下怎么操作 数据结构分析 rbd的元数据信息 [rootlab104 ~]# rbd create tes…...
WP最主题专业的wordpress主题开发
WP最主题(wpzui.com) WP最主题是一个提供高品质WordPress主题的平台。它注重主题的设计和功能,旨在为用户提供美观且实用的主题选择。其主题通常具有良好的用户体验、丰富的自定义选项以及优化的性能,能够满足不同类型的网站搭建…...
HomeAssistant本地化部署结合内网穿透打造跨网络智能家居中枢
文章目录 前言1. 添加镜像源2. 部署HomeAssistant3. HA系统初始化配置4. HA系统添加智能设备4.1 添加已发现的设备4.2 添加HACS插件安装设备 5. 安装cpolar内网穿透5.1 配置HA公网地址 6. 配置固定公网地址 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂…...
# 实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现
实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现 在当今数字化时代,计算机视觉技术正以前所未有的速度改变着我们的生活与工作方式。其中,人脸检测与分析作为计算机视觉领域的重要分支,已广泛应用于安防监控、智能交互、…...
SAP-ABAP:SAP的Open SQL和Native SQL详细对比
在SAP ABAP开发中,Open SQL和Native SQL是两种操作数据库的方式,它们的核心区别在于可移植性、功能范围及底层实现机制。以下是详细对比: 1. Open SQL:深入解析 1.1 核心特性 数据库抽象层 Open SQL 由 SAP 内核的 Database Interface (DBI) 转换为目标数据库的 SQL(如 …...
基于大模型构建金融客服的技术调研
OpenAI-SB api接口 https://openai-sb.com/ ChatGPT与Knowledge Graph (知识图谱)分享交流 https://www.bilibili.com/video/BV1bo4y1w72m/?spm_id_from333.337.search-card.all.click&vd_source569ef4f891360f2119ace98abae09f3f 《要研究的方向和准备》 https://ww…...
Python设计模式:命令模式
1. 什么是命令模式? 命令模式是一种行为设计模式,它将请求封装为一个对象,从而使您能够使用不同的请求、队列或日志请求,以及支持可撤销操作。 命令模式的核心思想是将请求的发送者与请求的接收者解耦,使得两者之间的…...
30天学Java第八天——设计模式
装饰器模式 Decorator Pattern 装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许通过动态地添加功能来扩展对象的行为,而不需要修改原有的类。 这种模式通常用于增强对象的功能,与继承相比,使用…...
Spring事务系列 三
Spring事务的传播机制 Spring事务系列 一-CSDN博客 Spring事务系列 二-CSDN博客 文章目录 系列文章目录 目录 文章目录 前言 一、Spring事务的传播机制 Propagation.REQUIRED: Propagation.SUPPORTS: Propagation.MANDATORY: Propagation.REQUIRES_NEW: Propagation.NOT_SUPPO…...
文件上传做题记录
1,[SWPUCTF 2021 新生赛]easyupload2.0 直接上传php 再试一下phtml 用蚁剑连发现连不上 那就只要命令执行了 2,[SWPUCTF 2021 新生赛]easyupload1.0 当然,直接上传一个php是不行的 phtml也不行,看下是不是前端验证,…...
机器学习的监督学习与无监督学习
机器学习主要分为监督学习和无监督学习两大类,两者的核心区别在于数据是否带有标签(Label)。以下是它们的详细对比和说明: 1. 监督学习(Supervised Learning) 定义:通过带有标签的训练数据&…...
C++ 入门三:函数与模板
一、内联函数:编译期嵌入的 “高效函数” 1. 什么是内联函数? 核心特性:在编译阶段,内联函数的函数体会直接嵌入到调用它的代码中,避免了普通函数的调用开销(如压栈、跳转、返回)。语法&#…...
解析券商qmt的优缺点
现在已经对于大QMT进行了一步步的深入了解与学习,也已经开始积木式搭建策略,进行交易了,但是,随时不断的深入,发现的问题也越来越多。下面开始逐一解析: 首页 | 迅投知识库 这是详细的说明。 目前券商给大…...
CSE lesson2 chrony服务器
CSE lesson2 chrony服务器 timedatectl命令 NTP(network time protocal)网络时间协议,时钟服务器同步时间的时候会使用到该协议进行时间同步。 #关闭/开启时间同步服务 [rootlocalhost ~]# timedatectl set-ntp 0/1#设置时间(必须关闭时间同步服务才能…...