STM32cubeMX之I2C学习(1)

学习参考  http://blog.csdn.net/luckywang1103/article/details/17549739

1、I2C波形

SCL为高时,SDA由1->0,表示开始传送

SCL为高时,SDA由0->1,表示传送结束

SCL为高时,SDA=0,表示传输数据0

SCL为高时,SDA=1,表示传输数据1

2、关于ACK

ACK信号:发送者在ACK时钟脉冲期间释放SDA线,接收者可以将SDA拉低并在时钟信号为高时保持低电平。

比如:在主机发送完地址之后的一个SCL周期内,主机首先会将SDA释放,这时SDA是高电平,如果发送的从机地址正确,则从机 会在这个周期内将SDA拉低,这就表示了ACK。 如果地址错误,则从机不会拉低SDA,后续传送数据就没有意义了。

如下发送地址 0xa0  1010 0000,地址正确会拉低SDA


如下发送地址0xaf 1010 1111,地址错误 不会拉低


3、关闭cubemx生成的代码的BUG

注意本文使用的I2C2

1.

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(hi2c->Instance==I2C2)
  {
  /* USER CODE BEGIN I2C2_MspInit 0 */
方法1,在这里提前打开时钟。
即使用这一句  <span style="font-family: Arial;">__I2C2_CLK_ENABLE();</span>
  /* USER CODE END I2C2_MspInit 0 */
  
    /**I2C2 GPIO Configuration    
    PB10     ------> I2C2_SCL
    PB11     ------> I2C2_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Peripheral clock enable */
    __I2C2_CLK_ENABLE();
  /* Peripheral interrupt init*/
    HAL_NVIC_SetPriority(I2C2_EV_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(I2C2_EV_IRQn);
    HAL_NVIC_SetPriority(I2C2_ER_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(I2C2_ER_IRQn);
  /* USER CODE BEGIN I2C2_MspInit 1 */
方法2,使用如下两句使用I2C强制复位
	__I2C2_FORCE_RESET();
  	__I2C2_RELEASE_RESET();
  /* USER CODE END I2C2_MspInit 1 */
  }

}
关于写函数,可以使用如下函数,但是切记,只能写寄存器为8位的I2C设备。

HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size,uint32_t Timeout);

HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size,uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
两者都基本上是一样的,先发送设备地址,随后发送需要发送的数据。 size用来指定要发送的字节个数。  这里的BUG就是发送2个及以上,除了第一个正确,后面其他的都没了  = =,由于没有逻辑仪,只有示波器,我也只能这样猜了。 实际上是真的只有第一个是正确,后者都是0.
读写函数可以参考如下:注意我使用的是I2C2  其中用到 hi2ce
Drive_StateTypdef i2c_write_byte(uint8_t  reg_addr,uint8_t reg_data)
{
	uint32_t timeout;
	HAL_I2C_StateTypeDef state;
	uint32_t err;
	timeout=HAL_GetTick()+300;//if in 300ms,the i2c is not ready then return error 
	while(1)
		{
			state=HAL_I2C_GetState(&hi2c2);
			if(state==HAL_I2C_STATE_READY||state==HAL_I2C_STATE_BUSY_RX)
				{
					err=HAL_I2C_Mem_Write_IT(&hi2c2,DeviceAddr,reg_addr,I2C_MEMADD_SIZE_8BIT,&reg_data,1);
					return err;
				}
			if(HAL_GetTick()>timeout)
				return Drive_TIMEOUT;
		}
}
Drive_StateTypdef i2c_read_byte(uint8_t  reg_addr,uint8_t* reg_data)
{
	uint32_t timeout;
	HAL_I2C_StateTypeDef state;
	uint32_t err;
	uint8_t data;
	timeout=HAL_GetTick()+300;//if in 300ms,the i2c is not ready then return error 
//	while(1)
//		{
//			state=HAL_I2C_GetState(&hi2c2);
//			if(state==HAL_I2C_STATE_READY||state==HAL_I2C_STATE_BUSY_TX)
//				{
//					err=HAL_I2C_Mem_Read_IT(&hi2c2,DeviceAddr,reg_addr,I2C_MEMADD_SIZE_8BIT,reg_data,1);
//					return err;
//				}
//			if(HAL_GetTick()>timeout)
//				return Drive_TIMEOUT;
//		}
	while(1)
		{
			state=HAL_I2C_GetState(&hi2c2);
			if(state==HAL_I2C_STATE_READY||state==HAL_I2C_STATE_BUSY_TX)
				{
					err=HAL_I2C_Mem_Read_IT(&hi2c2,DeviceAddr,reg_addr,I2C_MEMADD_SIZE_8BIT,&data,1);// active once receive
					timeout=HAL_GetTick()+300;//if in 300ms,the i2c is not ready then return error 
					while(1)
						{
							state=HAL_I2C_GetState(&hi2c2);
							if(state==HAL_I2C_STATE_READY||state==HAL_I2C_STATE_BUSY_TX)//it means the recieve is finish
								{
									*reg_data=data;
									return err;
								}
							if(HAL_GetTick()>timeout)
								return Drive_TIMEOUT;
						}
					
				}
			if(HAL_GetTick()>timeout)
				return Drive_TIMEOUT;
		}
}
如果使用的I2C设备寄存器是16位的,那么以上的方法该变一下,然后去读写是不行的,因为提到了,只有第一个8位数据是正确的,第二个8位全是0.因此提出了如下的写函数,读函数等以后再完成。这里的代码主要参考自cube mx的stm32f1xx_hal_i2c.c中的 static HAL_StatusTypeDef I2C_RequestMemoryWrite(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout)函数。
首先函数原型如下
static HAL_StatusTypeDef I2C_RequestMemoryWrite(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout)
{
  /* Generate Start */
  SET_BIT(hi2c->Instance->CR1, I2C_CR1_START);

  /* Wait until SB flag is set */
  if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_SB, RESET, Timeout) != HAL_OK)
  {
    return HAL_TIMEOUT;
  }

  /* Send slave address */
  hi2c->Instance->DR = I2C_7BIT_ADD_WRITE(DevAddress);

  /* Wait until ADDR flag is set */
  if(I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADDR, Timeout) != HAL_OK)
  {
    if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
    {
      return HAL_ERROR;
    }
    else
    {
      return HAL_TIMEOUT;
    }
  }

  /* Clear ADDR flag */
  __HAL_I2C_CLEAR_ADDRFLAG(hi2c);

  /* Wait until TXE flag is set */
  if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TXE, RESET, Timeout) != HAL_OK)
  {
    return HAL_TIMEOUT;
  }

  /* If Memory address size is 8Bit */
  if(MemAddSize == I2C_MEMADD_SIZE_8BIT)
  {
    /* Send Memory Address */
    hi2c->Instance->DR = I2C_MEM_ADD_LSB(MemAddress);
  }
  /* If Memory address size is 16Bit */
  else
  {
    /* Send MSB of Memory Address */
    hi2c->Instance->DR = I2C_MEM_ADD_MSB(MemAddress);

    /* Wait until TXE flag is set */
    if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TXE, RESET, Timeout) != HAL_OK)
    {
      return HAL_TIMEOUT;
    }

    /* Send LSB of Memory Address */
    hi2c->Instance->DR = I2C_MEM_ADD_LSB(MemAddress);
  }

  return HAL_OK;
}
写成自己的I2C写函数如下
Drive_StateTypdef I2C_Writes(uint16_t RegAddr,uint16_t RegData)
{
	int Timeout=300;
<span style="white-space:pre">	</span>int <span style="font-family: Arial;">DeviceAddr=0x2a;    //设备地址赋值,你可以改成其他方式</span>
  /* Generate Start */
  SET_BIT(hi2c2.Instance->CR1, I2C_CR1_START);

  /* Wait until SB flag is set */
  if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_SB, RESET, Timeout) != HAL_OK)
  {
    return Drive_HAL_TIMEOUT;
  }

  /* Send slave address */
  hi2c2.Instance->DR = I2C_7BIT_ADD_WRITE(DeviceAddr);//发送设备地址

  /* Wait until ADDR flag is set */
  if(I2C_WaitOnMasterAddressFlagUntilTimeout(&hi2c2, I2C_FLAG_ADDR, Timeout) != HAL_OK)
  {
    if(hi2c2.ErrorCode == HAL_I2C_ERROR_AF)
    {
      return Drive_HAL_ERROR;
    }
    else
    {
      return Drive_HAL_TIMEOUT;
    }
  }

  /* Clear ADDR flag */
  __HAL_I2C_CLEAR_ADDRFLAG(&hi2c2);

  /* Wait until TXE flag is set */
  if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_TXE, RESET, Timeout) != HAL_OK)
  {
    return Drive_HAL_TIMEOUT;
  }
	hi2c2.Instance->DR =RegAddr;//发送寄存器地址
	if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_TXE, RESET, Timeout) != HAL_OK)
  {
    return Drive_HAL_TIMEOUT;
  }
	hi2c2.Instance->DR =(RegData>>8)&0xff;//发送寄存高位数据
	if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_TXE, RESET, Timeout) != HAL_OK)
  {
    return Drive_HAL_TIMEOUT;
  }
	hi2c2.Instance->DR =(RegData&0xff);//发送寄存低位数据
  return Drive_HAL_OK;
}
其中,I2C_WaitOnFlagUntilTimeout()与I2C_WaitOnMasterAddressFlagUntilTimeout()  记得写成驱动过后,给拷贝过去,在 stm32f1xx_hal_i2c.c
中。经过测试,写波形完全正确。 致辞,硬件I2C写函数驱动完成。

现在提供I2C读写16位寄存器函数,主要参考 stm32f1xx_hal_i2c.c中的HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)与static HAL_StatusTypeDef I2C_MasterRequestRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Timeout)二者。
具体代码如下:
HAL_StatusTypeDef I2C_Reads(uint16_t RedAddr,uint16_t * RegData)
{
	int Timeout=300;
	uint16_t data=0;
  /* Enable Acknowledge */
  SET_BIT(hi2c2.Instance->CR1, I2C_CR1_ACK);

  /* Generate Start */
  SET_BIT(hi2c2.Instance->CR1, I2C_CR1_START);

  /* Wait until SB flag is set */
  if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_SB, RESET, Timeout) != HAL_OK)
  {
    return HAL_TIMEOUT;
  }

  /* Send slave address */
  hi2c2.Instance->DR = I2C_7BIT_ADD_WRITE(DeviceAddr);//发送设备地址

  /* Wait until ADDR flag is set */
  if(I2C_WaitOnMasterAddressFlagUntilTimeout(&hi2c2, I2C_FLAG_ADDR, Timeout) != HAL_OK)
  {
    if(hi2c2.ErrorCode == HAL_I2C_ERROR_AF)
    {
      return HAL_ERROR;
    }
    else
    {
      return HAL_TIMEOUT;
    }
  }

  /* Clear ADDR flag */
  __HAL_I2C_CLEAR_ADDRFLAG(&hi2c2);

  /* Wait until TXE flag is set */
  if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_TXE, RESET, Timeout) != HAL_OK)
  {
    return HAL_TIMEOUT;
  }
  
    hi2c2.Instance->DR = RedAddr;//发送寄存器地址

  /* Wait until TXE flag is set */
  if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_TXE, RESET, Timeout) != HAL_OK)
  {
    return HAL_TIMEOUT;
  }

  /* Generate Restart */
  SET_BIT(hi2c2.Instance->CR1, I2C_CR1_START);

  /* Wait until SB flag is set */
  if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_SB, RESET, Timeout) != HAL_OK)
  {
    return HAL_TIMEOUT;
  }

  /* Send slave address */
  hi2c2.Instance->DR = I2C_7BIT_ADD_READ(DeviceAddr);//发送设备地址

  /* Wait until ADDR flag is set */
  if(I2C_WaitOnMasterAddressFlagUntilTimeout(&hi2c2, I2C_FLAG_ADDR, Timeout) != HAL_OK)
  {
    if(hi2c2.ErrorCode == HAL_I2C_ERROR_AF)
    {
      return HAL_ERROR;
    }
    else
    {
      return HAL_TIMEOUT;
    }
  }
		/* Enable Pos */
		SET_BIT(hi2c2.Instance->CR1, I2C_CR1_POS);
  
		/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
		   software sequence must complete before the current byte end of transfer */
		__disable_irq();
  
		/* Clear ADDR flag */
		__HAL_I2C_CLEAR_ADDRFLAG(&hi2c2);
  
		/* Disable Acknowledge */
		CLEAR_BIT(hi2c2.Instance->CR1, I2C_CR1_ACK);
  
		 /* Re-enable IRQs */
		 __enable_irq(); 
  

		  /* Two bytes */
			/* Wait until BTF flag is set */
			if(I2C_WaitOnFlagUntilTimeout(&hi2c2, I2C_FLAG_BTF, RESET, Timeout) != HAL_OK)
			{
			  return HAL_TIMEOUT;
			}
  
			/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
			   software sequence must complete before the current byte end of transfer */
			 __disable_irq();
  
			/* Generate Stop */
			SET_BIT(hi2c2.Instance->CR1, I2C_CR1_STOP);
  
			/* Read data from DR */
			data= hi2c2.Instance->DR;
  
			/* Re-enable IRQs */
			__enable_irq();
  
			/* Read data from DR */
			data = (data<<8)+hi2c2.Instance->DR;
			*RegData=data;
  return HAL_OK;
}

以上就是改进后的I2C读写函数,读写16位寄存器的I2C设备的方法。测试成功读出






  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值