原创,转载请保留作者信息
目前仅在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
}