S&T的笔记 https://passport2.21ic.com/?600909 [收藏] [复制] [RSS] science and technology!

日志

STM32 EEPROM读写过程中被中断产生异常处理分析

已有 1533 次阅读2012-12-6 14:09 |个人分类:其它|系统分类:单片机| STM32, EEPROM

读写EEPROM时,STM32有中断产生可能会引起i2C死锁,然后为SCL为高,SDA一直为低,从新给EEPROM芯片上电又正常,或者直接把SDA拉高也恢复正常,查阅资料得知是EEPROM芯片把IIC总线拉死了。

网上有人是这么分析的,在正常情况下,I2C总线协议能够保证总线正常的读写操作。但是,当I2C主设备异常复位时(看门狗动作,板上电源异常导致复位芯片动作,手动按钮复位等等)有可能导致I2C总线死锁产生。总线死锁产生的原因是在I2C主设备进行读写操作的过程中.主设备在开始信号后控制SCL产生8个时钟脉冲,然后拉低SCL信号为低电平,在这个时候,从设备输出应答信号,将SDA信号拉为低电平。如果这个时候主设备异常复位,SCL就会被释放为高电平。此时,如果从设备没有复位,就会继续I2C的应答,将SDA一直拉为低电平,直到SCL变为低电平,才会结束应答信号。 而对于I2C主设备来说.复位后检测SCL和SDA信号,如果发现SDA信号为低电平,则会认为I2C总线被占用,会一直等待SCL和SDA信号变为高电 平。这样,I2C主设备等待从设备释放SDA信号,而同时I2C从设备又在等待主设备将SCL信号拉低以释放应答信号,两者相互等待,I2C总线进人一种 死锁状态。同样,当I2C进行读操作,I2C从设备应答后输出数据,如果在这个时刻I2C主设备异常复位而此时I2C从设备输出的数据位正好为0,也会导 致I2C总线进入死锁状态。
从上面的分析看,那么只要在读取的时候先将IIC总线复位一下应该能解决问题,搞了半个多月,以前一直是在想怎么样让它能正确的读写数据,现在可以试着从出了问题怎么去解决它这个角度出发看是否可行。
现在程序的读写的开始先发送I2C Stop条件,让设备释放总线,再进行读写,经过几天的测试能正确的读写到据。具体实现如下:
 
//读数据
u8 I2C_EE_ReadByte(u8 xChip, u16 xAddr, u8 *xpErr)
{
u8 dat; 
u8 errcnt;
u32 tmr;
 
//发送I2C_Stop条件也能让从设备释放总线
//if(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
{
I2C_Cmd(I2C1, DISABLE);
I2C_SDA_SCL_GPIO_MODE(GPIO_Speed_2MHz,GPIO_Mode_Out_OD,GPIO_Mode_Out_OD);
GPIO_ResetBits(SDA_port,SDA_pin);    //sda=0;
GPIO_SetBits(SCL_port,SCL_pin);      //scl=1;
GPIO_SetBits(SDA_port,SDA_pin);      //sda=1;
I2C_SDA_SCL_GPIO_MODE(GPIO_Speed_10MHz,GPIO_Mode_AF_OD,GPIO_Mode_AF_OD);
I2C_Cmd(I2C1, ENABLE);                                           
}
 
errcnt = 0;  
tmr = ulTimeOut_Time;
while((tmr--)&&(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)));
if(tmr ==0) errcnt++;
I2C_GenerateSTART(I2C1, ENABLE);                                                    
// 发送I2C的START信号,接口自动从从设备编程主设备
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)));              
// 检查I2C的EV5状态并清除
if(tmr ==0) errcnt++; 
I2C_Send7bitAddress(I2C1, xChip, I2C_Direction_Transmitter);                        
// 发送从地址(EEPROM设备地址)和写命令
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)));
// 检查I2C的EV6状态并清除
if(tmr ==0) errcnt++; 
I2C_SendData(I2C1, (u8)((xAddr >>8)&0x00ff));                                      
 // 发送 EEPROM 的存储空间地址(高字节)
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)));         
// 检查I2C的EV8状态并清除
if(tmr ==0) errcnt++; 
I2C_SendData(I2C1, (u8)(xAddr&0x00ff));                                             
// 发送 EEPROM 的存储空间地址(低字节)
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)));        
// 检查I2C的EV8状态并清除
if(tmr ==0) errcnt++; 
I2C_GenerateSTART(I2C1, ENABLE);                                                    
// 发送I2C的START信号,接口自动从从设备编程主设备
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)));              
// 检查I2C的EV5状态并清除
if(tmr ==0) errcnt++; 
I2C_Send7bitAddress(I2C1, xChip, I2C_Direction_Receiver);                           
// 发送从地址(EEPROM设备地址)和读命令
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)));   
// 检查I2C的EV6状态并清除
if(tmr ==0) errcnt++; 
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)));             
// 检查I2C的EV7状态并清除
if(tmr ==0) errcnt++; 
*xpErr = errcnt;
dat = I2C_ReceiveData(I2C1);                                                        
// 接收数据 
 
I2C_GenerateSTOP(I2C1, ENABLE);                                                     
// 发送I2C的STOP信号,接口自动从主设备编程从设备
I2C_Cmd(I2C1, DISABLE);                                                             
// 失能总线
return(dat);
}
 
//写数据
u8 I2C_EE_WriteByte(u8 xChip, u16 xAddr, u8 xDat)
{
u32 tmr;
u8 errcnt;
 
//发送I2C_Stop条件也能让从设备释放总线
//if(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
{
I2C_Cmd(I2C1, DISABLE);
I2C_SDA_SCL_GPIO_MODE(GPIO_Speed_2MHz,GPIO_Mode_Out_OD,GPIO_Mode_Out_OD);
GPIO_ResetBits(SDA_port,SDA_pin);    //sda=0;
GPIO_SetBits(SCL_port,SCL_pin);      //scl=1;
GPIO_SetBits(SDA_port,SDA_pin);      //sda=1;
I2C_SDA_SCL_GPIO_MODE(GPIO_Speed_10MHz,GPIO_Mode_AF_OD,GPIO_Mode_AF_OD);
I2C_Cmd(I2C1, ENABLE);                                                        
}
 
errcnt = 0;
tmr = ulTimeOut_Time;
while((tmr--)&&(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)));
if(tmr ==0) errcnt++;
I2C_GenerateSTART(I2C1, ENABLE);                                                    
// 发送I2C的START信号,接口自动从从设备编程主设备
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)));              
// 检查I2C的EV5状态并清除
if(tmr ==0) errcnt++; 
I2C_Send7bitAddress(I2C1, xChip, I2C_Direction_Transmitter);                        
// 发送从地址(EEPROM设备地址)
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)));
// 检查I2C的EV6状态并清除
if(tmr ==0) errcnt++; 
I2C_SendData(I2C1, (u8)((xAddr >>8)&0x00ff));                                       
// 发送 EEPROM 的存储空间地址(高字节)
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)));         
// 检查I2C的EV8状态并清除
if(tmr ==0) errcnt++; 
I2C_SendData(I2C1, (u8)(xAddr&0x00ff));                                             
// 发送 EEPROM 的存储空间地址(低字节)
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)));         
// 检查I2C的EV8状态并清除
if(tmr ==0) errcnt++; 
I2C_SendData(I2C1, xDat);                                                           
// 发送数据 
tmr = ulTimeOut_Time;
while((tmr--)&&(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)));         
// 检查I2C的EV8状态并清除
if(tmr ==0) errcnt++; 
 
I2C_GenerateSTOP(I2C1, ENABLE);                                                     
// 发送I2C的STOP信号,接口自动从主设备编程从设备
I2C_Cmd(I2C1, DISABLE);                                                         
 
tmr = ulTimeOut_Time;                                                            
while(tmr--); 
return(errcnt); 
}
 
 
 
交流QQ群:74204669

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)