void IIC_Start(void)
{
IIC_SDA=1;
IIC_SCL=1;
delay_us(5);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(6);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(6);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(6);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uchar IIC_Wait_Ack(void)
{
uchar ucErrTime=0;
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(IIC_SDA)
{
ucErrTime++;
if(ucErrTime250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(uchar txd)
{
uchar t;
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t8;t++)
{
IIC_SDA=(txd0x80)7;
txd=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uchar IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
for(i=0;i8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive=1;
if(IIC_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值 :读到的数据
uchar AT24CXX_ReadOneByte(uint ReadAddr)
{
uchar temp=0;
IIC_Start();
if(EE_TYPEAT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr8);//发送高地址
IIC_Wait_Ack();
}else IIC_Send_Byte(0XA0+((ReadAddr/256)1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
return temp;
}
//在AT24CXX指定地址写入一个数据
//WriteAddr :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(uint WriteAddr,uchar DataToWrite)
{
IIC_Start();
if(EE_TYPEAT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr8);//发送高地址
}else IIC_Send_Byte(0XA0+((WriteAddr/256)1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
}
//在AT24CXX里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(uint ReadAddr,uchar *pBuffer,uint NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Write(uint WriteAddr,uchar *pBuffer,uint NumToWrite)
{
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
void cheak_key()
{
if(key1==0)
{
fs=~fs;
delay(100);
while(key1==0);
}
if(key2==0)
{
TR0=~TR0;
delay(100);
while(key2==0);
}
if(key3==0) //数据清零复位
{
delay(100);
AT24CXX_WriteOneByte(0x01,0);
t=time=time_1=time_2=0;
led1=led2=1;
while(key3==0);
}
}
AT24C08和16是差不多的,单片机部分的原理图就不用了吧。以下是AT24Cxx是H函数
#ifndef __AT24C08_H__
#define __AT24C08_H__
#include "delay.h"
#defineOP_READ0xa1// 器件地址以及读取操作,0xa1即为1010 0001B
#defineOP_WRITE 0xa0// 器件地址以及写入操作,0xa1即为1010 0000B
sbit SCL=P3^1; //将串行时钟总线SDA位定义在为P3.1引脚
sbit SDA=P3^2; //将串行数据总线SDA位定义在为P3.2引脚
/*************************************************************
函数名称:start
函数功能:开始传送函数
入口参数:无
返回值 :无
*************************************************************/
void start( void )
{
SDA = 1; //SDA初始化为高电平“1”
SCL = 1; //开始数据传送时,要求SCL为高电平“1”
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SDA = 0; //SDA的下降沿被认为是开始信号
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)
}
/*************************************************************
函数名称:stop
函数功能:传送结束函数
入口参数:无
返回值 :无
*************************************************************/
void stop( void )
{
SDA = 0; //SDA初始化为低电平“0”_n
SCL = 1; //结束数据传送时,要求SCL为高电平“1”
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SDA = 1; //SDA的上升沿被认为是结束信号
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SDA=0;
SCL=0;
}
/*************************************************************
函数名称:ReadData
函数功能:从AT24Cxx读取数据,从AT24Cxx移入数据到MCU
入口参数:无
返回值 :ReadData()
*************************************************************/
unsigned char ReadData()
{
unsigned char i;
unsigned char x; //储存从AT24Cxx中读出的数据
for(i = 0; i 8; i++)
{
SCL = 1; //SCL置为高电平
x=1; //将x中的各二进位向左移一位
x|=(unsigned char)SDA; //将SDA上的数据通过按位“或“运算存入x中
SCL = 0; //在SCL的下降沿读出数据
}
return(x); //将读取的数据返回
}
/*************************************************************
函数名称:WriteCurrent
函数功能:向AT24Cxx的当前地址写入数据,在调用此数据写入函数前需首先调用开始函数start(),所以SCL=0
入口参数:y--储存待写入的数据
返回值 :WriteCurrent()
*************************************************************/
bit WriteCurrent(unsigned char y)
{
unsigned char i;
bit ack_bit; //储存应答位
for(i = 0; i 8; i++)// 循环移入8个位
{
SDA = (bit)(y0x80); //通过按位“与”运算将最高位数据送到S
//因为传送时高位在前,低位在后
_nop_(); //等待一个机器周期
SCL = 1; //在SCL的上升沿将数据写入AT24Cxx
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SCL = 0; //将SCL重新置为低电平,以在SCL线形成传送数据所需的8个脉冲
y = 1; //将y中的各二进位向左移一位
}
SDA = 1; // 发送设备(主机)应在时钟脉冲的高电平期间(SCL=1)释放SDA线,
//以让SDA线转由接收设备(AT24Cxx)控制
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SCL = 1; //根据上述规定,SCL应为高电平
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
ack_bit = SDA; //接受设备(AT24Cxx)向SDA送低电平,表示已经接收到一个字节
//若送高电平,表示没有接收到,传送异常
SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)
return ack_bit;// 返回AT24Cxx应答位
}
/***************************************************
函数名称:WriteSet
函数功能:向AT24Cxx中的指定地址写入数据
入口参数:add (储存指定的地址);dat(储存待写入的数据)
返回 值:无
***************************************************/
void WriteSet(unsigned char add, unsigned char dat)
// 在指定地址addr处写入数据WriteCurrent
{
start(); //开始数据传递
WriteCurrent(OP_WRITE); //选择要操作的AT24Cxx芯片,并告知要对其写入数据
WriteCurrent(add); //写入指定地址
WriteCurrent(dat); //向当前地址(上面指定的地址)写入数据
stop(); //停止数据传递
delayNms(4); //1个字节的写入周期为1ms, 最好延时1ms以上
}
/***************************************************
函数名称:ReadCurrent
函数功能:从AT24Cxx中的当前地址读取数据
入口参数:无
出口参数:x (储存读出的数据)
***************************************************/
unsigned char ReadCurrent( void )
{
unsigned char x;
start(); //开始数据传递
WriteCurrent(OP_READ); //选择要操作的AT24Cxx芯片,并告知要读其数据
x=ReadData(); //将读取的数据存入x
stop(); //停止数据传递
return x; //返回读取的数据
}
/***************************************************
函数名称:ReadSet
函数功能:从AT24Cxx中的指定地址读取数据
入口参数:set_addr
出口参数:x
***************************************************/
unsigned char ReadSet(unsigned char set_addr)
// 在指定地址读取
{
start(); //开始数据传递
WriteCurrent(OP_WRITE); //选择要操作的AT24Cxx芯片,并告知要对其写入数据
WriteCurrent(set_addr); //写入指定地址
return(ReadCurrent()); //从指定地址读出数据并返回
}
#endif
怎么调用?这样 WriteSet( 地址 ,数据);
#ifndef __AT24C02_H__
#define __AT24C02_H__
#includestc89.h //包含单片机寄存器的头文件
#includeintrins.h //包含NOP空指令的头文件
#define uchar unsigned char
#define uint unsigned int
/*宏定义器件地址*/ //AT24C器件ID 1 0 1 0 A2 A1 A0 RW
#define AT24_ID0 0XA0 //AT24C器件ID 1 0 1 0 0 0 0 RW
#define AT24_ID1 0XA2 //AT24C器件ID 1 0 1 0 0 0 1 RW
#define AT24_ID2 0XA4 //AT24C器件ID 1 0 1 0 0 1 0 RW
#define AT24_ID3 0XA6 //AT24C器件ID 1 0 1 0 0 1 1 RW
#define AT24_ID4 0XA8 //AT24C器件ID 1 0 1 0 1 0 0 RW
#define AT24_ID5 0XAA //AT24C器件ID 1 0 1 0 1 0 1 RW
#define AT24_ID6 0XAE //AT24C器件ID 1 0 1 0 1 1 1 RW
/*其他参数*/
#define AT24_INQ 255 //器件应答最大检测次数 超过此数量 无应答也会退出循环 以避免程序死循环
/*I2C总线驱动IO口*/
sbit I2C_SCL=P1^5;//串行时钟输入。SCL同步数据传输,上升沿数据写入,下降沿数据读出
sbit I2C_SDA=P3^6;//穿行地址和数据输入/输出。SDA是双向串行数据传输引脚,漏极开路,需外接上拉电阻到VCC(典型值10K)
/*最终使用的函数*/
void at24cxx_weite(uchar I2C_IDRW,I2C_ADDRESS,I2C_DATA);//写入AT24 I2C_IDRW器件地址 I2C_ADDRESS数据地址 I2C_DATA数据
uchar at24cxx_read(uchar I2C_IDRW,I2C_ADDRESS) ; //带地址的读取AT24 I2C_IDRW器件地址 I2C_ADDRESS数据地址
/*被调用的子函数*/
uchar at24cxx_rd(I2C_IDRD); //当前地址读取AT24 由 读取函数调用 I2C_IDRD 为器件地址
void at24cxx_com(uchar I2C_DATA); //AT24写公用函数 由写入读取函数调用 I2C_DATA 为要写入的变量(器件ID,数据地址,数据)
void at24cxx_start(); //AT24起始信号 由写入读取函数调用
void at24cxx_stop(); //AT24停止信号 由写入读取函数调用
void at24cxx_inquires(uchar I2C_IDRW);//AT24应答检测 由写入读取函数调用
void at24cxx_response(); //I2C总线应答检测 由写入读取函数调用
void at24cxx_ricom(); //器件应答与总线应答检测 公用函数 由器件应答和总线应答函数调用
/*字节写操作 起始条件 器件地址 写 字地址 数据停止条件*/
void at24cxx_weite(uchar I2C_IDRW,I2C_ADDRESS,I2C_DATA)//写入AT24I2C_IDRW器件地址 I2C_ADDRESS数据地址 I2C_DATA数据
{
//at24cxx_start(); //起始条件 开始写入数据
//at24cxx_com(I2C_IDRW0xfe); //写入芯片ID及读写选择位 0
//at24cxx_response(); //总线应答
at24cxx_inquires(I2C_IDRW0xfe);//器件应答检测 并写入器件ID以及读写选项 内部有起始条件
at24cxx_com(I2C_ADDRESS); //写入数据地址
at24cxx_response(); //总线应答
at24cxx_com(I2C_DATA); //写入数据
at24cxx_response(); //总线应答
at24cxx_stop(); //结束条件 终止写入数据
}
/*函数流程 起始条件 器件地址 写 字地址 起始条件 器件地址 读 读数据 停止条件*/
uchar at24cxx_read(uchar I2C_IDRW,I2C_ADDRESS) //带地址的读取AT24 I2C_IDRW器件地址 I2C_ADDRESS数据地址
{ uchar I2C_DATA; //最终读取的数据
//at24cxx_start(); //起始条件 开始写入数据
//at24cxx_com(I2C_IDRW0xfe); //写入芯片ID及读写选择位 0
//at24cxx_response(); //总线应答
at24cxx_inquires(I2C_IDRW0xfe); //器件应答检测 并写入器件ID以及读写选项 内部有起始条件
at24cxx_com(I2C_ADDRESS); //写入数据地址
at24cxx_response(); //总线应答
I2C_DATA=at24cxx_rd(I2C_IDRW|0x01);//写入芯片ID及读写选择位 1 程序内部有起始停止功能
return(I2C_DATA); //返回数据
}
/*起始条件 器件地址 读 数据 停止条件*/
/*当前地址读 起始条件 器件地址 读 数据 停止条件*/
/*接收器件地址(读/写选择为为1)、EEPROM应答ACK后,当前地址的数据就随时钟送出主器件无需应答0,但需要发送停止条件*/
uchar at24cxx_rd(I2C_IDRD) //当前地址读取AT24 I2C_IDRD为器件地址
{ uchar I2C_NUM,I2C_DAT; //定义8位变量用来做循环移位读取数据
at24cxx_start(); //起始条件
at24cxx_com(I2C_IDRD|0x01); //写入芯片ID及读写选择位 1
at24cxx_response(); //总线应答
for(I2C_NUM=0;I2C_NUM8;I2C_NUM++)
{ I2C_DAT=I2C_DAT1; //数据左移位
I2C_SCL=1; //拉高时钟线得到 要读的数据
_nop_(); //一个机器周期指令
if(I2C_SDA==1) //判断数据为1
{I2C_DAT=I2C_DAT|0X01;} //将数据变量赋值
I2C_SCL=0; //拉低时钟线 以获得新数据
nop_(); //一个机器周期指令
}
at24cxx_stop(); //结束条件 终止接收数据
return(I2C_DAT); //返回数据
}
/*由读写函数调用 目的:节省空间*/
void at24cxx_com(uchar I2C_DATA) //AT24写公用函数
{ uchar I2C_NUM,I2C_DAT; //定义8位变量用来做循环写入位
for(I2C_NUM=0;I2C_NUM8;I2C_NUM++)
{
I2C_DAT=(I2C_DATA7);//获得数据最高位
I2C_DAT=I2C_DAT0X01; //取数据的最高位数据
I2C_SCL=0; //拉低时钟线数据传输开始
_nop_(); //一个机器周期指令
I2C_SDA=I2C_DAT; //I2C数据线赋值
_nop_(); //一个机器周期指令
I2C_SCL=1; //拉高时钟线数据存储
_nop_(); //一个机器周期指令
I2C_DATA=I2C_DATA1; //数据移位
}
}
/*当SCL为高电平时SDA的下降沿(高到低)叫做起始条件(START)起始条件保持时间 最小值1.8V 0.6US:5V 0.25US 起始条件建立时间1.8V 0.6US:5V 0.25US */
void at24cxx_start()//AT24起始信号
{
I2C_SCL=1; //空闲总线
_nop_(); //一个机器周期指令
I2C_SDA=1; //空闲总线
_nop_(); //一个机器周期指令
I2C_SDA=0; //拉低数据线 发起起始信号
_nop_(); //一个机器周期指令
}
/*SDA的上升沿(低到高)叫做停止条件(STOP) 停止条件建立时间 最小值1.8V 0.6US:5V 0.25US */
void at24cxx_stop() //AT24停止信号
{
I2C_SDA=0; //拉低数据线 以便形成上升沿
_nop_(); //一个机器周期指令
I2C_SCL=1; //
_nop_(); //一个机器周期指令
I2C_SDA=1; //老高数据线 发起停止信号
_nop_(); //一个机器周期指令
}
/*一旦内部写周期启动,EEPROM输入无效,此时即可启动应答查询:发送起始条件和器件地址(读/写位为期望的操作)。
只有内部写周期完成EEPROM才会应答“0”。之后可继续读/写操作
应答查询流程:1.发送写命令 2.发送停止条件启动写周期 3.发送起始条件 4.发送控制字节R/W=0 5.器件是否应答(ACK=0)? 是 5.下一操作 否 重复2-4操作
*/
void at24cxx_inquires(uchar I2C_IDRW)//AT24应答查询
{ uchar AT24_NUM; //定义变量用来跳出循环应答检测
AT24_NUM=AT24_INQ; //赋值应答最大检测次数 避免无应答时程序死循环
while(AT24_NUM--) //循环判断应答
{ at24cxx_start(); //发送起始条件
at24cxx_com(I2C_IDRW); //写入芯片ID及读写选择位
at24cxx_ricom(); //调用应答公用检测函数
if(I2C_SDA==0) //应答成功
{break;} //退出循环
}
I2C_SCL=0; //应答结束拉低时钟线
_nop_(); //一个机器周期指令
I2C_SDA=1; //应答结束把数据线重新拉高
_nop_(); //一个机器周期指令
}
/*接收器拉低SDA线表示应答,应在应答脉冲器件保持稳定的低电平。当主器件做接收器时,必须发出数据传输结束的信号给发送器,
即它在最后一个字节之后的应答脉冲期间不会产生应噶信号(不拉低SDA)。这种情况下,发送器必须释放SDA线为高以便主器件产生停止条件*/
void at24cxx_response()//总线应答
{
at24cxx_ricom(); //调用应答公用检测函数
I2C_SCL=0; //应答结束拉低时钟线
_nop_(); //一个机器周期指令
I2C_SDA=1; //应答结束把数据线重新拉高
_nop_(); //一个机器周期指令
}
/*器件应答与总线应答公用函数 由于这部分代码相同 所以写成一个函数 以节省空间*/
void at24cxx_ricom() //器件应答与总线应答检测 公用函数
{
uchar AT24_NUM; //定义变量 用来无应答时跳出循环判断应答
I2C_SCL=0; //总线应答额外脉冲
_nop_(); //一个机器周期指令
I2C_SDA=1; //拉高数据线 以检测应答
_nop_(); //一个机器周期指令
I2C_SCL=1; //应答时钟脉冲
_nop_(); //一个机器周期指令
while(I2C_SDA) //判断应答
{
if(AT24_NUM255)//判断累加 总线应答检测最大时间(次数) 避免无应答时程序死循环
{AT24_NUM++;} //没有到则自+
else //否则
{break;} //退出循环判断应答
}
}
单片机读写AT24CXX的介绍就聊到这里吧,感谢您花时间阅读,谢谢。
本文标签:单片机读写AT24CXX