搜索
bottom↓
回复: 10

求救!!!24C02!!!我想写入200个字节该怎么办???

[复制链接]

出0入0汤圆

发表于 2013-1-7 20:02:56 | 显示全部楼层 |阅读模式
大侠们,求救啊,好几天不知道该怎么办了!小弟万分感谢啊
#include <reg51.h>
#include <intrins.h>  //因为用到_nop_();
#define uchar unsigned char
#define uint unsigned int
sbit SCL = P3^2;      //注意P1、P2、P3口有内部上拉电阻,可直接连SDA和SCL,若想用P0需外接上拉电阻,否则连上无法输出高电平!
sbit SDA = P3^3;
/*********74H573控制端*********/
sbit dula = P2^7;//段选
sbit wela = P2^6;//位选的数据               
sbit LED = P2^5;//秒显示
uchar j=0;     //用于计数50ms的个数的全局变量
//写入24C02的一组数据,8个字节对应24C02的一页(共32页),这里把这些要验证的常数放到程序存储区
uchar code ToSDAdataBuffer[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15
};
uchar ReceivedData[16];//用于存储接收的8个字节数据(1页)的数组
//本例51为单主机,24C02为从机,不需要总线裁决
//延时5us子程序
/*******数码管段选_时间********/
uchar code table_duan[]={0x3f,0x06,0x5b,0x4f,0x66,
                                             0x6d,0x7d,0x07,0x7f,0x6f};
/*********数码管位选**********/
uchar code table_wei[]={0xfe,0xfd,0xfb,0xf7,0xef};
uchar temp,a,b;
uchar tt=0;
///////////////////////////////////////////////////////////////////////////
void delay1(uint x)
{
    uint i,j ;
    for(i=x;i--;i>0)
    for(j=110;j--;j>0);
}

/**************************** 显示函数 **************************/
void display(uchar wee,uchar H_hour,uchar L_hour,uchar H_min,uchar L_min)
{
        dula=1;
        P0=table_duan[wee];//数码管5位,星期
        dula=0;
        wela=1;
        P0=table_wei[0];
        wela=0;       
        delay1(1);

        dula=1;
        P0=table_duan[H_hour];//数码管4位,小时十位
        dula=0;
        wela=1;
        P0=table_wei[1];
        wela=0;       
        delay1(1);

        dula=1;
        P0=table_duan[L_hour];//数码管3位,小时个位
        dula=0;
        wela=1;
        P0=table_wei[2];
        wela=0;       
        delay1(1);

        dula=1;
        P0=table_duan[H_min];//数码管2位,分钟十位
        dula=0;
        wela=1;
        P0=table_wei[3];
        wela=0;       
        delay1(1);

        dula=1;
        P0=table_duan[L_min];//数码管1位,分钟个位
        dula=0;
        wela=1;
        P0=table_wei[4];
        wela=0;       
        delay1(1);
}

///////////////////////////////////////////////////////////////////////////
void delay5us(void)
{
        _nop_();  //时序图要求开始建立时间tSU.STA大于4.7us,开始保持时间tHD.STA大于4us。51中每个_nop_ ();延时1个CPU cycle,即1us。
        _nop_();  //如考虑不同CPU频率不同,可用带参数的延时,参数在前面宏定义。
        _nop_();
        _nop_();
        _nop_();
}
//约2ms的延时
void delay(uchar t)
{
        uchar x,y;
        for(x=0;x<t;x++)
                for(y=0;y<250;y++);
}
//I2C初始化
void InitI2C(void)
{
        SDA = 1;//总线空闲时,因各设备都是集电极或漏极开路,上拉电阻使SDA和SCL线都保持高电平。
        SCL = 1;
        delay5us();
}
//产生I2C开始信号
void StartI2C(void)
{
        SDA = 1;//SDA在SCL为高期间由高变低表示开始,所以先要高
        SCL = 1;
        delay5us();//时序图要求tSU.STA(Start Set-up Time)大于4.7us
        SDA = 0;   //注意SDA拉低前后都要维持5us以上!
        delay5us();   //tHD.STA(Start Hold Time)大于4us
        SCL = 0;   //拉低SCL,准备发送或接收数据(这两句也可在写或读字节的程序中先将SCL置0,延时)
        delay5us();
}
//产生I2C结束信号
void StopI2C(void)
{
        SDA = 0;   //SDA在SCL为高期间由低变高,说明结束
        SCL = 1;
        delay5us();
        SDA = 1;
        delay5us();
}
//发送方在发完一个字节后检测接收方有没有应答。返回应答成功否。
bit ChkAck(void)
{  
        bit SDAtemp;
        SDA     =  1;  //释放SDA(置1),然后等待接收方应答将它拉低。确切的说,应是24C02发送字节最后一位的第8个时钟周期下降沿后经tAA
        //(SCL变低到SDA OUT有效的时间)约0.1-4.5us后拉低SDA,并随第9个时钟后结束。所以24C02正常时,SDA为1并不体现
        //(第8脉冲后马上被拉低了),但若器件坏了,就需要靠这个置1后不变来判断!(若不置1而上次发的数据最后一位为0就不好判断了)
        //从24C02的Block Diagram看,它只能在SDA为1时通过控制内部的Dout来把SDA拉低,但不能在SDA为0时将其置高!故主机要常将SDA置1,而SCl置0。
        SCL     =  1;  //WriteI2CByte中写完一字节后又将SCL拉低,这里拉高产生第9个时钟上升沿,然后在SCL为高期间对SDA进行检测
        delay5us();
        SDAtemp = SDA; //如果不用暂存变量,直接return SDA,就不会执行后面的SCL = 0,检测期间的第9个时钟就不完整了
        SCL     =  0;
        delay5us();
        return SDAtemp;
}
//51作为主机时,如果接收数据,模拟产生应答时序。形参Ack为0,则应答0,为1不应答。
void AckAsMaster(bit Ack)
{
        if(!Ack)
        SDA = 0;
        else
        SDA = 1;
        delay5us();
        SCL = 1;  //主机控制SCL时序。关键是保证在SCL脉冲上升沿之前SDA数据已稳定即可。
        delay5us();
        SCL = 0;
        delay5us();
}
//往I2C总线写一个字节的数据(即将一个字节的数据发送到SDA上)
void  WriteI2CByte(uchar ByteData)
{
        uchar i,temp;
        temp = ByteData;
        //  (StartI2C()最后已经先将SCL变0了):
        for(i=0;i<8;i++)
        {
                temp <<=  1; //左移一位,I2C要求由MSB最高位开始,移出的CY即要发送到SDA上的数据。下面考虑时序:
                SDA    =  CY; //此时SCL已为低,每次移一位送出去(下次进循环后SDA还保持着上次发出去的数据)
                delay5us();  //SDA IN数据变化中点SCL上升沿中点的一段时间是tSU.DAT,即数据建立时间Data In Set-up Time,需大于200ns,多延无所谓
                SCL    =  1;
                delay5us();  //tHIGH即Clock Pulse Width High,最小4us
                SCL    =  0;
                delay5us();  //tLOW即Clock Pulse Width Low,最小4.7us
        }   
}
//读取I2C总线一个字节的数据
uchar  ReadI2CByte()    //串行总线,51一位位接收从机发送到SDA上的数据,这里只考虑数据已在SDA上时如何存下来这几位,组成一个字节
{
        uchar i,ByteData;
        SDA = 1;            //SCL在ChkAck中已经置0了。注意SCL时序仍然由主机控制!24C02只能将SDA由高拉低,象橡皮筋松手又恢复高,而下面只是读SDA,没赋值
        //其实程序中多处给SDA置1都可省,因为检查应答时为0就正常,无所谓,写字节时也无所谓,就是在读之前要保证SDA为1!
        //因之前有WriteI2CByte(0xa1); 其实这句也可省略。
        delay5us();   //24C02作为发送方在第9个时钟的negative edge clocks data out of each device,所以现在SDA上为新数据
        for(i=0;i<8;i++)
        {
                SCL = 1;  //置时钟线为高使数据线上数据有效
                delay5us();
                ByteData = (ByteData<<1)|SDA; //SDA上已是新数据了,读之。data不管以前多少,左移后最右边为0,和SDA“按位或”后MLB就是SDA
                SCL = 0;
                delay5us();
        }
        return ByteData;
}

//页写。输入两参数,一个为首字地址,另一个是指向待写入数据数组的指针(括号内第二个参数也可写作uchar ToSDAdataBuffer[],即数组名代表首地址)。
bit PageWrite(uchar WordAddress,uchar *ToSDAdataBuffer)
{
//下面的程序我用的if嵌套,网上有些程序是顺序结构,但因为遇到return就返回主程序不再往下执行,所以效果是一样的。
        uchar i;
        StartI2C();
        WriteI2CByte(0xa0);//之所以没设DeviceAddress这个参数,是因为最后一位不属于地址。E2PROM一般前四位为1010,这里A2~A0接地,为0,最后一位0表示写
        if(!ChkAck())//检查应答函数返回0说明从机应答0成功。
        {       
//写8-bit data word address,即写到哪个存储单元(24C02有2kbits,所以数据字有2048/8=256个,故地址线有8位)
                WriteI2CByte(WordAddress);
                if(!ChkAck())
                {
                        for(i = 0; i < 16; i++)
                        {
                                WriteI2CByte(ToSDAdataBuffer[i]);
                                if(ChkAck())
                                {        //这里可添加错误处理代码。如用几个LED的亮灭组合表示此I2C器件有问题,类似主板错误提示。
                                        return 1;//一般返回1表示异常,且遇到return就退出整个子程序。
                                }
                        }
                        StopI2C();      //写完发送结束信号。
                        return 0;        //一般返回0表示程序正常
                }
                else return 1;        //之前可添加错误处理代码。
        }
        else return 1;
}
//不能用Current Address Read,因为那是24C02数据字地址计数器上次操作后加1的值;
//而SEQUENTIAL_READ如果不给一个要读取的开始地址,会从头输出,
//所以需要Random Read的开始部分,但不要停止信号。
bit SequentialRead(uchar WordAddress)
{
        uchar i;
        StartI2C();
        WriteI2CByte(0xa0);
        if (!ChkAck())
        {
                WriteI2CByte(WordAddress);
                if (!ChkAck())
                {
                        StartI2C();
//Device Address后紧跟的那一位R/W^是1说明是读,24C02内部就是根据最后这位来判断是从SDA上读数,还是往SDA上送数
                        WriteI2CByte(0xa1);
//之所以设为1是读,是因为根据WriteI2CByte子程序,最后给SDA赋1,P3^4就维持1,这样24C02内部Dout为高就将SDA拉低;如果最后一位是0,24C02没能力拉高!
                        if (!ChkAck())
                        {
                                for(i = 0;i < 16;i++)
                                {
                                        ReceivedData[i] = ReadI2CByte();
                                        AckAsMaster(0); //51此时接收数据,调用应答的函数(置SDA为0)
                                }
                                AckAsMaster(1);
                                StopI2C();
                                return 0;
                        }
                        else return 1;//之前可添加错误处理代码。
                }
                else return 1;
        }
        else return 1;
}
void main(void)
{
        TMOD = 0x01;    //方式1的16位计数器
        TH0  = (65536-50000)/256;
        TL0  = (65536-50000)%256;
        EA   = 1;
        ET0  = 1;
        TR0  = 1;     //启动定时器0工作
        InitI2C();
        if (PageWrite(0,ToSDAdataBuffer) == 0) //先执行页写操作,设从地址00开始,没问题就延迟一下再从同一地址读回来。
        {
                delay(100);                 //等待24C02页写操作完毕
                //if(SequentialRead(0) == 0)   //如果顺序读操作成功,则每隔1秒送P0口显示一个字节
                //{}
                SequentialRead(0);
        }
        while(1)
        {       
               
                temp = ReceivedData[j];
                a = temp / 10;
                b = temp % 10;
                display(0,0,0,a,b);
        }

}

void timer0() interrupt 1
{
        TH0=(65536-50000)/256;
        TL0=(65536-50000)%256;
        tt++;
        if(tt == 5)
        {
                tt = 0;
                LED = ~LED;
                j++;
                if(j == 16)j=0;

        }       
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2013-1-8 09:53:25 | 显示全部楼层
本帖最后由 lmt50211 于 2013-1-8 09:57 编辑

樓主可以看下我一直用的程序,支持多字節寫入。


/*********************************************************************************
项    目:IIC通讯程序
产品型号:
文件  名:
原理  图:
MCU     :SC91F72B
原始版本:V1.0
创建  人:mingtao_lu
创建日期:2010-11-08
描   述:用普通IO口模拟IIC通讯协议,进行数据存储
*******************************************************************************/
#include <SC91F72B.H>
#include <INTRINS.H>
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned long ulong;
//====================================
sbit SDA      =P3^6;  //AT24C02串行数据
sbit SCL      =P3^7;  //AT24C02串行时钟
sbit beep     =P2^1;  //蜂鸣器
//========================================
#define key_start  0x01
#define key_set    0x02
#define key_clr    0x04
#define key_down   0x10
#define key_up     0x20
#define key_chanle 0x40
//========================================
//===         蜂鸣器相关常量        ====//
#define short_buz_time  50
#define long_buz_time   150
#define short_off_time  50
//=============================================//
bit beep_bit;                               //蜂鸣器响标志位
bit t2500us;                                //2.5ms标志位
bit ack;                                    //24c02应答标志位
//==============================================//
uchar t125us,t50ms,t1s;                     //系统相关变量
uchar beepcnt,beeptimes;                    //鸣响时间,次数
uchar on_off_cnt[1];                           //开关机次数变量
//============================================//
void delay(uchar j) //延时
{
        uchar i;
        for(;j>0;j--)
        for(i=20;i>0;i--);
}
void short_buz(uchar cnts,uchar times)//短响cnts声,响times长
{
    beepcnt =times;
        beeptimes=cnts;
        beep_bit=1;
}
void beep_pro()  //蜂鸣器处理2.5ms调用一次
{
        if(beeptimes!=0)
        {
            if(beep_bit)
            {
                    if(--beepcnt==0)
                    {
                                beepcnt=short_off_time;
                            beep_bit=0;       
                            beeptimes--;
                            beep=0;
                    }       
            }
            else
            {
                    if(--beepcnt==0)
                    {
                                beepcnt=short_buz_time;
                            beep_bit=1;       
                    }       
            }
        }
}                     
//========================================================//
//===                 AT24C02读/写函数                ====//
void Start_I2c()//AT24C02开始信号
{
        SDA=1;            //发送起始条件的数据信号
        delay(1);         
        SCL=1;                    
        delay(10);        //起始条件建立时间大于4.7us,延时
        SDA=0;            //发送起始信号,起始条件锁定时间大于4μs
        delay(10);        //起始条件建立时间大于4.7us,延时
        SCL=0;            //钳住I2C总线,准备发送或接收数据
        delay(10);
}
/*******************************************************************
                      结束总线函数
函数原型: void  Stop_I2c();
功能:       结束I2C总线,即发送I2C结束条件.
********************************************************************/
void Stop_I2c()//AT24C02停止信号
{
        SDA=0;         //发送结束条件的数据信号
        _nop_();       //发送结束条件的时钟信号
        delay(10);     
        SCL=1;         //结束条件建立时间大于4μs
        delay(10);     
        SDA=1;         //发送I2C总线结束信号
        delay(10);
}
/*******************************************************************
                 字节数据传送函数
函数原型: void  SendByte(uchar c);
功能:  将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
     此状态位进行操作.(不应答或非应答都使ack=0 假)
     发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void  SendByte(uchar c)//AT24C02写入一字节数据
{
        uchar BitCnt;
        for(BitCnt=0;BitCnt<8;BitCnt++)  //要传送的数据长度为8位
        {
                if((c<<BitCnt)&0x80)
                SDA=1;                       //判断发送位
                else                        
                SDA=0;                       
                delay(10);                  
                SCL=1;                       //置时钟线为高,通知被控器开始接收数据位               
                delay(10);                   //保证时钟高电平周期大于4μs
                SCL=0;                       
        }                                
        delay(10);                       
        SDA=1;                           //8位发送完后释放数据线,准备接收应答位
        delay(10);                       
        SCL=1;                           
        delay(10);                       
        if(SDA==1)                       
        ack=0;                           
        else                             
        ack=1;                           //判断是否接收到应答信号
        SCL=0;
        delay(10);
}
/*******************************************************************
                 字节数据传送函数
函数原型: uchar  RcvByte();
功能:  用来接收从器件传来的数据,并判断总线错误(不发应答信号),
     发完后请用应答函数。
********************************************************************/
uchar  RcvByte()//AT24C02读出一字节数据
{
        uchar retc;
        uchar BitCnt;
        retc=0;
        SDA=1;                              //置数据线为输入方式
        for(BitCnt=0;BitCnt<8;BitCnt++)
        {
                _nop_();
                _nop_();
                SCL=0;                         //置时钟线为低,准备接收数据位
                _nop_();                       //时钟低电平周期大于4.7μs
                delay(10);
                SCL=1;                         //置时钟线为高使数据线上数据有效
                delay(10);
                retc=retc<<1;
                if(SDA==1)
                retc=retc+1;                  //读数据位,接收的数据位放入retc中
                delay(10);
        }
        SCL=0;
        delay(10);
        return(retc);
}
/********************************************************************
                     应答子函数
原型:  void Ack_I2c(bit a);

功能:主控器进行应答信号,(可以是应答或非应答信号)
********************************************************************/
void Ack_I2c(bit a)//AT24C02应答信号
{
        if(a==0)
        SDA=0;                  //在此发出应答或非应答信号
        else
        SDA=1;
        delay(10);
        SCL=1;                 //时钟低电平周期大于4μs      
        delay(10);
        SCL=0;                 //清时钟线,钳住I2C总线以便继续接收
        delay(10);
}
/*******************************************************************}
/*******************************************************************
                    向有子地址器件发送多字节数据函数
函数原型: bit  ISendStr(uchar sla,uchar suba,ucahr *s,uchar no);
功能:     从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
          地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
           如果返回1表示操作成功,否则操作有误。
注意:    使用前必须已结束总线。
********************************************************************/
bit ISendStr(uchar sla,uchar suba,uchar *s,uchar no)//AT24C02写多字节
{
        uchar i;
        Start_I2c();                    //启动总线
        SendByte(sla);                  //发送器件地址
        if(ack==0)return(0);            
        SendByte(suba);                 //发送器件子地址
        if(ack==0)return(0);            
        for(i=0;i<no;i++)               
        {                              
                SendByte(*s);               //发送数据
                if(ack==0)return(0);        
                s++;                        
        }                              
        Stop_I2c();                     //结束总线
        return(1);
}
/*******************************************************************
/*******************************************************************
/*******************************************************************
                    向有子地址器件读取多字节数据函数
函数原型: bit  ISendStr(uchar sla,uchar suba,ucahr *s,uchar no);
功能:     从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
          地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
           如果返回1表示操作成功,否则操作有误。
注意:    使用前必须已结束总线。
********************************************************************/
bit IRcvStr(uchar sla,uchar suba,uchar *s,uchar no)//AT24C02读多字节
{
        uchar i;
       
        Start_I2c();               //启动总线
        SendByte(sla);             //发送器件地址
        if(ack==0)return(0);
        SendByte(suba);            //发送器件子地址
        if(ack==0)return(0);
        Start_I2c();
        SendByte(sla+1);
        if(ack==0)return(0);
        for(i=0;i<no-1;i++)
        {
                *s=RcvByte();          //发送数据
                Ack_I2c(0);            //发送就答位
                s++;
        }
        *s=RcvByte();
        Ack_I2c(1);                //发送非应位
        Stop_I2c();                //结束总线
        return(1);
}
//====================================================================
void init(void)           //芯片初始化
{
    EA=0;
        RSTCFG=0x0b;//P1.0需切换为IO口。LVR(低电复位)设为3.5V
        WDTCR=0x90;        //打开看门狗,设WDTCKS[1:0]=00,即在524.288ms内要喂狗一次(建议做法)
    P1CFG1=0x50;//PxCFGx为GPIO模式配置寄存器,上电默认为准双向模式
        P1CFG0=0x00;//将P10-P15设为准双向模式,将P16,P17设为强推挽模式,
        P2CFG0=0x05;//将P20、P21都设为强推挽模式
        P3CFG1=0x01;//将P35设为准双向模式,P34、P36、P37设为强推挽模式
        P3CFG0=0x11;//将P30、P32设强推挽模式,P31、P33设为准双向模式
    P1=0xff;
    P2=0x03;
    P3=0xea;
        TMCON=0x00;//计时器采用Fosc/12
    TMOD=0x22;
    TH0=92;
    TL0=92;
    ET0=1;
    TR0=1;
        EA=1;
        short_buz(1,long_buz_time);//长响一声
}
//============================================//
void main()//主程序
{
        init();
        IRcvStr(0xa0,0x00,on_off_cnt,1);//从地址0x00中读出一字节数据到on_off_cnt
    while(1)
    {
        WDTCR=0x90;
                if(t2500us)
        {
            t2500us=0;
            buz_pro();
            t10ms++;
            if(t10ms>=4)
            {
                t10ms=0;
                short_buz(1,short_buz_time);//短响一声
                on_off_cnt[0]++;
                ISendStr(0xa0,0x00,on_off_cnt,1);//把一字节数据on_off_cnt写到0x00地址中
            }
        }
    }
}
//===========================================================//
void t_125us(void) interrupt 1  //定时器0每125us中断一次
{
//    TH0=0xff;//TH0=(65536-125)>>8;
//    TL0=0x06;//TL0=(65536-125)&255;
        if(beeptime)
        {
                beep=!beep;
        }
    t125us++;
        if(t125us>=20)
        {
                t125us=0;
        t2500us=1;
        }
}

出0入0汤圆

 楼主| 发表于 2013-1-8 14:17:21 | 显示全部楼层
lmt50211 发表于 2013-1-8 09:53
樓主可以看下我一直用的程序,支持多字節寫入。

首先,特别感谢您的回复!!!
其次想请教您几个问题:
1、向有子地址器件发送多字节数据函数
函数原型: bit  ISendStr(uchar sla,uchar suba,ucahr *s,uchar no);     
功能:     从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
          地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
           如果返回1表示操作成功,否则操作有误。
子地址器件具体是什么含义?
sla代表的是什么地址,看您下边的程序我知道suba是写入数据在24C02中的开始地址,还有就是 no 代表着是写入的字节数,在百度里边搜了许多相关的24C02函数,好多都是这样的,我知道它是2Kbit的容量,也就是2048/8=256个字节,我也查了24C02的相关参数,但是压根没有一个人提到最多可以往24C02中写入多少个字节的数据,我用的一个程序最多写40个字节,在往后就是乱码了。
       一开始不想看英文的技术文档,想着如果能在网上找到合适的程序也就算了,但是比较失望,不知道是没人研究过还是大家不愿意共享,所看到的程序都是大同小异,迫不得已今早在图书馆待了一个上午,下午研究技术文档,很郁闷。
如果您知道的话,请您指点下,真的是非常感谢!!!

出0入0汤圆

 楼主| 发表于 2013-1-8 14:18:10 | 显示全部楼层
lmt50211 发表于 2013-1-8 09:53
樓主可以看下我一直用的程序,支持多字節寫入。

还有,如果我想写入256以下的字节,您的程序可以实现不?

出0入0汤圆

发表于 2013-1-8 14:30:24 | 显示全部楼层
gbwaikp2011 发表于 2013-1-8 14:18
还有,如果我想写入256以下的字节,您的程序可以实现不?

可以呀,不过要根据器件做相应的处理,比如:24C02一次只能写入8字节(24C04是16字节),如果要写256字节的话就要调用32次;连续写入32字需要很长时间,会影响程序的实时性,建议程序用分时写入的方法会比较好。

出0入0汤圆

 楼主| 发表于 2013-1-8 14:44:19 | 显示全部楼层
lmt50211 发表于 2013-1-8 14:30
可以呀,不过要根据器件做相应的处理,比如:24C02一次只能写入8字节(24C04是16字节),如果要写256字节 ...

这个我知道,刚把技术文档看完,光说了pagewrite方式写入的话,24c02可以写入8个字节,当超过8个字节后会重新覆盖先前写入的字节,但是没有说怎么翻页写入,比如我要写入240个字节,按页写入的方式,在第0页写入8个字节后,翻页到第二页,在写8个字节,依次类推,直到写完240个字节,您是怎么理解的呢?帮帮我啊,好几天了没有结果,真是烦躁死了啊

出0入0汤圆

发表于 2013-1-8 15:05:50 | 显示全部楼层
本帖最后由 dlmaowf 于 2013-1-8 15:09 编辑

比方说一次写入8个字节。地址从00开始,则写入的地址是00--07
如果一次写入10个字节,但24C02一次只能写入8个字节,所以前8个字节写入地址是00--07
最后2个字节会返回到地址00写入,最终的结果就是地址00对应的是第9个字节,地址01对应的是第10个字节,地址02--07对应的是3--8字节的内容。
我是这么理解的,没有试验过

出0入0汤圆

发表于 2013-1-8 15:27:36 | 显示全部楼层
写完8个字节就STOP,然后延时10ms,重新开始

出0入0汤圆

发表于 2013-1-8 16:41:52 | 显示全部楼层
本帖最后由 BXAK 于 2013-1-8 17:12 编辑

用状态机方式:每次写一页8字节(24C02),然后间歇5ms,再写一页……相当于连写25页(共200字节),约125ms完成200字节写入,OK

STC官网的参考代码
  1. /**************************************
  2. AT24C04测试程序
  3. 主芯片  : STC90C52RC (12T)
  4. 工作频率: 12.000MHz
  5. **************************************/

  6. #include "REG51.H"
  7. #include "INTRINS.H"

  8. typedef unsigned char BYTE;
  9. typedef unsigned short WORD;

  10. sbit SCL = P3^4;                //AT24C04的时钟
  11. sbit SDA = P3^5;                //AT24C04的数据

  12. BYTE BUF[16];                   //数据缓存区

  13. BYTE code res[6] _at_ 0x23;

  14. BYTE code TESTDATA[] =
  15. {
  16.     0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
  17.     0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF
  18. };

  19. void Delay5us();
  20. void Delay5ms();
  21. void AT24C04_Start();
  22. void AT24C04_Stop();
  23. void AT24C04_SendACK(bit ack);
  24. bit AT24C04_RecvACK();
  25. void AT24C04_SendByte(BYTE dat);
  26. BYTE AT24C04_RecvByte();
  27. void AT24C04_ReadPage();
  28. void AT24C04_WritePage();

  29. void main()
  30. {
  31.     AT24C04_WritePage();
  32.     Delay5ms();
  33.     AT24C04_ReadPage();

  34.     while (1);

  35. }

  36. /**************************************
  37. 向AT24C04写1页(16字节)数据
  38. 将TESTDATA开始的16个测试数据写如设备的00~0F地址中
  39. **************************************/
  40. void AT24C04_WritePage()
  41. {
  42.     BYTE i;

  43.     AT24C04_Start();            //起始信号
  44.     AT24C04_SendByte(0xa0);     //发送设备地址+写信号
  45.     AT24C04_SendByte(0x00);     //发送存储单元地址
  46.     for (i=0; i<16; i++)
  47.     {
  48.         AT24C04_SendByte(TESTDATA[i]);
  49.     }
  50.     AT24C04_Stop();             //停止信号
  51. }

  52. /**************************************
  53. 从AT24C04读取1页(16字节)数据
  54. 将设备的00~0F地址中的数据读出存放在DATA区的BUF中
  55. **************************************/
  56. void AT24C04_ReadPage()
  57. {
  58.     BYTE i;

  59.     AT24C04_Start();            //起始信号
  60.     AT24C04_SendByte(0xa0);     //发送设备地址+写信号
  61.     AT24C04_SendByte(0x00);     //发送存储单元地址
  62.     AT24C04_Start();            //起始信号
  63.     AT24C04_SendByte(0xa1);     //发送设备地址+读信号
  64.     for (i=0; i<16; i++)
  65.     {
  66.         BUF[i] = AT24C04_RecvByte();
  67.         if (i == 15)
  68.         {
  69.             AT24C04_SendACK(1); //最后一个数据需要会NAK
  70.         }
  71.         else
  72.         {
  73.             AT24C04_SendACK(0); //回应ACK
  74.         }
  75.     }
  76.     AT24C04_Stop();             //停止信号
  77. }

  78. /**************************************
  79. 延时5微秒(STC90C52RC@12M)
  80. 不同的工作环境,需要调整此函数
  81. 当改用1T的MCU时,请调整此延时函数
  82. **************************************/
  83. void Delay5us()
  84. {
  85.     _nop_();
  86.     _nop_();
  87. }

  88. /**************************************
  89. 延时5毫秒(STC90C52RC@12M)
  90. 不同的工作环境,需要调整此函数
  91. 当改用1T的MCU时,请调整此延时函数
  92. **************************************/
  93. void Delay5ms()
  94. {
  95.     WORD n = 560;

  96.     while (n--);
  97. }

  98. /**************************************
  99. 起始信号
  100. **************************************/
  101. void AT24C04_Start()
  102. {
  103.     SDA = 1;                    //拉高数据线
  104.     SCL = 1;                    //拉高时钟线
  105.     Delay5us();                 //延时
  106.     SDA = 0;                    //产生下降沿
  107.     Delay5us();                 //延时
  108.     SCL = 0;                    //拉低时钟线
  109. }

  110. /**************************************
  111. 停止信号
  112. **************************************/
  113. void AT24C04_Stop()
  114. {
  115.     SDA = 0;                    //拉低数据线
  116.     SCL = 1;                    //拉高时钟线
  117.     Delay5us();                 //延时
  118.     SDA = 1;                    //产生上升沿
  119.     Delay5us();                 //延时
  120. }

  121. /**************************************
  122. 发送应答信号
  123. 入口参数:ack (0:ACK 1:NAK)
  124. **************************************/
  125. void AT24C04_SendACK(bit ack)
  126. {
  127.     SDA = ack;                  //写应答信号
  128.     SCL = 1;                    //拉高时钟线
  129.     Delay5us();                 //延时
  130.     SCL = 0;                    //拉低时钟线
  131.     Delay5us();                 //延时
  132. }

  133. /**************************************
  134. 接收应答信号
  135. **************************************/
  136. bit AT24C04_RecvACK()
  137. {
  138.     SCL = 1;                    //拉高时钟线
  139.     Delay5us();                 //延时
  140.     CY = SDA;                   //读应答信号
  141.     SCL = 0;                    //拉低时钟线
  142.     Delay5us();                 //延时

  143.     return CY;
  144. }

  145. /**************************************
  146. 向IIC总线发送一个字节数据
  147. **************************************/
  148. void AT24C04_SendByte(BYTE dat)
  149. {
  150.     BYTE i;

  151.     for (i=0; i<8; i++)         //8位计数器
  152.     {
  153.         dat <<= 1;              //移出数据的最高位
  154.         SDA = CY;               //送数据口
  155.         SCL = 1;                //拉高时钟线
  156.         Delay5us();             //延时
  157.         SCL = 0;                //拉低时钟线
  158.         Delay5us();             //延时
  159.     }
  160.     AT24C04_RecvACK();
  161. }

  162. /**************************************
  163. 从IIC总线接收一个字节数据
  164. **************************************/
  165. BYTE AT24C04_RecvByte()
  166. {
  167.     BYTE i;
  168.     BYTE dat = 0;

  169.     SDA = 1;                    //使能内部上拉,准备读取数据
  170.     for (i=0; i<8; i++)         //8位计数器
  171.     {
  172.         dat <<= 1;
  173.         SCL = 1;                //拉高时钟线
  174.         Delay5us();             //延时
  175.         dat |= SDA;             //读数据
  176.         SCL = 0;                //拉低时钟线
  177.         Delay5us();             //延时
  178.     }

  179.     return dat;
  180. }
复制代码

出0入0汤圆

发表于 2013-1-9 12:14:53 | 显示全部楼层
本帖最后由 lmt50211 于 2013-1-9 12:18 编辑
gbwaikp2011 发表于 2013-1-8 14:44
这个我知道,刚把技术文档看完,光说了pagewrite方式写入的话,24c02可以写入8个字节,当超过8个字节后会 ...


先定义一个数组:
on_off_cnt[200];

ISendStr(0xa0,0x00,on_off_cnt,8);//把数据on_off_cnt[0]-on_off_cnt[7]-写到0x00-0X07地址中
delayms(10);
ISendStr(0xa0,0x08,&on_off_cnt[8],8);//把数据on_off_cnt[8]-on_off_cnt[15]-写到0x08-0X0f地址中
delayms(10);
ISendStr(0xa0,0x10,&on_off_cnt[16],8);//把数据on_off_cnt[16]-on_off_cnt[23]-写到0x10-0X17地址中
delayms(10);
ISendStr(0xa0,0x18,&on_off_cnt[24],8);//把数据on_off_cnt[24]-on_off_cnt[31]-写到0x18-0X1f地址中
.................................
.................................
delayms(10);
ISendStr(0xa0,0xc0,&on_off_cnt[192],8);//把数据on_off_cnt[192]-on_off_cnt[199]-写到0x08-0X0f地址中

以此类推,写25次就可以把200字节写完。

读取的话就按地址:
IRcvStr(0xa0,0x00,on_off_cnt,1);//从地址0x00-0X07中读出数据到on_off_cnt[0]-on_off_cnt[7]
delayms(10);
IRcvStr(0xa0,0x08,&on_off_cnt[8],1);//从地址0x08-0X0f中读出数据到on_off_cnt[8]-on_off_cnt[16]
................................
...............................


出0入0汤圆

 楼主| 发表于 2013-1-9 13:00:18 | 显示全部楼层
lmt50211 发表于 2013-1-9 12:14
先定义一个数组:
on_off_cnt[200];

谢谢您热心的解答!!!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-18 10:52

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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