清翔零基础教你学51单片机_个人学习笔记(12)_IIC总线EEPORM(理论+实践)

说明

本人使用的是清翔的51单片机开发板,如果型号相同最方便,但是如果型号不同也可以参考,因为芯片都是一样的,只是外设不同而已,使用时只需要对照自己的开发板原理图稍微修改下引脚即可。

本次笔记对应视频教程的第35,,3,37 IIC总线EEPORM(理论+实践)

如果笔记之中有任何错误,请在评论区指出,谢谢

由于51单片机没有自带硬件IIC,所以本此使用模拟IIC,需要完全了解IIC的通信协议。本节使用板载AT24C02来使用IIC协议。在查阅资料的过程中看到了这篇文章,很不错嵌入式硬件入门——EEPROM(AT24C02+I2C协议)http://t.csdn.cn/S9sCM

网上找的AT24C02中文数据手册可以参考一下https://www.semiee.com/file/Source10/IDCHIP-AT24C02.pdf 

目录

一、常用的串行总线协议

二、IIC总线相关知识

2.1 概念

2.2  数据传输过程

2.3 传输过程规定

2.3.1 起始/终止信号

2.3.2 数据的传输结构

 2.3.3 寻址的详细过程

 三、原理图

四、编程-信号部分

4.1 起始信号

4.2 终止信号

4.3 主机读应答

4.4 主机发送应答

五、编程

5.1 创建工程

5.2 main.c

5.3 main.h

5.4 现象


一、常用的串行总线协议

  1. UART:异步通信方式(一条数据输入线,一条数据输出线)
  2. 1-Wire:即单总线(只有1条线)
  3. IIC:同步串行2线方式进行通信(一条时钟线一条数据线)
  4. SPI:同步串行3线方式进行通信(一条时钟,线一条数据输入线,一条数据输出线)

IIC可以多主机多从机。多从机需要通过寻址来找到对应的器件 

二、IIC总线相关知识

2.1 概念

2.2  数据传输过程

 多主机用的很少,初学者可以忽略。

2.3 传输过程规定

2.3.1 起始/终止信号

在SCL=1期间,SDA从高电平变成低电平,表示起始信号

在SCL=1期间,SDA从低电平变成高电平,表示终止信号

2.3.2 数据的传输结构

注意,发送数据时先发送最高位

每发送1个字节,需要跟一个响应位(ACK)。ACK就是主机释放SDA(让SDA=1),让数据接收方控制SDA产生一个低电平,则表示有响应 

数据传输的完整过程

 注:时序图中ACK并没有释放总线的过程,实际上是需要释放总线的,图里面省略了 

 写数据

读数据 

不发送停止信号,直接再次发送起始信号 

先发送从机地址(7位),再跟一个方向位,0表示要向从机写数据,1表示要从从机读取数据。后面跟要传输的数据,每传输1个字节要跟一个响应位,传输完成由主机产生停止信号。如果主机扔想继续在总线上通讯,可以不产生终止信号,而是直接再次产生一个起始信号,并对从机进行寻址,再传输数据。 

在IIC传输数据的过程中,在传输数据时,SDA只能在SCL = 0时改变,在SCL = 1时SDA必须保持稳定。

 2.3.3 寻址的详细过程

还有广播信号,初学不需要太多了解,不多介绍。

再给出另一个IIC器件数据手册中关于IIC的部分

 

Co(继续位):用于指示是否有多个字节需要连续传输。如果Co位为1,则表示还有更多字节需要传输;如果为0,则表示当前是最后一个字节。

D/C#(数据/命令选择位):用于指示发送或接收的数据是数据还是命令。如果D/C#位为1,则表示发送的是数据;如果为0,则表示发送的是命令。

ACK(确认):在I2C通信中,从设备在成功接收到一个字节的数据后会生成一个确认信号。ACK用于表示数据已被正确接收。

SA0(从设备地址位):用于指示从设备的地址。SA0位是从设备地址的最低有效位之一,其余位通过主设备发送来选择特定的从设备进行通信。

R/W#(读/写选择位):用于指示主设备对从设备进行读操作还是写操作。如果R/W#位为1,则表示进行读操作;如果为0,则表示进行写操作。

S(起始条件)/P(停止条件):起始条件表示I2C通信即将开始,由主设备发出;停止条件表示I2C通信结束,由主设备发出。起始条件和停止条件用于定义I2C通信的开始和结束点。

 

 

 线与:所有器件都是高,SDA才为高,又任何一个器件为低,那么SDA就为低,相当于逻辑与的关系。

 IIC介绍差不多到这里就结束了

 三、原理图

器件地址说明

 

根据原理图可见,A2,A1,A0全部都接地,是0,那么板载的AT24C02地址就是b1010 000,所以在起始信号之后,要发送A0(表示写数据)或A1(表示读数据)进行寻址 

四、编程-信号部分

4.1 起始信号

void I2Cstart()
{
    SDA = 1;
    SCL = 1;
    delay5us();
    SDA = 0;
    delay5us();
}

 

4.2 终止信号

void I2Cstop()
{
    SCL = 0;
    SDA = 0;
    SCL = 1;
    delay5us();
    SDA = 1;
    delay5us();
}

4.3 主机读应答

 

bit ReadACK()
{
    SCL = 0;
    SDA = 1;    //释放SDA
    SCL = 1;    //给一个脉冲让从机发送ACK
    delay5us();
    if (SDA)    //SDA=1,表示无应答
    {
        SCL = 0;
        return (1);
    }
    else
    {
        SCL = 0;
        return (0);
    }
}

SDA = 1表示非应答,SDA = 0表示应答 

 

4.4 主机发送应答

void SendACK(bit i)
{
    SCL = 0;
    SDA = i;
    SCL = 1;
    delay5us();
    SCL = 0;
    SDA = 1;
}

SDA = 1表示非应答,SDA = 0表示应答 

五、编程

5.1 创建工程

复制上一份工程文件夹,修改名称为“12.IIC通信AT24C02”,进入项目文件夹,打开工程文件,删除main.c函数的内容。

5.2 main.c

#include <intrins.h>


uchar num;

void main()
{
    timer0init(0x01, 0xed,0xff);
    EA = 0;
    AT24C02Write(0x02, 0xed);    //在0x02地址写入0xed(237)
    delay(1);                    //刚写入数据,需要时间处理才能读取,否则读取不到
    num = AT24C02Read(0x02);
    EA = 1;
    while(1);
}


void delay5us()
{
    _nop_();
}

void I2Cstart() //产生起始信号
{
    SCL = 0;
    SDA = 1;
    SCL = 1;
    delay5us();
    SDA = 0;
    delay5us();
    SCL = 0;
}

void I2Cstop()  //产生终止信号
{
    SCL = 0;
    SDA = 0;
    SCL = 1;
    delay5us();
    SDA = 1;
    delay5us();
}

bit ReadACK()   //读从机应答
{
    SCL = 0;
    SDA = 1;    //释放SDA
    SCL = 1;    //给一个脉冲让从机发送ACK
    delay5us();
    if (SDA)    //SDA=1,表示无应答
    {
        SCL = 0;
        return (1);
    }
    else        //SDA=0表示应答
    {
        SCL = 0;
        return (0);
    }
}

void SendACK(bit i) //写应答
{
    SDA = i;
    SCL = 1;
    delay5us();
    SCL = 0;
    SDA = 1;    //释放总线
}

void I2CSendByte(uchar DAT) //发送1个字节
{
    uchar i;
    for (i=0;i<8;i++)
    {
        SCL = 0;
        SDA = DAT&0x80;
        SCL = 1;
        DAT <<= 1;
    }
    ACKflag = ReadACK();    //看从机是否应答
}

uchar I2CReadByte()     //读取1个字节
{
    uchar i;
    uchar DAT;
    for (i=0;i<8;i++)
    {
    //不能把DAT放在DAT |= SDA;的后面,若放在它后面,那么当接收最后一位后,DAT还会左移,最高位就丢了
        DAT <<= 1;
        SCL = 0;    //SCL=0才允许SDA变化,给从机时间让它调整SDA状态
        SCL = 1;
        DAT |= SDA;
    }
    return (DAT);
}


void AT24C02Write(uchar address, uchar DAT) //在指定地址写入指定数据
{
    I2Cstart();
    I2CSendByte(0xA0);  //AT24C02的地址,A0表示写入,A1表示读取
    I2CSendByte(address);   //发送要把数据写入到AT24C02的哪里
    I2CSendByte(DAT);       //要写入的数据是什么
    I2Cstop();          //发送停止信号
}

uchar AT24C02Read(uchar address)    //在指定地址读取1个字节数据
{
    uchar DAT;
    I2Cstart();
    I2CSendByte(0xA0);  //AT24C02的地址,A0表示写入,A1表示读取
    I2CSendByte(address);   //发送要把读AT24C02哪里的数据
    I2Cstart();
    I2CSendByte(0xA1);  //AT24C02的地址,A0表示写入,A1表示读取
    DAT = I2CReadByte();
    SendACK(1);         //发送非应答
    I2Cstop();          //发送停止信号
    return DAT;
}

开始关闭了中断是为了避免中断影响到AT24C02的数据写入和读取。 

定时器0中断函数(初始化函数直接复制之前的就行)

    //定时器0中断
void timer0() interrupt 1
{
    SEG_DIS3(num);
    TH0 = 0xed;
    TL0 = 0xff;
}

注意,EEPROM每个区域是有写入次数寿命的,大概10万到100万之间的写入次数,读取不限。因此要特别注意不要在循环中使用写入,否则循环几分钟EEPROM就被写坏了。

5.3 main.h

void delay5us();
void I2Cstart(); //产生起始信号
void I2Cstop();  //产生终止信号
bit ReadACK();  //读应答
void SendACK(bit i);    //发送应答
void I2CSendByte(uchar DAT); //发送1个字节
uchar I2CReadByte();     //读取1个字节
void AT24C02Write(uchar address, uchar DAT); //在指定地址写入指定数据
uchar AT24C02Read(uchar address);    //在指定地址读取1个字节数据

bit ACKflag;

 写入函数声明和变量声明

5.4 现象

 当运行一次程序之后,把mian函数中写入数据的部分删除,数码管依然能正常显示,说明数据已经被永久存储在芯片中了

void main()
{
    timer0init(0x01, 0xed,0xff);
    EA = 0;
    //AT24C02Write(0x02, 0xed);    //在0x02地址写入0xed(237)
    //delay(1);
    num = AT24C02Read(0x02);
    EA = 1;
    while(1);
}

可以利用此功能存储一些重要的系统数据文件,下次启动时直接调用上次运行结束后的数据。

本次笔记对应视频教程的第35,,3,37 IIC总线EEPORM(理论+实践),到此结束。我觉得对于IIC的讲解已经比较明晰了,启动,结束,应答,数据传输过程中的规定等都有讲到,初学者要再认真看一下数据手册,熟悉数据传输的过程。

下次笔记将对应视频教程的第38,39,40集 数模转换与模数转换(理论+实践)

如果笔记之中有任何错误,请在评论区指出,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值