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(); 
    }
  • 相关阅读:
    mysql 锁相关的视图(未整理)
    MYSQL的用户变量(@)和系统变量(@@)
    mysql杀死线程
    mysql 系统变量和session变量
    Oracle DB 移动数据
    MySQL数据库------常用函数
    练习-----查询
    练习建表
    课堂笔记总结
    数据库--------用代码来创建表
  • 原文地址:https://www.cnblogs.com/LittleTiger/p/4726864.html
Copyright © 2011-2022 走看看