zoukankan      html  css  js  c++  java
  • 硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器(转)

    源:硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器

    硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16

    使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便

    软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位

    256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节

    上代码,求测试和讨论

    #include "MY51.H"
    //转载请注明:http://xouou.iteye.com  求测试讨论
    //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(); 
    }
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 字符串逆序
    Java实现 蓝桥杯VIP 算法训练 字符串逆序
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 成绩的等级输出
    Java实现 蓝桥杯VIP 算法训练 成绩的等级输出
    Qt 自定义model实现文件系统的文件名排序
  • 原文地址:https://www.cnblogs.com/LittleTiger/p/4726864.html
Copyright © 2011-2022 走看看