hkq1989 发表于 2010-7-23 17:18:06

关于24C02的头痛的问题,有哪位碰到过这些问题,请指点一下

关于24C02迷惑的问题
我想多次向24C02写入多个数据,源程序都写好,在送入数据时,如果每次写入4个数据,有时可以显示正常,有时不正常,如果每次写入6个数,则显示就出现不可预料的问题。请问单次向24C02写入数据个数有什么限制吗?附上源程序,测试过源程序没有什么问题,就是在一次向24C02写入数据个数上有问题,看了别人很多贴貌似是与24C02一页写入的数据个数限制有关,具体有什么关系,问题出在哪调试了好久都没有弄明白。请大侠帮忙看看,谢谢

这是24C02的读写程序,测试貌似没有问题,也许是这其中问题我没发现,请指导
/********************************************************************************************************************/
//*******************************************************************

//***IIC总线芯片为24C02,读写程序。
//*******************************************************************
#include "reg52.h"
#include<intrins.h>
#defineuchar unsigned char        
#defineuintunsigned int
#definenop_nop_()       /* 定义空操作指令 */
sbit SCL=P3^4;                                /*24C01 CSL接脚=89C51 T0 P3.4*/
sbit SDA=P3^5;                                /*24C01 SDA接脚=89C51 T1 P3.5*/
bit TSendAddress(uchar slaaddress,uchar subaddress,uchar * s,uchar no); /* 向有子地址器件写入6字节数据函数 */
bit TRcvAddress(uchar slaaddress,uchar subaddress,uchar * s,uchar no);/* 向有子地址器件读取6字节数据函数 */
bit acknow;                                 /* 应答标志位 acknow=1 表示正常响应 acknow=0 表示未响应 */

void delay (unsigned int value)                /*延时副程式*/
{
      while (value!=0) value--;                /*10US延时*/
}
                       



void Start();                           /* 起动总线函数 */
void Stop();                            /* 结束总线函数 */
void SendByte(uchar c);               /* 8951发数据或地址给8583 字节数据发送函数 */
uchar RcvByte();                        /* 8951从24C01读数据字节数据接收函数 */
void Ack(bit a);                        /* 主机8951应答子函数 */




/*******************************************************************/
//   24C02芯片读写      起动总线函数               
//函数原型: voidStart();
//功能:   启动总线,发送24c02启动条件.
//==================================================================
void Start()
{//SDA=0;

// SCL=0;
   SDA=1;           /*发送起始条件的数据信号*/
   nop;
   SCL=1;
   nop;            /*起始条件建立时间大于4.7us,延时*/
   nop;
   nop;
   nop;
   nop;   
   SDA=0;           /*发送起始信号*/
   nop;            /* 起始条件锁定时间大于4us*/
   nop;
   nop;
   nop;
   nop;      
   SCL=0;           /*钳住总线,准备发送或接收数据 */
   nop;
   nop;

}
//==================================================================
//                      结束总线函数               
//函数原型: voidStop();
//功能:   结束总线,发送24c02结束条件.
//==================================================================
void Stop()
{
// SCL=0;
SDA=0;        /*发送结束条件的数据信号*/
nop;           /*发送结束条件的时钟信号*/
SCL=1;        /*结束条件建立时间大于4us*/
nop;
nop;
nop;
nop;
nop;
SDA=1;        /*发送总线结束信号*/
nop;
nop;
nop;
nop;

}
//==================================================================
//               89X52发数据或地址给24c02 字节数据发送函数               
//函数原型: voidSendByte(uchar c);
//功能:   将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
//      此状态位进行操作.(不应答或非应答都使acknow=0)   
//             发送数据正常,acknow=1; acknow=0表示被控器无应答或损坏。
//==================================================================
voidSendByte(uchar c)
{ uchar count;
for(count=0;count<8;count++)/*要传送的数据长度为8位*/
    {if((c<<count)&0x80)
         SDA=1;                /*判断发送位*/
   else
         SDA=0;               
   nop;
   SCL=1;               /*置时钟线为高,通知被控器开始接收数据位*/
      nop;
      nop;             /*保证时钟高电平周期大于4us*/
      nop;
      nop;
      nop;         
   SCL=0;
    }
    nop;
    nop;
    SDA=1;                /*8位发送完后释放数据线,准备接收应答位*/
    nop;
    nop;   
    SCL=1;
    nop;
    nop;
    nop;
    if(SDA==1)
      acknow=0;      /* 24c02无应答 */   
    else
      acknow=1;      /* 发送数据正常 */
    SCL=0;
    nop;
    nop;
}
//==================================================================
//               89X52从24c02读数据字节数据接收函数               
//函数原型: ucharRcvByte();
//功能:          用来接收从器件传来的数据,并判断总线错误(不发应答信号),
//          发完后请用应答函数应答从机。
//==================================================================
ucharRcvByte()
{uchar retc=0;
uchar count;
SDA=1;                             /*置数据线为输入方式*/
for(count=0;count<8;count++)
   {    nop;         
      SCL=0;                  /*置时钟线为低,准备接收数据位*/
      nop;
      nop;               /*时钟低电平周期大于4.7us*/
      nop;
      nop;
      nop;
      SCL=1;                  /*置时钟线为高使数据线上数据有效*/
      nop;
      nop;
      retc=retc<<1;
      if(SDA==1)
      retc=retc+1;/*读数据位,接收的数据位放入retc中 */
      nop;
      nop;
}
SCL=0;   
nop;
nop;
return(retc);
}
//==================================================================
//                     主机89X52应答子函数
//函数原型:void Ack(bit a);
//功能:      主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
//==================================================================
void Ack(bit a)
{if(a==0)                  
   SDA=0;                /*在此发出应答或非应答信号 */
else                     /* 应答 a=0非应答 a=1 */
   SDA=1;
nop;
nop;
nop;      
SCL=1;
nop;
nop;                  /*时钟低电平周期大于4us*/
nop;
nop;
nop;
SCL=0;                     /*清时钟线,钳住I2C总线以便继续接收*/
nop;
nop;   
}
//==================================================================
//               用户接口函数                                 
// 有无子地址表示是否向芯片的特定地址写数据
//          向有子地址器件写入6字节数据函数对应数据资料中的写模式            
//函数原型: bitTSendAddress(uchar slaaddress,uchar subaddress,uchar * s,uchar no);
//功能:   从启动总线到发送从地址,子地址,数据,结束总线的全过程,从器件
//          地址slaaddress,子地址subaddress,发送内容是s指向的内容,no=字节数
//          如果返回1表示操作成功,否则操作有误。
//注意:    使用前必须已结束总线。
//==================================================================
bit TSendAddress(uchar slaaddress,uchar subaddress,uchar * s,uchar no) /* 向有子地址器件写入6字节数据函数 */
{uchar i;
   Start();                               /*启动总线*/
   SendByte(slaaddress);      /*发送器件地址*/
   if(acknow==0)
return(0);
   SendByte(subaddress);      /*发送器件子地址*/
   if(acknow==0)
return(0);
   for(i=0;i<no;i++)
   {
   SendByte(* s);
                          /*发送数据*/
        if(acknow==0)
    return(0);
        s++;
   }   
   Stop();                                /*结束总线*/
   return(1);
}
//==================================================================
//                  向有子地址器件读取4字节数据函数               
//函数原型: bitTRcvAddress(uchar slaaddress,uchar subaddress,uchar * s,uchar no);
//功能:   从启动总线到发送从地址,子地址,读数据,结束总线的全过程,从器件
//          地址slaaddress,子地址subaddress,读出的内容放入s, no=字节数
//         如果返回1表示操作成功,否则操作有误。
//注意:    使用前必须已结束总线。
//==================================================================
bit TRcvAddress(uchar slaaddress,uchar subaddress,uchar * s,uchar no)
{ uchar i;
   Start();                           /*启动总线*/
   SendByte(slaaddress);                /*发送器件从地址*/
// if(acknow==0)
// return(0);
   SendByte(subaddress);               /*发送器件子地址*/
   if(acknow==0)
   return(0);
   Stop();                   /*结束总线*/
   Start();                       /*重新启动总线*/
   SendByte(slaaddress+1);
// if(acknow==0)
// return(0);
   for(i=0;i<no-1;i++)
    {* s=RcvByte();               /*接收数据*/
   Ack(0);                     /*发送应答位*/
   s++;
    }                  
   * s=RcvByte();               /*接收数据*/
   Ack(1);
   Stop();                   /*结束总线*/
   return(1);
}
//==================================================================

/********************************************************************/
void main()
{        
   uchark,f,stad=0;
   uchar out={8,9,6,7,5,4};
   Lcminit();                /*液晶初始化*/
   delay_10ms(10);        /*延时100ms*/
   Lcmcls();                   /*全屏清零*/
   for(k=0;k<63;k++){
      stad=k*4;
      TSendAddress(0xa0,stad,&out,4);//连续存储每次存储四个字节
      delay_10ms(10);
   }
   
while(1){
   for(f=0;f<63;f++) {
         uchar st=f*4;
         TRcvAddress(0xa0,st,&out1,4);//读出四个字节
         delay_10ms(50);
         for(i=0;i<40;i++) print1();//显示读出的数据
          Lcmcls();
          for(i=0;i<50;i++)print2();//显示读出的数据
          Lcmcls();
   }
}
将写入和读出改为每次6个字节就不会出现预想的结果了。
路过的请指点一写入与读出数据个数是否有规定,如果没有请哪位有时间的帮忙看看程序是否有问题。

zhaojun_xf 发表于 2010-7-23 17:30:44

连线写不紧跟页面大小有关,而且还跟起始地址有关,如:0x00开始可以写入8个,0x01开始可以写入7个,0x02开始可以写入6个。。。

hkq1989 发表于 2010-7-23 17:45:27

回复【1楼】zhaojun_xf
-----------------------------------------------------------------------

0x00开始可以写入8个,0x01开始可以写入7个,0x02开始可以写入6个。。。 是指最大写入个数吗?我在程序中要将整个存储空间写满,每次写入个数相同,该注意什么问题?谢谢

easytime 发表于 2010-7-23 18:14:49

The data word address lower three (1K/2K) or four (4K, 8K, 16K) bits are internall
incremented following the receipt of each data word. The higher data word address bit
are not incremented, retaining the memory page row location. When the word address
internally generated, reaches the page boundary, the following byte is placed at th
beginning of the same page. If more than eight (1K/2K) or sixteen (4K, 8K, 16K) dat
words are transmitted to the EEPROM, the data word address will "roll over"and pre
ous data will be overwritten.

24c02的文档里详细说明,一个page就是8个字节。你写的时候,如果有超过这个边界,它就会翻卷回来写的,比如你从0x06开始写6个数据,那么结果写的地址就是0x06,0x07,0x00,0x01。。。翻卷回来把之前的覆盖了,所以你要注意边界问题。这只是讨论页写的注意事项,不代表你的程序的工作也都是没问题了。比如时序,地址等等,你也可以用逻辑分析仪抓捕这个过程,看看问题到底在哪里。

http://cache.amobbs.com/bbs_upload782111/files_31/ourdev_570300.jpg
(原文件名:iic_full_chain.jpg)

hkq1989 发表于 2010-7-23 18:32:27

哎呀,卡在这真难受,整个东西什么都稳定了,结果数据存不了,读不出,还是过不去。。。。。啊夏天调程序,遇到过不去的问题,疼啊。。。。。

hkq1989 发表于 2010-7-23 18:33:44

回复【3楼】easytime
-----------------------------------------------------------------------

恩,我再试试,怎么调整都摸不清到底是怎么回事,出现的结果和理解的不一样

ADO1234 发表于 2010-7-23 19:09:56

一页最多8个数据了

hkq1989 发表于 2010-7-23 21:41:41

回复【6楼】ADO1234
-----------------------------------------------------------------------

一页的首地址有限制吗?是仅为8的倍数的一页的首地址还是任意开始的地址可以作为页地址?为何每次写六个数据会出错??

hkq1989 发表于 2010-7-23 22:45:22

有哪位可以指导一下,卡在这了走不了,纠结。。。

helloshi 发表于 2010-7-24 06:55:18

TSendAddress(0xa0,stad,&out,4);//连续存储每次存储四个字节

TSendAddress内部要处理每次写到页边界结束地址要延时一段时间。

helloshi 发表于 2010-7-24 07:08:27

//连续写
uchar AT24CWrite(uchar *buf,uint addr,uint length)
{
uintaddress;
uintaddress_end;       //字结束地址
uintpage_address_end;//页结束位置
uintpage,page_end;   //页号,跨器件

address= addr;
address_end =address+length;
if((address_end)>EEPROM_SIZE_MAX)return 1; //超出最多芯片最大容量

page            = address   / PAGE_SIZE;   //起始页
page_end          = address_end / PAGE_SIZE;   //结束页

while(page <= page_end)
{
          IICStart();      //起始信号
          if(!IICWrite((uchar)DEVICE_ADDRESS(address)|WRITE)){IICStop();return 1;}//器件地址
          
    #if DEVICE >= 32 // AT24C32以上写高地址
          if(!IICWrite((uchar)(address % EEPROM_SIZE)>>8)){IICStop();return 1;}   //字高地址
    #endif
          if(!IICWrite((uchar)(address % EEPROM_SIZE))){IICStop();return 1;}      //字低地址

          
          if(page++ == page_end) page_address_end = address_end;//最后一页
          else page_address_end = PAGE_SIZE*(page);         //完整一页
          
          while(address<page_address_end){
      if(!IICWrite(*buf++)){IICStop(); return 1;}
                  address++;
          }
          IICStop();       //停止信号

          twr();//延时,等待页写完
}
return 0;
}

easytime 发表于 2010-7-24 15:18:37

呵呵,上面大家把能想到的都说过了。主要是看不到你的问题在哪里,写了什么,读回来的又是什么。找我联系逻辑分析仪,包你立刻能搞定。实时看到数据线上的字节内容,调试一目了然。
http://cache.amobbs.com/bbs_upload782111/files_31/ourdev_570459.jpg
(原文件名:iic_settings.jpg)

http://cache.amobbs.com/bbs_upload782111/files_31/ourdev_570460.jpg
(原文件名:iic_start.jpg)

hkq1989 发表于 2010-7-31 01:01:40

回复【10楼】helloshi
//连续写
uchar at24cwrite(uchar *buf,uint addr,uint length)
{
uintaddress;
uintaddress_end;       //字结束地址
uintpage_address_end;//页结束位置
uintpage,page_end;   //页号,跨器件
address= addr;
address_end =address+length;
if((address_end)&gt;eeprom_size_max)return 1; //超出最多芯片最大容量
page            = address   / page_size;   //起始页
page_end          = address_end / pag......
-----------------------------------------------------------------------

我在处理中采取了笨办法,就是把有些存储空间给空出来,你的这个我将在接下的应用中好好研究,能否将你的24CXX的存储相关的程序发给我一下,呵呵在网上看了很久你的这个思路第一次看到

sunnyhook 发表于 2010-7-31 22:04:50

你的IIC总线是模拟时序时序的,在存取数据时时序不应该被打断,这就需要做一些中断保护,比如操作IIC时将串口中断关闭,或者必要时将全局中断关闭,等操作完总线再将中断使能恢复。

yjtyxd 发表于 2010-9-19 09:36:22

mak

snfjhudng 发表于 2015-5-16 14:28:55

你每次写完4个字节,加点延时就可以了。
页: [1]
查看完整版本: 关于24C02的头痛的问题,有哪位碰到过这些问题,请指点一下