打印

I2C总线之程序正确读写PCF8563、读写AT24C256异常的问题

[复制链接]
3179|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
bmfw|  楼主 | 2015-1-20 16:42 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 bmfw 于 2015-1-20 16:45 编辑

近几日在研究I2C总线,发现程序可以正确读写PCF8563,把程序修改后读写24C256时,只有在0X0000地址可以正确读写,其它地址都无**确显示,不太明白,附上有关程序,望各位老大抽空看看,指点一下

#define PCF8563Addr   0xa2
#define AT24C256Addr 0xa6
#define DELAY5US    nop();nop();nop();nop();nop()                                                                  
/*端口位定义*/
sbit SDA=P2^2;                /*模拟I2C数据传送位*/
sbit SCL=P2^7;                /*模拟I2C时钟控制位*/
/*状态标志*/

bit ack;//应答标志位

/////////////////////////////////////////////////
//////I2C总线驱动子程序开始//////////////////////
/****启动I2C总线,即发送I2C起始条件**************/
static void Start_I2C()
{
  SDA=1;        //发送起始条件的数据信号
  nop();
  SCL=1;        //起始条件建立时间大于4.7us
  DELAY5US;      
  SDA=0;        //发送起始信号*/
  DELAY5US; // 起始条件锁定时间大于4.7μs      
  SCL=0;        //钳住I2C总线,准备发送或接收数据
  nop();nop();
}
/****结束I2C总线,即发送I2C结束条件**************/
static void Stop_I2C()
{
  SDA=0;  //发送结束条件的数据信号
  nop();   //发送结束条件的时钟信号
  SCL=1;  //结束条件建立时间大于4.7μs
  DELAY5US;
  SDA=1;  //发送I2C总线结束信号
  DELAY5US;
}
/****向I2C总线发送一个字节,并请求应答,若收到应答则返回1*/
static void SendByte_I2C(uchar Byte)
{
  uchar data i;
  for(i=0;i<8;i++)//要传送的数据长度为8位
    {
          if((Byte<<i)&0x80){SDA=1;}else{SDA=0;}//判断发送位
          nop();
          SCL=1;//置时钟线为高,通知被控器开始接收数据位
          DELAY5US;//保证时钟高电平周期大于4.7μs
          SCL=0;
    }
  nop();nop();
  SDA=1;//8位发送完后释放数据线,准备接收应答位
  nop();nop();   
  SCL=1;
  nop();nop();nop();
  if(SDA==1){ack=0;}else{ack=1;}//判断是否接收到应答信号
  SCL=0;
  nop();nop();
}
/****从总线接收一个字节,并返回该字节,不回,****/
/****送应答信号,调用前保证SDA=1*****************/        
static uchar RcvByte_I2C()
{
  uchar data Data,i;
  Data=0;
  SDA=1;//置数据线为输入方式
  for(i=0;i<8;i++)
    {
          nop();
          SCL=0;//置时钟线为低,准备接收数据位
          DELAY5US;
          SCL=1;//置时钟线为高使数据线上数据有效
          nop();nop();
          Data=Data<<1;
          if(SDA==1)Data=Data+1; //读数据位,接收的数据位放入retc中
          nop();nop();
    }
  SCL=0;   
  nop();nop();
  return(Data);
}
/****向I2C总线发送应答信号**********************/
static void Ack_I2C(bit a)
{
  if(a==0){SDA=0;}else{SDA=1;}
  nop();nop();nop();
  SCL=1;
  DELAY5US;
  SCL=0;//清时钟线,钳住I2C总线以便继续接收
  nop();nop();   
}                                                  
//////I2C总线驱动子程序结束//////////////////////

//////8位地址驱动子程序开始//////////////////////                                                                                          
/****向pAddr器件内的Addr地址发送Data数据********/
static bit ISendByte(uchar pAddr,uchar Addr,uchar Data)
{
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr);if(ack==0)return(0);//发送单元地址
  SendByte_I2C(Data);if(ack==0)return(0);//发送数据
  Stop_I2C();//结束总线
  return(1);  
}
/****向pAddr器件内的Addr地址开始发送Num数据*****/
static bit ISendStr(uchar pAddr,uchar Addr,uchar *s,uchar Num)
{
  uchar data i;
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr);if(ack==0)return(0);//发送器件单元地址
  for(i=0;i<Num;i++)
    {
          SendByte_I2C(*s);if(ack==0)return(0);//发送数据
          s++;
    }
  Stop_I2C();//结束总线
  return(1);
}                                                                                
/****从pAddr器件内的Addr地址接收一个字节的数据**/
static bit IRcvByt(uchar pAddr,uchar Addr,uchar *c)
{
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0); //发送器件物理地址
  SendByte_I2C(Addr);if(ack==0)return(0);//发送器件单元地址
  Start_I2C();//启动总线
  SendByte_I2C(pAddr+1);if(ack==0)return(0);//发送单元地址
  *c=RcvByte_I2C();//读取数据
  Ack_I2C(1);//发送非就答位
  Stop_I2C();//结束总线
  return(1);
}
//////8位地址驱动子程序结束//////////////////////
//////以上程序参考了周立功单片机的有关程序

///////////////////////////////////////////////////////////
/////与PCF8563配套的有关程序开始///////////////////////////
/****PCF8563初始化******************************/
/*****读取PCF8563上的状态字进行判断,若没有初化过,则对其初化.....
******初化为日期20010525 时间09-00-00 星期5*****/
void IniPcf8563()
{
  uchar data i,dat;
  uchar code IniDat[13]={0x00,0x00,0x56,0x59,0x12,0x12,0x01,0x01,0x15,0X00,0X00,0X00,0X00};
  ///////////////////////控制 控制 秒   分   时   日   星期 月   年   分报警时报警日报警星期报警
  /*定义为2001年5月25日星期五8:30分,前两个0是控制寄存器的命令,(前两字节是状态字)*/
  //发送PCF8563的地址,调试提示,总线出错把0CH寄存器,用做初化标记
  IRcvStr(PCF8563Addr,0x0c,&i,1);  
  if(i==0)return;//判断是否初化过
  for(i=1;i<13;i++)
   {
    dat=IniDat;
    ISendStr(PCF8563Addr,i,&dat,1);
   }
}
/****从8位pAddr器件内的Addr地址接收Num个字节的数据*/
bit IRcvStr(uchar pAddr,uchar Addr,uchar *s,uchar Num)
{
  uchar data i;
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr);if(ack==0)return(0);//发送器件单元地址
  Start_I2C();//启动总线
  SendByte_I2C(pAddr+1);if(ack==0)return(0);//发送读单元地址命令
  for(i=0;i<Num-1;i++)
    {
      *s=RcvByte_I2C();
      Ack_I2C(0);//发送就答位
      s++;
    }
  *s=RcvByte_I2C();
  Ack_I2C(1);//发送非应位
  Stop_I2C();//结束总线
  return(1);
}
/////与PCF8563配套的有关程序结束///////////////////////////


//////16位地址驱动子程序开始/////////////////////
/****向pAddr器件内的Addr16位地址发送Data数据********/
static bit I16SendByte(uchar pAddr,uint16 Addr,uchar Data)
{
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr>>8);if(ack==0)return(0);//发送单元地址高8位
  SendByte_I2C(Addr&0xff);if(ack==0)return(0);//发送单元地址低8位
  SendByte_I2C(Data);if(ack==0)return(0);//发送数据
  Stop_I2C();//结束总线
  return(1);  
}
/****向pAddr器件内的Addr16位地址开始发送Num数据*****/
static bit I16SendStr(uchar pAddr,uint16 Addr,uchar *s,uchar Num)
{
  uchar data i;
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr/256);if(ack==0)return(0);//发送单元地址高8位
  SendByte_I2C(Addr%256);if(ack==0)return(0);//发送单元地址低8位
  for(i=0;i<Num;i++)
    {
          SendByte_I2C(*s);if(ack==0)return(0);//发送数据
          s++;
    }
  Stop_I2C();//结束总线
  return(1);
}
/****从pAddr器件内的Addr16位地址接收一个字节的数据**/
bit I16RcvByt(uchar pAddr,uint16 Addr,uchar *c)
{
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0); //发送器件物理地址
  SendByte_I2C(Addr>>8);if(ack==0)return(0);//发送单元地址高8位
  SendByte_I2C(Addr&0xff);if(ack==0)return(0);//发送单元地址低8位
  Start_I2C();//启动总线
  SendByte_I2C(pAddr+1);if(ack==0)return(0);//发送单元地址
  *c=RcvByte_I2C();//读取数据
  Ack_I2C(1);//发送非就答位
  Stop_I2C();//结束总线
  return(1);
}
//////16位地址驱动子程序结束/////////////////////
/////与AT24C256配套的有关程序开始//////////////////////////
void IniAT24C256()
{//在主程序中初始化几个数据,然后进行读,                                 
  uchar data Dat;
  uchar code IniAT24C256[9]={0x20,0x15,0x01,0x14,0x10,0x56,0x55,0x19,0x99};
  I16SendByte(AT24C256Addr,0x0000,0x69);//在0X0000发送数据0X69
  Dat=IniAT24C256[0];
  I16SendStr(AT24C256Addr,0x0001,&Dat,1);//在0X0001发送数据0X20
  I16SendByte(AT24C256Addr,0x0002,0x34);//在0X0002发送数据0X34   
}
/****从16位pAddr器件内的Addr16位地址接收Num个字节的数据*/
bit I16RcvStr(uchar pAddr,uint16 Addr,uchar *s,uchar Num)
{
  uchar data i;
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr/256);if(ack==0)return(0);//发送单元地址高8位
  SendByte_I2C(Addr%256);if(ack==0)return(0);//发送单元地址低8位
  Start_I2C();//启动总线
  SendByte_I2C(pAddr+1);if(ack==0)return(0);//发送读单元地址命令
  for(i=0;i<Num-1;i++)
    {
      *s=RcvByte_I2C();
      Ack_I2C(0);//发送就答位
      s++;
    }
  *s=RcvByte_I2C();
  Ack_I2C(1);//发送非应位
  Stop_I2C();//结束总线
  return(1);
}
/////与AT24C256配套的有关程序结束//////////////////////////
main()
{
    uchar data TimeRam[7];//定义时间缓冲区

uchar data DataRam[3;//定义AT24C256测试数据缓冲区
    IniPcf8563();//初始化8563
    IniAT24C256();//初始化AT24C256
    while(1)
    {
         IRcvStr(PCF8563Addr,0x02,TimeRam,7);//从0x02开始读取时间数据
         ...... /////显示时间数据
         I16RcvStr(AT24C256Addr,0x0000,DataRam,3);//从0X0000 开始读3个数据
         ......////显示测试数据
    }
}

相关帖子

沙发
ayb_ice| | 2015-1-20 16:50 | 只看该作者
写需要时间,在写操作完成之前是不接受新的指令的

使用特权

评论回复
评分
参与人数 1威望 +3 收起 理由
bmfw + 3 很给力!
板凳
ningling_21| | 2015-1-20 17:32 | 只看该作者
AT24C256的器件地址是A6 ?

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
bmfw + 2 赞一个!
地板
bmfw|  楼主 | 2015-1-20 17:55 | 只看该作者
本帖最后由 bmfw 于 2015-1-20 18:00 编辑

1:那我是加延时还是判断应答位比较合适?
2:A1A0都被我接电源+5V了,所以地址A6应该是正确的

AT24C256.JPG (11.49 KB )

AT24C256.JPG

使用特权

评论回复
5
bmfw|  楼主 | 2015-1-20 21:41 | 只看该作者
本帖最后由 bmfw 于 2015-1-20 21:45 编辑

AT24C256写字节需要10ms的时间,需要修改的有关程序如下(对二三楼表示感谢!)
//////16位地址驱动子程序开始/////////////////////
/****向pAddr器件内的Addr16位地址发送Data数据********/
static bit I16SendByte(uchar pAddr,uint16 Addr,uchar Data)
{
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);Delay1ms(10);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr>>8);Delay1ms(10);if(ack==0)return(0);//发送单元地址高8位
  SendByte_I2C(Addr&0xff);Delay1ms(10);if(ack==0)return(0);//发送单元地址低8位
  SendByte_I2C(Data);Delay1ms(10);if(ack==0)return(0);//发送数据
  Stop_I2C();//结束总线
  return(1);  
}
/****向pAddr器件内的Addr16位地址开始发送Num数据*****/
static bit I16SendStr(uchar pAddr,uint16 Addr,uchar *s,uchar Num)
{
  uchar data i;
  Start_I2C();//启动总线
  SendByte_I2C(pAddr);Delay1ms(10);if(ack==0)return(0);//发送器件物理地址
  SendByte_I2C(Addr>>8);Delay1ms(10);if(ack==0)return(0);//发送单元地址高8位
  SendByte_I2C(Addr&0xff);Delay1ms(10);if(ack==0)return(0);//发送单元地址低8位
  for(i=0;i<Num;i++)
    {

AT24C256.JPG (86.52 KB )

AT24C256写字节需要10mS

AT24C256写字节需要10mS

使用特权

评论回复
评论
ningling_21 2015-1-23 21:55 回复TA
这个时间不是写一个字节,应该是写一个页,一页一般是16-32字节,要看看具体芯片 
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

86

帖子

0

粉丝