标题:
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器
[打印本页]
作者:
51黑电子迷
时间:
2017-1-14 23:45
标题:
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器
硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16
使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节
上代码,求测试和讨论
#include "MY51.H"
//转载请注明: 求测试讨论
//stc89c52rc,11.0592MHz晶振
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; //下降沿
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(u8 byteData) //mcu发送一个字节
{
u8 i;
u8 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_();
}
u8 iic_readByte() //读一个字节
{
u8 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() //处理应答信号
{
u8 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 AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据
{
u8 largePage = address/256; //24c04是512字节(寻址范围0~511),largePage最大值是1
u8 addressOffset = address%256; //largePage=0的话地址范围是(0~255)
iic_start();
iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写
iic_checkACK(); //mcu处理应答信号
iic_sendByte(addressOffset); //指定要写入的器件内地址在 largePage块中的偏移
iic_checkACK();
iic_sendByte(dataByte); //写数据
iic_checkACK();
iic_stop();
delayms(2);
//按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间
//并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待
}
void AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节)
{
while(numBytes--)
{
AT24Cxx_writeByte(address++,*buf++);
}
}
void AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中
{
u8 largePage = beginAddr/256; //计算largePage,256字节为一大页
u8 addressOffset = beginAddr%256; //计算相对于largePage的偏移
iic_start(); //起始信号
iic_sendByte(0xa0|(largePage<<1)); //控制字,写
iic_checkACK(); //处理应答信号
iic_sendByte(addressOffset); //要读取的目标地址偏移
iic_checkACK(); //处理应答信号
iic_start(); //发送起始信号
iic_sendByte(0xa1|(largePage<<1)); //控制字,读
iic_checkACK(); //处理应答信号
while(dataSize--) //读取dataSize个字节,最大256个字节
{ //dataSize用u16类型会暴掉ram的
*buf++=iic_readByte(); //读取一个个字节并保存到缓冲区buf中
iic_sendACK(dataSize); //发送应答,当dataSize为0时mcu发送非应答
}
iic_stop(); //发送停止信号
}
void main()//测试
{
u8 buf[3]; //接受数据的缓冲区
u8 arr[7]={0x06,1,2,3,4,0x55,0x33}; //待写入的数据
iic_init(); //总线初始化
AT24Cxx_writeData(0x00+256,sizeof(arr),arr); //向指定地址处开始写入7字节的数据
P1=0xff; //调试代码,用P1口的led显示
delayms(1000); //调试代码
AT24Cxx_readData(0x00+256,sizeof(buf),buf); //从指定地址开始读3个字节
P1=buf[2]; //也就是2 //led灯显示数值
while(1)
{
P1=~P1;
delayms(500);
}
}
复制代码
//my51.h中主要用到
#include<reg52.h>
#include"mytype.h"
void delayms(u16 ms) //软延时函数
{
u16 i,j;
for(i=ms;i>0;i--)
{
for(j=113;j>0;j--)
{}
}
}
复制代码
对代码进行了改进
去掉了在写数据时的
delayms(2);
这句软延时代码低效 ,而且没有保障
改成加一个检测函数
bool check_icWriteComplete() //检测eeprom是否对内部擦写完成
{
iic_start();
iic_sendByte(0xa0);
return iic_checkACK();
}
复制代码
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1