AT24C02使用详解
这篇文章是写给一个学弟看的,关于IIC,关于24C02的单字节写入\读取..页写入和读取,,学弟总是害怕协议,,,我总是对人家说,本来就这样的,协议就是人家这样规定的,,,如果你早生几十年你也可能规定个IIC协议......
我的单片机和24C02通信,,,我的单片机就叫主机,,,24C02叫从机
先看IIC
IIC协议规定开始传输数据的时候要先发一个起始信号,,,目的应该是告诉从机要开始通信了,准备准备
终止信号就是拜拜啦,再见!
起始信号就是 在SCL在高电平期间SDA来一个下降沿,,终止信号就是在SCL在高电平期间SDA来一个上升沿(所以协议上才会说,在正常传输数据的时候,只有在SCL为低电平的时候,数据线SDA的高低电平状态才允许改变,要不然岂不是和起始信号或者终止信号冲突了)
/*******************************************************************
起动总线函数
函数原型: void Start_I2c();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/voidStart_I2c()
{
SDA=1;/*发送起始条件的数据信号*/_Nop();
SCL=1;
_Nop();/*起始条件建立时间大于4.7us,延时*/_Nop();
_Nop();
_Nop();
_Nop();
SDA=0;/*发送起始信号*/_Nop();/*起始条件锁定时间大于4μs*/_Nop();
_Nop();
_Nop();
_Nop();
SCL=0;/*钳住I2C总线,准备发送或接收数据*/_Nop();
_Nop();
}
voidStop_I2c()
{
SDA=0;/*发送结束条件的数据信号*/_Nop();/*发送结束条件的时钟信号*/SCL=1;/*结束条件建立时间大于4μs*/_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1;/*发送I2C总线结束信号*/_Nop();
_Nop();
_Nop();
_Nop();
}
发送完起始信号就能传输数据了
下面是程序
voidSendByte(unsignedcharc)
{
unsignedcharBitCnt;//SCL=0; 起始信号最后是SCL=0;所以不用写了for(BitCnt=0;BitCnt<8;BitCnt++)/*要传送的数据长度为8位*/{if((c<
SDA=1;
}else{
SDA=0;
}
_Nop();
SCL=1;/*置时钟线为高,通知被控器开始接收数据位*/_Nop();
_Nop();/*保证时钟高电平周期大于4μs*/_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1;/*8位发送完后释放数据线,准备接收应答位*/_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();if(SDA==1)/*判断是否接收到应答信号*/ack=0;//没有接收到应答信号elseack=1;//接收到应答信号SCL=0;
_Nop();
_Nop();
}
现在说一下接收,,,假设上面发送完0xaa以后,从机就返回给我们数据(11001100, 0xcc),当然SCL为低电平的时候模块准备数据,,SCL为高电平的时候,从机就把数据放在了SDA上,这样循环8次,一个8位数据就过来了
整体上应该是
Start_I2c();起始信号程序
SendByte(0xaa);
判断下ack是不是等于1,应答了(是继续执行还是停止看自己了)
Data = RcvByte();//接收数据
Ack_I2c(1);//发送非应答,就是SDA=1;,这个程序在下面
Stop_I2c();发送停止信号
接收程序如下
unsignedcharRcvByte()
{
unsignedcharretc;
unsignedcharBitCnt;
retc=0;
SDA=1;/*置数据线为输入方式*/for(BitCnt=0;BitCnt<8;BitCnt++)
{
_Nop();
SCL=0;/*置时钟线为低,准备接收数据位*/_Nop();
_Nop();/*时钟低电平周期大于4.7μs*/_Nop();
_Nop();
_Nop();
SCL=1;/*置时钟线为高使数据线上数据有效*/_Nop();
_Nop();
retc=retc<<1;if(SDA==1)retc=retc+1;/*读数据位,接收的数据位放入retc中*/_Nop();
_Nop();
}
SCL=0;
_Nop();
_Nop();return(retc);
}
应答或者非应答程序如下
/********************************************************************
应答子函数
函数原型: void Ack_I2c(bit a);
功能: 主控器进行应答信号(可以是应答0或非应答1信号,由位参数a决定)
********************************************************************/voidAck_I2c(bit a)
{if(a==0)SDA=0;/*在此发出应答或非应答信号*/elseSDA=1;
_Nop();
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();/*时钟低电平周期大于4μs*/_Nop();
_Nop();
_Nop();
SCL=0;/*清时钟线,钳住I2C总线以便继续接收*/_Nop();
_Nop();
}
IIC其实就这样了,主要看支持IIC通信的芯片的资料了,写好这些就是IIC通用的了
资料链接
https://wenku.baidu.com/view/3fc8558002d276a200292ef9.html
现在看芯片资料如何写进去一个字节
关于器件的地址
写就是0xa0;;;;读就是0xa1
所以写函数就是
/**
* @brief 向24C02写数据
* @param Data--数据
* @param Address--地址
* @param None
* @retval None
* @example
**/unsignedcharWriteData(unsignedcharData,unsignedcharAddress)
{
Start_I2c();
SendByte(0xa0);//最后一位为0写入if(ack==0)return(0);
SendByte(Address);//发送地址if(ack==0)return(0);
SendByte(Data);//发送数据if(ack==0)return(0);
Stop_I2c();//结束总线return(1);
}
关于应答哈我的SendByte(unsigned char c)函数里面发送完8位数据后就写了应答,然后把应答标志给ack,,后面直接判断的ack
现在想想为什么叫应答...直接说判断从机正没正确接收到数据就完了呗,就是把SDA拉高,然后把SCL拉高,等一会然后判断SDA引脚有没有被从机拉低,拉低了就说明好了......没拉低从机可能接收的数据不正确
_Nop();
_Nop();
SDA=1; /*8位发送完后释放数据线,准备接收应答位*/
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)/*判断是否接收到应答信号*/
ack=0;//没有接收到应答信号
else
ack=1;//接收到应答信号
SCL=0;
_Nop();
_Nop();
}
再看从任意地址读一个数据
注意哈第一个发送的器件地址是0xa0,后面的是0xa1
所以程序如下
/**
* @brief 从24C02读出数据
* @param None
* @param Address--地址
* @param None
* @retval 读到的数据
* @example
**/unsignedcharReadData(unsignedcharAddress)
{
unsignedcharData =0;
Start_I2c();
SendByte(0xa0);//最后一位为0if(ack==0)return(0);
SendByte(Address);//发送地址if(ack==0)return(0);
Start_I2c();
SendByte(0xa1);//最后一位为1if(ack==0)return(0);
Data=RcvByte();
Ack_I2c(1);//发送非就答位Stop_I2c();//结束总线return(Data);
}
现在看页写
把程序先放上,对了写的时候的开始地址最好是0,8,16,24,32,40,68,,,,8的倍数,要不然数据可能有错误,当然我用的芯片页写最多一次能写入8个字节.....感觉有点少哈......可以在现在的基础上再做一个函数实现哈,,或者用写单字节的for循环下....
/**
* @brief 向24C02写数据----页写,,,最多一次写入8个字节,多了会覆盖前面的
* @param Data--数据
* @param StartAddress--开始的地址--最大255
* @param None
* @retval None
* @example
**/unsignedcharPageWrite(unsignedchar*Data,unsignedcharAddress,unsignedcharcnt)
{
Start_I2c();
SendByte(0xa0);//最后一位为0写入if(ack==0)return(0);
SendByte(Address);//发送地址if(ack==0)return(0);while(cnt--)
{
SendByte(*Data++);//发送数据if(ack==0)return(0);
DelayMs(10);
}
Stop_I2c();//结束总线return(1);
}
现在看页读
看程序
/**
* @brief 从24C02读出数据----页读
* @param Data--数据指针
* @param StartAddress--开始的地址--最大255
* @param None
* @retval None
* @example
**/unsignedcharPageRead(unsignedchar*Data,unsignedcharAddress,unsignedcharcnt)
{
Start_I2c();
SendByte(0xa0);//最后一位为0if(ack==0)return(0);
SendByte(Address);//发送要读的地址if(ack==0)return(0);
Start_I2c();
SendByte(0xa1);//最后一位为1if(ack==0)return(0);while(cnt--)
{*Data =RcvByte();
Data++;
Ack_I2c(0);//发送应答位DelayMs(10);
}
Ack_I2c(1);//发送非应答位Stop_I2c();//结束总线return(1);
}
说一下读的时候最好开始读取的地址是8的倍数,读取的数据个数也是8的倍数,,,我测试的如果不是这样有时候,第二次页读的时候就会读错........
这芯片和8干上了............
还有一个立即读,,,看明白就行,就是立即返回当前读地址加1后的那个数据
源码链接
链接:http://pan.baidu.com/s/1i4M7BId%20密码:r9ov