单片机学习!
目录
前言
一、I2C外设简介
1.1 硬件I2C外设
1.2 硬件I2C功能
1.2.1 多主机模型
1.2.2 7位地址和10位地址的模式
1.2.3 通讯速度
1.2.4 支持DMA
1.2.5 兼容SMBus协议
1.2.6 芯片型号资源
二、I2C框图
2.1 引脚
2.2 SDA数据控制
2.3 SCL时钟控制
三、I2C基本结构
四、硬件I2C操作流程
4.1 主机发送
4.2 主机接收
五、软件I2C和硬件I2C波形对比
总结
前言
之前博文讲述了I2C的协议规定和通信意义,其中通信协议的时序是一个很重要的东西:
- 只要理解清楚时序的意义,就可以按照协议的规定去翻转通信引脚的高低电平。
- 只要翻转产生的时序波形满足通信协议的规定,那通信双方就能理解并解析这个波形。
这样通信就实现了。
使用软件I2C,是通过手动拉低或释放时钟线,然后再手动对每个数据位进行判断、拉低或释放数据线,这样来产生波形。由于I2C是同步时序,对每一位的持续时间要求不严格,某一位的时间长点、短点或者中途暂停一会儿时序影响都不大。所以I2C是比较容易用软件模拟的,在实际项目中,软件模拟的I2C也是非常常见的。
但是作为一个协议标准,I2C通信也是可以有硬件收发电路的,就像之前博文的USART串口通信一样,先分析了串口的时序波形,但是在程序中并没有用软件去手动翻转电平来实现这个波形。
- 这是因为串口是异步时序,每一位的时间要求很严格,不能过长也不能过短,更不能中途暂停一会儿。串口时序虽然可以用软件模拟,但是操作起来比较困难,所以也没有列出软件模拟串口的代码。
- 另外由于串口的硬件收发器在单片机中普及程度非常高,基本上每个单片机都有串口的硬件资源,而且硬件实现的串口使用起来还非常简单,所以串口通信基本都是借助硬件收发器来实现的。
硬件USART串口的使用流程,也是之前博文描述过的内容回顾一下:首先配置USART外设,然后写入数据寄存器DR,这时硬件收发器就会自动生成波形发送出去,最后等待发送完成的标志位即可。这就是硬件实现串口的方法。
同样I2C也是可以有软件模拟和硬件收发器自动操作这两种实现方式。
- 对于串口这样的异步时序,软件实现非常麻烦;硬件实现非常简单。所以串口的实现基本是全都倒向硬件了。
- 而对于I2C这样的同步时序来说,软件实现反而简单且灵活,硬件实现却不能让人完全省心。所以I2C的实现软件模拟还是非常多的。
但是考虑到硬件I2C也有独有的优势,比如,执行效率比较高,可以节省软件资源,功能比较强大,可以实现完整的多主机通信模型,时序波形规整,通信速率快等等。所以硬件I2C也是有相应的应用场景的。
- 如果只是简单应用可以选择比较灵活的软件I2C。
- 如果对性能指标要求比较高就可以考虑一下硬件I2C。
本文就主要分析一下I2C的硬件实现,也就是STM32内部的I2C外设。
一、I2C外设简介
- STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
- 支持多主机模型
- 支持7位/10位地址模式
- 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
- 支持DMA
- 兼容SMBus协议
- STM32F103C8T6 硬件I2C资源:I2C1、I2C2
1.1 硬件I2C外设
STM32内部集成了硬件I2C收发电路。就像USART外设,它是串口通信的硬件收发器,这里I2C的外设就是I2C通信的硬件收发器。有了硬件收发器之后就可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能。也就是由硬件电路来自动翻转引脚电平,软件只需要写入控制寄存器CR和数据寄存器DR就可以实现协议了。
当然为了实时监控时序的状态,软件还得读取状态寄存器SR ,来了解外设电路当前处于什么状态。
这就像是开车一样:
- 写入控制寄存器CR就像是踩油门、打方向盘来控制汽车的运行;
- 读取状态寄存器SR就像是观看仪表盘,来了解汽车的运行状态。
有了这些寄存器就可以完全掌握外设电路的运行了。STM32有库函数封装之后,运行操作就更简单了。
有了I2C外设的存在,硬件自动实现时序就可以减轻CPU的负担,节省软件资源。另外由硬件来做这些可以更加专注,时序生成的性能、效率也会更高。这就是I2C外设存在的意义,就是用硬件电路实现I2C通信。
1.2 硬件I2C功能
STM32的I2C外设:
- 支持多主机模型;
- 支持7位/10位地址模式;
- 支持不同的通讯速度,其中标准速度高达100kHz,快速高达400kHz;
- 支持DMA;
- 兼容SMBus协议。
以上是硬件I2C的功能指标,本文只重点介绍一主多从、7位地址的I2C。对于多主机模型,10位地址等可以通过手册进一步学习,这里只是大概介绍一下。
1.2.1 多主机模型
I2C通信,分为主机和从机,主机就是拥有主动控制总线的权利。从机只能在主机允许的情况下才能控制总线。
在一主多从的模型下就是I2C总线上挂载唯一的主机,挂载的从机有多个。一主多从的模型就比较容易操作,主机一个人掌控所有,所有从机都听主机的命令,不存在权力冲突。
进阶版的I2C还设计了多主机的模型,对于多主机模型,又可以分为固定多主机和可变多主机。
固定多主机就是在I2C总线上有两个或更多个固定的主机,挂载的从机也为固定从机。
这种状态就像是在教室里,讲台上同时站了多个老师,下面坐的所有学生可以被任意一个老师点名。老师可以主动发起对学生的控制,学生不能去控制老师。当两个老师同时想说话时,就是总线冲突状态,这时就要进行总线仲裁了。仲裁失败的一方让出总线控制权。这种讲台上站多个老师的情况就是固定多主机。
可变多主机模型就是在I2C总线上挂载了多个设备,总线上没有固定的主机和从机。任何一个设备都可以在总线空闲时跳出来作为主机,然后指定其他任何一个设备进行通信,当这个通信完成之后,跳出来的这个主机就要退回到从机的位置。
这个状态就像是在教室里,只有一堆学生,没有老师,默认情况下所有学生都是从机,都不能说话,当有某个学生想说话时,就得跳出来,变成主机然后指定其他任何一个学生进行通信,通信完成后再坐下,变为从机。当有多个学生同时跳出来时,就是总线冲突状态,这时就要进行总线仲裁,仲裁失败的一方让出总线控制权。那这种所有设备一视同仁,谁要做主机,谁就跳出来的模型,就是可变多主机。
对于STM32的I2C而言,使用的就是可变多主机的模型,虽然只需要一主多从,没有其他设备跟STM32去争主机的位置,但是STM32是按照可变多主机的模型设计的,所以还是要按照谁要做主机,谁就跳出来的思路来操作。这就是STM32的I2C的多主机设计。
1.2.2 7位地址和10位地址的模式
之前博文的I2C时序图介绍的是7位地址的模式,
也就是起始条件之后,紧跟的一个字节必须是7位地址+读写位。这种7位地址是最简单最常见的。
但是很显然,7位地址只有128种情况,如果设备非常多,那就不够用了。
- 如果一条总线必须要挂载128个以上的设备,那7位地址必然是不够用的。
- 另外如果有非常多的厂商都来申请I2C的地址,那也必然会有部分型号的芯片,他们的地址是一样的。不过对于不同芯片地址一样,其实也好办,因为地址的低位通常是可配置的,前面几位都一样,后面几位可配置的配置为不一样就行了,只要不在一个总线上挂载过多的设备就行。
- 另外即使确实需要很多设备,条件允许的情况下,也可以开辟多条I2C总线。
所以地址的问题一般好解决。当然在协议上,分配更多的地址也是一种可行的方案。
STM32的I2C总线就支持10位地址模式,10位地址最多就有1024种可能了。
10位地址的设计:I2C起始之后的第一个字节必须是寻址+读写位,所以这第一个字节只能有7位地址。那只需要再规定,起始之后的前两个字节都作为寻址就行了,这就是10位地址的基本思路。
那10位地址占用两个字节的设计,第一个字节有7个空位,第二个字节有8个空位,按理说加一起是15位地址。但是I2C只有10位地址模式,而剩下还有5位的数据去当标志位了。
因为在发送第一个字节之后。不能确定后面这个字节是不是寻址位,所以这就需要在第一个字节写个特定的数据,作为10位寻址模式的标志位。这个标志位就是11110,也就是如果第二个字节也是寻址,那第一个字节的前5位就必须是11110。
- 如果第一个字节的前五位不是11110就说明这个是7位寻址。看上文时序图,波形前五位是11010,就说明这时7位寻址的。
- 如果第一个字节前5位是11110,那么第一个字节剩下的两位和第二个字节的8位就都作为寻址,一共就是10位地址了。
当然11110开头的地址作为10位地址模式的标志位,是不会在7位地址模式下出现的。以上就是7位地址和10位地址的区别。
1.2.3 通讯速度
支持不同的通讯速度,标准速度高达100kHz,快速高达400kHz。速度是协议规定的标准速度,也就是说如果某个设备声称支持快速的I2C,那它就支持最大400kHz的时钟频率。当然作为一个同步协议,这个时钟并不严格,所以只要不超过最大频率,多少速度都可以,所以一般频率的具体值关注都不多。
1.2.4 支持DMA
支持DMA,DMA可以在多字节传输的时候可以提高传输效率。比如指定地址读多字节或写多字节的时序。如果想要连续读或写非常多的字节,那用DMA自动转运数据的话,这个过程的效率就会大大提升。当然如果只有几个字节,那就没必要用DMA。这就是DMA配合通信的应用。
1.2.5 兼容SMBus协议
SMBus(System Management Bus)是系统管理总线。SMBus是基于I2C总线改进而来的,主要用于电源管理系统中。SMBus和I2C非常像,所以STM32的I2C外设就顺便兼容了一下SMBus,这里了解一下即可。
1.2.6 芯片型号资源
STM32F103C8T6型号芯片的硬件I2C资源有I2C1和I2C2两个独立的I2C外设。可以看出,这里资源的限制也是硬件I2C和软件I2C的区别之一。因为硬件I2C必须要有硬件电路的支持,所以硬件I2C的资源是有限的。比如STM32F103C8T6型号的芯片最多就只能有两路硬件I2C的总线。但是对于软件I2C,资源一般没有很大的限制,只需要复制一下代码,就可以开辟一条新的I2C总线。所以软件I2C只要代码能存的下,基本上是想开几路就开几路,没有资源的限制。这是硬件I2C和软件I2C资源的差异。以上就是STM32的I2C外设的简介。
二、I2C框图
图中展示的就是STM32内部I2C外设的结构图。
2.1 引脚
最左边是I2C外设的通信引脚SDA和SCL,就是I2C通信的两个引脚。还有一个SMBALERT是SMBus用的,I2C用不到,不用管的。
像这样外设模块引出来的引脚,一般都是借助GPIO口的复用模式与外部世界相连接的。具体是复用在了哪个GPIO口,还是需要查询引脚定义表:
在复用功能这两栏里找;
- I2C2的SCL就复用在了PB10引脚,SDA就复用在了PB11引脚;
- I2C1的SCL复用在了PB6引脚,SDA就复用在了PB7引脚。
另外I2C1的SCL和SDA还有重映射的机会,SCL可以重映射到PB8引脚,SDA可以重映射到PB9引脚。
以上就是硬件I2C外设与GPIO口的复用关系。因为内部电路设计的时候,这些引脚就是连接好了的,所以如果想使用硬件I2C就只能使用它连接好的指定引脚,不像软件I2C引脚可以任意指定。硬件I2C引脚固定就这几个,不能任意更改。所以硬件I2C对引脚限制也比较大。
2.2 SDA数据控制
I2C框图上面一块是SDA,也就是数据控制部分,这里数据收发的核心部分是数据寄存器DR(DATA REGISTER)和数据移位寄存器。
当需要发送数据时,可以把一个字节数据写到数据寄存器DR,当移位寄存器没有数据移位时,这个数据寄存器的值就会进一步,转到移位寄存器里。在移位的过程中,就直接可以把下一个数据放到数据寄存器里等着了,一旦前一个数据移位完成,下一个数据就可以无缝衔接,继续发送。当数据由数据寄存器转到移位寄存器时,就会置状态寄存器的TXE标志位为1,表示发送寄存器为空,这就是发送的流程。
在接收时也是这一路,输入的数据,一位一位地从引脚移入到寄存器里,当一个字节的数据收齐之后,数据就整体从移位寄存器转到数据寄存器,同时置标志位RXNE,表示接收寄存器非空。这时就可以把数据从数据寄存器读出来了。
这个流程和串口那里很相似,串口的数据收发也是由数据寄存器和移位寄存器两级实现的。只不过串口是全双工,数据收和发是分开的。而I2C是半双工,数据收发是同一组数据寄存器和位移寄存器。但是数据寄存器和移位寄存器的配合,设计思路都是异曲同工。
有了数据寄存器和移位寄存器这一块,SDA的数据收发就可以完成了。至于什么时候收,什么时候发,就需要写入控制寄存器的对应位进行操作。对于起始条件、终止条件、应答位什么的都由数据控制模块的控制电路可以完成。
数据收发之后,下方还有两个功能:
- 一个是比较器和自身地址寄存器,双地址寄存器。
- 另一个是帧错误校验计算和帧错误校验寄存器。
首先说一下,帧错误校验计算和帧错误校验寄存器这两块内容了解即可。比较器和地址寄存器这是从机模式使用的。STM32的I2C是基于可变多主机模型设计的。STM32不进行通信的时候就是从机,作为从机就可以被别人召唤。能被别人召唤就应该有从机地址,从机地址就是由自身地址寄存器指定。可以自定义一个从机地址,写到这个寄存器。当STM32作为从机在被寻址时,如果收到的寻址通过比较器判断后,和自身地址相同,那STM32就作为从机,响应外部主机的召唤。并且STM32支持同时响应两个从机地址,所以就有自身地址寄存器和双地址寄存器。这一块需要在多主机模型下来理解。把角色转换一下,STM32作为从机才需要有这一部分。
帧错误数据校验计算是STM32设计的一个数据校验模块,当发送一个多字节的数据帧时,在这里硬件可以自动执行CRC校验计算。CRC校验计算是一种很常见的数据校验算法。他会根据前面这些数据进行各种数据运算。然后会得到一个字节的校验位,附加在数据帧后面,在接收到这一帧数据后,STM32的硬件也可以自动执行校验的判定。如果数据在传输的过程中出错了,CRC校验算法就通不过,硬件就会置校验错误标志位。提示数据错误,使用时需注意。这个校验过程就跟串口的奇偶校验差不多,也是用于进行数据有效性验证的。
SDA这一块,就只需要掌握数据寄存器和移位寄存器配合的这部分就可以。
2.3 SCL时钟控制
SCL这部分,时钟控制是用来控制SCL线的。在时钟控制寄存器写对应的位,电路就会执行相应的功能。看一下控制逻辑电路这一块连接的电路结构。写入控制寄存器可以对整个电路进行控制。读取状态寄存器可以得知电路的工作状态。
下面是中断,当内部有一些标志位置1之后,可能事件比较紧急,就可以申请中断。如果开启了这个中断,那当这个事件发生后,程序就可以跳到中断函数来处理这个事件了。
下方还有DMA请求与响应,在进行很多字节的收发时,可以配合DMA来提高效率。
以上就是I2C外设的结构框图。
三、I2C基本结构
把I2C框图中不常用的东西去掉,整理一下就得到内部的简化结构图。
首先移位寄存器和数据寄存器DR的配合是通信的核心部分。
这里因为I2C是高位先行,所以移位寄存器是向左移位。在发送的时候,最高位先移出去,然后是次高位......最后是最低位。一个SCL时钟移位一次,一共移位8次就能把一个字节由高位到低位依次放到SDA线上了。
那在接收的时候数据通过GPIO口从右边依次移进来,最终移8次一个字节就接收完成了。在GPIO口这里,使用硬件I2C的时候,两个对应的GPIO口都要配置成复用开漏输出的模式。
- 复用就是GPIO口的状态是交由片上外设来控制的。
- 开漏输出是I2C协议要求的端口配置。
在这里即使是开漏输出模式,GPIO口也是可以进行输入的。
在SCL这里时钟控制器通过GPIO去控制时钟线。结构图里简化成一主多从的模型了,所以时钟控制器到SCL只画了输出的方向。实际上在多主机的模型下时钟线也会进行输入。
下面看SDA这一路,输出数据通过GPIO口输出到端口。输入数据也是通过GPIO输入到移位寄存器。移位寄存器与GPIO连接的两个箭头具体是连接在GPIO的哪个位置呢,看下图复用开漏或推挽输出模式。
因为要使用开漏输出,所以P-MOS是没有的。
移位寄存器输出的数据通向GPIO接在了来自片上外设的复用功能输出。所以I2C外设的输出就接在来自片上外设的复用功能输出之后控制N-MOS的通断,进而控制这一路I/O引脚是拉低到低电平还是释放悬空。也就是图中橙色线标记的电路结构。
对于输入部分可以看到,虽然这是复用开漏输出,但是输入这一路任然有效,I/O引脚的高低电平通过上方TLL肖特基除法器进入片上外设,来进入复用功能输入。也就是图中绿色线标记的电路结构。
- I2C外设通向GPIO输出就接在复用功能输出;
- I2C外设通向GPIO输入就接在复用功能输入。
看I2C基本结构图:
- 移位寄存器到GPIO的这一条线就是接GPIO复用输出;
- GPIO到移位寄存器的这一条线就是接GPIO复用输出。
- GPIO与SDA的双箭头连线就是I/O引脚。
数据控制器是黑盒模型。最后还有一个开关控制,也就是用I2C_Cmd函数配置,配置使能外设,外设就能正常工作了。
四、硬件I2C操作流程
接下来看一下硬件I2C的操作流程,以下是主机发送和主机接收的操作流程。操作流程图里有I2C时序,代码编写就参考这个流程来写。手册里有从机发送、从机接受、主机发送、主机接收四个流程。从机部分这里不做介绍,本文只展示主机发送和主机接收的流程。
4.1 主机发送
当STM32想要执行指定地址写的时候就要按照这个主发送器传送序列图来进行。
这里有7位地址的主发送和10位地址的主发送。
区别是:
- 7位地址,起始条件后的一个字节是寻址;
- 10位地址,起始后的两个字节都是寻址。
10位地址中的前一个字节写的是帧头。内容是5位的标志位11110+2位地址+1位读写位。后一个字节内容就是纯粹的8位地址了。前后两个字节加在一起构成10位的寻址。
这里主要关注7位地址。7位主发送的时序流程是:
起始S,从机地址,应答A。后面是数据1,应答A,数据2,应答A,......,数据N,应答A,最后是停止P。
因为I2C协议只规定了起始之后必须是寻址,至于后面数据的用途,并没有明确的规定。这些数据可以由各个芯片厂商自己来规定。比如MPU6050规定就是寻址之后,数据1为指定寄存器地址,数据2为指定寄存器地址下的数据,之后的数据N就是从指定寄存器地址开始依次往后写。这就是一个典型的指定地址写到时序流程。
从头来总结一下7位主发送序列图:
首先初始化之后,总线默认空闲状态,STM32默认是从模式。为了产生一个起始条件,STM32需要写入控制寄存器。这个得看一下手册的寄存器描述:
在控制寄存器CR1中,有个START位,在这一位写1就可以产生起始条件了。当起始条件发出后,这一位可以由硬件清除。所以只要在START这一位写1,STM32就自动产生起始条件了。就是上文说的写入控制寄存器,就像是踩油门刹车,来控制硬件运行。
之后STM32由从模式转为主模式,也就是多主机模型下,STM32有数据要发,就要跳出来。然后控制完硬件电路之后,就要检查标志位,来看看硬件有没有达到需要的状态。
在起始条件S之后,会发生EV5事件,这个EV5事件就可以当成是标志位。在手册里都是用EVx(Event)这个事件来代替标志位的。
这里设置EVx事件而不直接产生标志位的原因是有的状态会同时产生多个标志位,EVx事件就是组合了多个标志位的一个大标志位。在库函数中也会有对应的检查EVx事件是否发生的函数。所以就当成一个大标志位来理解就行了。
EV5事件就是SB(Strat Bit)标志位为1,SB是状态寄存器的一位,表示了硬件的状态。就像是开车时,看一下仪表盘这个意思。在手册状态寄存器SR1中可以找到这一位:
SB这一位置1,代表起始条件已发送。软件读取SR1寄存器后,也就是查看了这一位,然后写数据寄存器的操作将清除该位。写数据寄存器DR就是接下来的操作。所以按照正常的流程来,这个状态寄存器是不需要手动清楚的。
然后继续这个流程,当检测到起始条件已发送时。就可以发送一个字节的从机地址了。从机地址需要写到数据寄存器DR中,写入DR之后,硬件电路就会自动把这一个字节转到移位寄存器里。再把这一个字节发送到I2C总线上,之后硬件会自动接收应答并判断。如果没有应答,硬件会置应答失败的标志位。这个标志位可以申请中断来提醒。
在寻址完成之后,会发生EV6事件,下面EV6事件的解释就是ADDR标志位为1。在手册中可以找到ADDR标志位:
ADDR标志位在主模式状态下就代表地址发送结束。
EV6事件结束后,是EV8_1事件。EV8_1事件就是TxE标志位=1,移位寄存器空,数据寄存器空,这时需要写入数据寄存器DR进行数据发送了。一旦写入DR之后,因为移位寄存器也是空的。所以DR会立刻转到移位寄存器进行发送。这时就是EV8事件,移位寄存器非空,数据寄存器空。这时就是移位寄存器正在发数据的状态。所以流程这里,数据1的时序就产生了。
这里数据寄存器和移位寄存器的配合要把结构图里的内容记好。发送的时候,数据先写入数据寄存器,如果移位寄存器没有数据,再转到移位寄存器进行发送。这个流程要梳理清楚。
在数据1后面的应答A处EV8事件没有了,对应写入DR将清除该事件。 所以按理说,这个位置应该是写入了下一个数据,也就是数据2,在这个时刻就被写入到数据寄存器里等着了。接收应答位之后,数据2就转入移位寄存器进行发送。此时的状态是移位寄存器非空,数据寄存器空。所以这时,EV8事件就又发生了。之后到数据2这个位置,EV8事件又没有了,数据2还正在移位发送。但此时下一个数据已经被写到数据寄存器等着了。所以这个时候EV8事件消失,一直等到应答A之后,产生EV8事件。写入数据寄存器,EV8事件消失。
按照这个流程,一旦检测到EV8事件,就可以写入下一个数据了。
最后,想发送的数据写完之后就没有新的数据可以写入到数据寄存器了。当移位寄存器当前的数据移位完成时,此时就是移位寄存器空,数据寄存器也空的状态,这个事件就是图中的EV8_2。EV8_2是TxE=1,也就是数据寄存器空。BTF(Byte Transfer Finished),这个是字节发送结束标志位。手册里可以找到BTF字节:
BTF字节发送结束。在发送时,当一个新数据将被发送且数据寄存器还未被写入新的数据时,BTF标志位置1。这个意思就是当前的移位寄存器已经移完了,该找数据寄存器要下一个数据了,但是一看,数据寄存器没有数据,这就说明主机不想发了,这时就代表字节发送结束,是时候停止了。
所以在图中,当检测到EV8_2时,就可以产生终止条件了。产生终止条件应该在控制寄存器里有相应的位可以控制。手册中,控制寄存器CR1中,STOP这一位:
STOP这一位写1就会在当前字节传输,或在当前起始条件发出后产生停止条件。到这里一个完整的时序就发送完成了。
7位主发送的整个过程看上去可能比较复杂,操作和事件都比较多。但是简单来说就是写入控制寄存器CR或者数据寄存器DR就可以控制时序单元的发生。比如产生起始条件,发送一个字节数据。时序单元发生后,检查相应的EV事件,其实就是检查状态寄存器SR,来等待时序单元发送完成。然后依次按照这个流程操作,等待,操作,等待,......,这样就能实现时序了。当然在程序中有库函数,不需要实际去配置寄存器,所以这个过程会比想象中简单一些。以上就是主机发送的流程。
4.2 主机接收
主机接收的流程,图中有7位主接收和10位主接收。
看7位主接受的时序。
流程是:
起始S,从机地址+读,接收应答A,然后就是接收数据1,发送应答A(1),接收数据2,发送应答A,.....,最后一个数据N给非应答NA,之后终止P。
可以看出,这个时序应该是当前地址读的时序。指定地址读的复合模式这里没有给,得需要组合一下。
下方10位地址的当前地址读就复杂一些了,
接收流程是:
起始S,发送帧头,这个帧头里的读写位应该还是写操作位,因为后面还要跟着发送第二个字节的地址。之后继续发送第二个字节的8位地址,这样才能进行寻址。要想转入读的时序,必须再发送重复起始条件Sr,发送帧头,这次帧头的读写位就是读操作位了。因为发送读的指令之后,必须要立刻转入读的时序。所以第二个字节的地址就没有了,直接转入接收数据的时序。
以上10位地址的操作流程稍微复杂一些,下文以7位地址的主接收来分析。
首先,写入控制寄存器的START位产生起始条件,然后等待EV5事件,看图下方解释,EV5事件就代表起始条件已发送。之后是寻址(地址),接收应答A,结束后产生EV6事件,EV6事件代表寻址已完成。
之后数据1这里代表数据正在通过移位寄存器进行输入。EV6_1事件解释为没有对应的事件标志,只适用于接收1个字节的情况。EV6_1这里可以看到数据1其实还正在移位,还没收到呢,所以这个事件就没有标志位。之后当这个时序单元完成时,硬件会自动根据配置,把应答位A(1)发送出去。
如何配置是否要给应答呢,手册里明确给了应答位的配置,控制寄存器CR1里有一位ACK,应答使能:
ACK应答使能,如果写1,在接收到1个字节后就返回1个应答;写0就是不给应答。
当数据1和应答的这个时序单元结束后,就说明移位寄存器就已经成功移入1个字节的数据1了,这时移入的1个字节就整体转移到数据寄存器,同时置RxNE标志位,表示数据寄存器非空,也就是收到了一个字节的数据,这个状态就是EV7事件。EV7事件的解释是,RxNE=1,数据寄存器非空,读DR寄存器清除该事件。也就是收到数据了,当把这个数据读走之后,这个事件就没有了。时序图中EV7事件没有了,说明此时数据1被读走,当然数据1还没读走的时候,数据2就可以直接移入移位寄存器了,之后数据2移位完成,收到数据2,产生EV7事件,读走数据2,EV7事件没有了。然后按照这个流程就可以一直接收数据了。
最后当不需要继续接收时,就要在最后一个时序单元发生时,提前把应答位控制寄存器ACK置0,并且设置终止条件请求。EV7_1事件的解释和EV7一样,不过后面加了一句,设置ACK=0和STOP请求,也就是需要结束了。
之后在数据N这个时序完成后,由于设置了ACK=0,所以这里就会给出非应答NA。最后由于设置STOP位,所以产生终止条件。
这样接收一个字节的时序就完成了。整体上主机接收和主机发送时序差不多,写入控制寄存器CR和读取数据寄存器DR产生时序单元,然后等待相应的事件,来确保时序单元完成。以上就是主机接收的时序流程。程序编写就需要对应主机发送和主机接收的时序操作流程来设计实现硬件I2C的代码。
五、软件I2C和硬件I2C波形对比
下图是软件I2C和硬件I2C的波形对比。了解波形图可以加深对I2C协议的理解。
软件I2C的波形图:
硬件I2C的波形图:
上方两幅波形图都是指定地址读的时序,来看一下软件模拟的时序和硬件生成的时序有什么异同。
首先从引脚电平变化趋势上来看,软件模拟和硬件生成这两个的波形都是一样的,对应的数据也都是一样的。
然后从时钟线的规整程度上看,硬件I2C的波形会更加规整,硬件I2C这里每个时钟周期、占空比都非常一致;软件I2C这里由于操作引脚之后,都加了延时,这个延时有时候加的多,有时候加的少。所以软件时序的时钟周期、占空比可能不规整。不过由于I2C是同步时序,这些不规整也没有影响。
SCL低电平写,高电平读,虽然整个电平的任意时候都可以读写,但是一般要求保证尽早的原则。所以可以直接认为是SCL下降沿写,上升沿读。
- 时序图中软件I2C在第一个下降沿之后,因为操作端口之后有一些延时,所以在这个下降沿之后等了一会儿,才进行写入操作。后面的写入也是等了一会儿。
- 但在硬件I2C这里数据写入都是紧贴下降沿的,这里一碰到SCL下降沿,SDA立马就切换数据了,每次下SCL降沿时,SDA都是如此。在读的时候,虽然把绿线画在高电平中间了,但实际上读的时刻也是紧贴上升沿进行的。
之后看应答结束这个时刻:
- 软件I2C的主机,过了一会儿才变换数据。
- 硬件I2C的从机在SCL下降沿立刻释放了SDA。
所以
- 软件I2C的应答结束这里出现了一个短暂的高电平。
- 硬件I2C应答结束后,SCL下降沿,从机立刻释放SDA,同时主机也立刻拉低SDA,所以硬件I2C的时序图里就出现了一个小尖峰。
硬件操作的I2C,包括有些是从机的硬件操作的部分,SDA的数据变化都是在SCL下降沿进行的。软件操作的I2C波形可能就不是那么标准了。当然还是因为I2C同步时序的原因,不标准的波形也完全不影响通信,这也正是同步时序的好处,可以容忍不标准的波形。
以上就是软件和硬件部分的波形对比。
总结
以上就是今天要讲的内容,本文仅仅简单介绍了I2C的硬件实现,也就是STM32内部的I2C外设。