深入解析STM32 HAL库I2C序列通讯
STM32 HAL库I2C 序列通讯详解
序列通讯函数
序列通讯函数仅仅在中断和dma模式提供,不太清楚为什么不在阻塞模式提供,可能因为提供了mem系列函数吧
HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,
uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,
uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size,
uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size,
uint32_t XferOptions);
对于dma系列同理
XferOptions 选项
#define I2C_FIRST_FRAME ((uint32_t)I2C_SOFTEND_MODE)
#define I2C_FIRST_AND_NEXT_FRAME ((uint32_t)(I2C_RELOAD_MODE | I2C_SOFTEND_MODE))
#define I2C_NEXT_FRAME ((uint32_t)(I2C_RELOAD_MODE | I2C_SOFTEND_MODE))
#define I2C_FIRST_AND_LAST_FRAME ((uint32_t)I2C_AUTOEND_MODE)
#define I2C_LAST_FRAME ((uint32_t)I2C_AUTOEND_MODE)
#define I2C_LAST_FRAME_NO_STOP ((uint32_t)I2C_SOFTEND_MODE)
这些选项赋予了函数序列通讯功能,下面引用一段 博客
的描述
I2C_FIRST_AND_LAST_FRAME
如果在调用以上函数时传入了该参数,则和普通的I2C传输函数没有什么区别,字如其名,本次传输的是第一次也是最后一次,传输将会在发送或接收了函数参数指定的字节数后结束(主机会发送STOP结束通信)。
I2C_FIRST_FRAME
该参数在开始第一次传输的时候使用。即如果你需要在一次传输结束之后不释放总线马上开始另一次方向相同且目标从机都相同的传输(此次是transmit下次必须还是transmit,不可以转为receive,且传输的从机必须是同一个),那么就选择这个参数。使用此参数,会在传输结束之后让I2C硬件继续占用总线,直到下一次传输开始。
传入这个参数后,I2C不会发出RESTART
信号,只是传输数据之后保持对总线的占用,因此此次连接的从机会继续对总线保持监测。因此在下一次传输时,通信不能改变方向且必须和同一个从机进行;除非使用I2C_LAST_FRAME_NO_STOP
、I2C_OTHER_FRAME
、I2C_OTHER_AND_LAST_FRAME
参数。
I2C_NEXT_FRAME
字如其名。在之前已经调用过一次传输函数并且传入的参数为I2C_FIRST_FRAME之后,第二次调用同一个传输接口,并希望之后继续占用总线,不要释放(比如有第三次传输或更多次的传输),则使用此参数。注意,传输的方向必须和第一次相同。如第一次使用HAL_I2C_Master_Seq_Receive_IT()并传入了I2C_FIRST_FRAME,那么本次也应该调用HAL_I2C_Master_Seq_Receive_IT(),同时传入I2C_NEXT_FRAME参数。
在完成本次传输之后,下一次的通信不能改变方向且必须和同一个从机进行;除非使用I2C_LAST_FRAME_NO_STOP、I2C_OTHER_FRAME 、I2C_OTHER_AND_LAST_FRAME参数。
I2C_FIRST_AND_NEXT_FRAME
该参数是2和3的结合。仅仅是为了代码的复用性和编程更加方便而添加的。在第一次和第二次调用传输接口的时候,都可以传入这个参数。即I2C占有总线之后,连续进行两次方向相同的传输,就可以使用这个参数,同时保持总线不释放,继续占用。
I2C_LAST_FRAME
在之前已经调用过传输接口函数,即我们已经占有了总线的情况下,希望在本次传输结束后终止,即释放总线发出STOP信号,则传入此参数。注意,传输方向需要和之前相同,地址也需要相同。在本次传输结束之后,主机将会发出STOP信号,释放总线。
I2C_LAST_FRAME_NO_STOP
注意,这是最关键的一个参数。此参数在已经完成一次传输并且希望在下一次传输中调换方向时使用。很多时候我们需要先向从机传送消息,写入“命令码”,从机会根据命令码准备反馈消息;完成写入后,主机需要立刻启动读取,将数据读回。为了防止其他主机占用总线,我们就需要这个接口。
在之前一次传输中若调用了参数为I2C_FIRST_FRAME
、I2C_NEXT_FRAME
、I2C_FIRST_AND_NEXT_FRAME
的传输函数,且在此次希望改变传输的方向,则在调用传输函数时传入此参数。
以I2C_LAST_FRAME_NO_STOP为参数的传输函数中,首先会发出restart,然后重新发送从机地址(当然包括读写位),再进行数据写入/读取。并且在此次传输结束之后,总线不会被释放。如果希望在下一次传输中再次变换传输方向,可以继续传入此参数,或使用I2C_OTHER_FRAME 、I2C_OTHER_AND_LAST_FRAME参数
引用到此为止
现在让我们仔细观察,宏定义后面其实是另外三个宏
#define I2C_SOFTEND_MODE
#define I2C_RELOAD_MODE
#define I2C_AUTOEND_MODE
这三个中转跳后发现I2C_SOFTEND_MODE
的值是0
这很关键,这意味着其实刚才的6种模式就只有三种模式
也就是说,注意看注释
#define I2C_SOFTEND_MODE //不提供stop信号
#define I2C_RELOAD_MODE //不发送地址
#define I2C_AUTOEND_MODE //普通通讯,正常的模式(提供addr stop)
突然使用变得更加简单和易用
这也解释了要想传输改变方向比然需要提供addr,这意味着需要结束当前通讯不能使用I2C_RELOAD_MODE
即上面说的
在完成本次传输之后,下一次的通信不能改变方向且必须和同一个从机进行;除非使用I2C_LAST_FRAME_NO_STOP、I2C_OTHER_FRAME
、I2C_OTHER_AND_LAST_FRAME参数。
作者:dyyt