infree的笔记 https://bbs.21ic.com/?23848 [收藏] [复制] [RSS]

日志

新写的i2c eeprom读写程序

已有 2657 次阅读2007-3-17 15:12 |个人分类:嵌入式资源|系统分类:单片机

原创,转载请保留作者信息

目前仅在24C08A上作了验证,欢迎大家反馈使用信息!

//这是头文件

#ifndef _i2c_H
#define _i2c_H
#include <reg52.h>
#include <stdio.h>
#include "global.h"

void test_write(void);
void test_read(void);
void delay_10us(void);
void i2c_init(void);
unsigned int  i2c_sequential_read(unsigned int start_addr,INT8U * dest_buf,unsigned int length);
unsigned int  i2c_sequential_write(unsigned int start_addr,char data_buf[],unsigned int length);
#endif

 

//这是主文件

/*************************************************************************************
title:   AT24Cxx i2c interface EEPROM read/write public function

description: used to read data from At24cxx device or write data to At24cxx device;
                 support random byte read/write and sequential read /write;
                 test on At24C08A used the test_write() and test_read()  function;

                 Only for reference ,No guarantee;
                 If you use these function,please remain the description ,and others comment
                 to show respect to author's  working,thanks!
plarform: 89C5x,11.0592Mhz
author: infree


data: 2007.3.17

version: V1.0


**************************************************************************************/

#include "i2c.h"
#include "global.h"
#include "serial.h"
#include "rfm.h"
#include "stdio.h"

#define AT24C08   // 1024 byte ,edited according your device
#define TRUE 1
#define FALSE 0


#define INT8U unsigned char           //无符号8位数

 


/* SCL port define */
sbit SCL    = P3^4;
sbit SDA    = P3^5;

//A2,A1,A0 all tied to VCC,please modified according your assignment
#ifdef AT24C02
    #define device_addr_write 0xae   /*WRITE TO THE DEVICE ADDRESS*/
    #define  device_addr_read 0xaf     /*READ FROM THE DEVICE ADDRESS*/
    #define I2C_MAX_ADDR 255 //定义最大的word address
    #define BLOCK_SIZE 8  //定义写块的大小
#else
   #ifdef AT24C04
    #define device_addr_write 0xac    /*WRITE TO THE DEVICE ADDRESS*/
    #define  device_addr_read 0xad     /*READ FROM THE DEVICE ADDRESS*/
    #define I2C_MAX_ADDR 511 //定义最大的word address
    #define BLOCK_SIZE 16  //定义写块的大小
   #else
        #ifdef AT24C08
               #define device_addr_write 0xa8    /*WRITE TO THE DEVICE ADDRESS*/
               #define  device_addr_read 0xa9     /*READ FROM THE DEVICE ADDRESS*/
  #define I2C_MAX_ADDR 1023  //定义最大的word address
  #define BLOCK_SIZE 16  //定义写块的大小
   #endif
    #endif
#endif


INT8U xdata tmp_buf[1024];//这是用用于测试的缓冲区,实际应用时可去掉;

/*for AT24C08A,the DEVICE ADDRESS BYTE configration is
  D7 D6 D5 D4 D3 D2 D1 D0
 +--+---+--+--+-+--+--+----+
 |1 | 0 |1 |0 |A2|P1|P0|R/W|
 +--+---+--+--+-+--+--+----+
 P1,P0 are the word address the 10th,and 9th bits;
*/
void delay_10us(void)
{
  INT8U i;
  for (i=0;i<1;i++);
}

#if 0  //因为在我的项目中其它文件中已定义了这个函数,所以这里屏掉了
//如果要使用这个函数,改导#if 1 即可;;
//这是用于51 MCU,11.0592MHz下的1ms延时程序
//请根据自己的平台修改j的值;;
void Delay_ms(unsigned int amS){ // 1ms延时程序
  unsigned int j;
  while(amS--)
   {
     for(j=0;j<80;j++)
      {;}
   };
}
#endif

void i2c_start(void)
{
  SDA=HIGH;
  SCL=HIGH;
  delay_10us();
  SDA=LOW;
  delay_10us();
  SCL=LOW;
}

void i2c_stop(void)
{
  SDA=LOW;
  delay_10us();
  SCL=HIGH;
  delay_10us();
  SDA=HIGH;
}

bit i2c_clock(void)
{
  bit sample;
  SCL=HIGH;
  delay_10us();
  sample=SDA;
  SCL=LOW;
  return(sample);
}

void i2c_ack(void)
{
  SDA=LOW;
  i2c_clock();
}

void i2c_non_ack(void)
{
  SDA=HIGH;
  i2c_clock();
}

bit i2c_send(INT8U i2c_data)
{
  INT8U i;
  for (i=0;i<8;i++)
  {
    SDA=(bit)(i2c_data&0x80);
    i2c_data=i2c_data<<1;
    i2c_clock();
  }
  SDA=HIGH;
  return(~i2c_clock());
}

INT8U i2c_receive(void)
{
  INT8U i;
  INT8U i2c_data=0;
  for (i=0;i<8;i++)
  {
    SDA=HIGH;
    i2c_data =i2c_data<<1;
    if (i2c_clock()) i2c_data++;
  }
  return (i2c_data);
}


//读取i2c eeprom中address地址中的一个字节内容
//返回的即是那个字节数据
INT8U i2c_read_byte(unsigned int address)
{
   INT8U device_addr,word_addr;
   INT8U recv_data;
   unsigned int tmp;
   INT8U tmp1,tmp2;

       tmp=I2C_MAX_ADDR;

          if(address >tmp)
          return 0xFF;//超限则一直返回0xFF
          tmp1=address/256;
    tmp2=device_addr_read;

    device_addr=tmp2 + tmp1*2;
 word_addr=address%256;
  

     
       i2c_start();
      //先发送设备地址
      if (i2c_send(device_addr&0xFE)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
    #ifdef MY_DEBUG //调试开关
    SBUF=0xAA;
    #endif
          return 0xFF;
       }
      //再发送字节地址
      if (i2c_send(word_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
     #ifdef MY_DEBUG //调试开关
             SBUF=0xBB;
     #endif
          return 0xFF;
       }
  
 i2c_start(); 

       //再发送设备地址
      if (i2c_send(device_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
     #ifdef MY_DEBUG //调试开关
             SBUF=0xCC;
     #endif
          return 0xFF;
       }
      
 
      recv_data=i2c_receive( );
   #ifdef MY_DEBUG //调试开关
         SBUF=recv_data;
   #endif
       i2c_non_ack();
           i2c_stop();

    return recv_data;
  
  

}

//向i2c eeprom中address地址写一个字节内容
//返回的是操作结果0为未成功,1为成功
INT8U i2c_write_byte(unsigned int address,INT8U my_data)
{
    INT8U device_addr,word_addr;
    unsigned int tmp;
    INT8U tmp1,tmp2;
 
 tmp1=address/256;
 tmp2= device_addr_write;
 device_addr= tmp2+ tmp1*2;
 word_addr=address%256;
     tmp=I2C_MAX_ADDR;
     if (address>tmp)
      return 0;//超越地址范围,写入不成功
      i2c_start();
      if (i2c_send(device_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
          return 0;
       }

      if (i2c_send(word_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
          return 0;
       }
      if (i2c_send(my_data)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
           i2c_stop();
           return 0;
       }
        //i2c_non_ack();
  i2c_stop();
 Delay_ms(10);//这里是为了等待前次的写操作完成
    return 1;
  
}

void i2c_init(void)
{

  SCL=1;
  delay_10us();
  SDA=1;
  delay_10us();
 
}

//从地址start_addr开始读取lenth个字节到dest_buf
//返回读取的字节数,0为读取不成功
//注意:在24cxx的顺序读取多个字节的时序中(sequential_read)
//24cxx本身只能对页块内的地址(word address: 0~255)进行自动增1
//而对超过一个页块的访问需要重新开始一个sequential_read读时序
unsigned int  i2c_sequential_read(unsigned int start_addr,INT8U * dest_buf,unsigned int length)
{
    INT8U device_addr,word_addr;
   INT8U recv_data;
   unsigned int tmp;
   INT8U tmp1,tmp2;

   tmp=I2C_MAX_ADDR;

          if(start_addr+length-1>tmp)
          return 0xFF;//超限则一直返回0xFF
        tmp1=start_addr/256;
        tmp2=device_addr_read;
    device_addr=tmp2+tmp1*2;
 word_addr=start_addr%256;
  

     
       i2c_start();
      //先发送设备地址
      if (i2c_send(device_addr&0xFE)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
     #ifdef MY_DEBUG //调试开关
              SBUF=0xAA;
     #endif
          return 0xFF;
       }
      //再发送字节地址
      if (i2c_send(word_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
     #ifdef MY_DEBUG //调试开关
                SBUF=0xBB;
     #endif
          return 0xFF;
       }
  
 i2c_start(); 

       //再发送设备地址
      if (i2c_send(device_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
     #ifdef MY_DEBUG //调试开关
                SBUF=0xCC;
     #endif
          return 0xFF;
       }
      
      for (tmp=0;tmp<length-1;tmp++)
       {
        
         dest_buf[tmp]=i2c_receive( );
  // Delay_ms(1);
   if((start_addr+tmp)%256!=255)
         i2c_ack( );
   else//这是最后一个字节地址,需要更换设备地址
    {
    device_addr=device_addr+0x02;
        word_addr=0;

         i2c_non_ack();
               i2c_stop();
   Delay_ms(6);
   //重新开始一轮读时序
          i2c_start();
                     //先发送设备地址
                     if (i2c_send(device_addr&0xFE)==0)//i2c_send  返回的是已被取反的SDA
                      {//NAK
                         i2c_stop();
         #ifdef MY_DEBUG //调试开关
                                           SBUF=0xAA;
         #endif
                         return 0xFF;
                      }
                     //再发送字节地址
                     if (i2c_send(word_addr)==0)//i2c_send  返回的是已被取反的SDA
                      {//NAK
                         i2c_stop();
        #ifdef MY_DEBUG //调试开关
                                          SBUF=0xBB;
        #endif
                         return 0xFF;
                      }
                 
                i2c_start(); 
              
                      //再发送设备地址
                     if (i2c_send(device_addr)==0)//i2c_send  返回的是已被取反的SDA
                      {//NAK
                         i2c_stop();
         #ifdef MY_DEBUG //调试开关
                                           SBUF=0xCC;
         #endif
                         return 0xFF;
                      }
    }
       }
    dest_buf[length-1]=i2c_receive( );
   //Delay_ms(1);
       i2c_non_ack();
           i2c_stop();

    return length;
 
}

//从地址start_addr开始将data_buf中的lenth个字节写入eeprom
//返回写入的字节数,0为写入不成功
unsigned int  i2c_sequential_write(unsigned int start_addr,INT8U data_buf[],unsigned int length)
{
      INT8U device_addr,word_addr;
  
   unsigned int tmp;
   INT8U tmp1,tmp2;

   tmp=I2C_MAX_ADDR;

          if(start_addr+length-1>tmp)
          return 0xFF;//超限则一直返回0xFF
         
        tmp1=start_addr/256;
        tmp2=device_addr_write;
    device_addr=tmp2+tmp1*2;
 word_addr=start_addr%256;

  i2c_start();//开始
  //写设备地址
      if (i2c_send(device_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
          return 0;
       }
     //写字节地址
      if (i2c_send(word_addr)==0)//i2c_send  返回的是已被取反的SDA
       {//NAK
          i2c_stop();
          return 0;
       }

  for (tmp=0;tmp<length;tmp++)
   {
     
                 if (i2c_send(data_buf[tmp])==0)//i2c_send  返回的是已被取反的SDA
                  {//NAK
                      i2c_stop();
                      return 0;
                  }
      if((start_addr+tmp)%BLOCK_SIZE==BLOCK_SIZE-1)//到了一个块的最后一个字节地址
       {
         i2c_stop(); //结束一次块写
     Delay_ms(10);//等待内部完成写入
     if((start_addr+tmp)%256==255)//到了页边沿,需要更换设备地址
      {
      device_addr+=2;
      }
    
       i2c_start();//开始
                     //写设备地址
                         if (i2c_send(device_addr)==0)//i2c_send  返回的是已被取反的SDA
                          {//NAK
                             i2c_stop();
                             return 0;
                          }
                        //写字节地址
                        tmp1=(start_addr+tmp+1)%256;
                         if (i2c_send(tmp1)==0)//i2c_send  返回的是已被取反的SDA
                          {//NAK
                             i2c_stop();
                             return 0;
                          }
       }
    
   }
 
  i2c_stop();
  Delay_ms(10);//这里是为了等待前次的写操作完成
    return 1;
 
}

 


void test_write(void)
{
  int i;
#if 0
  for (i=0;i<1024;i++)
   {
     i2c_write_byte(i, i/256);
   Delay_ms(6);
   }
 #endif

   for (i=0;i<1024;i++)
   {
   tmp_buf=i/256;
   }
   i2c_sequential_write(0, tmp_buf, 1024);
 
}
void test_read(void)
{
   int i;

 #if 0
  for (i=0;i<1024;i++)
   {
     i2c_read_byte(i);
   Delay_ms(1);
   }
  #endif
        #if 1
    for (i=0;i<1024;i++)
   {
    tmp_buf=0x55;  
   }
 #endif
      i2c_sequential_read(0, (INT8U *)tmp_buf, 30);
// i2c_sequential_read(256, (INT8U *)tmp_buf, 256);
// i2c_sequential_read(512, (INT8U *)tmp_buf, 256);
// i2c_sequential_read(768, (INT8U *)tmp_buf, 256);
 
      #if 1
    for (i=0;i<1024;i++)
   {
    #ifdef MY_DEBUG //调试开关
       SBUF=tmp_buf;
   Delay_ms(1);
   #endif
   }
 #endif
}

 


 

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)