IIC&EEPROM合成文件

点击打开在线编译器,边学边练

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 <reg52.h>
#include <iic.h>
#include <intrins.h>
 
#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”添加到工程文件中

iic7


5.main.c测试代码

#include <reg52.h>
#include <function.h>//详见第六章第8讲
#include <lcd.h>     //详见第十一章第3讲
#include <iic.h>
 
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);
}

本文固定URL:https://www.dotcpp.com/course/394

上一课:EEPROM简单使用
第一章 单片机入门
第二章 LED
第三章 蜂鸣器
第四章 数码管
第五章 独立按键
第六章 多文件编程
第七章 外部中断
第八章 定时器
第九章 舵机与超声波模块
第十章 串口通信
第十一章 1602液晶屏
第十二章 IIC通信
第十三章 红外遥控与温度传感器
第十四章 AD与DA
第十五章 混合例程
第十六章 完结
Dotcpp在线编译      (登录可减少运行等待时间)