STM32 I2C协议(一主多从)
一主多从是指:单片机作为主机,主导I2C总线的运行,挂载再I2C总线的所有外部模块是从机,从机只有被点名后才能获得控制I2C总线,不能在未经运行的情况下去碰I2C总线,防止冲突。
在总线冲突时,I2C协议会进行仲裁,仲裁胜利的一方获得总线控制权,失败的一方自动变为从机 ,当然多主机的情况下,还要进行时钟同步,多主机的情况下,协议时比较复杂的,有兴趣去了解。
I2C作为一个通信协议,必须在软件上和硬件上做出规定
硬件上的规定:电路如何连接,端口的输入输出模式是什么样的
软件上的规定:你的时序是怎么定义的,字节是如何传输的,高位先行还是低位先行,一个完整的时序由那些部分构成。 硬件规定和软件规定配合起来就是完整的通信协议啊
任何时候主机都是完全掌握SCL线, 另外在空闲状态下,主机可以主动发起对SDA的控制, 只有从机发送数据和从机应答的时候,主机才会把SDA的控制权交给从机。
挂载上的从机,对于SCL时钟线,任何时候都是被动的读取,从机不允许控制SCL线;对于SDA数据线,从机不允许主动发起对SDA的控制,只有在主机发送读取从机的命令或者从机应答的时候,从机才能短暂的获取SDA的控制权。
如何规定SDA和SCL的输入输出模式呢?
因为SDA是数据线,I2C是半双工,主机和从机可以在输入和输出之间反复切换,为了避免总线没有协调好,而导致电源短路的状态(例子:主机输出1,从机输出0),I2C的设计是,禁止所有设备输出强上拉的高电平,采用外置弱上拉加开漏输出的电路结构,就是这两条规定:
可以把SCL和SDA看成弹簧拉杆子的模型:
规定:所有人不可以向上推杆子,只能选择向下拉或者放手
如果你要输出低电平,就往下拽,弹簧被拉伸,杆子处于低电平状态;如果你要输出高电平, 你就放手,杆子在弹簧的拉力下,回弹到高电平。这就是一个弱上拉的高电平,但是完全不影响数据传输。
这么做的好处:1,完全杜绝了电源短路的现象,保证了电路的安全
2,避免了引脚模式的频繁切换,开漏加弱上拉的模式,同时兼并了输入和输出的功能,你要是想输出,那就操作杆子,输入的话,观察杆子高低就行。
3.会有一个“线与”的现象,只要有任意一个或多个设备输出了低电平,总线就处于低电平,只有所有设备都输出高电平,总线才输出高电平。I2C,可以利用这个电路特征,来执行多主机模式下的时钟同步和总线仲裁。
这里的起始条件和终止条件类似于串口里的起始位和停止位,一个完整的数据帧总是以起始条件开始,终止条件结束,另外,起始和终止,都是由主机产生的,从机不允许主动碰总线。
这里注意是高位先行,因为主机由对SCL的控制权,主机可以不紧不慢的放置数据位到SDA线上,然后将SCL释放,从机就的立刻读取SDA上的数据,一般在SCL上升沿,就已经读取了,因为主机不会管从机的速度,主机控制SCL。(同步运输,不会受中断影响,中断发送后,SDA和SCL停止变化,中断回来后,继续操作)
和发送字节类似,还是SCL高电平读数据,低电平写数据,不过从机要在SCL的下降沿之后快速写入数据。(主机接收前,释放SDA,感觉就像是主机不拉杆子,把拉杆子的控制权交给从机)。
发送应答和接收应答的时序和发送一个字节和接收一个字节的一位是一样的,可以理解为发送一位和接收一位
接收应答:就像是主机刚发送一个字节,说,你们有没有收到啊? 我现在把SDA放手了,如果有人收到的话,你就把SDA拽下来,然后主机在高电平的时候读取数据,发现确实有人拽下来了,那就说明有人收到了。 如果主机松手后,SDA回到高电平,说明没人应答 ,可能是没人收到字节,或者收到了没有应答。
发送应答:是告诉从机还要不要发送了,如果从机发送后得到了主机的应答,那从机继续发送,如果从机没有得到主机的应答,那从机会认为,我发送了一个数据,主机不理我,可能主机不想要把,这时从机就会乖乖的交出SDA控制权,防止干扰主机之后的操作。
对于I2C的完整时序主要有:
指定地址写,当前地址读,指定地址读
I2C一主多从,如何确实访问的是那个设备?
每个从机设备都有一个唯一的从机设备地址,相当于每个设备的名字,主机在起始条件之后,要先发送一个字节叫一下从机的名字,所有从机都会收到第一个字节,和自己的名字对比,如果一样,我就响应后面的读写操作,如果不同,那么后面的操作我就不管了。
从机设备地址分为7位和十位的,7位简单应用广泛,7位为例:
如果挂载多个相同的设备,从机设备地址的最后几位可以在电路中改变,来保证每个设备不一样
这个是在指定的地址写入一个字节,如果需要写入多个字节,可以把(Data和RA的部分) 多重复几次,(Reg Address)会自增的;当前地址读和指定地址读同理。
对于读数据在停止位前需要给个非应答位SA,表示主机不要要数据了。
作者:Hidden_the_past