51单片机学习笔记,模拟iic总线连续读写24c02存储器
2019-04-15 13:13 发布
生成海报
AT24C02A, 2K SERIAL EEPROM:
Internally organized with 32 pages of 8 bytes each, the 2K requires an 8-bit data word address for random word addressing.
24c02有32个页,每页8字节,本帖中不讨论页写的方式
-------------------------------------------------------------------
AT24C02内部设有一个8位控制寄存器,其每一位的含义如下: Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 1 0 1 0 A2 A1 A0 R/W 其中前4位数据是芯片固定的标识, A2/A1/A0用于选择总线上待访问的I2C器件,R/W=1读操作,R/W=0写操作; I2C总线上最多可以扩展8片同样的2K容量EEPROM存储器, 或者是4片4Kb的EEPROM, 或者是2片容量为8Kb的EEPROM存储器。 或者是1片容量为16Kb的EEPROM存储器(此时硬件就固定了,因为A2/A1/A0已经被P2P1P0占用), 如果扩展8片2K以内容量的EEPROM存储器,每片存储器将对应一个地址, 我们的实验板上的AT24C02的A2/A1/A0引脚全部接地, 所以在实验中读写控制字分别为:0xa1/0xa0
主芯片stc89c52rc,晶振11.0592M
#include
#include "MY51.H"
sbit sda=P2^0; //总线连接口定义
sbit scl=P2^1; //总线连接口定义
void delayus() //需要4个机器周期,大概4.34us
{
; //晶振频率11.0592M,机器周期为1.085微秒
}
void iic_start() //启动信号
{
sda=1;
scl=1;
delayus(); //sda和scl同为高电平保持4.7us以上
_nop_(); //1.085us,共5.78us,下面sda=0是下降沿,不能计算在延时时间中
sda=0; //下降沿
delayus(); //sda低电平保持4us以上 ,这里是4.34us满足要求
}
void iic_stop() //停止信号
{
sda=0;_nop_(); //准备状态
scl=1;
delayus(); //该状态稳定时间要求保持4us以上
sda=1; //scl高电平期间,sda来一个上升沿
delayus(); //sda保持4.7us以上,4.34加上函数返回时间大于4.7us
//注:此时scl和sda都为1
}
void iic_sendByte(uchar byteData) //mcu发送一个字节
{
uchar i;
uchar temp=byteData;
for(i=0;i<8;i++)
{
temp=temp<<1; //移动后最高位到了PSW寄存器的CY位中
scl=0; //准备
_nop_(); //稳定一下
sda=CY; //将待发送的数据一位位的放到sda上
_nop_();
scl=1; //每一个高电平期间,ic器件都会将数据取走
_nop_();
}
scl=0; //如果写成scl=1;sda=1就是停止信号,不能这么写
_nop_();
sda=1; //释放总线,数据总线不用时要释放
_nop_();
}
uchar iic_readByte() //读一个字节
{
uchar i,temp;
scl=0; //准备读数据
_nop_();
sda=1; //释放总线
_nop_();
for(i=0;i<8;i++)
{
scl=1; //mcu开始取数据
delayus(); //scl为高电平后,ic器件就会将1位数据送到sda上
//总共用时不会大于4.34us的,然后就可以让mcu读sda了
temp=(temp<<1)|sda; //读一位保存到temp中
scl=0;
delayus();
}
return temp;
}
bool iic_checkACK() //处理应答信号
{
uchar errCounts=255; //定义超时量为255次
scl=1;
_nop_();
while(sda)
{ //在一段时间内检测到sda=0的话认为是应答信号
if(0==errCounts)
{
scl=0; //钳住总线
_nop_();
return false; //没有应答信号
}
errCounts--;
}
scl=0; //钳住总线,为下1次通信做准备
_nop_();
return true; //成功处理应答信号
}
void iic_init() //总线初始化
{
scl=1;
sda=1;
delayus();
}
void iic_sendACK(bool b_ACK) //发送应答或非应答信号
{
scl=0; //准备
_nop_();
if(b_ACK) //ACK
{
sda=0;
}
else //unACK
{
sda=1;
}
_nop_();
scl=1;
delayus(); //大于4us的延时
scl=0; //钳住scl,以便继续接收数据
_nop_();
}
void AT24C02_writeByte(uchar address,uchar dataByte)//向24c02写一字节数据
{
iic_start();
iic_sendByte(0xa0);//mcu写控制字,前4位固定1010,后三位地址0,末位0是写
iic_checkACK(); //mcu处理应答信号
iic_sendByte(address); //准备在指定地址处写入
iic_checkACK();
iic_sendByte(dataByte); //写数据
iic_checkACK();
iic_stop();
delayms(2);
//按字节写入时,24c02在接收到停止信号后将数据擦写到内部,这需要时间
//并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待
}
void AT24C02_writeData(uchar address,uchar numBytes,uchar* buf)//写入任意长度数据
{
while(numBytes--)
{
AT24C02_writeByte(address++,*buf++);
}
}
void AT24C02_readData(uchar beginAddr,uchar dataSize,uchar* buf)//读取任意长度字节
{
iic_start(); //起始信号
iic_sendByte(0xa0); //控制字,写
iic_checkACK(); //处理应答信号
iic_sendByte(beginAddr); //发送地址
iic_checkACK(); //处理应答信号
iic_start(); //发送起始信号
iic_sendByte(0xa1); //控制字,读
iic_checkACK(); //处理应答信号
while(dataSize--) //读取dataSize个字节
{
*buf++=iic_readByte(); //读取一个个字节并保存到缓冲区buf中
iic_sendACK(dataSize); //发送应答,当dataSize为0时发送非应答
}
iic_stop(); //发送停止信号
}
void main()
{
uchar buf[2]; //接受数据的缓冲区
uchar arr[34]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, //0x00-0x0f
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,//0x10-0x1f
32,0x55}; //0x20-0x21
iic_init(); //总线初始化
//AT24C02_writeByte(0x08,0x11); //向指定地址处写入一个字节数据,代码测试
AT24C02_writeData(0x00,sizeof(arr),arr); //向指定地址处开始写入34字节的数据
AT24C02_readData(0x20,sizeof(buf),buf); //从指定地址开始读2个字节
P1=buf[1]; //buf中的第二个元素就是arr中的最后一个数据0x55
while(1){P1=~P1;delayms(500);} //将这个0x55用led灯显示出来10101010变化
}
my51.h头文件中主要用到
#include
typedef unsigned char uchar ;
void delayms(uint16 ms) //软延时函数
{
uint16 i,j;
for(i=ms;i>0;i--)
{
for(j=113;j>0;j--)
{}
}
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮