在看了I2C的通讯过程的主发送器通讯过程以后,发现有个地方很难理解。如下图:
就是如图所示的红色方框,按照野火所说的,在发送地址并被从机响应(A)之后,会产生两个事件:一个是地址被响应,从而将状态寄存器2(SR2)的ADDR位写1,表示地址匹配上了从机并返回了响应(A);另一个就是将状态寄存器2(SR2)的TxE位写1了,表示发送数据寄存器此时为空,可以往里面写数据,也就是发送准备就绪了。
其实说的没错,但是对照代码就很难理解了。如下:
/**
* @brief 写一个字节到I2C EEPROM中
* @param
* @arg pBuffer:缓冲区指针
* @arg WriteAddr:写地址
* @retval 无
*/
uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
{
/* Send STRAT condition */
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Send EEPROM address for write */
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);
/* 检测EV6事件 */
while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
}
/* Send the EEPROM's internal address to write to */
I2C_SendData(EEPROM_I2Cx, WriteAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
}
/* Send the byte to be written */
I2C_SendData(EEPROM_I2Cx, *pBuffer);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
/* Send STOP condition */
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
return 1;
}
代码中,/* 检测EV6事件 */这段,在发送了EEPROM的地址以后,只检测了EV6事件,按照野火的说法,产生了EV6和EV8事件才能继续下面的操作,代码只执行了EV6的检测,不得不让人怀疑代码的正确性。
实际上,代码没有错,通过查看I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED的宏替换以及I2C_CheckEvent函数(此处不贴出),如下:
/* --EV6 */
#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */
I2C_CheckEvent函数体内,就是SR1和SR2两个寄存器的操作,而上面的宏就是来对SR1和SR2赋值的。也就是说EV6事件,对SR1和SR2的对应位赋值1了。那么分别是哪些位呢?只需要关注0x00070082里的‘8’和‘2’,也就是SR2的1位和7位(0位开始),对应的就是SR2的ADDR位和TxE位,也就是说,库函数里的EV6其实应该是野火说的EV6个EV8事件。野火的说法反正是让我困扰了好一会,起码是一知半解吧!所以其实通讯图里,EV6应该是ADDR=1 & TxE=1;后面的EV8只是TxE=1就行了。不然,看代码以为在发送地址并且从机响应了地址之后,只检测了EV6(ADDR=1),而没有检测EV8(TxE=1)呢。
其实对比主机接收的通讯过程就好理解了。主机接收的时候页有EV6事件,这个事件的宏替换如下:
#define I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ((uint32_t)0x00030002) /* BUSY, MSL and ADDR flags */
也只看低位的'2',这个2表示SR2的1位,也就是ADDR位,也就是在接收通讯中EV6事件单指响应了主机发送的地址,而不等待发送数据寄存器为空这个事件。因为接收通讯中,用不到发送数据寄存器,不会往外发数据了。
发送接收的事件是不一样,这个要清楚。否则会产生误解,阅读代码的时候也会产生困惑。