STM32 I2C详解

这篇具有很好参考价值的文章主要介绍了STM32 I2C详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

STM32 I2C详解

I2C简介

  • I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线

  • 两根通信线:

    • SCL(Serial Clock)串行时钟线,使用同步的时序,降低对硬件的依赖,同时同步的时序稳定性也比异步的时序更高。
    • SDA(Serial Data)串行数据线,半双工,一根线兼具发送和接收,最大化利用资源。
  • 同步,半双工

  • 带数据应答

  • 支持总线挂载多设备(一主多从、多主多从)

    • 一主多从 单片机作为主机,主导I2C总线的运行,挂载在I2C总线的所有外部模块都是从机,从机只有被主机点名之后才能控制I2C总线,不能在未经允许的情况下去碰I2C总线,防止冲突。

    这就像是在教室里,老师是主机主导课程的进行,所有学生都是从机,所有从机可以同时被动地听老师讲课,但是从机只有在被老师点名之后才能说话,不可以在未经允许的情况下说话,这样课堂才能有条不紊地进行。

    • 多主多从 在总线上任何一个模块都可以主动跳出来,成为新的主机。

    这就像是在教室里,老师正在讲课,突然有个学生站起来说,老师打断一下,接下来让我来说,所有同学听我指挥,但是,同一个时间只能有一个人说话。这时就相当于发生了总线冲突,在总线冲突时,I2C协议会进行仲裁,仲裁胜利的一方取得总线控制权,失败的一方自动变成从机。由于时钟线也是由主机控制的,所以在多主机的模型下,还要进行时钟同步。

硬件电路

  • 所有I2C设备的SCL连在一起,SDA连在一起

  • 设备的SCL和SDA均要配置成开漏输出模式

  • SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

  • 禁止所有设备输出强上拉的高电平,采用外置弱上拉电阻和开漏输出的结构。

    • 完全杜绝了电源短路的现象,保证电路的安全。
    • 避免了引脚模式的频繁切换。
    • 这个模式会有一个线与的现象,只要有任意一个或多个设备输出了低电平,总线就处于低电平,只有所有的设备都输出高电平,总线才处于高电平,I2C可以利用这个电路特性,执行多主机模式下的时钟同步和总线仲裁。

一主多从模型

  • 主机 CPU就是单片机,作为总线的主机,主机的权利很大,包括对SCL线的完全控制,任何时候,都是主机完全掌控SCL线,另外在空闲状态下,主机可以主动发起对SDA的控制,只有在从机发送数据和从机应答的时候,主机才会转交SDA的控制权给从机。
  • 从机 被控IC也就是挂载在I2C总线上的从机,这些从机可以是姿态传感器、OLED、存储器、时钟模块等等,从机的权利比较小,对于SCL时钟线,在任何时刻都只能被动地读取,从机不允许控制SCL线,对于SDA数据线,从机不允许主动发起对SDA的控制,只有在主机发送读取从机的命令后,或者从机应答的时候,从机才能短暂地取得SDA的控制权。

STM32 I2C详解,stm32,嵌入式硬件,单片机

STM32 I2C详解,stm32,嵌入式硬件,单片机

I2C时序基本单元

  • 起始条件:SCL高电平期间,SDA从高电平切换到低电平

I2C总线处于空闲状态时,SCL和SDA都处于高电平状态,也就是没有任何一个设备去碰SCL和SDA,SCL和SDA由外挂的上拉电阻拉高至高电平,总线处于平静的高电平状态,当主机需要进行数据收发时,首先要打破总线的宁静,产生一个起始条件,这个起始条件就是:

  • SCL处于高电平不去动它,然后把SDA拽下来,产生一个下降沿,当从机捕获到这个SCL高电平,SDA下降沿信号时,就会进行自身的复位,等待主机的召唤。
  • 然后在SDA下降沿之后,主机要再把SCL拽下来,拽下SCL,一方面是占用这个总线,另一方面也是为了方便我们这些基本单元的拼接,除了起始和终止条件,每个时序单元的SCL都是以低电平开始,低电平结束。,这样这些单元拼接起来,SCL才能连续。

STM32 I2C详解,stm32,嵌入式硬件,单片机

  • 终止条件:SCL高电平期间,SDA从低电平切换到高电平

SCL先放手,回弹到高电平,SDA再放手,回弹到高电平,产生一个上升沿,这个上升沿触发终止条件,同时终止条件之后,SCL和SDA都是高电平,回归到最初的平静状态。

STM32 I2C详解,stm32,嵌入式硬件,单片机

这个起始和终止条件就类似串口时序里的起始位和停止位,一个完整的数据帧,总是以起始条件开始,终止条件结束,另外,起始和终止,都是由主机产生的,从机不允许产生起始和终止,所以在总线空闲状态时,从机必须始终双手放开,不允许主动跳出来,去碰总线。

  • 主机发送一个字节: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位数据,也就是一个字节。

高位先行,所以第一位时一个字节的最高位bit 7,然后依次是次高位 bit 6,最后发送最低位 bit 0,这与串口不同,串口时序是低位先行,I2C是高位先行。

由于这里有时钟线进行同步,如果主机一个字节发送一半,突然进中断了,不操作SCL和SDA了,那时序就会在中断的位置不断拉长,SCL和SDA都暂停变化,传输也完全暂停,等中断结束后,主机回来继续操作,传输仍然不会出问题,这就是同步时序的好处。

由于这整个时序是主机发送一个字节,所以在这个单元里,SCL和SDA全程由主机掌控,从机只能被动读取

STM32 I2C详解,stm32,嵌入式硬件,单片机

  • 主机接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

释放SDA其实就相当于切换成输入模式,或者可以这样理解,所有设备包括主机都始终处于输入模式,当主机需要发送的时候,就可以主动去拉低SDA,而主机在被动接收的时候,就必须先释放SDA,不要去动它,以免影响别人发送,因为总线是线与的特征,任何一个设备拉低了,总线就是低电平,如果你接收的时候,还拽着SDA不放手,那别人无论发什么数据,总线都始终是低电平。

主机在接收之前要释放SDA,这时从机就取得了SDA的控制权,从机需要发送0,就把SDA拉低,从机需要发送1,就放手,SDA回弹到高电平,然后同样的,低电平变换数据,高电平读取数据,实线部分表示主机控制的电平,虚线部分表示从机控制的电平,SCL全程由主机控制,SDA主机在接收前要释放,交由从机控制,之后还是一样,因为SCL时钟是由主机控制的,所以从机的数据变换基本上都是贴着SCL下降沿进行的,而主机可以在SCL高电平的任意时刻读取,这就是接收一个字节的时序。
STM32 I2C详解,stm32,嵌入式硬件,单片机

  • 主机发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答。

主机在接收从机发来的一个字节之后,我们也要给从机发送一个应答位,发送应答位的目的是告诉从机,是否还要继续发送,如果从机发送一个数据之后,得到了主机的应答,那从机就还会继续发送,如果从机没得到主机的应答,那从机就会认为,我发送了一个数据,主机不想接收,这时从机就会释放SDA,交出SDA的控制权,防止干扰主机之后的操作。

STM32 I2C详解,stm32,嵌入式硬件,单片机

  • 从机接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)。

我们在调用发送一个字节之后,就要紧跟着调用接收应答的时序,用来判断从机有没有收到刚才给它的数据,如果从机收到了,那在应答位这里,主机释放SDA的时候,从机就应该立刻把SDA拉下来,然后在SCL高电平期间,主机读取应答位,如果应答位为0,就说明从机确实收到了。

STM32 I2C详解,stm32,嵌入式硬件,单片机

I2C时序

  • 指定地址写

  • 对于指定设备(Slave Address 从机地址),在指定地址(Reg Address 寄存器地址)下,写入指定数据(Data)

  • 空闲状态时,SCL和SDA都是高电平,然后主机需要给从机写入数据的时候,SCL高电平期间,拉低SDA,产生起始条件(Start S),在起始条件之后,紧跟着的时序,必须是发送一个字节的时序,字节的内容必须是从机地址+读写位,从机地址是7位,读写位是1位,加起来正好是1个字节8位,发送从机的地址,就是确定通信的对象,发送读写位,就是确认接下来是要写入还是要读出。

  • SCL低电平期间,SDA变换数据,SCL高电平期间,从机读取SDA,这里用绿色的线,来标明了从机读到的数据,比如一开始,从机收到的第一位就是高电平1,然后SCL低电平,主机继续变换数据,因为第二位还是1,所以这里SDA电平没有变换,然后SCL高电平,从机读到第二位是1,之后继续,低电平变换数据,高电平读取数据,第三位就是0,这样持续8次,就发送了一个字节数据,其中这个数据的定义是,高7位,表示从机地址,比如这个波形下,主机寻找的从机地址就是1101 000,这个就是MPU6050的地址。

  • 然后最低位,表示读写位,0表示,之后的时序主机要进行写入操作,1表示,之后的时序主机要进行读出操作,这里是0,说明之后我们要进行写入操作, 目前,主机是发送了一个字节,字节的内容转换为16进制,高位先行,就是0xD0,然后根据协议规定,紧跟着的单元,就得是接收从机的应答位(Receive Ack,RA),在这个时刻,主机要释放SDA,所以如果单看主机的波形,释放SDA之后,引脚电平回弹到高电平,但是根据协议规定,从机要在这个位拉低SDA,所以单看从机波形,从机该应答的时候,立刻拽住SDA,然后应答结束之后,从机再放开SDA,那现在综合两者的波形,结合线与的特性,在主机释放SDA之后,由于SDA也被从机拽住了,所以主机松手后,SDA并没有回弹高电平,这个过程就代表从机产生了应答,最终高电平期间,主机读取SDA,发现是0,就说明,主机进行寻址,从机给主机应答,传输没问题。如果主机读取SDA,发现是1,就说明,主机进行寻址,应答位期间,主机放开SDA,但是没有从机拽住SDA,没有从机给主机应答,那就直接产生停止条件,并提示一些信息,这就是应答位。

  • 然后这个上升沿,就是应答位结束之后,从机释放SDA产生的,从机交出了SDA的控制权,因为从机要在低电平尽快变换数据,所以这个SDA的上升沿和SCL的下降沿,几乎是同时发生的,由于之前我们读写位给了0,所以应答结束后,我们要继续发送一个字节,同样的时序再来一遍,第二个字节,就可以送到指定设备的内部了,从机设备可以自己定义第二个字节和后续字节的用途,一般第二个字节可以是寄存器地址或者是指令控制字等,比如MPU6050定义的第二个字节就是寄存器地址,比如AD转换器,第二个字节可能就是指令控制字,比如存储器,第二个字节可能就是存储器地址,图示这里,主机发送这样一个波形,数据位0001 1001,即,主机向从机发送了0x19这个数据,在MPU6050中就表示,主机要操作0x19地址下的寄存器了,接着同样,是从机应答,主机释放SDA,从机拽住SDA,SDA表现为低电平,主机收到应答位为0,表示收到了从机的应答。

  • 然后继续,同样的流程再来一遍,主机再发送一个字节,这个字节就是主机想要写入到0x19地址下寄存器的内容了,比如我这里发送了0xAA的波形,就表示,主机要在0x19地址下,写入0xAA,最后是接收应答位,如果主机不需要继续传输了,就可以产生停止条件(Stop,P),在停止条件之前,先拉低SDA,为后续SDA的上升沿做准备,然后释放SCL,再释放SDA,这样就产生了SCL高电平期间,SDA的上升沿,这样一个完整的数据帧就拼接完成了。

这个数据帧的目的就是,对于指定从机地址为1101000的设备,在其内部0下0x19地址的寄存器,写入0xAA这个数据,这就是指定地址写的时序。

  • 如果想写入多个字节,就可以重复多次发送一个字节和接收应答,这样第一个数据就写入到了指定地址0x19的位置,注意,写入一次数据后,地址指针会自动+1,变成0x1A,所以第二个数据就写入到了0x1A的位置,同理第三个数据就写入的是0x1B的位置,以此类推,这样这个时序就进阶为,在指定的位置开始,按顺序连续写入多个字节,比如你需要连续写入多个寄存器,就可以考虑这样操作,这样在一个数据帧里,就可以同时写入多个字节,执行效率会会比较高。
  • 同理当前位置读和指定位置读,也可以多次执行最后一部分时序,由于地址指针在读后也会自增,所以这样就可以连续读出一片区域的寄存器,效率也会非常高。
  • 注意,如果只想读一个字节就停止的话,在读完一个字节之后,一定要给从机发个非应答(Send Ack,SA),非应答,就是该主机应答的时候,主机不把SDA拉低,从机读到SDA为1,就代表主机没有应答,从机收到非应答之后,就知道主机不想要继续了,从机就会释放总线,把SDA控制权交还给主机,如果主机读完仍然给从机应答了,从机就会认为主机还想要数据,就会继续发送下一个数据,而这时,主机如果想产生停止条件,SDA可能就会因为被从机拽住了,而不能正常弹回到高电平,如果主机想连续读取多个字节,就需要在最后一个字节给非应答,而之前的所有字节都要给应答。
  • 简单来说就是,主机给应答了,主机就会继续发,主机给非应答了,从机就不会再发了,交出SDA的控制权,从机控制SDA发送一个字节的权利,开始于读写标志位为1,结束于主机给应答位1,这就是主机给从机发送应答位的作用。

STM32 I2C详解,stm32,嵌入式硬件,单片机

  • 当前地址读
  • 对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
  • 最开始,SCL高电平期间,拉低SDA,产生起始条件,起始条件开始后,主机必须首先调用发送一个字节,来进行从机的寻址和指定读写标志位,比如图示的波形,表示本次寻址的目标是1101 000的设备,同时最后一位读写位标志为1,表示主机接下来想要读取数据,紧跟着,发送一个字节之后,接受一下从机应答位,从机应答0,代表从机收到了第一个字节。
  • 在从机应答位之后,从这里开始,数据的传输方向就要反过来了,因为刚才主机发出了读的指令,所以这之后,主机就不能继续发送了,要把SDA的控制权交给从机,主机调用接收的一个字节的时序,进行接受操作,然后在这一块,从机就得到了主机的允许,可以在SCL低电平期间写入SDA,然后主机在SCL高电平期间读取SDA,那最终,主机在SCL高电平期间依次读取8位,就接收到了从机发送的一个字节数据,0000 1111也就是0xF,也就是0x0F。
  • 那现在问题就来了,这个0x0F是从机哪个寄存器的数据呢,我们看到,在读的时序中,I2C协议的规定是,主机进行寻址时,一旦读写标志位给了1,下一个字节就要立马转为读的时序,所以主机还来不及指定,我想要读哪个寄存器,就得开始接收了,所以这里就没有指定地址的这个环节,那主机并没有指定寄存器的地址,从机到底该发哪个寄存器的数据呢,这就需要用到我们上面说的当前地址指针了。
  • 在从机中,所有的寄存器被分配到了一个线性区域中,并且,会有一个单独的指针变量,指示着其中一个寄存器,这个指针上电默认,一般指向0地址,并且,每写入一个字节和读出一个字节后,这个指针就会自动自增依次,移动到下一个位置,那么在调用当前地址读的时序时,主机没有指定要读哪个地址,从机就会返回当前指针指向的寄存器的值,那假设,我刚刚调用了这个指定地址写的时序,在0x19的位置写入了0xAA,那么指针就会+1,移动到0x1A的位置,我再调用这个当前地址读的时序,返回的就是0x1A地址下的值,如果再调用一次,返回的就是0x1B地址下的值,依次类推,这就是当前地址读时序的逻辑。由于当前地址读,并不能指定读的地址,所以这个时序用的不是很多。

STM32 I2C详解,stm32,嵌入式硬件,单片机

  • 指定地址读

  • 对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)

  • 将指定地址写中的前面一部分指定地址的时序(把最后面的写数据这一部分去掉),然后把前面这一段设置地址,还没有指定些什么数据的时序,给它追加到这个当前地址读时序的前面,就得到了指定地址读的时序,一般我们也把它称作复合格式,下面的时序可分为两部分,前面一部分是指定地址写,但是只指定了地址,还没来得及写,后面的部分是当前地址读,因为我们刚指定了地址,所以再调用当前地址读,两者加在一起,就是指定地址读了。

  • 首先最开始,仍然是启动条件,然后发送一个字节,进行寻址,这里指定从机地址是1101000,读写标志位是0,代表我们要进行写操作,经过从机应答之后,再发送一个字节,第二个字节用来指定地址,这个数据就写入到从机的地址指针里了,也就是说,从机接收到这个数据之后,他的寄存器指针就指向了0x19这个位置。

  • 之后,我们要写入的数据不给他发,而是直接再来个起始条件,这个Sr(Start Repeat)的意思就是重复起始条件,相当于另起一个时序,因为指定读写标志位只能跟着起始条件的第一个字节,所以如果想切换读写方向,只能再来个起始条件,然后起始条件后,重新寻址并且指定读写标志位,此时读写标志位是1,代表我要开始读了,接着,主机接收一个字节,这个字节就是0x19下的数据,这就是指定地址读。

  • 另外,在Sr之前,也可以加一个停止条件,这样就是两个完整的时序了,先起始,写入地址,停止,因为写入的地址会存在地址指针里面,所以这个地址并不会因为时序的停止而消失,我们就可以再起始,都当前位置,停止,这样两条时序也可以完成任务,但是I2C协议官方规定的复合格式就是一整个数据帧,就是先起始再重复起始再停止,相当于把两条时序拼接成一条了。

STM32 I2C详解,stm32,嵌入式硬件,单片机

通信协议的时序是一个很重要的东西,我们只要理解清楚了这个时序的意义,就可以按照它协议的规定,去翻转通信引脚的高低电平,只要我们反转产生的这个时序波形,满足了通信协议的规定,那通信双方就能理解并解析这个波形,这样,通信自然而然就实现了。

软件I2C,手动拉低或释放时钟线,然后再手动对每个数据位进行判断,拉低或释放数据线,这样来产生时序波形。由于I2C是同步时序,对每一位的持续时间要求不严格,某一位的时间长点短点,或者中途暂停一会儿时序,影响都不大。

串口是异步时序,每一位的时间要求很严格,不能过长也不能过短,更不能中途暂停一会。

I2C 外设简介

  • STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担(由硬件电路来自动翻转引脚电平,软件只需要写入控制寄存器CR和数据寄存器DR,就可以实现协议了,为了实时监控时序的状态,软件还得读取状态寄存器SR,来获取外设电路的状态信息)。

  • 支持多主机模型(I2C通信,分为主机和从机,主机,就是拥有主动控制总线的权利,而从机,只能在主机允许的情况下,才能控制总线,在一主多从的模型下,只有唯一一个主机,可以挂载多个从机,主机操控所有从机。

    • 固定多主机 有两个或更多固定的主机,挂载多个从机,从机可以被任意一个主机控制,当有多个主机都想控制总线时,就是总线冲突状态,这时就要进行总线仲裁了,仲裁失败的一方让出总线控制权。

    • 可变多主机 可以在总线上挂载多个设备,总线上没有固定的主机和从机,任何一个设备,都可以在总线空闲时跳出来作为主机,然后指定其他任何一个设备进行通信,当这个通信完成之后,这个跳出来的主机就要退回到从机的位置。当有多个从机想要跳出来作为主机时,就是总线冲突状态,这时就要进行总线仲裁,仲裁失败的一方让出总线控制权。

  • 支持7位/10位地址模式

    • 7位地址只有128种情况,如果挂载的设备非常多,会不够用。如果一条总线必须要挂载128个以上的设备,那7位地址必然是不够用的,另外,如果有非常多的厂商都来申请I2C的地址,那也必然会有部分型号的芯片,它们的地址是一样的,对于不同芯片地址一样,由于地址的低位通常是可配置的,地址高位都一样,配置低位不一样即可,只要不在一个总线上挂载过多的设备就行。(另外确实需要很多的设备,条件允许的情况下,也可以开辟多条I2C总线,所以地址的问题,一般好解决)
    • 10位地址,最多就有1024种情况了
      • I2C起始之后的第一个字节,必须是寻址+读写位,这一个字节只能有7位地址,那只需要再规定,起始之后的前两个字节,都作为寻址,就可以实现10位地址模式了,并将第一个字节的前5位作为标志位。因为,当发送第一个字节之后,无法判断后面这个字节还是不是寻址,所以这就需要再第一个字节写个特定的数据,作为10位寻址模式的标志位,这个标志位就是11110,也就是如果你第二个字节也是寻址,那第一个字节的前5位就必须是11110,那么第一个字节剩下的两位,和第二个字节的8位都作为寻址,就实现了10位地址寻址。
  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)

  • 支持DMA 在多字节传输的时候可以提高传输效率,比如指定地址读多字节、写多字节的时序,如果想要连续读或写非常多的字节,那用一下DMA自动帮我们转运数据,可以提高整个过程的效率。

  • 兼容SMBus协议 SMBus(System Management Bus),系统管理总线,SMBus是基于I2C总线改进而来的,主要用于电源管理系统中。

  • STM32F103C8T6 硬件I2C资源:I2C1、I2C2

I2C框图

  • I2C的通信引脚SDA和SCL,(SMBus的通信引脚SMBALERT),这种外设模块引出来的引脚,一般都是借用GPIO口的复用模式与外部电路相连,查询引脚定义表可知,I2C2_SCL和I2C2_SDA分别复用在了PB10和PB11这两个端口,I2C1_SCL和I2C1_SDA分别复用在了PB6和PB7这两个端口,另外I2C1这两个引脚,还可以重映射到PB8和PB9这两个引脚。
  • I2C是半双工,数据收发是同一组数据寄存器和移位寄存器
    • 当我们需要发送数据时,可以把一个字节数据写到数据寄存器DR,当移位寄存器没有数据移位时,这个数据寄存器中的值就会转到移位寄存器里,在移位的过程中,就可以直接把下一个数据放到数据寄存器里等待了,一旦前一个数据移位完成,下一个数据就可以无缝衔接,继续发送,当数据由数据寄存器转到移位寄存器时,就会置状态寄存器的TXE位为1,表示发送寄存器为空,这就是发送的流程。
    • 当我们要接收数据时,输入的数据,一位一位地从引脚移入到移位寄存器里,当一个字节的数据收齐之后,数据就整体地从一位寄存器转到数据寄存器,同时置标志位RXNE,表示接收寄存器非空,这时我们就可以把数据从数据寄存器读出来了。
    • 至于什么时候收发数据,需要我们写入控制寄存器的对应位进行操作,对于起始条件、终止条件、应答位也都有控制电路可以完成。
  • 比较器和地址寄存器是从机模式使用的,因为STM32的I2C是基于可变多主机模型设计的,STM32不进行通信的时候,就是从机,既然作为从机,就可以被主机召唤,所以就应该有从机地址,而这个从机地址就可以由自身地址寄存器指定,我们可以自定一个从机地址,写到这个寄存器。当STM32作为从机,在被寻址时,如果收到的寻址通过比较器判断,和自身地址相同,那STM32就作为从机,响应外部主机的召唤,并且STM32支持同时响应两个从机地址,所以就有自身地址寄存器和双地址寄存器。
  • 数据校验模块,当我们发送一个多字节的数据帧时,硬件可以自动执行CRC校验计算,CRC是一种很常见的数据校验算法,它会根据前面这些数据,进行各种数据运算,然后会得到一个字节的校验位,附加在这个数据帧的后面,在接收到这一帧数据后,STM32的硬件也可以自动执行校验的判定,如果数据在传输的过程中出错了,CRC检验算法就通不过,硬件就会置校验错误标志位,告知数据错误。
  • 时钟控制,用来控制SCL线,在时钟控制寄存器写对应的位,电路就会执行对应的功能。
  • 控制逻辑寄存器,写入控制寄存器,可以对整个电路进行控制。
  • 读取状态寄存器,可以得知电路的工作状态。
  • 中断,当内部有一些标志位置1之后,可能事件比较紧急,就可以申请中断。如果开启了这个中断,那当这个事件发生后,程序就可以跳到中断函数来处理这个事件了。
  • DMA请求与响应,在进行很多字节的收发时,可以配合DMA来提高效率。

STM32 I2C详解,stm32,嵌入式硬件,单片机

I2C基本结构

  • 移位寄存器和数据寄存器DR的配合,是通信的核心部分。由于I2C是高位先行,所以这个移位寄存器是向左移位。
    • 在发送的时候,最高位先移出去,然后是次高位等等,一个SCL时钟,移位一次,移位8次,这样就能把一个字节,由高位到低位,依次放到SDA线上了。
    • 在接收的时候,数据通过GPIO口从右边依次移进来,最终移8次,一个字节就接收完成了。
  • GPIO口这里,使用硬件I2C的时候,都要配置成复用开漏输出的模式,复用就是GPIO口的状态是交由片上外设来控制的,开漏输出是I2C协议要求的端口配置,即使配置成开漏输出模式,GPIO口也是可以进行输入的。
  • SCL这里,时钟控制器通过GPIO去控制时钟线。
  • SDA这里,输出数据,通过GPIO,输出到端口,输入数据,也是通过GPIO,输入到移位寄存器。
    • 开漏输出时,P_MOS无效,移位寄存器输出的数据,通向GPIO,就接在了来自片上外设——复用功能输出这个位置,之后控制N_MOS的通断,进而控制这个I/O引脚,是拉低到低电平还是释放悬空。
    • 对于输入部分,可以看到,虽然这是复用开漏输出,但是输入这一路仍然有效,I/O引脚的高低电平,通过复用功能输入,进入片上外设,来进行复用功能的输入。
  • 开关控制 I2C_Cmd()使能外设

STM32 I2C详解,stm32,嵌入式硬件,单片机

STM32 I2C详解,stm32,嵌入式硬件,单片机

主机发送

  • 初始化之后,总线默认空闲状态,STM32默认是从模式,为了产生一个起始条件,需要写入控制寄存器(在START位写1),之后STM32由从模式转为主模式。
  • 控制完硬件电路之后,我们要检查标志位,来看看硬件有没有达到我们想要的状态,在这里,起始条件之后,会发生EV5事件,这个EV5事件,可以当作是标志位。(EV5事件,就是SB(Start Bit)标志位置1,SB是状态寄存器的一个位,表示了硬件的状态,置一代表起始条件已发送,软件读取SR1寄存器后,也就是查看了该位,然后写数据寄存器的操作将清楚该位)

手册中都是用EVx(Event)这几个事件来代替标志位的。为什么要设计这个EVx事件,而不直接产生标志位呢,这是因为,有的状态会同时产生多个标志位,所以这个EVx事件,就是组合了多个标志位的一个大标志位,在库函数中,也就对应的,检查EVx事件是否发生的函数,把它当成一个大标志位即可。

  • 检测起始条件已发送时,就可以发送一个字节的从机地址了,从机地址需要写到数据寄存器DR中,写入DR之后,硬件电路就会自动把这一个字节,转到移位寄存器里,再把这一个字节发送到I2C总线上,之后硬件会自动接收应答并判断,如果没有应答,硬件就会置应答失败的标志位,这个标志位可以申请中断来告知我们。当寻址完成之后,会发生EV6事件(EV6事件,就是ADDR标志位为1,表示地址发送结束),EV6事件结束后,是EV8_1事件(EV8_1事件就是TxE标志位=1,移位寄存器空,数据寄存器空),这时需要我们写入数据寄存器DR进行数据发送了,一旦写入DR之后,因为移位寄存器也是空,所以DR会立刻转到移位寄存器进行发送,这时就会发生EV8事件(EV8事件就是TxE标志位=1,移位寄存器非空,数据寄存器空),这时就是移位寄存器正在发送数据的状态。
  • 流程这里,数据1的时序就产生了,这个数据寄存器和移位寄存器的配合,就是发送的时候,数据先写入数据寄存器,如果移位寄存器没有数据,再转到移位寄存器进行发送。
  • 在EV8结束的位置,对应写入DR将清除该事件,所以在这个位置应该写入了下一个数据,也就是数据2,在这个时刻数据2就被写入到数据寄存器里等待了,接收应答位之后,数据2就转入移位寄存器进行发送,此时的状态是移位寄存器非空,数据寄存器空,所以这时,EV8事件就又发生了,EV8事件结束后,数据2还在移位发送,但此时下一个数据,已经被写道数据寄存器里等待了,之后再次应答,产生EV8事件,写入数据寄存器,EV8事件消失。按这个流程来,一旦我们检测到EV8事件,就可以写入下一个数据了。
  • 最后,当我们想要发送的数据读写完之后,这时就没有新的数据可以写入到数据寄存器了,当一位寄存器当前的数据移位完成时,此时就是移位寄存器空,数据寄存器也空的状态,这时就会产生EV8_2事件。

EV8_2 就是

  • TxE=1,也就是数据寄存器空
  • BTF (Byte Transfer Finished 字节发送结束标志位)=1,表示字节发送结束 。当一个新数据将被发送且数据寄存器还未被写入新数据,这个意思就是当前的移位寄存器已经移完了,该找数据寄存器要下一个数据了,但是数据寄存器没有数据,这就说明主机不想发了,这时就代表字节发送结束,是时候停止了。
  • 当检测到EV8_2时,就可以产生终止条件了。在控制寄存器CR中的STOP位写1,就会在当前字节传输或当前起始条件发出后产生终止条件。
  • 整个过程,简单来说就是,写入控制寄存器CR或数据寄存器DR,可以控制时序单元的发生,比如产生起始条件,发送一个字节数据,时序单元发生后,检查相应的EV事件,起始就是检查状态寄存器SR,来等待时序单元发送完成,然后依次按照这个流程,操作、等待、操作、等待……这样就能实现时序了。

STM32 I2C详解,stm32,嵌入式硬件,单片机

主机接收

  • 写入控制寄存器的START位,产生起始条件,然后等待EV5事件(起始条件已发送)
  • 寻址,接收应答,产生RV6事件(寻址已完成)
  • 发送数据1,代表数据正在通过移位寄存器进行输入,EV6_1事件(没有对应的事件标志,只适用于接收1个字节的情况),之后当这个时序单元完成时,硬件会自动根据我们的配置,把应答位发送出去。这个时序单元结束后,就说明移位寄存器就已经成功移入一个字节的数据1了,这时,移入的一个字节就整体转移到数据寄存器中,同时置RxNE标志位,表示数据寄存器非空,也就是收到了一个字节的数据。这个状态就是EV7事件(EV7事件 就是RxNE=1,数据寄存器非空,读DR寄存器清除该事件,也就是,收到数据,当我们把数据读走之后,EV7事件结束)
  • EV7事件结束后,说明数据1被读走,当然数据1还没读走的时候,数据2就可以直接移入移位寄存器了,之后,数据2移位完成,收到数据2,产生EV7事件,读走数据2,EV7事件结束。按照这个流程就可以一直接收数据了。
  • 当我们不需要继续接收时,需要在最后一个时序单元发生时,提前把应答位控制寄存器ACK置0,并且设置终止条件请求,这就是EV7_1事件(设置ACK=0和STOP请求),也就是我们想要结束了。在这个时序完成后,由于设置了ACK=0,所以就会给出非应答,最后,由于设置STOP位,所以产生终止条件,这样接收一个字节的时序就完成了。
  • 整体流程,写入控制寄存器CR和读取数据寄存器DR,产生时序单元,然后等待相应事件,来确保时序单元的完成。

如何配置是否要给应答,在控制寄存器CR中的ACK(应答使能)位写1,表示,在接收一个字节后返回一个应答(匹配的地址或数据),写0,表示无应答返回。

STM32 I2C详解,stm32,嵌入式硬件,单片机

软件/硬件波形对比(指定地址读)

软件I2C

  • 由于操作引脚之后,都加了延时,这个延时有时候加的多,有时候加的少,所以软件时序的时钟周期、占空比可能不规整,不过由于I2C是同步时序,这些不规整也没有什么影响。
  • SCL低电平写,高电平读,虽然整个低电平的任意时候都可以写,整个高电平的任意时刻都可以读,但是一般要求保证尽早的原则,所以可以直接认为是SCL下降沿写,SCL上升沿读。软件I2C在下降沿之后,因为操作端口之后有一些延时,所以等了一会才进行写入操作,SDA才变换数据。
  • 应答结束,从机在SCL下降沿立刻释放了SDA,但是软件I2C的主机,过了一会儿才变换数据,因此会出现一个短暂的高电平。

STM32 I2C详解,stm32,嵌入式硬件,单片机

硬件I2C

  • 硬件I2C的波形更加规整,每个时钟的周期、占空比都非常一致。
  • 数据的写入,都是紧贴下降沿的,SCL下降沿,SDA马上切换数据。
  • 应答结束后,SCL下降沿,从机立刻释放SDA,同时zhu机也立刻拉低SDA。所以这里就出现一个小尖峰。

STM32 I2C详解,stm32,嵌入式硬件,单片机文章来源地址https://www.toymoban.com/news/detail-754500.html

到了这里,关于STM32 I2C详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32学习】——STM32-I2C外设&硬件读写MPU6050&软硬件读写波形对比

    目录 前言 一、I2C外设 二、硬件I2C操作流程 1.主机发送时序 3.其他时序

    2024年02月10日
    浏览(49)
  • STM32F407系列硬件I2C笔记

    STM32F407系列有3个硬件I2C: I2C1:该接口位于GPIOB引脚上,包括PB6(I2C1_SCL)和PB7(I2C1_SDA)。 I2C2:该接口位于GPIOB引脚上,包括PB10(I2C2_SCL)和PB11(I2C2_SDA)。 I2C3:该接口位于GPIOA和GPIOC引脚上,包括PA8(I2C3_SCL)和PC9(I2C3_SDA)。   硬件I2C的速度比软件I2C更快,硬件I2C通常可以

    2024年02月04日
    浏览(53)
  • STM32 SHT40驱动源码(使用硬件I2C)

    目录 简介: SHT40.c: SHT40.h 测试结果:         SHT40是瑞士Sensirion公司推出的第四代温湿度传感器,内部集成加热器用于去除表面微小液滴。集成I2C接口,典型的相对湿度精度1.8%RH,典型温度精度0.2℃,运行在0-100%RH和-40-125℃的环境中。 主控:STM32H7B0VBT6 平台:STM32CubeIDE SHT4

    2024年03月19日
    浏览(75)
  • STM32F030硬件I2C代码及解析

    刚接触STM32的时候,第一个学习的就是I2C,当时去网上学习别人写得I2C代码,虽然能用,但是当时并不理解为什么要这么配置,特别希望有人把代码掰碎了讲讲看,今天突然想起来,就把以前写的I2C代码拿出来掰碎了捋捋,希望对新手有些帮助。 先说说STM32的I2C: ST的M3系列还

    2024年02月08日
    浏览(48)
  • STM32 硬件IIC 控制OLED I2C卡死问题

    #更新通知:2023-09-06 STM32L151 固件库 使用I2C 太难了,又宕机了,建议不要在固件库版本上尝试硬件IIC 了,一般人真用不了,直接使用软件模拟的,或者不要使用固件库了,用HAL 库吧,据说HAL 库没这么多问题,不死心的我还是死心了,等有空再研究吧 3.1 I2C模式,我这里选的

    2024年02月09日
    浏览(51)
  • STM32 I2C详解

    I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线 两根通信线: SCL(Serial Clock)串行时钟线,使用同步的时序,降低对硬件的依赖,同时同步的时序稳定性也比异步的时序更高。 SDA(Serial Data)串行数据线,半双工,一根线兼具发送和接收,最大化利用资源。 同步,半

    2024年02月05日
    浏览(54)
  • STM32配合cubeMX硬件I2C驱动0.96寸OLED

    目录 一、简单介绍 1.1   OLED 1.2   I2C协议 二、实战 2.1 工程配置 2.2 测试工程 2.3 波形分析 三、驱动OLED 3.1 初始化代码 3.2 清屏函数 3.3 设置坐标函数 3.4 显示字符函数 3.5 显示字符串函数 3.6 显示图片函数 附录 驱动代码文件 oled.c oled.h f6x8.h 有机发光二极管 (英语:Organic

    2024年02月08日
    浏览(64)
  • 01_STM32软件+硬件I2C读取MPU6050(HAL库)

    目录 1、I2C简介 2、I2C时序单元 2.1 起始条件 2.2 终止条件 2.3 发送一个字节 2.4 接收一个字节 2.5 发送应答 2.6 接收应答 3、I2C完整时序 3.1 指定地址写一个字节 3.2 当前地址读一个字节 3.2 指定地址读一个字节 4、简单软件I2C代码(HAL) 4.1 软件I2C 4.2 软件I2C读MPU6050寄存器 5、ST

    2024年04月17日
    浏览(49)
  • RT-Thread在STM32硬件I2C的踩坑记录

    参考文章: 1.将硬件I2C巧妙地将“嫁接”到RTT原生的模拟I2C驱动框架 2.基于STM32F4平台的硬件I2C驱动实现笔记 3.《rt-thread驱动框架分析》- i2c驱动   最近打算用RT-Thread做一个小demo玩玩,其中需要用I2C通信驱动一个oled屏幕,但是找了一圈也没找到RTT中对硬件I2C的支持方式以及

    2024年02月11日
    浏览(74)
  • STM32F407硬件I2C实现MPU6050通讯(CUBEIDE)

    工程代码 https://download.csdn.net/download/weixin_52849254/87886714 I2C1通道可选择三种不同的通讯协议:I2C、SMBus-Alert-mode、SMBus-two-wire-Interface。 SMBus (System Management Bus,系统管理总线), 为系统和电源管理这样的任务提供了一条控制总线,SMBus与I2C总线之间在时序特性上存在一些差别 修改

    2024年02月09日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包