上一篇文章大体介绍了I2C总线的硬件时序,这次我来上点干活,贴点I2C总线的实现代码。
在stm32F072或者L072单片机上运行通过,keil版本V5.33.0.0
模拟IO口
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = SDA_Pin|SCL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LCD_SCL_GPIO_Port, &GPIO_InitStruct);
正常来说I2C的引脚都是OD的,但是这里只驱动一个芯片,可以使用推拉模式,将两个引脚手动拉高,使其处于空闲状态。
起始信号就是在SCL高电平的时候拉低SDA。
HAL版本
这里我使用as5600芯片作为例子。
HAL_I2C_Mem_*都是HAL提供的内置代码
(#) 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()
当然还有中断和DMA版本的。
以下是参考代码。
float angle_prev=0;
int full_rotations=0; // full rotation tracking;
float angle_d; //GetAngle_Without_Track()的返回值
float angle_cd; //GetAngle()的返回值
#define AS5600_ADDRESS (0x36<<1)
//发送单字节时序
void AS5600_Write_Reg(uint16_t reg, uint8_t *value)
{
HAL_I2C_Mem_Write(&hi2c2, AS5600_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, value, 1, 50);
}
//发送多字节时序
void AS5600_Write_Regs(uint16_t reg, uint8_t *value, uint8_t len)
{
HAL_I2C_Mem_Write(&hi2c2, AS5600_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, value, len, 50);
}
//IIC读多字节
void AS5600_Read_Reg(uint16_t reg, uint8_t* buf, uint8_t len)
{
HAL_I2C_Mem_Read(&hi2c2, AS5600_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 50);
}
#define DATA_SIZE 2
#define PI 3.1415926
uint8_t Angle_Hight_Register_Addr=0x0c;
//得到弧度制的角度,范围在0-6.28
float GetAngle_Without_Track(void)
{
int16_t in_angle;
uint8_t temp[DATA_SIZE]={0};
AS5600_Read_Reg( Angle_Hight_Register_Addr, temp, DATA_SIZE);
in_angle = ((int16_t)temp[0] <<8) | (temp[1]);
angle_d = (float)in_angle * (2.0f*PI) / 4096;
//angle_d为弧度制,范围在0-6.28
return angle_d;
}
float GetAngle(void)
{
float val = GetAngle_Without_Track();
float d_angle = val - angle_prev;
//计算旋转的总圈数
//通过判断角度变化是否大于80%的一圈(0.8f*6.28318530718f)来判断是否发生了溢出
//如果发生了,则将full_rotations增加1(如果d_angle小于0)或减少1(如果d_angle大于0)。
if(fabs(d_angle) > (0.8f*2.0f*PI) ) full_rotations += ( d_angle > 0 ) ? -1 : 1;
angle_prev = val;
return ((float)full_rotations * 2.0f * PI + angle_prev)/(2*PI)*360;
}
库函数版本
下面是库函数版本,使用at24c16这个eeprom作为例子。
AT24C16的存储容量为16K bit,内容分成256页,每页8Byte,共2048Byte。
AT24C16支持I2C,总线数据传送协议I2C,总线协议规定任何将数据传送到总线的器件作为发送器。任何从总线接收数据的器件为接收器。数据传送是由产生串行时钟和所有起始停止信号的主器件控制的。主器件和从器件都可以作为发送器或接收器,但由主器件控制传送数据(发送或接收)的模式,由于A0、A1和A2可以组成000~111八种情况,即通过器件地址输入端A0、A1和A2可以实现将最多8个AT24C16器件连接到总线上,通过进行不同的配置进行选择器件
void IIC_Config(void)
{
I2C_InitTypeDef I2C_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
/*
主机模式
普通模式 100kHZ
I2C时钟频率32000KHz
使用模拟滤波器
不使用数字滤波器
上升时间100ns
下降时间10ns
*/
I2C_InitStruct.I2C_Ack=I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_AnalogFilter=I2C_AnalogFilter_Enable;
I2C_InitStruct.I2C_DigitalFilter=0x00;
I2C_InitStruct.I2C_Mode=I2C_Mode_I2C;
I2C_InitStruct.I2C_OwnAddress1=0x00;
I2C_InitStruct.I2C_Timing=0x20D22E37;
I2C_Init(I2C1,&I2C_InitStruct);
}1
void IIC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
/*PB9-I2C_SDA PB8-I2C_SCK */
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType=GPIO_OType_OD;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Connect PXx to I2C_SCL*/
GPIO_PinAFConfig( GPIOB , GPIO_PinSource8, GPIO_AF_1);
/* Connect PXx to I2C_SDA*/
GPIO_PinAFConfig( GPIOB ,GPIO_PinSource9, GPIO_AF_1);
}
void IIC_Init(void){
IIC_GPIO_Config();
IIC_Config();
I2C_Cmd(I2C1,ENABLE);
}
5、例程:I2C与AT24C16进行通讯
1)宏定义
#define AT24Cxx_FLAG_TIMEOUT ((uint32_t)0x1000)
#define AT24Cxx_LONG_TIMEOUT ((uint32_t)(10 * AT24Cxx_FLAG_TIMEOUT))
#define AT24Cxx_MAX_TRIALS_NUMBER 300
#define AT24Cxx_OK 0
#define AT24Cxx_FAIL 1
#define AT24Cxx_I2C I2C1
#define AT24Cxx_PAGESIZE 16
#define AT24Cxx_HW_Address 0xA0
2)AT24C02初始化
void AT24CXX_Init(void)
{
IIC_Init();
AT24Cxx_Address = AT24Cxx_HW_Address;
}
//等待操作完成
uint32_t AT24Cxx_WaitEepromStandbyState(void)
{
__IO uint32_t sEETrials = 0;
/* Configure CR2 register : set Slave Address and end mode */
I2C_TransferHandling(AT24Cxx_I2C,
AT24Cxx_Address, 0,
I2C_AutoEnd_Mode,
I2C_No_StartStop);
do
{
/* Initialize sEETimeout */
AT24Cxx_Timeout = AT24Cxx_FLAG_TIMEOUT;
/* Clear NACKF */
I2C_ClearFlag(AT24Cxx_I2C,
I2C_ICR_NACKCF | I2C_ICR_STOPCF);
/* Generate start */
I2C_GenerateSTART(AT24Cxx_I2C, ENABLE);
/* Wait until timeout elapsed */ while (AT24Cxx_Timeout-- != 0);
/* Check if the maximum allowed numbe of trials has bee reached */ if (sEETrials++ == AT24Cxx_MAX_TRIALS_NUMBER)
{
/* If the maximum number of trials has been reached, exit the function */ return AT24Cxx_TIMEOUT_UserCallback();
}
} while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_NACKF) != RESET);
/* Clear STOPF */
I2C_ClearFlag(AT24Cxx_I2C, I2C_ICR_STOPCF);
/* Return sEE_OK if device is ready */ return AT24Cxx_OK;
}
//AT24C02页写数据
uint32_t AT24Cxx_WritePage(uint8_t* pBuffer, uint16_t WriteAddr, uint8_t* NumByteToWrite)
{
uint32_t DataNum = 0;
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, 2, I2C_Reload_Mode, I2C_Generate_Start_Write);
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT; while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TXIS) == RESET)
{ if((AT24Cxx_Timeout--) == 0)
{ return AT24Cxx_TIMEOUT_UserCallback();
}
}
/* Send MSB of memory address */
I2C_SendData(AT24Cxx_I2C, (uint8_t)((WriteAddr & 0xFF00) >> 8));
/* Wait until TXIS flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TXIS) == RESET)
{ if((AT24Cxx_Timeout--) == 0)
{ return AT24Cxx_TIMEOUT_UserCallback();
}
}
/* Send LSB of memory address */
I2C_SendData(AT24Cxx_I2C, (uint8_t)(WriteAddr & 0x00FF));
/* Wait until TCR flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TCR) == RESET)
{ if((AT24Cxx_Timeout--) == 0)
{ return AT24Cxx_TIMEOUT_UserCallback();
}
}
/* Update CR2 : set Slave Address , set write request, generate Start and set end mode */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, (uint8_t)(*NumByteToWrite), I2C_AutoEnd_Mode, I2C_No_StartStop);
while (DataNum != (*NumByteToWrite))
{
/* Wait until TXIS flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TXIS) == RESET)
{ if((AT24Cxx_Timeout--) == 0)
{ return AT24Cxx_TIMEOUT_UserCallback();
}
}
/* Write data to TXDR */
I2C_SendData(AT24Cxx_I2C, (uint8_t)(pBuffer[DataNum]));
/* Update number of transmitted data */
DataNum++;
}
/* Wait until STOPF flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_STOPF) == RESET)
{ if((AT24Cxx_Timeout--) == 0)
{ return AT24Cxx_TIMEOUT_UserCallback();
}
}
/* Clear STOPF flag */
I2C_ClearFlag(AT24Cxx_I2C, I2C_ICR_STOPCF); return AT24Cxx_OK;
}
//AT24C02写任意长度数据
void AT24Cxx_WriteBuffer(uint8_t* pBuffer, uint16_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t NumOfPage = 0, NumOfSingle = 0, count = 0;
uint16_t Addr = 0;
Addr = WriteAddr % AT24Cxx_PAGESIZE; count = AT24Cxx_PAGESIZE - Addr;
NumOfPage = NumByteToWrite / AT24Cxx_PAGESIZE;
NumOfSingle = NumByteToWrite % AT24Cxx_PAGESIZE;
/*!< If WriteAddr is sEE_PAGESIZE aligned */ if(Addr == 0)
{
/*!< If NumByteToWrite < sEE_PAGESIZE */ if(NumOfPage == 0)
{
/* Store the number of data to be written */
AT24Cxx_DataNum = NumOfSingle;
/* Start writing data */
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
}
/*!< If NumByteToWrite > sEE_PAGESIZE */ else
{ while(NumOfPage--)
{
/* Store the number of data to be written */
AT24Cxx_DataNum = AT24Cxx_PAGESIZE;
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
WriteAddr += AT24Cxx_PAGESIZE;
pBuffer += AT24Cxx_PAGESIZE;
} if(NumOfSingle!=0)
{
/* Store the number of data to be written */
AT24Cxx_DataNum = NumOfSingle;
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
}
}
}
/*!< If WriteAddr is not sEE_PAGESIZE aligned */ else
{
/*!< If NumByteToWrite < sEE_PAGESIZE */ if(NumOfPage== 0)
{
/*!< If the number of data to be written is more than the remaining space
in the current page: */ if (NumByteToWrite > count)
{
/* Store the number of data to be written */
AT24Cxx_DataNum = count;
/*!< Write the data conained in same page */
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
/* Store the number of data to be written */
AT24Cxx_DataNum = (NumByteToWrite - count);
/*!< Write the remaining data in the following page */
AT24Cxx_WritePage((uint8_t*)(pBuffer + count), (WriteAddr + count), (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
}
else
{
/* Store the number of data to be written */
AT24Cxx_DataNum = NumOfSingle;
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
}
}
/*!< If NumByteToWrite > sEE_PAGESIZE */ else
{
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / AT24Cxx_PAGESIZE;
NumOfSingle = NumByteToWrite % AT24Cxx_PAGESIZE; if(count != 0)
{
/* Store the number of data to be written */
AT24Cxx_DataNum = count;
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
WriteAddr += count;
pBuffer += count;
}
while(NumOfPage--)
{
/* Store the number of data to be written */
AT24Cxx_DataNum = AT24Cxx_PAGESIZE;
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
AT24Cxx_WaitEepromStandbyState();
WriteAddr += AT24Cxx_PAGESIZE;
pBuffer += AT24Cxx_PAGESIZE;
} if(NumOfSingle != 0)
{
/* Store the number of data to be written */
AT24Cxx_DataNum = NumOfSingle;
AT24Cxx_WritePage(pBuffer, WriteAddr, (uint8_t*)(&AT24Cxx_DataNum));
AT24Cxx_WaitEepromStandbyState();
}
}
}
}
//AT24C02读任意数据
uint32_t AT24Cxx_ReadBuffer(uint8_t* pBuffer, uint16_t ReadAddr, uint16_t* NumByteToRead)
{
uint32_t NumbOfSingle = 0, Count = 0, DataNum = 0, StartCom = 0; /* Get number of reload cycles */
Count = (*NumByteToRead) / 255;
NumbOfSingle = (*NumByteToRead) % 255; /* Configure slave address, nbytes, reload and generate start */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); /* Wait until TXIS flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT; while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TXIS) == RESET)
{ if((AT24Cxx_Timeout--) == 0)
{ return AT24Cxx_TIMEOUT_UserCallback();
}
} /* Send MSB of memory address */
I2C_SendData(AT24Cxx_I2C, (uint8_t)((ReadAddr & 0xFF00) >> 8)); /* Wait until TXIS flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TXIS) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
} /* Send LSB of memory address */
I2C_SendData(AT24Cxx_I2C, (uint8_t)(ReadAddr & 0x00FF)); /* Wait until TC flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT; while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TC) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
}
/* If number of Reload cycles is not equal to 0 */
if (Count != 0)
{ /* Starting communication */
StartCom = 1; /* Wait until all reload cycles are performed */
while( Count != 0)
{
/* If a read transfer is performed */
if (StartCom == 0)
{ /* Wait until TCR flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TCR) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
}
}
/* if remains one read cycle */
if ((Count == 1) && (NumbOfSingle == 0))
{ /* if starting communication */
if (StartCom != 0)
{ /* Configure slave address, end mode and start condition */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, 255, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);
} else
{ /* Configure slave address, end mode */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, 255, I2C_AutoEnd_Mode, I2C_No_StartStop);
}
} else
{ /* if starting communication */
if (StartCom != 0)
{ /* Configure slave address, end mode and start condition */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, 255, I2C_Reload_Mode, I2C_Generate_Start_Read);
} else
{ /* Configure slave address, end mode */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, 255, I2C_Reload_Mode, I2C_No_StartStop);
}
} /* Update local variable */
StartCom = 0;
DataNum = 0; /* Wait until all data are received */
while (DataNum != 255)
{
/* Wait until RXNE flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT; while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_RXNE) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
} /* Read data from RXDR */
pBuffer[DataNum]= I2C_ReceiveData(AT24Cxx_I2C); /* Update number of received data */
DataNum++;
(*NumByteToRead)--;
}
/* Update Pointer of received buffer */
pBuffer += DataNum;
/* update number of reload cycle */
Count--;
} /* If number of single data is not equal to 0 */
if (NumbOfSingle != 0)
{
/* Wait until TCR flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_TCR) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
} /* Update CR2 : set Nbytes and end mode */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, (uint8_t)(NumbOfSingle), I2C_AutoEnd_Mode, I2C_No_StartStop); /* Reset local variable */
DataNum = 0; /* Wait until all data are received */
while (DataNum != NumbOfSingle)
{
/* Wait until RXNE flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT; while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_RXNE) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
} /* Read data from RXDR */
pBuffer[DataNum]= I2C_ReceiveData(AT24Cxx_I2C); /* Update number of received data */
DataNum++;
(*NumByteToRead)--;
}
}
}
else
{ /* Update CR2 : set Slave Address , set read request, generate Start and set end mode */
I2C_TransferHandling(AT24Cxx_I2C, AT24Cxx_Address, (uint32_t)(NumbOfSingle), I2C_AutoEnd_Mode, I2C_Generate_Start_Read); /* Reset local variable */
DataNum = 0; /* Wait until all data are received */
while (DataNum != NumbOfSingle)
{ /* Wait until RXNE flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT;
while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_RXNE) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
} /* Read data from RXDR */
pBuffer[DataNum]= I2C_ReceiveData(AT24Cxx_I2C); /* Update number of received data */
DataNum++;
(*NumByteToRead)--;
}
}
/* Wait until STOPF flag is set */
AT24Cxx_Timeout = AT24Cxx_LONG_TIMEOUT; while(I2C_GetFlagStatus(AT24Cxx_I2C, I2C_ISR_STOPF) == RESET)
{ if((AT24Cxx_Timeout--) == 0) return AT24Cxx_TIMEOUT_UserCallback();
} /* Clear STOPF flag */
I2C_ClearFlag(AT24Cxx_I2C, I2C_ICR_STOPCF); /* If all operations OK, return sEE_OK (0) */
return AT24Cxx_OK;
}
//超时函数
uint32_t AT24Cxx_TIMEOUT_UserCallback(void)
{ /* Block communication and all processes */
while (1)
{
}
}
以上是关于I2C的部分参考示例代码,如果对你有所帮助,请点赞收藏评论。