I2C通信

使用同步时序就可以极大地降低单片机对硬件电路的依赖。异步时序的好处是省一根时钟线,节省资源,缺点就是对时间要求太严格,对硬件电路依赖比较严重。同步时序的好处,就是对时间要求不严格,对硬件电路不怎么依赖。

主机对SCL线完全控制,另外在空闲状态下,主机可以主动发起对SDA的控制,只有在从机发送数据和从机应答的时候,主机才会转交SDA的控制权给从机。从机可以是姿态传感器、OLED、存储器、时钟模块等。对于SCL时钟线,对于从机,在任何时候都只能被动的读取,从机不允许控制SCL线,对于SDA数据线,从机不允许主动发起对SDA的控制。只有主机发送读取从机的命令后,或者从机应答的时候,从机才能短暂地取得SDA的控制权。为了避免SDA总线没协调好导致电源短路这个问题,I2C的设计是禁止所有设备输出强上拉的高电平,采用外置弱上拉电阻加开漏输出的电路结构。右图的左边是SCL的结构,右边是SDA的结构。开漏输出是去掉强上拉的开关管,输出低电平时,下管导通,是强下拉,输出高电平时,下管断开,但是没有上管了,此时引脚处于浮空的状态,这样的话所有的设备只能输出低电平而不能输出高电平。为了避免高电平造成的引脚浮空,就需要在SCL和SDA各外置一个上拉电阻,这是通过一个电阻拉到高电平的,是一个弱上拉。第一,这样完全杜绝了电源短路的现象,保证电路的安全。第二,避免了引脚模式的频繁切换,开漏加上弱上拉的模式,同时兼具了输入和输出的功能,因为开漏模式下,输出高电平就相当于断开引脚,所以在输入之前,可以直接输出高电平,不需要再切换成输入模式了。第三,就是这个模式会有一个“线与”的现象,就是只要有任意一个或多个设备输出了低电平,总线就处于低电平,只有所有的设备都输出高电平,总线才处于高电平。I2C可以利用这种电路特征,执行多主机模式下的时钟同步和总线仲裁,所以SCL虽然在一主多从模式下可以用推挽输出,但是它仍然采用了开漏加上拉输出的模式,因为在多主机模式下会利用这个特征。

SCL和SDA处于空闲状态时,都处于高电平状态,也就是没有任何一个设备去碰SCL和SDA,由外挂的上拉电阻拉高至高电平。当从机捕获到SCL高电平,SDA下降沿信号时,就会进行自身的复位,等待主机的召唤,然后在SDA下降沿之后,主机要再把SCL拽下来,拽下SCL,一方面是占用总线,另一方面是为了方便我们这些基本单元的拼接。SDA回弹到高电平产生上升沿触发终止条件,同时终止条件之后,SCL和SDA都是高电平,回归到最初的平静状态。起始和终止都是由主机产生的。

一般在SCL上升沿时刻,就已经读取SDA完成了,因为时钟是主机控制的,从机并不知道什么时候就会开始产生下降沿。 

释放SDA其实就相当于切换成输入模式,或者说所有设备包括主机都始终处于输入模式,当主机需要发送的时候,就可以主动去拉低SDA,而主机在被动接收的时候,就必须先释放SDA。从机的数据变换基本上都是贴着SCL下降沿进行的,而主机可以在SCL高电平的任意时刻读取 

从机要是在发送完之后没有收到主机的应答,那就会交出SDA控制权 

从机设备地址,在I2C协议标准里分为7位地址和10位地址,如果有相同的芯片挂载在同一条总线,就需要用到地址中的可变部分了,一般器件地址的最后几位是可以在电路中改变的,比如MPU6050地址的最后一位,就可以由板子上的AD0引脚确定,这个引脚接低电平,那它的地址就是1101000,接高电平就是1101001 

Slave Address从机地址       Reg Address寄存器地址

第一份字节的内容,必须是从机地址+读写位,加起来一个字节8位,0表示之后的时序主机要进行写入操作,1表示之后的时序主机要进行读出操作,接着是接收从机的应答位(Receive Ack,RA)。在这个时刻,主机要释放SDA。释放SDA之后,引脚电平回弹到高电平,但是根据协议规定,从机要在这个位拉低SDA,应答的时候从机立刻拽住SDA,然后应答结束之后从机再放开SDA。结合线与特性,在主机释放SDA之后,由于SDA也被从机拽住了,所以主机松手后,SDA并没有回弹高电平,这个过程就代表从机产生了应答。RA的上升沿,就是应答位结束后,从机释放SDA产生的,从机交出了SDA的控制权,因为从机要在低电平尽快变换数据,所以这个上升沿和SCL的下降沿几乎是同时发生的。第二个字节就可以送到指定设备的内部了,从机设备可以自己定义第二个字节和后续字节的用途,一般第二个字节可以是寄存器地址或者是指令控制字等。

一旦读写标志位给1了,下一个字节就立马转为读的时序,所以主机还来不及指定想要读哪个寄存器,就开始接收了,所以这里没有指定地址这个环节,在从机中所有的寄存器被分配到了一个线性区域中,并且会有一个单独的指针变量,指示着其中一个寄存器,这个指针上电默认一般指向0地址,并且每写入一个字节和读出一个字节后,这个指针就会自动自增一次,移动到下一个位置,那么在调用当前地址读的时序时。所以从机会返回当前指针指向的寄存器的值。 

复合格式

Sr(Start Repeat)的意思就是重复起始条件,相当于另起一个时序。如果只想读一个字节就停止的话,在读完一个字节之后,一定要给从机发个非应答(Send Ack,SA),非应答就是该主机应答的时候主机不把SDA拉低,从机就会把SDA控制权换个主机 

PS-产品说明书,RM-寄存器映像

6轴就是3轴加速度,3轴角速度

9轴就是3轴加速度,3轴角速度,3轴磁场强度

10周就是在9轴的基础上加一个气压强度

欧拉角是飞机机身相对于初始3个轴的夹角,飞机机头下倾或者上仰,这个轴的夹角叫做俯仰,Pitch;飞机机身左翻滚或者右翻滚,这个轴的夹角叫做滚转,Roll;飞机机身保持水平,机身向左转或者向右转,这个轴的夹角叫做偏航,Yaw。

加速度计具有静态稳定性,不具有动态稳定性

陀螺仪中间是一个有一定质量的旋转轮,外面是3个轴的平衡环,当中间的旋转轮高速旋转的时候,根据角动量守恒的原理,这个旋转轮具有保持它原有角动量的趋势,这个趋势可以保持旋转轴方向不变,当你外部物体的方向转动时,内部旋转轴的方向并不会转动,这就会在平衡环连接处发生角度偏差,如果我们在连接处放一个旋转的电位器,测量电位器的电压,就能得到旋转的角度了。辅助理解:测量角速度的陀螺仪可以想象成游乐园的旋转飞椅,中间的轴转的越快,这个椅子就飞的越远,最终测量对向两个椅子飞起来的距离,或者飞起来的夹角,就能得到中间轴的角速度了。 如果想通过角速度得到角度,只需要对角速度进行积分即可,局限性在与当物体静止时角速度值会因为噪声无法完全归零,经过积分的不断累积,这个小噪声就会导致计算出来的角度产生缓慢的飘逸。总结就是陀螺仪具有动态稳定性不具有静态稳定性。陀螺仪和加速度计互补滤波,可以得到稳定的姿态角。

满量程对应量化范围最大值时的物理量

低通滤波器可以消除一些输出数据的抖动

时钟为AD转换和内部其他电路提供时钟,控制分频系数就可以控制AD转换的快慢了 

右边是MPU6050的芯片,左下角是一个8针的排针,左上角是一个LDO,低压差线性稳压器。

XCL和XDA是芯片里面的主机I2C通信引脚,该设计是为了拓展芯片功能,MPU6050融合出来的姿态角是有缺陷的——绕Z轴的角度,也就是偏航角,它的漂移无法通过加速度计进行修正。就像是让你坐在车里,不看任何窗户,然后让你辨别当前车子的行驶方向,短时间内可以通过陀螺仪得知方向的变化,从而确定变化后的行驶方向,但是时间一长,车子到处转弯,没有稳定的参考就会迷失方向。这时候就需要一个指南针,提供长时间的稳定偏航角进行参考,来对陀螺仪感知的方向进行纠正。这就是9轴姿态传感器多出来的磁力计的作用。XCL和XDA,通常就是用于外接磁力计或者气压计,接上之后,MPU6050的主机接口可以直接访问这些拓展芯片的数据,读取到MPU6050里,MPU6050有个DMP单元,进行数据融合和姿态解算。

INT引脚,也就是中断输出脚,可以配置芯片内部的一些事件,来触发中断引脚的输出。

LDO是供电的逻辑,这个芯片的VDD供电是2.375~3.46V,属于3.3V供电的设备,不能直接接5V。所以为了扩大供电范围,加了一个3.3V的稳压器,输入端电压VCC_5V可以在3.3V到5V之间,然后经过3.3V的稳压器,稳定输出3.3V电压,给芯片端供电。只要3.3V有电,电源指示灯就会亮。

灰色部分是芯片内部的传感器,还有一个温度传感器。这个芯片内部的转换,都是全自动进行的,类似AD连续转换+DMA转运,每个ADC输出,对应16位的数据寄存器,不存在数据覆盖的问题,我们配置好转换频率之后,每个数据就自动以我们设置的频率刷新到数据寄存器,需要数据的时候直接读就行了。每个传感器都有个自测单元,用来验证芯片好坏。当启动自测后,芯片内部会模拟一个外力施加在传感器上,这个外力导致传感器数据会比平时大一些。先使能自测,读取数据,再使能自测,读取数据,两个数据相减得到的数据叫自测响应。这个自测响应,芯片手册里给出了一个范围,如果在这个范围内就没问题,如果不在芯片可能就坏了。下面有个东西叫电荷泵,或者叫充电泵,CPOUT引脚需要外接一个电容,电荷泵是一种升压电路,升压原理:有个电池,电压5V,再来个电容和电池并联,电池给电容充电,充满之后电容也相当于一个5V的电池,然后再修改电路的接法,把电池和电容串联起来,这样输出就是10V的电压,凭空把电池电压升高到了两倍,不过由于这个电容电荷比较少,用一下就不行了,所以这个并联、串联的切换速度要快, 趁电容还没放电完,就要及时并联充电,后续加个电源滤波,就能进行平稳的升压。这里由于陀螺仪内部需要一个高电压支持,所以这里设计了一个电荷泵进行升压,当然这个升压过程是自动的,不需要我们管。

右边是寄存器和通信接口。中断状态寄存器,可以控制内部的哪些事件到中断引脚的输出。FIFO是先入先出寄存器,可以对数据流进行缓存。配置寄存器,可以对内部的各个电路进行配置。传感器寄存器,也就是数据寄存器,存储了各个传感器的数据。工厂校准,意思就是内部的传感器都进行了校准。然后右边的数据运动处理器,简称DMP,是芯片内部自带的一个姿态解算的硬件算法,配合官方的DMP库,可以进行姿态解算。FSYNC是帧同步。(/CS)~SDA/(SD)是从机的SPI和I2C接口,AUX_CL~AUX_DA是主机的I2C接口,用于和MPU6050扩展的设备进行通信,Serial Interface Bypass Mux是接口旁路选择器,就是一个开关,如果拨到上面,辅助的I2C引脚就和正常的I2C引脚接到一起,这样两路总线就合在一起了。如果拨到下面,辅助的I2C引脚就由MPU6050控制,两条I2C总线独立分开

依次是:采样频率分频器(决定AD转换速度以及数据寄存器刷新速度,采样频率等于陀螺仪输出时钟频率/(1+分频值),不使用低通滤波器时,陀螺仪时钟为8KHz,使用滤波器就是1KHz)、配置寄存器(外部同步设置和低通滤波器配置(滤波参数,使输出数据更加平滑,配置滤波器参数越大,输出数据抖动越小,0表示不使用))、陀螺仪配置寄存器(自测、满量程)、加速度计配置寄存器(自测、满量程、高通滤波器(内置小功能,运动检测用到,对输出数据没有影响))

数据寄存器,包括加速度计XYZ轴、温度传感器、陀螺仪XYZ轴的数据,_L表示低8位,_H表示高8位 

电源管理寄存器1和电源管理寄存器2(1:设备复位,所有回到默认值、睡眠模式,芯片不工作进入低功耗、循环模式,设备进入低功耗过一段时间启动一次,唤醒频率由下一个寄存器决定;2:待机模式,不要哪个可以禁用哪个数据),WHO_AM_I是器件ID号。 上电默认进入睡眠模式,要记得先解除睡眠模式。

-------------------------------------------------------------------------------------------------------------------------------- 

软件I2C读写

第一个任务,把SCL和SDA都初始化为开漏输出模式

第二个任务,把SCL和SDA置高电平

开漏输出模式仍然可以输入,输入时先输出1,再直接读取输入数据寄存器就行了

时序单元:

起始条件:先确保释放SDA,再释放SCL,然后再拉低SDA、拉低SCL

终止条件:先拉低SDA,然后再释放SCL、释放SDA

除了终止条件以SCL高电平结束,其他所有单元都要保证SCL以低电平结束

发送字节:先放数据,再拉高SCL,再拉低SCL

接受字节:先把SDA释放(输出1),然后拉高SCL,读值,拉低SCL

发送应答:发送1位

接收应答:接收1位

I2C引脚都是开漏输出+弱上拉的配置,主机输出1并不是强置SDA为高电平,而是释放SDA

睡眠模式写入寄存器是无效的,睡眠模式是电源管理器1的SLEEP位控制的,把这个寄存器写入0x00,就能解除睡眠模式了,这个寄存器的地址是0x68

PWR_MGMT_1,设备复位给0,不复位,睡眠模式,给0,解除睡眠,循环模式,给0,不需要循环,无关位给0,温度传感器失能给0不失能 ,最后3位选择时钟,给000选择内部时钟,001选择X轴的陀螺仪时钟

PWR_MGMT_2,前两位,循环模式唤醒频率,给00,不需要。后6位,每一个轴的待机位,全都给0,不需要待机

采样时钟频率,寄存器数据越小输出数据越快

配置寄存器,外部同步,全都给0,不需。数字低通滤波器,根据需求来,给110是最平滑的滤波

陀螺仪配置寄存器,前面三位是自测使能,都给0不自测,满量程选择,根据需求设定,11最大量程

加速度计配置寄存器,自测给000,满量程也设11,最后高通滤波器,不用给00 

--------------------------------------------------------------------------------------------------------------------------------

硬件实现I2C

 主机,就是拥有主动控制总线的权利,从机只能在主机允许的情况下,才能控制总线

多主机模型可以分为固定多主机和可变多主机

固定多主机就是在这条总线上,有两个或更多个固定的主机,始终固定为主机,总线冲突需要进行总线仲裁,仲裁失败的一方让出总线控制权。

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

STM32的I2C使用的是可变多主机的模型

10位寻址的基本思路:起始之后的前两个字节都作为寻址,15位地址长度配上10位地址,这多出的5位当标志位去了,作用是告诉别人用的是10位地址。这就需要在第一个字节写个特定的数据,作为10位寻址模式的标志位,这个标志位就是11110,也就是说第二个字节也是寻址的话,那第一个字节的前5位就必须是11110。这一标志位是不会在7位地址模式下出现的。

SMBus(System Management Bus),是系统管理总线,SMBus是基于I2C总线改进而来的,主要用于电源管理系统中。与I2C非常像,所以STM32的I2C外设就顺便兼容了SMBus

 SMBALERT是SMBus用的

SDA与SCL是借用GPIO口的复用模式与外部世界相连的

当我们需要发送数据时,可以把一个字节数据写到数据寄存器DR,当移位寄存器没有数据移位时,这个数据寄存器的值就会进一步转到移位寄存器里,在移位的过程中,我们就可以直接把下一个数据放到数据寄存器里等着了,一旦前一个数据移位完成,下一个数据就可以无缝衔接,继续发发送,当数据由数据寄存器转到移位寄存器时,就会置状态寄存器的TXE位为1,表示发送寄存器为空。

接收时,输入的数据一位一位地从引脚移入到移位寄存器里,当一个字节的数据收齐之后,数据就整体从移位寄存器转到数据寄存器,同时置标志位RXNE,表示接收寄存器非空,这时我们就可以把数据寄存器读取出来了。

数据控制用于配置起始条件、终止条件等

比较器和地址寄存器是从机模式使用的,在可变多主机模式下STM32不通信就是从机。作为从机,自身的地址由自身地址寄存器来指定,我们可以自定一个从机地址,写到这个寄存器。当STM32作为从机,在被寻址时,如果收到的寻址通过比较器判断,和自身地址相同,那STM32就作为从机,响应外部主机的召唤,并且STM32支持同时响应两个从机地址,所以就有自身地址寄存器和双地址寄存器。

帧错误校验(PEC)计算,是STM32设计的一个数据校验模块,当我们发送一个多字节的数据帧时,在这里硬件可以自动执行CRC校验计算。如果数据在传输过程中出错了,CRC校验算法就通不过,硬件就会置校验错误标志位。

时钟控制,是用来控制SCL线的。在这个时钟控制器写对应的位,电路就会执行对应的功能。控制逻辑电路,写入控制寄存器,可以对整个电路进行控制,读取状态寄存器,可以得知电路的工作状态。

之后是中断,当内部有一些标志位置1之后,可以申请中断。

最后是DMA请求与响应。

I2C高位先行,所以移位寄存器是向左移位

GPIO口都要配置成复用开漏输出的模式,复用就是GPIO的状态是交由片上外设来控制 

当STM32想要执行指定地址写的时候,就要按照这个主发送器传送序列图来进行

7位寻址1个字节,10位寻址2个字节,后者内容是5位的标志位11110+2位地址+1位读写位

STM32默认是从模式,为了产生一个起始条件,STM32需要写入控制寄存器,在控制寄存器CR1中,有个START位,在这一位写1,就可以产生起始条件了。当起始条件发出后,这一位可以由硬件清除。所以只要在这一位写1,STM32就自动产生起始条件了。之后STM32由从模式转为主模式,检查标志位来看硬件有没有达到我们想要的状态。在这里起始条件后,会发生EV5事件,你可以把它当作标志位,手册里都是用EV(Event)事件来代替标志位的。有的状态会同时产生多个标志位,所以EV几事件就是组合了多个标志位的一个大标志位,在库函数由对应的检查EV几事件是否发生的函数

EV5,SB(Start Bit)标志位为1,SB是状态寄存器的一个位,表示了硬件的状态,1代表起始条件已发送,0代表未发送,软件读取SR1寄存器后,写数据寄存器操作将清除该位,写数据寄存器DR就是我们接下来的操作。

如果没有应答,硬件就会置应答失败的标志位,可以用于申请中断。

当寻址完成之后,会发生EV6事件,ADDR标志位为1,ADDR标志位在主模式状态下就代表地址发送结束。

EV8_1事件是TXE标志位为1,移位寄存器空,数据寄存器空,需要写DR

EV8事件是移位寄存器非空,数据寄存器空,移位寄存器正在发数据,写入DR将清除该事件。EV8结束那一刻数据2就写入DR了,一旦检测到EV8事件,就可以写入下一个数据了。

EV8_2事件:当移位寄存器当前的数据移位完成时,此时就是移位寄存器空,数据寄存器也空的状态,TXE=1,数据寄存器空,BTF(Byte Transfer Finished)字节发送结束标志位=1,(在发送时当一个数据将被发送且数据寄存器还未被写入新的数据)。当检测到EV8_2时,就可以产生终止条件了。控制器CR1中,STOP位写1就会在当前字节传输或在当前起始条件发出后产生停止条件

10位主接收第一个帧头里的读写位是写,第二个字节发地址,后面重复起始,后面帧头就是读的,立刻转入读的时序。

EV6_1事件,没有对应的事件标志,只适于接收1个字节的情况,移位寄存器接收完数据后硬件会自动根据我们的配置把应答位发送出去。配置是否应答,控制寄存器CR1有1位ACK应答使能,如果写1在接收到一个字节后就返回一个应答,写0就是不给应。移入的一个字节整体转移到数据寄存器,同时置RXNE标志位,表示数据寄存器非空,也就是收到了一个字节的数据,这个状态就是EV7事件,读DR寄存器清除该事件。

当不需要接收数据时,需要在最后一个数据单元发生时,提前把刚才说的应答位置0,并且设置终止条件请求,这就是EV7_1事件,读DR寄存器清除该事件,设置ACK等于0和STOP请求

----------------------------------------------------------------------------------------------------------------------------

第一步,配置I2C外设,对I2C外设进行初始化

第二步,控制外设电路,实现指定地址写的时序

第三步,控制外设电路,实现指定地址读的时序

硬件上

第一步,开启I2C外设和对应GPIO口的时钟

第二步,把I2C外设对应的GPIO口初始化为复用开漏模式

第三步,使用结构体,对整个I2C进行配置

第四步,I2C_Cmd,使能I2C

void I2C_DeInit(I2C_TypeDef* I2Cx);
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_DMALastTransferCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

//生成起始条件
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
//生成终止条件
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

//配置CR1的ACK位,应答使能,是否给从机应答
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address);
void I2C_DualAddressCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_GeneralCallCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState);

//把数据写到数据寄存器,当写一个字节至DR寄存器时,自动启动数据传输,一旦传输开始(TXE=1),
//如果能及时把下一个需要传输的数据写入DR寄存器,I2C模块将保持连续的数据流
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);

//读取DR数据,接收到的字节被拷贝到DR寄存器(RXNE=1),在接收到下一个字节(RXNE=1)之前
//读出数据寄存器,即可实现连续的数据传送
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);

//发送7地址的专用函数
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
uint16_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register);
void I2C_SoftwareResetCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_NACKPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_NACKPosition);
void I2C_SMBusAlertConfig(I2C_TypeDef* I2Cx, uint16_t I2C_SMBusAlert);
void I2C_TransmitPEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_PECPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_PECPosition);
void I2C_CalculatePEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
uint8_t I2C_GetPEC(I2C_TypeDef* I2Cx);
void I2C_ARPCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_FastModeDutyCycleConfig(I2C_TypeDef* I2Cx, uint16_t I2C_DutyCycle);

ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx);
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);

//读取、清除标志位
void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT);
void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT);

I2C状态监控函数

第一种,基本状态监控,使用I2C_CheckEvent,同时判断一个或者多个标志位,来确定EV几EV几这个状态是否发生

第二种,高级状态监控,使用I2C_GetLastEvent,直接把SR1和SR2两个状态寄存器,拼接成16位的数据返回

第三种,基于标志位的状态监控,使用I2C_GetFlagStatus,可以判断某一个标志位是否置1了

I2C1和I2C2都是APB1的外设

复用就是GPIO的控制权要交给硬件外设

I2C_Mode,I2C模式,SMBus总线的设备,SMBus总线的主机

I2C_ClockSpeed,配置SCL的时钟频率,写一个数就行,数值越大,SCL频率越高,数据传输就越快,不能超过400k

I2C_DutyCycle,时钟占空比,只有在时钟频率大于100kHz,也就是进入到快速状态时才有用,在小于等于100kHz的标准速度下,占空比是固定的1:1,也就是低电平时间比高电平时间约等于1:1,只有16:9和2:1,16:9就是低:高=16:9,为了快速传输而设计的,因为低电平数据变化,高电平数据读取,数据变化需要一定时间来翻转波形,尤其是数据的上升沿,变化比较缓慢,所以在快速传输的状态下,要给低电平多分配一些资源,要不然低电平数据变化来不及,你高电平数据读取也没用。

I2C_Ack,应答位配置,配置寄存器的ACK位,用于确定在接收一个字节后是否给从机应答

I2C_AcknowledgeAddress,指定STM32作为从机,可以响应几位的地址,可以选择响应10位地址或者响应7位地址

I2C_OwnAddress1,自身地址1,用于指定STM32自身的地址,方便别的主机呼叫它,要根据上面的属性来设置 

当我们调用起始条件之后,如果当前还有字节正在移位,那这个起始条件将会延迟,等待当前字节发送完毕后,才能产生

终止条件也会等当前字节接收完成后,再产生终止条件的波形

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值