数码之家

 找回密码
 立即注册
搜索
查看: 2149|回复: 7

为啥24C02读写失败

[复制链接]
发表于 2023-8-1 13:10:55 | 显示全部楼层 |阅读模式
/****************************************************************
AT24C前四位固定为1010 A1-A2由管脚电平默认接地,最后一位表示读写操作所以AT24C读地址0xa1 写地址0xa0
写AT24C02的时候从器件地址为10100 0000,(0xa0),读AT24C02的时候从器件地址为10100 0001,(0xa1)
***************************************************************/
#include<reg51.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
        #define AT24C02_ADDRESS  0xA0 //0xA0 1010 00000写地址
uint Count;
uint Set_Count;
//        unsigned  int Count;
//      unsigned  int Set_Count;
uint Num_L;
uint Num_H;

uint num1;
uint  num2;

char yiwei_Count;//移位计数

sbit I2C_SCL= P1^6;
sbit I2C_SDA= P1^7;

sbit Start_Dianji=P3^0; //电机启动_dianji
sbit  forward=P3^1; //正转检测
//sbit  back=P3^1;    //反转检测
sbit  run=P3^7;      //运行信号
sbit  jia_up=P1^0;// 增加键
sbit  jian_down=P1^1;//减少键
sbit  yiwei_up=P1^2; //移位键
sbit  qingling=P1^3; //清零键
uchar code ledcode[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};         //共阴数码管编码0-9
uchar data Ledbuff[8]={1};//显示缓冲区

#define I2CDelay_us(){_nop_();_nop_();_nop_();_nop_();}//voidI2CDelay_us
/***********************
AT24C初始化
*************************/
void I2C_init()
{
        I2C_SCL=1;
  //    I2CDelay_us(4);
        I2C_SDA=1;//首先确保SCL SDA都是高电平
  // I2CDelay_us(4);
       
}

/***********************
产生总线起始信号
*************************/
void I2C_Start(void )
{
  I2C_SDA=1;//首先确保SCL SDA都是高电平
//  I2CDelay_us(5);
        I2C_SCL=1; //确保SCL高电平
  //   I2CDelay_us(5);
        I2C_SDA=0;//先在SCL为高时拉低SDA,即为起始信号
//    I2CDelay_us(5);
        I2C_SCL=0; //在拉低 SCL,钳住I2C总线准备发送或接收数据
}
/***********************
产生总线停止信号:先拉低SDA在拉低SCL
*************************/

  void I2C_Stop(void )
{
        I2C_SDA=0;//首先确保SCL SDA都是低电平
        I2C_SCL=1; //先拉高 SCL
        I2C_SDA=1;//在拉高 SDA
   /*
I2C_SCL=0;
I2C_SDA=0;
//    I2CDelay_us(4);
   I2C_SCL=1;
I2C_SDA=1;
//   I2CDelay_us(4);
   */
}
/*******************************************************************************
*@brief I2C发送一个字节数据
*@param Byte要发送的字节
*@retval 无
起始信号后必须送一个从机地址(7)位,1-7位为要接收器件的地址,第八位读写位0发送1接收,第9位ACK应答位,
紧接着为第一个数据字节,然后一位应答位ACK后面继续第二个数据字节
**********************************************************************************/
void  I2C_SendByte(unsigned char Byte)
{
unsigned char i;
        for(i=0;i<8;i++)
           {
                I2C_SDA=Byte&(0x80>>i);
                I2C_SCL=1; //先拉高 SCL
                  I2C_SCL=0; //SCL       
             }
}
/***********************************************************************************************
*@brief I2C读取 接收一个字节
*@param 无
*@retval  读取 接收到的一个字节数据
********************************************************************************************/
unsigned char I2C_ReceiveByte(void)
{
unsigned char i,Byte=0x00;//
        I2C_SDA=1; //
        for(i=0;i<8;i++)
        {
        I2C_SCL=1; //先拉高 SCL
        if(I2C_SDA){Byte |= (0x80>>i); }
        I2C_SCL=0; //SCL       
        }
return Byte;
}
/*********************
*@brief I2C发送应答Ack
*@param AckBit应答位 0为应答(成功) 1为非应答(失败)
*@retval 无
************************/
void I2C_SendAck(unsigned char AckBit)
{

        I2C_SDA=AckBit;
        I2C_SCL=1; //先拉高 SCL
        I2C_SCL=0; //SCL       
}
/*********************
*@brief I2C接收应答位
*@param 无
*@retval AckBit应答位 0为应答(成功) 1为非应答(失败)
************************/
unsigned char I2C_ReceiveAck(void)
{
unsigned char AckBit;
        I2C_SDA=1;
        I2C_SCL=1; //先拉高 SCL
  AckBit=I2C_SDA;
        I2C_SCL=0; //SCL
return AckBit; //返回值
}
/**********向AT24C写数据***********
*@brief AT24C写入一个字节
*@param WordAddress要写入字节的地址
*@param Data要写入的数据
*@retval无
写多字节时,写入一个字节。在写一个字节前,必须延时5ms
************************/
void AT24C_WriteByte(unsigned char WordAddress,Data)
{
  I2C_Start();//启动总线
  I2C_SendByte(AT24C02_ADDRESS);//发送写操作地址+写数据(0xa0)
        I2C_ReceiveAck();                      //等待应答
        I2C_SendByte(WordAddress);//要写入的地址
        I2C_ReceiveAck();       //等待应答完成
   I2C_SendByte(Data);  //要写入的数据,第一字节 ,第二字节注意:每个字节都回应一个应答位0,如果没有回应说明写入不成功
        I2C_ReceiveAck();       //等待完成  注意:每个字节都回应一个应答位0,如果没有回应说明写入不成功
        I2C_Stop(); //发送结束信号:停止总线
}
/************从AT24C中读出数据*********
*@brief AT24C读取一个字节
*@param WordAddress要读出字节的地址
*@param 无
*@retval要读出的数据
************************/
unsigned char AT24C_ReadByte(unsigned char WordAddress) //void
{
        unsigned char Data;
  I2C_Start();                    //发送起始信号:启动总线
   I2C_SendByte(AT24C02_ADDRESS);  //接上首字节,发送器件写操作地址+写数据(0xa0)这里写操作时维纶把所要读的数据地址AT24C02_ADDRESS通知读取哪个地址信息
        I2C_ReceiveAck(); //等待完成应答
        I2C_SendByte(WordAddress);//发送要读取内存的地址(WORD ADDRESS)
        I2C_ReceiveAck();       //等待应答完成
         I2C_Start();//在次启动总线
        I2C_SendByte(AT24C02_ADDRESS| 0x01);  //对E2PROM进行读操作(0XA1)E2PROM会自动向主机发回数据,读取一个字节后回应一个应答位,后会继续传送下一个地址的数据0xa1
        I2C_ReceiveAck();       //等待完成
        Data= I2C_ReceiveByte();  //要读出的数据到Data
   I2C_SendAck(1);       //等待完成::如果不想读了,就发送一个非应答位NAK(1),发送结束,停止总线
        I2C_Stop(); //停止总线
        return Data ;//返回值
}

/***********************************/
void delayms(uint ms)

{
uchar k;
while(ms--)
{
  for(k = 0; k < 120; k++);
}
}
void display()                        //显示程序display(uchar a,b,c,d,e)        char i;
{
        static unsigned char i = 0;         
        switch(i)//使用多分支选择语句 i=count display 0x代表16进制
        {
                case(0):Ledbuff[7]=Set_Count/1000;break;   //取设定千位字符送缓存
                case(1):Ledbuff[6]=Set_Count/100%10;break; //取设定百位字符送缓冲
                case(2):Ledbuff[5]=Set_Count/10%10;break;   //取设定十位字符送缓冲
                case(3):Ledbuff[4]=Set_Count%10;break;     //取设定个位字符送缓存
                  
                case(4):Ledbuff[3]=Count/1000;break;         //取计数千位字符送缓存
                case(5):Ledbuff[2]=Count/100%10;break;
                case(6):Ledbuff[1]=Count/10%10;break;
                case(7):Ledbuff[0]=Count%10;break;
        }
        P0=0x00;   //消阴:段码全部低电平关闭
        P2=~(0x01<<i); //P2位选,左移i位取反
        P0=ledcode[Ledbuff];  //P0字符刷新显示
        delayms(1); //显示2MS
        i=++i%8;   //自加1
}
/*
void  Adjust(void)        //按键设定匝数,用P2.4个位-P2.7(千位)前四位数码管显示
  {       
          
                if(yiwei_up==0) //移位按键按下
                  {
                         while(!yiwei_up); //等待移位按键松开
                         if(yiwei_Count<3) //移位
                           {
                            yiwei_Count +=1;
                           }
                   else       //如果>3
          {                 
            yiwei_Count=0;       //设定位在个位
          }                               
                   }
               
                if(Set_Count>=0 && Set_Count<9999)//最大9999yiwei_Count=0; //设定加 jia_up==0
                  {
                                  if (jia_up==0)   //增加按键按下                                                    
                                         {       
                                           while(!jia_up);//等待加按键松开
                                                  {
                             if (yiwei_Count==0)        //               
                             {
                                     Set_Count += 1;         //设定+1
                             }
                                                          if (yiwei_Count==1&&Set_Count<9990)
                                                                 {
                                     Set_Count += 10;         //设定+1
                              }
                                                          if (yiwei_Count==2&&Set_Count<9900)
                                                                 {
                                     Set_Count += 100;         //设定+1
                             }       
                                                                if (yiwei_Count==3 && Set_Count<9000)
                                                                 {
                                     Set_Count += 1000;         //设定+1
                             }
                                                  }       
                                         }       
                 if (jian_down==0)   //减少按键按下                                                    
                                {       
                                           while(!jian_down);//等待按键松开
                                                 {
                             if (yiwei_Count==0&&Set_Count>1)        //        移位在个位       
                              {
                                      Set_Count -= 1;         //设定+1
                              }
                                                          if (yiwei_Count==1&&Set_Count>9)
                                                                   {
                                      Set_Count -= 10;         //设定+1
                               }
                                                          if (yiwei_Count==2 && Set_Count>99)
                                                                   {
                                      Set_Count -= 100;         //设定+1
                               }       
                                                                 if (yiwei_Count==3 && Set_Count>999)
                                                                   {
                                       Set_Count -= 1000;         //设定+1
                               }
                                                 }       
                                                 //写入数据
/*        AT24C_WriteByte(0,Set_Count%256);
        delayms(5) ; //显示2MS
        AT24C_WriteByte(1,Set_Count/256);
        delayms(5); //显示2MS
                                        }       
                */       
/**********************************************************
主函数
**********************************************************/               
void main()
{
        I2C_init();
//        init();        //初始化24C02
        //        num=5678;          //num为小于等于65535的整数。   */
        Set_Count=1234;

       Num_H  =Set_Count/256;  //1234/256就简单了,高位:取的是整数倍,不被整除的部分自然就被剔除了1234/256=4
        Num_L =Set_Count%256;  //%256是取余,低位:也就是你把前面的数值除以256取余,商跟除数则是256的整数倍部分1234%256=210  ;4*256=1024  %256=1234-1024=210
        AT24C_WriteByte(0,Num_L);
   delayms(10); //显示2MS
        AT24C_WriteByte(1,Num_H  );
   delayms(10); //显示2MS

        num1=AT24C_ReadByte(0);        //用地址0单元存储num十六进制表示时的低两位
        num2=AT24C_ReadByte(1);        //用地址0单元存储num十六进制表示时的高两位
        Count=num2*256+num1;
   //写入24C02
              //       AT24C_WriteByte(0,Set_Count%256); //低8位写入0x00
   /*
    AT24C_WriteByte(0,120); //低8位写入0x00
                       delayms(5); //显示2MS

        //              AT24C_WriteByte(1,Set_Count/256); //高8位写入0x01
      AT24C_WriteByte(1,0); //高8位写入0x01
                      delayms(5); //显示2MS

        //读取数据  /*

        Num_L = AT24C_ReadByte(0);
        Num_H  |=AT24C_ReadByte(1)<<8;
       Count= Num_H+Num_L;
*/
while(1)
        {
/* Adjust();       
          if (jia_up==0)   //增加按键按下                                                    
                 {       
                 while(!jia_up);//等待加按键松开
                  {   
                     Set_Count += 1;         //设定+1
                  }
               }
         if (jian_down==0)   //减少按键按下                                                    
                {       
                         while(!jian_down);//等待按键松开
                                 {          
                                   Set_Count -= 1;         //设定+1
                                 }
                  }
                  if(yiwei_up==0) //保存按键按下,向AT24C写入数据
                  {
                         while(!yiwei_up); //等待移位按键松开                        
                           {
                           AT24C_WriteByte(0,Set_Count%256);
                       delayms(6) ; //显示2MS
                      AT24C_WriteByte(1,Set_Count/256);
                      delayms(6); //显示2MS
                           }
                }          
               
                if( qingling==0) //读取按键按下
                  {
                         while(! qingling); //等待移位按键松开                        
                           {
                             Count=AT24C_ReadByte(0);
                             Count |=AT24C_ReadByte(1)<<8;
                          // Count  = Num_H+Num_L;
                           }
                }        */   
        display();               
     }
}



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
发表于 2023-8-1 21:37:50 | 显示全部楼层
那些delay为啥不要100K或者400K,别搞太快
回复 支持 反对

使用道具 举报

发表于 2023-8-2 07:19:33 | 显示全部楼层
上示波器看看时序对不对啊,先把速度放慢看能不能跑起来。C51模拟IIC的代码漫天都是,找个改改,是在不行上AI帮你。KEIL仿真能看到管脚的电平波形(我没用过)可以试试
回复 支持 反对

使用道具 举报

发表于 2023-8-2 08:35:49 | 显示全部楼层
没错,模拟IIC是很容易成功的,网上下载一个,稍微修改一下(主要是看延时程序用不用修改一下)就能成功。
回复 支持 反对

使用道具 举报

发表于 2023-8-8 17:54:47 | 显示全部楼层
读写EEprom延时需要满足手册里最大读写时间,记得需要写入10MS左右。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-9 10:40:18 | 显示全部楼层
unsigned char I2C_ReceiveAck(void)
{
unsigned char AckBit;
        I2C_SDA=1;
        I2C_SCL=1; //先拉高 SCL
  AckBit=I2C_SDA;
        I2C_SCL=0; //SCL
//这里加上:I2C_SDA=1;读写正常
   I2C_SDA=1;
return AckBit; //返回值
回复 支持 反对

使用道具 举报

发表于 2023-8-11 10:48:03 | 显示全部楼层
unsigned char I2C_ReceiveAck(void)
{
unsigned char AckBit,Tm;
  AckBit=I2C_SDA;//端口若是准双向口,假读一次释放,如果是OC输出,则I2C_SDA=1释放,若是推挽输出则切换为输入
         I2C_SCL=1; //先拉高 SCL
   while(I2C_SDA)
{Tm++;
if(Tm>250)
{
I2C_Stop();
Return 1;//失败

}};
         I2C_SCL=0; //SCL
return 0; //返回值
}
回复 支持 反对

使用道具 举报

发表于 2023-8-11 10:59:05 | 显示全部楼层
就是I2C的时许
回复 支持 反对

使用道具 举报

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

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-2 06:26 , Processed in 0.249601 second(s), 11 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

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