使用STM8L的硬件I2C(二)硬件I2C的事件和检测

STM8L的硬件I2C(二)硬件I2C的事件和检测

其他文章请参见:
(一)硬件I2C的简介
(三)硬件I2C中断读写(流程及代码)
(四)硬件I2C的使用注意

1、STM8L STD库对I2C状态的定义

I2C有Master写、Master读、Slave写、Slave读4种模式。

直接寄存器编程当然可以,不过必须吃透Reference手册,而且代码不直观,不易理解。
STD标准库对这四种方式做了抽象,对通信的各阶段都定义了状态进行指示,从而使得
使用逻辑更直观易懂。

下面以Master读写为例进行介绍:

  • Master写流程:
    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组合判断事件时,必须读取多次
#include "i2c.h"#define I2C_SPEED 100000 // I2C总线速度,单位为Hzvoid i2c_init(void){ GPIO_InitTypeDef GPIO_InitStruct; I2C_InitTypeDef I2C_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 使能I2C1时钟 // 配置GPIOB6和GPIOB7为复用推挽输出 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); // 配置I2C1为标准模式,时钟速度为100kHz I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = 0x00; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_ClockSpeed = I2C_SPEED; I2C_Init(I2C1, &I2C_InitStruct); I2C_Cmd(I2C1, ENABLE); // 使能I2C1}void i2c_write(uint8_t addr, uint8_t *data, uint16_t len){ uint32_t timeout = 0; while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) { if (++timeout > 0x10000) return; } I2C_GenerateSTART(I2C1, ENABLE); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) { if (++timeout > 0x10000) return; } I2C_Send7bitAddress(I2C1, addr << 1, I2C_Direction_Transmitter); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if (++timeout > 0x10000) return; } while (len--) { I2C_SendData(I2C1, *data++); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if (++timeout > 0x10000) return; } } I2C_GenerateSTOP(I2C1, ENABLE);}void i2c_read(uint8_t addr, uint8_t *data, uint16_t len){ uint32_t timeout = 0; while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) { if (++timeout > 0x10000) return; } I2C_GenerateSTART(I2C1, ENABLE); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) { if (++timeout > 0x10000) return; } I2C_Send7bitAddress(I2C1, addr << 1, I2C_Direction_Receiver); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if (++timeout > 0x10000) return; } while (len--) { if (len == 0) I2C_AcknowledgeConfig(I2C1, DISABLE); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) { if (++timeout > 0x10000) return; } *data++ = I2C_ReceiveData(I2C1); } I2C_AcknowledgeConfig(I2C1, ENABLE); I2C_GenerateSTOP(I2C1, ENABLE);}
05-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值