【STM32CubeF1 I2C_HAL库的使用】

STM32CubeF1 I2C_HAL库的使用


前言

在CubeIDE中可以使用内置的CubeMX功能很快速的配置STM32的硬件IIC功能,带来了极大的方便。


一、CubeMx中的配置

在CubeMx中选择需要使用的I2C通道,要使用硬件I2C中断时,只需要使能事件中断和错误中断,之后设置相应的中断优先级即可;当要配置DMA功能,也可以在DMA Settings中添加DMA TX或RX功能。、

在这里插入图片描述
之后,根据你的配置选项,生成的工程文件便会自动配置相应的I2C初始化功能。

二、HAL库中I2C中断的处理流程

在stm32的中断向量表中只提供了两种IIC中断,I2Cx_EV_IRQHandler事件中断和I2Cx_ER_IRQHandler错误中断,而发送完成和接收完成等事件都会以回调函数的形式,在事件中断函数中被执行。

当我们配置IIC中断之后,在stm32f1xx_it.c中断源文件中可以看到I2Cx_EV_IRQHandler中新增了中断处理函数。

在这里插入图片描述
进入到HAL_I2C_EV_IRQHandler中,可以看到所有IIC事件处理的流程;
下面以HAL_I2C_Master_Transmit_IT中断发送为例子。

void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c)
{
  uint32_t sr1itflags;
  uint32_t sr2itflags               = 0U;
  uint32_t itsources                = READ_REG(hi2c->Instance->CR2);
  uint32_t CurrentXferOptions       = hi2c->XferOptions;
  HAL_I2C_ModeTypeDef CurrentMode   = hi2c->Mode;
  HAL_I2C_StateTypeDef CurrentState = hi2c->State;

  /* Master or Memory mode selected */
  if ((CurrentMode == HAL_I2C_MODE_MASTER) || (CurrentMode == HAL_I2C_MODE_MEM))
  {
    sr2itflags   = READ_REG(hi2c->Instance->SR2);
    sr1itflags   = READ_REG(hi2c->Instance->SR1);

    /* Exit IRQ event until Start Bit detected in case of Other frame requested */
    if ((I2C_CHECK_FLAG(sr1itflags, I2C_FLAG_SB) == RESET) && (IS_I2C_TRANSFER_OTHER_OPTIONS_REQUEST(CurrentXferOptions) == 1U))
    {
      return;
    }

    /* SB Set ----------------------------------------------------------------*/
    if ((I2C_CHECK_FLAG(sr1itflags, I2C_FLAG_SB) != RESET) && (I2C_CHECK_IT_SOURCE(itsources, I2C_IT_EVT) != RESET))
    {
      /* Convert OTHER_xxx XferOptions if any */
      I2C_ConvertOtherXferOptions(hi2c);
	
      I2C_Master_SB(hi2c);     //1. EV5处理,开始信号的处理和发送地址字节数据
    }
    /* ADDR Set --------------------------------------------------------------*/
    else if ((I2C_CHECK_FLAG(sr1itflags, I2C_FLAG_ADDR) != RESET) && (I2C_CHECK_IT_SOURCE(itsources, I2C_IT_EVT) != RESET))
    {
      I2C_Master_ADDR(hi2c);  //2. 清除ADDR位标志;
    }
    /* I2C in mode Transmitter -----------------------------------------------*/
    else if (I2C_CHECK_FLAG(sr2itflags, I2C_FLAG_TRA) != RESET)
    {
      /* Do not check buffer and BTF flag if a Xfer DMA is on going */
      if (READ_BIT(hi2c->Instance->CR2, I2C_CR2_DMAEN) != I2C_CR2_DMAEN)
      {
        /* TXE set and BTF reset -----------------------------------------------*/
        if ((I2C_CHECK_FLAG(sr1itflags, I2C_FLAG_TXE) != RESET) && (I2C_CHECK_IT_SOURCE(itsources, I2C_IT_BUF) != RESET) && (I2C_CHECK_FLAG(sr1itflags, I2C_FLAG_BTF) == RESET))
        {
       		// 3. 配置发送数据,并发送第一个数据Byte1 
          I2C_MasterTransmit_TXE(hi2c);
        }
        /* BTF set -------------------------------------------------------------*/
        else if ((I2C_CHECK_FLAG(sr1itflags, I2C_FLAG_BTF) != RESET) && (I2C_CHECK_IT_SOURCE(itsources, I2C_IT_EVT) != RESET))
        {
          if (CurrentState == HAL_I2C_STATE_BUSY_TX)
          {
        	// 4.发送最后一个数据Byte2,
        	// 5.之后,产生结束信号 
            I2C_MasterTransmit_BTF(hi2c);
          }
        }
      }
    }

事件的处理流程和文档描述的一致,打开STM32的参考文档,可以看到在作为Master发送模式下,IIC事件的处理流程,如下图所示:

在这里插入图片描述

1.首先由主机发起START开始信号,触发事件EV5,此时进入HAL_I2C_EV_IRQHandler事件中断中,处理EV5:在上图中所说的,向DR寄存器写入地址和读写标志位。调用I2C_Master_SB(hi2c)处理此流程;
2.然后从机应答,触发EV6事件,再次进入HAL_I2C_EV_IRQHandler中断,通过读SR1和SR2寄存器,清除ADDR位标志;调用I2C_Master_ADDR(hi2c);处理此流程;
3.之后,再次进入中断函数中进行数据发送,开始写DR寄存器;对应I2C_MasterTransmit_TXE(hi2c);
4.最后一个字节需要发送,移位寄存器非空,则向DR急寄存器写入数据;对应I2C_MasterTransmit_BTF(hi2c),此处发送最后一个字节;
5.字节发送完成之后,BTF置1,产生STOP信号,结束数据传输过程。此时会调用发送完成回调函数HAL_I2C_MasterTxCpltCallback(hi2c),用户可以在该函数中定义发送完成之后要执行的动作。该过程同样在I2C_MasterTransmit_BTF(hi2c)中完成。

在地址位7bit,外加两个字节数据的整个Master发送过程中国,HAL_I2C_EV_IRQHandler会被调用 5 次.

二、HAL库中I2C中的API如何使用,有何差异

在HAL库的I2C源文件中,对所有提供的API进行了注释,下面做个简单总结;
I2C的操作API主要分为阻塞和非阻塞两大类。

(#) No-Blocking mode functions with Interrupt are :
        (++) HAL_I2C_Master_Transmit_IT()   
        (++) HAL_I2C_Master_Receive_IT()
        (++) HAL_I2C_Slave_Transmit_IT()
        (++) HAL_I2C_Slave_Receive_IT()
        (++) HAL_I2C_Mem_Write_IT()
        (++) HAL_I2C_Mem_Read_IT()
        (++) HAL_I2C_Master_Seq_Transmit_IT()
        (++) HAL_I2C_Master_Seq_Receive_IT()
        (++) HAL_I2C_Slave_Seq_Transmit_IT()
        (++) HAL_I2C_Slave_Seq_Receive_IT()
        (++) HAL_I2C_EnableListen_IT()
        (++) HAL_I2C_DisableListen_IT()
        (++) HAL_I2C_Master_Abort_IT()
  (#) No-Blocking mode functions with DMA are :
        (++) HAL_I2C_Master_Transmit_DMA()
        (++) HAL_I2C_Master_Receive_DMA()
        (++) HAL_I2C_Slave_Transmit_DMA()
        (++) HAL_I2C_Slave_Receive_DMA()
        (++) HAL_I2C_Mem_Write_DMA()
        (++) HAL_I2C_Mem_Read_DMA()
        (++) HAL_I2C_Master_Seq_Transmit_DMA()
        (++) HAL_I2C_Master_Seq_Receive_DMA()
        (++) HAL_I2C_Slave_Seq_Transmit_DMA()
        (++) HAL_I2C_Slave_Seq_Receive_DMA()

    (#) A set of Transfer Complete Callbacks are provided in non Blocking mode:
        (++) HAL_I2C_MasterTxCpltCallback()
        (++) HAL_I2C_MasterRxCpltCallback()
        (++) HAL_I2C_SlaveTxCpltCallback()
        (++) HAL_I2C_SlaveRxCpltCallback()
        (++) HAL_I2C_MemTxCpltCallback()
        (++) HAL_I2C_MemRxCpltCallback()
        (++) HAL_I2C_AddrCallback()
        (++) HAL_I2C_ListenCpltCallback()
        (++) HAL_I2C_ErrorCallback()
        (++) HAL_I2C_AbortCpltCallback()

2.1 阻塞模式

在没有使用中断时,IIC的读写只能采用轮询和阻塞的方式,直到数据发送完成,使用到的API有以下几种:

API备注
HAL_I2C_Master_Transmit主机发送数据时使用
HAL_I2C_Master_Receive主机接收数据时使用
HAL_I2C_Slave_Transmit从机发送数据时使用
HAL_I2C_Slave_Receive从机接收数据时使用
HAL_I2C_Mem_Write主机写内存地址时使用,可以用于写某寄存器数据
HAL_I2C_Mem_Read主机读内存地址时使用,可以用于读某寄存器数据
HAL_I2C_IsDeviceReady检测从设备是否准备好
   (#) Blocking mode functions are :
        (++) HAL_I2C_Master_Transmit()
        (++) HAL_I2C_Master_Receive()
        (++) HAL_I2C_Slave_Transmit()
        (++) HAL_I2C_Slave_Receive()
        (++) HAL_I2C_Mem_Write()
        (++) HAL_I2C_Mem_Read()
        (++) HAL_I2C_IsDeviceReady()

2.2 非阻塞模式

非阻塞模式,也就是指中断模式,当数据传输结束之后,会通过中断的方式通知用户;前面说到,采用中断的方式,会关联到相对应的中断回调函数,不同的API函数也就是对应不同的回调函数。

2.3 用于IO中断操作的API

API关联回调函数
HAL_I2C_Master_Transmit_ITHAL_I2C_MasterTxCpltCallback
HAL_I2C_Master_Receive_ITHAL_I2C_MasterRxCpltCallback
HAL_I2C_Slave_Transmit_ITHAL_I2C_SlaveTxCpltCallback
HAL_I2C_Slave_Receive_ITHAL_I2C_SlaveRxCpltCallback
HAL_I2C_Master_Abort_ITHAL_I2C_AbortCpltCallback

此外,还有HAL_I2C_ErrorCallback函数,当IIC传输错误产生时,会进入HAL_I2C_ErrorCallback函数中。

2.4 用于存储器读取中断操作的API

API关联回调函数
HAL_I2C_Mem_Write_ITMemTxCpltCallback
HAL_I2C_Mem_Read_ITMemRxCpltCallback

HAL_I2C_Mem_Write_IT :用于向IIC设备的具体寄存器地址的空间 写 数据

HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);

HAL_I2C_Mem_Read_IT:用于 读取 IIC设备的具体寄存器地址的数据

HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);

2.5 用于序列传输操作的API

一个IIC的数据传输流程,从START信号开始,第一个字节为从机地址和R/W数据方向位,之后便是数据的传输,最终以STOP信号结束。如果主机想要重新访问其他地址的设备,主机可以不需要发送STOP信号,直接发起新的START信号即可开始新一轮的数据传输。在一个传输过程中,不单单只是读或写,可以是多种组合。

在上一遍中提到,在一个传输过程中,往往是读写操作组合流程,传输方向改变的操作,举一个简单的例子,例如读一个BMP180传感器的内置参数,首先需要先发送参数所在的寄存器地址,之后,才能读取到数据;数据传输的方向会发生改变。而使用序列传输的函数,则可以根据需求构造相应的传输序列。
注意:对于此情况,可以直接调用:HAL_I2C_Mem_Read_IT

在这里插入图片描述
这个序列由两个数据帧组成,第一个数据帧不带STOP信号,之后重新生成一个带START信号的完整数据帧,可以看到,BMP180对该序列数据应答正常(虽然该读取气压的操作不正确)。
第一帧的XferOptoin参数为I2C_FIRST_FRAME:

> HAL_I2C_Master_Seq_Transmit_IT(&hi2c1, 0xEE, data1, 1, I2C_FIRST_FRAME);

第二帧的XferOptoin参数为I2C_LAST_FRAME:

HAL_I2C_Master_Seq_Receive_IT(hi2c, 0xEE, i2c_rx_buff, 3, I2C_LAST_FRAME);

在HAL_I2C_Master_Seq_Transmit_IT完成后的HAL_I2C_MasterTxCpltCallback中调用第二帧的接收中断函数,即可组成该序列。

> /** @defgroup I2C_XFEROPTIONS  I2C Sequential Transfer Options   * @{ 
> */
> #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)

不同的XferOptoin配置的适用条件如下:

 (+) Option field values are defined through I2C_XferOptions_definition and are listed below:
      (++) I2C_FIRST_AND_LAST_FRAME: No sequential usage, functional is same as associated interfaces in no sequential mode
      (++) I2C_FIRST_FRAME: Sequential usage, this option allow to manage a sequence with start condition, address
                            and data to transfer without a final stop condition
      (++) I2C_FIRST_AND_NEXT_FRAME: Sequential usage (Master only), this option allow to manage a sequence with start condition, address
                            and data to transfer without a final stop condition, an then permit a call the same master sequential interface
                            several times (like HAL_I2C_Master_Seq_Transmit_IT() then HAL_I2C_Master_Seq_Transmit_IT()
                            or HAL_I2C_Master_Seq_Transmit_DMA() then HAL_I2C_Master_Seq_Transmit_DMA())
      (++) I2C_NEXT_FRAME: Sequential usage, this option allow to manage a sequence with a restart condition, address
                            and with new data to transfer if the direction change or manage only the new data to transfer
                            if no direction change and without a final stop condition in both cases
      (++) I2C_LAST_FRAME: Sequential usage, this option allow to manage a sequance with a restart condition, address
                            and with new data to transfer if the direction change or manage only the new data to transfer
                            if no direction change and with a final stop condition in both cases
      (++) I2C_LAST_FRAME_NO_STOP: Sequential usage (Master only), this option allow to manage a restart condition after several call of the same master sequential
                            interface several times (link with option I2C_FIRST_AND_NEXT_FRAME).
                            Usage can, transfer several bytes one by one using HAL_I2C_Master_Seq_Transmit_IT(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME)
                              or HAL_I2C_Master_Seq_Receive_IT(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME)
                              or HAL_I2C_Master_Seq_Transmit_DMA(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME)
                              or HAL_I2C_Master_Seq_Receive_DMA(option I2C_FIRST_AND_NEXT_FRAME then I2C_NEXT_FRAME).
                            Then usage of this option I2C_LAST_FRAME_NO_STOP at the last Transmit or Receive sequence permit to call the opposite interface Receive or Transmit
                              without stopping the communication and so generate a restart condition.
      (++) I2C_OTHER_FRAME: Sequential usage (Master only), this option allow to manage a restart condition after each call of the same master sequential
                            interface.
                            Usage can, transfer several bytes one by one with a restart with slave address between each bytes 
                            using HAL_I2C_Master_Seq_Transmit_IT(option I2C_FIRST_FRAME then I2C_OTHER_FRAME)
                              or HAL_I2C_Master_Seq_Receive_IT(option I2C_FIRST_FRAME then I2C_OTHER_FRAME)
                              or HAL_I2C_Master_Seq_Transmit_DMA(option I2C_FIRST_FRAME then I2C_OTHER_FRAME)
                              or HAL_I2C_Master_Seq_Receive_DMA(option I2C_FIRST_FRAME then I2C_OTHER_FRAME).
                            Then usage of this option I2C_OTHER_AND_LAST_FRAME at the last frame to help automatic generation of STOP condition.

2.6 用于DMA传输操作的API

(#) No-Blocking mode functions with DMA are :
(++) HAL_I2C_Master_Transmit_DMA()
(++) HAL_I2C_Master_Receive_DMA()
(++) HAL_I2C_Slave_Transmit_DMA()
(++) HAL_I2C_Slave_Receive_DMA()
(++) HAL_I2C_Mem_Write_DMA()
(++) HAL_I2C_Mem_Read_DMA()
(++) HAL_I2C_Master_Seq_Transmit_DMA()
(++) HAL_I2C_Master_Seq_Receive_DMA()
(++) HAL_I2C_Slave_Seq_Transmit_DMA()
(++) HAL_I2C_Slave_Seq_Receive_DMA()

其相关联的回调函数,与不带DMA后缀的函数一致。

2.7 HAL_I2C_Mem_Read_IT 函数解析

HAL_I2C_Mem_Read_IT 的序列同上面说的一样,先写寄存器,然后再读寄存器数据,在一个传输过程中,发生了数据方向的切换,从发送变为了接收。
使用HAL_I2C_Mem_Read_IT 读取bmp180设备ID数据,其数据传输逻辑如下,先写入要读取的寄存器地址,然后再重新产生新的START信号,接收返回的数据。
在这里插入图片描述
源码中:
在发送为第一个字节0xD0之后,
在I2C_MemoryTransmit_TXE_BTF 中,产生新的Sr信号。

else if (hi2c->EventCount == 2U)
  {
    if (CurrentState == HAL_I2C_STATE_BUSY_RX)
    {
      /* Generate Restart */
      hi2c->Instance->CR1 |= I2C_CR1_START;
      hi2c->EventCount++;
    }

在处理新的START信号的事件中,进行了方向位的切换。

在这里插入图片描述


总结

以上便是关于 STM32 HAL I2C使用的全部内容,内容源于STM32F1 HAL库中源码的注释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值