学习参考 http://blog.youkuaiyun.com/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);
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,®_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设备的方法。测试成功读出