单片机
返回首页

51单片机-IIC&EEPROM合成文件

2021-08-27 来源:eefocus

1.浅释E2Write函数


宋老师的例程lesson14_3和lesson14_4里的“E2Write(unsigned char *buf, unsigned char addr, unsigned char len)”书写内容是不一样的,lesson14_4的E2Write函数比lesson14_3的E2Write函数运行高效,lesson14_3的E2Write函数是每写入一个字节就要经历起始信号,停止信号,写入下一个字节又要把这些步骤经历一遍。而lesson14_4的E2Write函数支持EEPROM页写入(参考《手把手教你学51单片机》文档14.3.3节),所以在建立起文件时我们选用lesson14_4的E2Write函数。


大家新建两个文件“iic.c”和“iic.h”,我们把IIC的相关函数和EEPROM的相关函数都合成在“iic.c”这个单独文件里。


2.iic.c的代码


#include

#include

#include

  

#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}

  

/* 产生总线起始信号 */

void I2CStart()

{

    I2C_SDA = 1; //首先确保SDA、SCL都是高电平

    I2C_SCL = 1;

    I2CDelay();

    I2C_SDA = 0; //先拉低SDA

    I2CDelay();

    I2C_SCL = 0; //再拉低SCL

}

 

/* 产生总线停止信号 */

void I2CStop()

{

    I2C_SCL = 0; //首先确保SDA、SCL都是低电平

    I2C_SDA = 0;

    I2CDelay();

    I2C_SCL = 1; //先拉高SCL

    I2CDelay();

    I2C_SDA = 1; //再拉高SDA

    I2CDelay();

}

 

/* I2C总线写操作,dat-待写入字节,返回值-从机应答位的值 */

unsigned char I2CWrite(unsigned char dat)

{

    unsigned char ack;                 //用于暂存应答位的值

    unsigned char mask;                //用于探测字节内某一位值的掩码变量

   

    for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行

    {

        if ((mask&dat) == 0)  //该位的值输出到SDA上

            I2C_SDA = 0;

        else

            I2C_SDA = 1;

        I2CDelay();

        I2C_SCL = 1;          //拉高SCL

        I2CDelay();

        I2C_SCL = 0;          //再拉低SCL,完成一个位周期

    }

    I2C_SDA = 1;   //8位数据发送完后,主机释放SDA,以检测从机应答

    I2CDelay();

    I2C_SCL = 1;   //拉高SCL

    ack = I2C_SDA; //读取此时的SDA值,即为从机的应答值

    I2CDelay();

    I2C_SCL = 0;   //再拉低SCL完成应答位,并保持住总线

   

    return (!ack); //应答值取反以符合通常的逻辑:

                   //0=不存在或忙或写入失败,1=存在且空闲或写入成功

}

 

/* I2C总线读操作,并发送应答或者非应答信号,返回值-读到的字节 */

unsigned char I2CReadNAK_OR_ACK(unsigned char nak_or_ack)

{

    unsigned char mask;

    unsigned char dat;

   

    I2C_SDA = 1;                       //首先确保主机释放SDA

    for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行

    {

        I2CDelay();

        I2C_SCL = 1;      //拉高SCL

        if(I2C_SDA == 0)  //读取SDA的值

            dat &= ~mask; //为0时,dat中对应位清零

        else

            dat |= mask;  //为1时,dat中对应位置1

        I2CDelay();

        I2C_SCL = 0;      //再拉低SCL,以使从机发送出下一位

    }

    I2C_SDA = nak_or_ack; //8位数据发送完后,传入的参数NAK_OR_ACK决定是否应答,为1不应答,为0应答

    I2CDelay();

    I2C_SCL = 1;   //拉高SCL

    I2CDelay();

    I2C_SCL = 0;   //再拉低SCL完成非应答位,并保持住总线

   

    return dat;

}

 

/* E2读取函数,buf-数据接收指针,addr-E2中的起始地址,len-读取长度 */

void E2Read(unsigned char *buf, unsigned char addr, unsigned char len)

{

    do {                               //用寻址操作查询当前是否可进行读写操作

           I2CStart();

           if (I2CWrite(0x50<<1))      //应答则跳出循环,非应答则进行下一次查询

           {

               break;

           }

           I2CStop();

    } while(1);

    I2CWrite(addr);                    //写入起始地址

    I2CStart();                        //发送重复启动信号

    I2CWrite((0x50<<1)|0x01);          //寻址器件,后续为读操作

    while (len > 1)                    //连续读取len-1个字节

    {

        *buf++ = I2CReadNAK_OR_ACK(0); //最后字节之前为读取操作+应答

        len--;

    }

    *buf = I2CReadNAK_OR_ACK(1);       //最后一个字节为读取操作+非应答

    I2CStop();

}

 

/* E2写入函数,buf-源数据指针,addr-E2中的起始地址,len-写入长度 */

void E2Write(unsigned char *buf, unsigned char addr, unsigned char len)

{

    while (len > 0)

    {

        //等待上次写入操作完成

        do {                          //用寻址操作查询当前是否可进行读写操作

               I2CStart();

               if (I2CWrite(0x50<<1)) //应答则跳出循环,非应答则进行下一次查询

               {

                   break;

               }

               I2CStop();

        } while(1);

        //按页写模式连续写入字节

        I2CWrite(addr);           //写入起始地址

        while (len > 0)

        {

            I2CWrite(*buf++);     //写入一个字节数据

            len--;                //待写入长度计数递减

            addr++;               //E2地址递增

            if ((addr&0x07) == 0) //检查地址是否到达页边界,24C02每页8字节,

            {                     //所以检测低3位是否为零即可

                break;            //到达页边界时,跳出循环,结束本次写操作

            }

        }

        I2CStop();

    }

}


3.iic.h的代码


#ifndef __IIC_H__

#define __IIC_H__

  

sbit I2C_SCL = P3^7;

sbit I2C_SDA = P3^6;

void I2CStart();//产生总线起始信号

void I2CStop(); //产生总线停止信号

unsigned char I2CWrite(unsigned char dat);//I2C总线写操作,dat-待写入字节,返回值-从机应答位的值

unsigned char I2CReadNAK_OR_ACK(unsigned char nak_or_ack);//I2C总线读操作,并发送应答或者非应答信号,返回值-读到的字节

void E2Read(unsigned char *buf, unsigned char addr, unsigned char len); //E2读取函数,buf-数据接收指针,addr-E2中的起始地址,len-读取长度

void E2Write(unsigned char *buf, unsigned char addr, unsigned char len);//E2写入函数,buf-源数据指针,addr-E2中的起始地址,len-写入长度

  

#endif


4.部分代码的修改


“unsigned char I2CReadACK()”和“unsigned char I2CReadNAK()”这里我们合成了一个函数为


“unsigned char I2CReadNAK_OR_ACK(unsigned char nak_or_ack)”,利用参数的传递决定是否产生应答。


那么在main.c中,几乎也是只需要出现EEPROM的写函数“E2Write()”和读函数“E2Read()”而已了。


记得把“iic”添加到工程文件中

12.7.png

5.main.c测试代码


#include

#include //详见第六章第8讲

#include      //详见第十一章第3讲

#include

  

void main()

{

    unsigned char buf[]={'We can learn SCM well!'};//我们可以学好单片机

    unsigned char str[sizeof(buf)];                //数组长度与buf的一样

   

    InitLcd1602();                //初始化液晶

    E2Write(buf,0x8E,sizeof(buf));//把buf数组里面的内容在EEPROM中从地址0x8E开始写,直到把数组里的内容全部写完进去,在EEPROM中保存起来

   

    delay_ms(1000);               //过1秒之后再读出里面的内容显示在液晶屏上

   

    E2Read(str,0x8E,sizeof(buf)); //用另一个数组存取从EEPROM中读出的内容

    LcdShowStr_len(0, 0,str, 16);

    LcdShowStr(0, 1, str+16+1);

    while(1);

}

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 如何利用ESP8266制作一个简单的四轴飞行器

  • 非常简单的150W功放电路图

  • 如何使用LED驱动器LM3915制作振动计

  • 一个简单的立体声平衡指示器电路

  • 分享一个电网倾角计电路

  • 一种构建12V和230V双直流电源的简单方法

    相关电子头条文章