最近在做一个需要驱动 AT24c02的项目,发现数据一直有出错的问题,芯片换掉之后出错的位置会变化,怀疑了各种问题,包括:(1)时序的问题 (2)引脚翻速度的问题 (3)芯片本身的问题 。最终发现问题出在停止信号的延时上,根据芯片的手册,每写一个数据到芯片,必须延时最少5ms ,等待at24c02芯片自己处理完毕,才能继续写入下一个数据。完整代码贴在下方
#define SDA_H GPIO_SetBits(EE_GPIO_PORT,EE_SDA)
#define SDA_L GPIO_ResetBits(EE_GPIO_PORT,EE_SDA)
#define SCL_H GPIO_SetBits(EE_GPIO_PORT,EE_SCL)
#define SCL_L GPIO_ResetBits(EE_GPIO_PORT,EE_SCL)
#define SDA_read GPIO_ReadInputDataBit(EE_GPIO_PORT,EE_SDA)
#define EEW_ADD 0XA0
#define EER_ADD 0XA1
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*
*func: I2C_GPIO_Config
*description: I2C2 I/O configuration
*call : internal
*/
static void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_IS;
GPIO_IS.GPIO_Pin = EE_SCL|EE_SDA;
GPIO_IS.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_IS.GPIO_Mode = GPIO_Mode_OUT;
GPIO_IS.GPIO_OType = GPIO_OType_OD;
GPIO_Init(EE_GPIO_PORT,&GPIO_IS);
SDA_H;
SCL_H;
}
void I2C_OUT(void) //SDA是输出方向
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=EE_SDA;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出模式
GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//SDA_H;
//SCL_H;
}
void I2C_IN(void) //SDA是输入方向
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=EE_SDA;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN; //输入上拉模式
GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void I2C_EE_Init(void)
{
I2C_GPIO_Config();
}
uint8_t I2C_Start(void)
{
#if 1
I2C_OUT();
SDA_H;
SCL_H;
Delay_10us(4);
SDA_L;
Delay_10us(4);
SCL_L;
return TRUE;
#endif
}
void I2C_Stop(void)
{
#if 1
I2C_OUT();
SCL_L;
SDA_L;
SCL_H;//改动位置
Delay_10us(4);
SDA_H;
Delay_10us(1000);//每个数据发送完毕之后,必须延时最少5ms,给eeprom芯片足够的处理时间,不然数据会出错
#endif
}
void I2C_Ack(void)
{
#if 1
I2C_OUT();
SCL_L;
SDA_L;
Delay_10us(20);
SCL_H;
Delay_10us(20);
SCL_L;
#endif
}
void I2C_NoAck(void)
{
#if 1
I2C_OUT();
SCL_L;
SDA_H;
Delay_10us(2);
SCL_H;
Delay_10us(2);
SCL_L;
#endif
}
uint8_t I2C_WaitAck(void) //返回为:=1有ACK,=0无ACK
{
#if 1
u16 ucErrTime=0;
I2C_IN();
SDA_H;
Delay_10us(1);
SCL_H;
Delay_10us(1);
while(SDA_read)
{
ucErrTime++;
if(ucErrTime>250)
{
I2C_Stop();
return FALSE;
}
}
SCL_L;
return TRUE;
#endif
}
void I2C_SendByte(uint8_t SendByte) //数据从高位到低位//
{
#if 1
uint8_t i=0;
I2C_OUT();
SCL_L; //拉低时钟开始数据传输
for(i=0;i<8;i++)
{
if(SendByte&0x80)
SDA_H;
else
SDA_L;
SendByte<<=1;
Delay_10us(2); //对TEA5767这三个延时都是必须的
SCL_H;
Delay_10us(2);
SCL_L;
Delay_10us(2);
}
#endif
}
uint8_t I2C_ReceiveByte(u8 ack) //数据从高位到低位//
{
#if 1
unsigned char i,receive=0;
I2C_IN();
for(i=0;i<8;i++ )
{
SCL_L;
Delay_10us(2);
SCL_H;
receive<<=1;
if(SDA_read)receive++;
Delay_10us(1);
}
SCL_L;
if (!ack)
I2C_NoAck(); //发送nACK
else
I2C_Ack(); //发送ACK
return receive;
#endif
}
void I2C_WriteByte(uint8_t SendByte, uint8_t WriteAddress, uint8_t DeviceAddress)
{
I2C_Start();
I2C_SendByte(DeviceAddress); //发送器件写命令
I2C_WaitAck();
I2C_SendByte(WriteAddress); //发送低地址
I2C_WaitAck();
I2C_SendByte(SendByte); //发送字节
I2C_WaitAck();
I2C_Stop();
}
uint8_t EepromReadByte(uint16_t FlashAddress,uint8_t Type)
{
if(Type==Read_Eeprom)
{
u8 temp=0;
I2C_Start();
I2C_SendByte(0XA0); //发送器件写命令
I2C_WaitAck();
I2C_SendByte(FlashAddress); //发送低地址
I2C_WaitAck();
I2C_Start();
I2C_SendByte(0XA0|1); //发送器件读命令
I2C_WaitAck();
temp=I2C_ReceiveByte(0);
I2C_Stop();
return temp;
}
else
{
return Sys_Value.Eeprom_Buf[(u8)FlashAddress];
}
}
void __eeprom_write_8(unsigned short addr_eep,uint8_t data)
{
// Sys_Value.Eeprom_Buf[addr_eep]=data;
I2C_WriteByte(data,addr_eep,0xA0);
}