STM8L的硬件I2C(二)硬件I2C的事件和检测
其他文章请参见:
(一)硬件I2C的简介
(三)硬件I2C中断读写(流程及代码)
(四)硬件I2C的使用注意
1、STM8L STD库对I2C状态的定义
I2C有Master写、Master读、Slave写、Slave读4种模式。
直接寄存器编程当然可以,不过必须吃透Reference手册,而且代码不直观,不易理解。
STD标准库对这四种方式做了抽象,对通信的各阶段都定义了状态进行指示,从而使得
使用逻辑更直观易懂。
下面以Master读写为例进行介绍:
-
Master写流程:
-
Master读流程:
2.EV状态的定义
上述抽象出的各种状态(EV1-EV9),标准库中的实际定义宏如下:
其中EV1-EV4是Slave用,EV5-EV9是Master用。
- 状态名一览
I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED : EV1
I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED : EV1
I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED : EV1
I2C_EVENT_SLAVE_BYTE_RECEIVED : EV2
(I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL) : EV2
I2C_EVENT_SLAVE_BYTE_TRANSMITTED : EV3
(I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL) : EV3
I2C_EVENT_SLAVE_ACK_FAILURE : EV3_2
I2C_EVENT_SLAVE_STOP_DETECTED : EV4
I2C_EVENT_MASTER_MODE_SELECT : EV5
I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : EV6
I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED : EV6
I2C_EVENT_MASTER_BYTE_RECEIVED : EV7
I2C_EVENT_MASTER_BYTE_TRANSMITTING : EV8
I2C_EVENT_MASTER_BYTE_TRANSMITTED : EV8_2
I2C_EVENT_MASTER_MODE_ADDRESS10 : EV9
状态值的定义(stm8l15x_i2c.h)
typedef enum
{
/* --EV5 */
I2C_EVENT_MASTER_MODE_SELECT = (uint16_t)0x0301, /*!< BUSY, MSL and SB flag */
/* --EV6 */
I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED = (uint16_t)0x0782, /*!< BUSY, MSL, ADDR, TXE and TRA flags */
I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED= (uint16_t)0x0302, /*!< BUSY, MSL and ADDR flags */
/* --EV9 */
I2C_EVENT_MASTER_MODE_ADDRESS10= (uint16_t)0x0308, /*!< BUSY, MSL and ADD10 flags */
/* --EV7 */
I2C_EVENT_MASTER_BYTE_RECEIVED = (uint16_t)0x0340, /*!< BUSY, MSL and RXNE flags */
/* --EV8 */
I2C_EVENT_MASTER_BYTE_TRANSMITTING = (uint16_t)0x0780, /*!< TRA, BUSY, MSL, TXE flags */
/* --EV8_2 */
I2C_EVENT_MASTER_BYTE_TRANSMITTED = (uint16_t)0x0784, /*!< EV8_2: TRA, BUSY, MSL, TXE and BTF flags */
/* --EV1 (all the events below are variants of EV1) */
/* 1) Case of One Single Address managed by the slave */
I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED= (uint16_t)0x0202, /*!< BUSY and ADDR flags */
I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED = (uint16_t)0x0682, /*!< TRA, BUSY, TXE and ADDR flags */
/* 2) Case of Dual address managed by the slave */
I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED= (uint16_t)0x8200, /*! DUALF and BUSY flags */
I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED = (uint16_t)0x8680, /*! DUALF, TRA, BUSY and TXE flags */
/* 3) Case of General Call enabled for the slave */
I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED = (uint16_t)0x1200, /*!< EV2: GENCALL and BUSY flags */
/* --EV2 */
I2C_EVENT_SLAVE_BYTE_RECEIVED = (uint16_t)0x0240, /*!< BUSY and RXNE flags */
/* --EV4 */
I2C_EVENT_SLAVE_STOP_DETECTED = (uint16_t)0x0010, /*!< STOPF flag */
/* --EV3 */
I2C_EVENT_SLAVE_BYTE_TRANSMITTED = (uint16_t)0x0684, /*!< TRA, BUSY, TXE and BTF flags */
I2C_EVENT_SLAVE_BYTE_TRANSMITTING = (uint16_t)0x0680, /*!< TRA, BUSY and TXE flags */
/* --EV3_2 */
I2C_EVENT_SLAVE_ACK_FAILURE= (uint16_t)0x0004 /*!< AF flag */
} I2C_Event_TypeDef;
如果上述预定义的状不满足需求(一般情况下是足够的),可自定义其他状态值。
可以看出,上述状态的值本质上还是寄存器的状态组合。
比如,I2C_EVENT_MASTER_MODE_SELECT,也就是EV5,其代表的含义为Master控制选中,
其实就是BUSY, MSL 以及 SB 三个标志位被设定的情况。
即:总线忙+Master模式选中+Start生成,代表Master已经控制总线并已发出Start,通讯就绪。
3、STM8L 硬件I2C的使用模式
STM8L中,使用I2C有3种基本的模式,即:
- Polling mode(轮询)
- Interrupt mode(中断)
- DMA mode(DMA)
Polling要通过不停的检查寄存器的值来判断当前通信所处状态,读写代码是阻塞执行的,代码最简单。
中断模式是通过中断触发做动作,代码发出读写开始的指令后,后续无需阻塞执行,代码最复杂。
DMA是走DMA通道传输数据,别途再说明。
4、STM8L标准库所提供的各种I2C状态判断和用法区别
大家使用库的I2C函数判断状态时,总会被多个看起来功能类似却又不太一样的函数困扰。
不清楚应该使用哪一种,在什么样的场合下使用。
STD标准库提供了3种主要的状态监测方式,并分别围绕3个函数进行:
- 基本状态监测 Basic state monitoring (Using I2C_CheckEvent() function)
- 高级状态监测 Advanced state monitoring (Using the function I2C_GetLastEvent())
- 基于标志的状态监测 Flag-based state monitoring (Using the function I2C_GetFlagStatus())
这三种其实没有本质不同,下层都是判断寄存器的值组合。简单的说,
- 基本状态监测:I2C_CheckEvent()用于判断I2C是否正处于某种状态(EV)。
- 高级状态监测:I2C_GetLastEvent()返回当前SR1/SR3的组合值(错误除外),由用户自己判断当前是何种状态
- 标志的状态监测:I2C_GetFlagStatus()用于单独判断单独某个寄存器的位状态
从使用类型上来说,基本状态监测适用于Polling模式,即在不停的轮询当前状态,等待所需的I2C状态并做后续处理。高级状态监测更适合中断下使用,通过事件中断、缓冲中断等判断I2C当前状态,灵活度是最好的(同时更繁琐)。基于标志的状态监测一般用来判断简单状态,如总线忙,或者用于调试。
其实标准库只是对寄存器编程的一层薄封装,无论那种方式,其内在依然是相关寄存器值的检查。
4.1 Basic state monitoring 基本状态监测
它的核心是使用I2C_CheckEvent()函数做出和预期状态是否一致的判断。
I2C_CheckEvent函数将状态寄存器(SR1、SR2和SR3)内容与给定事件进行比较(可以是一个或多个Flag的组合)。如果当前状态包含被检查的标志,则返回SUCCESS,如果当前状态中缺少一个或多个标志,则返回ERROR。
何时使用:
- 此功能适用于大多数应用程序以及初始化操作
- 适用于需要定义自己事件的用户
限制:
如果发生错误(即除了监视的标志外,还设置了错误标志), 尽管通信可能已经失败,
但I2C_CheckEvent函数依然可能会返回SUCCESS。
在这种情况下,建议使用错误中断来监视错误,在中断处理中对错误进行判断
对于错误管理,建议使用以下功能:
- 使用I2C_ITConfig()函数配置并启用错误中断(I2C_IT_ERR)。
- 实现I2Cx_IRQHandler()函数,作为I2C中断入口函数(x为1,STM8L只有I2C1)
- 使用I2C_GetFlagStatus()函数或I2C_GetITStatus()函数确定发生了什么错误
- 使用I2C_ClearFlag()函数或I2C_ClearITPendingBit()函数来清中断
- 使用I2C_softwarerestcmd()函数或I2C_GenerateStop()函数()返回到正确的通信状态
4.2 Advance Monitoring 高级状态监测
i2c_GetLastEvent()函数返回一个Uint16,其中保存了SR1&SR3状态寄存器的值(SR3为高8位,SR1为低8位)。
何时使用:
- 和基本监测一样可以适应大多数的场合,而且能避免i2c_GetFlagStatus()函数的局限性(见下)。
可以比较预定义事件或者是用户定义的自定义值。 - 当需要同时监视多个标志时。不过与i2c_CheckEvent()函数相反i2c_GetLastEvent()函数可以用户自行判断所需事件。
限制:
用户(有时)需要定义自己的事件。如果只检查通信标志,而忽略错误标志,则错误管理和上述简单模式相同。
4.3 Flag-based state monitoring 基于标志的状态监测
何时使用:
- 用于特定的程序或者调试阶段
- 适合一次只检查一种Flag的情况(多数的I2C状态需要同时判断多个Flag)
限制:
- 有些I2C寄存器,如SR1和SR2读取后会被硬件自动清除,因此读取1次Flag可能会让其他位被清除。
- 检查由多个Flag组合判断事件时,必须读取多次