OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1398|回复: 2

AT24C512 读取问题

[复制链接]

10

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
69
金钱
69
注册时间
2026-2-9
在线时间
25 小时
发表于 2026-3-3 19:40:52 | 显示全部楼层 |阅读模式
1金钱
AT24C512 在硬件读取时即读取正常,但在块读取时加电后只能正确读取一次,以后只读取第一个字节,其他的都是0XFF

代码如下。

void I2C_Config()
{        RCC->APB2ENR|=1<<3;    //使能PORTB时钟       
          RCC->APB1ENR|=1<<21;   //使能I2C1时钟
        GPIOB->CRL&=0X00FFFFFF;
        GPIOB->CRL|=0XFF000000;//PB.5 推挽输出            
   GPIOB->ODR|=1<<6;      //PB.5 输出高
        I2C1->TRISE=0X25;
        I2C1->CR2=0X0024;
        I2C1->CCR=0XB4;
        I2C1->CR1=0X0401;
}


//***********************************************************************************************************************************************************************************
u8 AT24Cxxx_ReadOneByte(u16 ReadAddr)
{        u8 temp=0;                                                                                                                                                                      
        I2C1->CR1|=0X0100;                                                //起始信号                       
        while(!(I2C1->SR1&(1<<0)));                        //等待起始信号发送完成       
        if(EE_TYPE>AT24C16)
        {        I2C1->DR=(0XA0);           //发送写命令
                while(!(I2C1->SR1&(1<<1)));               
                I2C1->DR=(ReadAddr>>8);//发送高地址            
        }else
                I2C1->DR=0XA0+(ReadAddr/256<<1);
        while(!(I2C1->SR1&(1<<1)));                        //等待地址发送完成
        temp=I2C1->SR2;
        I2C1->DR=ReadAddr%256;
        while(!(I2C1->SR1&(1<<7)));                        //等待发送缓冲区空
        I2C1->CR1|=0X0100;
        while(!(I2C1->SR1&(1<<0)));                        //等待起始信号发送完成
        I2C1->DR=0XA1;
        while(!(I2C1->SR1&(1<<1)));                        //等待地址发送完成
        temp=I2C1->SR2;
        while(!(I2C1->SR1&(1<<6)));                         //等待收到数据
        temp=I2C1->DR;       
        I2C1->CR1&=~0X0400;                                                 //关闭应答
        I2C1->CR1|=0X0200;                                                 //产生停止信号
        return temp;
}

void AT24Cxxx_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{        u8 temp;                                                                                                                                                                          
        I2C1->CR1|=0X0100;                                                //起始信号                       
        while(!(I2C1->SR1&(1<<0)));                        //等待起始信号发送完成
        if(EE_TYPE>AT24C16)
        {        I2C1->DR=(0XA0);           //发送写命令
                while(!(I2C1->SR1&(1<<1)));               
                I2C1->DR=(WriteAddr>>8);//发送高地址            
        }else
                I2C1->DR=0XA0+((WriteAddr/256)<<1);
        while(!(I2C1->SR1&(1<<1)));                       
        temp=I2C1->SR2;
        I2C1->DR=WriteAddr%256;                                 //发送低地址
        while(!(I2C1->SR1&(1<<7)));       
        I2C1->DR=DataToWrite;                                         //发送字节       
        while(!(I2C1->SR1&(1<<7)));               
        I2C1->CR1|=0X0200;                                                 //产生停止信号
        delay_ms(10);                                         //EEPROM的写入速度比较慢,加入延迟
}





//***********************************************************************************************************************************************************************************
#if AT24Cx_Write
        void  AT24Cxxx_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
        {        while(NumToWrite--)
                {        AT24Cxxx_WriteOneByte(WriteAddr,*pBuffer);
                        WriteAddr++;
                        pBuffer++;
                }
        }
#else
        void AT24Cxxx_PWrite(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
        {        u8 temp;               
                I2C1->CR1|=0X0100;                                                //起始信号                       
                while(!(I2C1->SR1&(1<<0)));                        //等待起始信号发送完成
                if(EE_TYPE>AT24C16)
                {        I2C1->DR=(0XA0);           //发送写命令
                        while(!(I2C1->SR1&(1<<1)));               
                        I2C1->DR=(WriteAddr>>8);//发送高地址            
                }else
                        I2C1->DR=0XA0+((WriteAddr/256)<<1);
                while(!(I2C1->SR1&(1<<1)));                       
                temp=I2C1->SR2;
                I2C1->DR=WriteAddr%256;                                 //发送低地址
                while(!(I2C1->SR1&(1<<7)));       
                while(NumToWrite--)
                {        I2C1->DR=*pBuffer++;                                        
                        while(!(I2C1->SR1&(1<<7)));       
                }
                I2C1->CR1|=0X0200;                                                 //产生停止信号
                delay_ms(10);                                         //EEPROM的写入速度比较慢,加入延迟
        }

////***********************************************************************************************************************************************************************************
        void AT24Cxxx_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
        {        u8 i;
        w:        if (WriteAddr%AT24+NumToWrite<=AT24)  
                {        AT24Cxxx_PWrite(WriteAddr,pBuffer,NumToWrite);goto p;        }
                if (WriteAddr%AT24+NumToWrite>AT24)
                {        i=AT24-WriteAddr%AT24;
                        AT24Cxxx_PWrite(WriteAddr,pBuffer,i);
                        if (WriteAddr==0)
                        {        NumToWrite=NumToWrite-AT24;
                                WriteAddr+=AT24;
                                pBuffer+=AT24;
                        }else
                        {        NumToWrite=NumToWrite-i;
                                WriteAddr+=i;
                                pBuffer+=i;
                        }
                        goto w;
                }
                p:i++;
        }
#endif
//--------------------------------------------------------------------------------
#if AT24Cx_Read
        void AT24Cxxx_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
        {        while(NumToRead)
                {        *pBuffer++=AT24Cxxx_ReadOneByte(ReadAddr++);       
                        NumToRead--;
                }
        }
#else
        void AT24Cxxx_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
        {        u8 temp=0;                                                                                                                                                                      
                I2C1->CR1|=0X0100;                                                //起始信号                       
                while(!(I2C1->SR1&(1<<0)));                        //等待起始信号发送完成       
                if(EE_TYPE>AT24C16)
                {        I2C1->DR=(0XA0);           //发送写命令
                        while(!(I2C1->SR1&(1<<1)));               
                        I2C1->DR=(ReadAddr>>8);//发送高地址            
                }else
                        I2C1->DR=0XA0+(ReadAddr/256<<1);
                while(!(I2C1->SR1&(1<<1)));                        //等待地址发送完成
                temp=I2C1->SR2;
                I2C1->DR=ReadAddr%256;
                while(!(I2C1->SR1&(1<<7)));                        //等待发送缓冲区空       

                I2C1->CR1|=0X0100;                                                //起始信号
                while(!(I2C1->SR1&(1<<0)));                        //等待起始信号发送完成
                I2C1->DR=0XA1;
                while(!(I2C1->SR1&(1<<1)));                        //等待地址发送完成
                temp=I2C1->SR2;
                while(NumToRead--)
                {        while(!(I2C1->SR1&(1<<6)));                         //等待收到数据
                        temp=I2C1->SR2;
                        *pBuffer++=I2C1->DR;       
                       
                }
                I2C1->CR1&=~0X0400;                                                 //关闭应答
                I2C1->CR1|=0X0200;                                                 //产生停止信号
        }
#endif














回复

使用道具 举报

0

主题

95

帖子

0

精华

高级会员

Rank: 4

积分
500
金钱
500
注册时间
2026-1-29
在线时间
31 小时
发表于 前天 13:46 | 显示全部楼层
根据你的问题描述和提供的代码分析,以下是导致 AT24C512 块读取异常的根本原因及解决方案:

一、故障根源分析
1. ACK 机制失控
在你的 AT24Cxxx_Read 函数中,ACK 控制逻辑缺失:

while(NumToRead--) {
    while(!(I2C1->SR1 & (1<<6)));     // 等待 RXNE (接收缓冲区非空)
    temp = I24C1->SR2;                // &#10060; 错误!此处应先读取 SR1 再读 SR2
    *pBuffer++ = I2C1->DR;            // 读取数据
}

      
关键缺陷:循环内未动态控制 ACK 位。每读完一个字节后,若需继续接收下一字节,必须保持 ACK=1;若是最后一字节则设 ACK=0。你的代码始终未修改 I2C1->CR1 的 ACK 位(见下图),导致从机持续发送数据直至主动停止,引发总线冲突。


2. STOP 条件过早生成
你在循环结束后统一生成 STOP 信号:


I2C1->CR1 &= ~0X0400;         // &#10060; 错误!应在最后一字节前关闭 ACK
I2C1->CR1 |= 0X0200;          // &#10060; 错误!STOP 位置不当

      
后果:STOP 条件会在所有数据传输完毕前被提前触发,强制终止正在进行的传输。
3. 状态寄存器误操作
错误的寄存器读取顺序加剧问题:


temp = I2C1->SR2;              // &#10060; 危险!未优先清除 SR1 标志

      
原理:I2C 要求先读 SR1 再读 SR2/DR 以清除标志位。跳过此步会导致后续操作误判状态。
二、修复方案
Step 1:重构 ACK 控制逻辑
修改循环体,动态管理 ACK 状态:

while(NumToRead--) {
    while (!(I2C1->SR1 & (1 << 6)));       // 等待 RXNE (接收缓冲区非空)
   
    // 根据剩余字节数决定是否发送 NACK
    if (NumToRead == 0) {                  // 若是最后一个字节
        I2C1->CR1 |= (1 << 10);             // &#9989; 发送 NACK (ACK=0)
    } else {
        I2C1->CR1 &= ~(1 << 10);            // &#9989; 保持 ACK=1
    }
   
    temp = I2C1->SR2;                      // &#9989; 正确顺序:先读 SR1 → 再读 SR2
    *pBuffer++ = I2C1->DR;                  // &#9989; 最后读数据
}

      
Step 2:精准植入 STOP 条件
将 STOP 指令移至正确位置:

// 在循环结束后立即生成 STOP
if (NumToRead == 0) {                     // 确保仅在全部字节接收完后动作
    I2C1->CR1 |= (1 << 9);                 // &#9989; 生成 STOP 条件
}

      
Step 3:完善初始化配置
增强 I2C 初始化鲁棒性:


void I2C_Config() {
    // ... [原有配置保持不变] ...
   
    // 新增关键设置
    I2C1->CR1 |= (1 << 10);                 // &#9989; 初始使能 ACK (ACK=1)
    I2C1->CR1 &= ~(1 << 3);                 // &#9989; 禁止调试模式 (可选)
}

      
三、验证方法
示波器抓包:观测 SCL/SDA 波形,确认以下几点:

每个字节后的 ACK/NACK 脉冲是否符合预期。
STOP 条件是否严格发生在最后一个字节之后。
无多余 Start/Stop 干扰。
边界测试:

分别尝试读取 1/2/3/.../n 字节,逐步逼近故障临界点。
重点关注跨页地址时的连续性表现。
替代芯片验证:

更换另一批次 AT24C512 排除个体损坏嫌疑。
对比同一批次其他合格产品的读写行为。
四、延伸思考
此类问题的本质是对 I&#178;C 协议层的精细化把控不足。建议后续开发遵循以下原则:

严格时序管控:任何微小的信号畸变都可能引发连锁反应。
冗余校验机制:添加 CRC 校验或回读验证保障数据完整性。
模块化封装:将底层驱动抽象成独立组件便于维护升级。
回复

使用道具 举报

8

主题

605

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3044
金钱
3044
注册时间
2016-5-13
在线时间
193 小时
发表于 前天 15:31 | 显示全部楼层
uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr)
{                                  
        uint8_t temp=0;                                                                                                                                                               
    iic_start();  
        if(EE_TYPE>AT24C16)
        {
                iic_send_byte(0XA0);           //发送写命令
                iic_wait_ack();
                iic_send_byte(ReadAddr>>8);//发送高地址
                //iic_wait_ack();                 
        }else iic_send_byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据          

        iic_wait_ack();
    iic_send_byte(ReadAddr%256);   //发送低地址
        iic_wait_ack();            
        iic_start();                     
        iic_send_byte(0XA1+((ReadAddr/256)<<1));         //进入接收模式                          
        iic_wait_ack();         
    temp=iic_read_byte(0);                  
    iic_stop();//产生一个停止条件            
        return temp;
}

void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)
{                                                                                                                                                                                          
    iic_start();  
        if(EE_TYPE>AT24C16)
        {
                iic_send_byte(0XA0);            //发送写命令
                iic_wait_ack();
                iic_send_byte(WriteAddr>>8);//发送高地址
                //iic_wait_ack();                 
        }else
        {
                iic_send_byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据
        }         
        iic_wait_ack();          
    iic_send_byte(WriteAddr%256);   //发送低地址
        iic_wait_ack();                                                                                                              
        iic_send_byte(DataToWrite);     //发送字节                                                          
        iic_wait_ack();                                
    iic_stop();//产生一个停止条件
        delay_ms(10);         
}

24CXX_H

#define AT24C512    65535

#define EE_TYPE     AT24C04  把这行改为  #define EE_TYPE     AT24C512





虽然不明白你们在说什么,但感觉很厉害的样子。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-7-17 00:24

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表