看寄存器文档写了一天硬件I2C,写了好几遍都不对,最后还是用库了。。。。。。。。看了下火哥的代码,发现其实stm32固件库已经把每一步需要检测的标志位封装好了,我们要做的只是根据文档来检测这些标志,并采取下一步动作。下面是STM32和AT24C02的硬件I2C分析
硬件I2C主发送
下图是stm32主发送时序图和AT24C02的写字节操作流程图,其中EV5、EV6、EV8_1、EV8、EV8_2、EV9这些事件标志在stm32的官方固件库都有详细编写出来(stm32f10x_i2c.h),我们只需调用检测即可。野火的硬件I2C也就是在这两个图和官方固件库的基础上编写的。
有详细的英文注释,很好理解
void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)//将pBuffer地址内的数据写入AT24C02的指定地址中
{
/* Send STRAT condition */
I2C_GenerateSTART(I2C2, ENABLE);
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/* Send the EEPROM's internal address to write to */
I2C_SendData(I2C2, WriteAddr);
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* Send the byte to be written */
I2C_SendData(I2C2, *pBuffer);
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* Send STOP condition */
I2C_GenerateSTOP(I2C2, ENABLE);
}
硬件I2C主接收
和发送一样,这些事件标志在官方库中都有写出,但是需要注意由于是操作AT24C02所以根据它的要求在读之前会需要先写入需要读出的数据所在地址,所以接收数据前还需要先写数据。
以下是野火的AT2402读操作函数:
void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)//从ReadAddr处读出
{
//*((u8 *)0x4001080c) |=0x80;
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)); // Added by Najoua 27/08/2008
/* Send START condition */
I2C_GenerateSTART(I2C2, ENABLE);
//*((u8 *)0x4001080c) &=~0x80;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/* Clear EV6 by setting again the PE bit */
I2C_Cmd(I2C2, ENABLE);
/* Send the EEPROM's internal address to write to */
I2C_SendData(I2C2, ReadAddr);
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/*以上都还为写操作阶段,接下来准备读*/
/* Send STRAT condition a second time */
I2C_GenerateSTART(I2C2, ENABLE);
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* Send EEPROM address for read */
I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Receiver);
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
/* While there is data to be read */
while(NumByteToRead)
{
if(NumByteToRead == 1)
{
/* Disable Acknowledgement */
I2C_AcknowledgeConfig(I2C2, DISABLE);
/* Send STOP Condition */
I2C_GenerateSTOP(I2C2, ENABLE);
}
/* Test on EV7 and clear it */
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED))
{
/* Read a byte from the EEPROM */
*pBuffer = I2C_ReceiveData(I2C2);
/* Point to the next location where the byte read will be saved */
pBuffer++;
/* Decrement the read bytes counter */
NumByteToRead--;
}
}
/* Enable Acknowledgement to be ready for another reception */
I2C_AcknowledgeConfig(I2C2, ENABLE);
}
硬件I2C主发送操作后的检测函数
火哥他对寄存器文档把握很准,需要什么不需要什么都很了解,当然也很熟悉官方固件库的内容,绝逼是老手了。下面一个函数是再次写入设备地址,用于检测从设备是否有响应,判断是否从设备已完成了以上数据写入操作。
void I2C_EE_WaitEepromStandbyState(void)
{
vu16 SR1_Tmp = 0;
do
{
/* Send START condition */
I2C_GenerateSTART(I2C2, ENABLE);
/* Read I2C2 SR1 register */
SR1_Tmp = I2C_ReadRegister(I2C2, I2C_Register_SR1);
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(I2C2, I2C_Register_SR1) & 0x0002));
/* Clear AF flag */
I2C_ClearFlag(I2C2, I2C_FLAG_AF);
/* STOP condition */
I2C_GenerateSTOP(I2C2, ENABLE);
}
模拟I2C
对于模拟I2C这里只记录几点重要的地方,代码就不贴出来了。
- 开始信号:SDA、SCL都为高的前提下,SDA跳为低电平。
- 停止信号:SCL为高时,SDA由低电平跳为高电平。
- 数据变化:只有当SCL为低电平的时候SDA线上的数据(高低电平)才可以变化。
- 等待ACK时,需要将SDA的GPIO引脚变为浮空输入。