嵌入式硬件入门——EEPROM(AT24C02+I2C协议)

      最后更新:2022-08-01 18:04:13 手机定位技术交流文章

      EEPROM是指带电可擦可编程只读存储器。这是一个在停电后不会丢失数据的内存芯片.
      本文着重介绍了AT24C02的应用(即如何编程实现读写函数,偏向软件)

      文章目录
      EEPROM
      EEPROM简介
      EEPROM开发过程
      EEPROM分类
      AT24C02
      AT24C02简介
      AT24C02管脚定义和说明
      I2C协议(AT24C02)
      I2C总线概念
      I2C总线术语
      I2C启动及终止条件
      I2C传输数据(单字)
      I2C总线时序
      AT24C02特别时间序列
      I2C设备地址(7位)
      读写AT24C02
      字节写
      页写
      当前地址读
      选择读(随机读)
      连续读
      51单片控制器AT24C02
      I2C协议代码
      普羅特斯實驗

      EEPROM
      EEPROM简介,EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。这是一个在停电后不会丢失数据的内存芯片.EEPROM可以用于删除计算机或专门设备上的现有信息,重新编程。一般用在即插即用。
      .        EEPROM(带电可擦可编程只读存储器)是用户可更改的只读存储器(ROM),它可以通过超过正常电压的函数去除和重新编程(重写)。与EPROM芯片不同,EEPROM可以修改而不从计算机中删除。在EEPROM中,当计算机在使用时,它可以经常重复编程,因此, EEPROM的寿命是一个重要的设计考虑参数。EEPROM是一种特殊的闪存,它的应用通常是个人计算机中的电压去除和重新编程.
      ——百度百科

      EEPROM开发过程
      下面是关于白杜百科全书的引用。

      1.ROM(Read Only Memory,只读存储器):
      在微机的发展初期,BIOS存储在ROM中。存储器内部的信息是存储器制造过程中的,在工厂里特别地记录下来,内容只能读取,不能改变,一旦烧录进去,用户只能验证输入的信息是正确的,不能再作任何修改。如果在信息中发现任何错误,则只有舍弃不用,重新订做一份。ROM在生产线上生产,由于成本高,一般只用于大规模应用。
      2.PROM(Programmable ROM,可编程ROM):
      由于ROM制造和升级的不便,后来人们发明了PROM。PROM内没有从工厂生产的资料,用户可以使用专门的程序员来编写自己的数据,但只有一个机会,一旦写完,就不能修改,若是出了错误,已经写的芯片只能被取消.PROM具有与ROM相同的特性,但是它比ROM更贵,数据的写入速度比ROM的输出速度慢,一般而言,它只适用于低需求或在ROM大规模生产核查之前。
      3.EPROM(Erasable Programmable ROM,可擦除可编程ROM):
      EPROM芯片可以重复删除和写字,问题是,PROM芯片只能写出一个缺陷。EPROM芯片具有明显的特征,在前面的陶瓷封筒上,开有一个玻璃窗口,透过该窗口,你可以看到内部集成电路,穿过孔的紫外线照亮芯片的内部,消去内部的数据,要完成芯片除去操作, 使用EPROM消去器.在EPROM中输入的数据由专业程序员执行,并且您必须添加一个特定的编程电压(VPP=12~24V,取决于芯片类型。EPROM模式从27开始。例如,27C020(8*256K)是一个2M位元的EPROM芯片。在EPROM芯片写入信息后,还用不透明纸或胶片盖窗,避免因紫外线辐射而损坏信息.
      4.EEPROM(Electrically Erasable Programmable ROM,电可擦除可编程ROM):
      由于EPROM操作不便,大多数后来的主板的BIOS ROM芯片使用EEPROM。EEPROM删除不需要使用其他设备,它通过电子信号修改其内容,字节是最少修改的单元,你不需要洗掉所有的信息来写入。它完全释放EPROM消去器和程序员的限制。当EEPROM编写数据时,你仍然需要使用一定的编程电压,此时,只需使用制造商的专有更新程序,便于编辑内容,所以,它属于双电压芯片。使用EPROM芯片的双电压特性,它可以使 BIOS 具有良好的反病毒功能,在升级时,按跳板开关进入“开启”位置,也就是说,将相应的编程电压添加到芯片上,易于升级;正常使用,然后按开关到关闭位置,它防止CIH类病毒非法修改 BIOS芯片。所以,许多主板仍然使用EEPROM作为 BIOS芯片和作为其自己的主板的主要特征。

      EEPROM分类
      以下内容仅供参考,由网络组织

      按生产商分类:
      1.Atmel(AT,atmel,由Microchip取得):AT24C系列,AT45DB系列等;
      2.Microchip(微芯科技):11AA02E系列、25AA02E系列、MC93C系列等;
      3.ST半导体:M24C系列,M93C系列等。
      4.聚乙烯半导体(国内):GT24C系列、GT93C系列等;
      还有许多EEPROM制造商,其中四大(实际上是三个)是全球三大供应商(2018年,ST占37%,Microchip(Atmel)占23%,Polymer半导体占8%)2%)

      按通信协议分:
      1.I2C:I2C总线(Inter-Integrated Circuit,内部整合电路)是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两条线来传输到与总线连接的设备之间的信息。
      2.SPI:SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。
      3.Microwire:Microwire串行接口是一种简单的四线串行接口,包括串行数据输入(SI),串行数据输出(SO),串行变换时钟(SK),芯片选择(CS),实现高速串行数据通信。这是SPI的简化接口,能满足常规的外部要求。
      它也可以根据存储空间大小、字符串/平行等分类,这里没有详细的描述。

      AT24C02
      AT24C02简介
      本文以AT24C02为例,介绍了EEPROM的基本知识和方法。

      AT24C02是一个2Kbit串行CMOSE2PROM,内部包含256个8位字符,CATALYST的先进的CMOS技术大大降低了设备的功率消耗。AT24C02具有16位页写字缓冲器。该设备通过IIC总线接口操作,有一个特殊的写字保护功能。
      .
      ——百度百科

      AT24C02管脚定义和说明
      管脚定义
      管脚功能对照表

      功能说明

      注:AT24C01/02/04/08/16 附录(管道定义)相同。

      AT24C01/02/04/08/16支持I2C总线数据传输协议I2C总线协议规定,任何设备将数据发送到总线作为发送器.任何接收数据的设备都是接收器。数据传输由产生序列时钟和所有初始停止信号的主设备控制。主要设备和从设备可以是传送器或接收器,但数据传输方式(发送或接收)由主设备控制,通过器件地址输入端 A0、A1 和 A2 可以实现将最多 8 个 24CW01 和 24CW02 器件,4 242 CW04 设备,两个24CW08设备和一个24WC16设备连接到总线。
      .
      -AT24CXX中文手册

      管脚描述

      SCL 串行时钟:
      AT24WC01/02/04/08/16串行时钟输入管是一个产生设备发送或接收的所有数据的时钟。
      SDA序列数据/地址:
      AT24WC01/02/04/08/16 双向串行数据/地址管脚用于器件所有数据的发送或接收,SDA 是一个开漏输出管脚,可与其它开漏输出或集电极开路输出进行线或(wire-OR)。
      .A0 A1 A2 设备地址输入端口:
      这些输入脚用于设置多个设备级别连接时的设备地址,这些脚被悬挂时的默认值为0(除24WC01)。
      在使用24WC01或24WC02时,最多可分类8个设备。如果只有一个24WC02被公共汽车接通,这些三个地址输入脚(A0、A1、A2)可以悬挂或连接到Vss,如果只有一个24WC01被公共汽车接通,这些三个地址输入脚(A0, A1, A2)必须与Vss连接。
      使用24WC04时最多可连接4台设备,该设备只使用A1和A2地址管。A0 管脚未用,它可以连接到Vss或悬挂。如果只有一个24WC04由总线地址,A1和A2地址管可以悬挂或连接到Vss。
      当使用 24WC08 时最多可连接 2 个器件,且仅使用地址管脚 A2, A0、A1 管脚未用,可以连接到Vss 或悬空 。如果只有一个 24WC08 被总线寻址 A2 管脚可悬空或连接到 Vss。
      当使用 24WC16 时最多只可连接 1 个器件,所有地址管脚 A0、A1、A2 都未用 管脚可以连接到Vss 或悬空。
      .
      WP 写保护:
      如果WP管道连接到Vcc,所有的内容都是被写字保护(只读)。 当WP管道连接到Vss或暂停时,设备可以执行正常的读写操作。
      .
      -AT24CXX中文手册

      I2C协议(AT24C02)
      I2C总线概念:
      I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两条线来传输到与总线连接的设备之间的信息。
      主要设备用于启动总线传输数据,并生成时钟以打开传输设备,在此点,所处理的任何设备被认为是来自设备。主机与接收机之间的关系在主线上并不稳定。它取决于当前数据传输的方向。如果主机想向接收者发送数据,然后,主机首先从设备中定位,然后主动向设备发送数据,最后终止来自主机的数据传输;如果主机希望从设备接收数据,首先,由设备发送的主机地址,然后由设备发送的数据由主机接收,最后, 主机终止接收过程.在这种情况下,主机负责生成定时钟和终止数据传输。
      .
      ——百度百科

      I2C总线术语

      多主机 、 仲裁和同步 本条不涉及:
      在使用AT24C02时(假设主控制芯片是单片机),单片主机,AT24C02是自机,如果一台单片机将数据写入AT24C02,一个单一的卡片机就是传送器,AT24C02是一个接收器,反之,如果一台单片机读取AT24C02的数据,一个单一的卡片机就是接收机,AT24C02是一个发射器。

      I2C启动及终止条件:
      起始信号当SCL是高压时,SDA从高压变为低压。
      停止信号当SCL是高电压时,SDA从低电压转向高电压。
      我们可以通过“启动信号沿着信号下,停止信号沿着信号上”来帮助记忆。

      要点

      1.启动信号和停止信号都由主机发送。
      2.公共汽车在起始状态之前处于闲置状态(此时SDA和SCL都是高压等价),在进入起始状态后,公共汽车处于繁忙状态。
      3.只要主机不发送停止信号,公共汽车将保持繁忙。

      I2C传输数据(单字)
      字节格式
      每个发送到SDA线的字节必须为8位,每个传输可以发送的字节数是无限的。每个节点必须遵循响应位置。首先,数据在最高位(MSB)传输,如果机器在完成其他功能后无法接收或发送下一个完整的数据字符串(例如内部中断服务程序),它能保持Clock Line SCL低压水平,迫使主机进入等待状态,当机器准备好接下数据字符串并释放时钟线SCL后,数据传输继续进行。
      应答响应
      数据传输必须有一个响应,并由主机生成相应的响应时钟脉冲。 发送器在响应时钟脉冲期间释放SDA线(高)。
      在响应时钟脉冲期间,接收器必须拉下SDA线,以保持高压时钟脉冲期间的稳定低压。
      .
      ——百度百科

      下图左边1至8个钟周期发送1个字符地址数据(搜索+阅读/写),在第九个钟表周期中从机器上拉下SDA响应信号),继续传输第二个字符串。 如果在等待了很长一段时间后,SDA没有降低,这表明服务器很忙或无法与服务器通信,那么服务器需要发送停止信号结束通信(或发送起始信号重新建立通信)。

      第二个字符串可以由主机写,也可以由主机读(由第一个字符串的最后一个决定,详见下面)。

      如果第二个节点是主机写数据然后,当第二个字符转移完成并到达右边的第九个时钟周期时,如果接收器发送一个响应信号,表明字符转移成功,则主机可以选择发送另一个起始信号(也称为Sr信号的重新启动)继续传输数据;它也可以发送停止信号,此通信是关闭的。 如果在等待了很长时间后SDA没有降低,这表明服务器很忙或无法与服务器进行通信,那么服务器需要发送停止信号结束通信(或发送起始信号重新建立通信)。

      如果第二个节点是主机读数据然后,当第二个字符转移完成并到达右边的第九个时钟周期时,主机可以发送非响应信号(保持SDA高)通知发送者数据传输的结束,让发送者释放数据线,并允许主机生成停止信号重新起始信号.在某些情况下(例如连续阅读),主机可以发送响应信号,继续数据传输。

      数据位发送:
      在I2C总线上传输的每个数据都有时钟脉冲相对(或同步控制),即,在SCL串行时钟的同步下,每个数据在SDA上顺序传输。进行数据传送时,在SCL高压时,SDA的电压平衡必须稳定,低电平为数据0,高电平为数据1。只有在SCL水平较低时,SDA上的水平才会被允许改变逻辑0具有低压,逻辑1具有高压。

      I2C总线时序

      参见AT24CXX手册下表所示4.5V~5.5V参数

      AT24CXX时间表:


      AT24C02特别时间序列:
      因为不同的I2C设备都有自己的特性,有时只有使用标准I2C协议才能完成芯片的特定功能。比如温湿度传感器在每次发送读取温湿度数据的指令后还需要等待传感器处理完数据才能成功读取,AT24C02也有类似的要求,在等待写字周期后将数据写入AT24C02。

      此外,AT24C02还具有一个调用时间序列:


      I2C设备地址(7位):
      AT24C02设备地址:

      下面是AT24CXX的设备地址(第一行是AT24C02,它容量为2K),我们发现AT24CXX系列芯片的地址是所有四个高位,1010的相同,这些都是由制造商内置的,不能更改。

      AT24C02的下三个地址(不包括读写位数)与芯片的三个参数相符,即这些地址可以由用户设置,23=8,因此I2C总线可以载有8个AT24C02。

      AT24C02的地址是7位元二进制,下图的最后一个是读写(数据方向),1代表读取数据,0代表写入数据。

      因此,7位设备地址加上1位读写位构成I2C地址数据。 在I2C总线路由过程中,通常在初始条件之后的第一个字符决定了主机选择的主机,而该字符的最后一个字符决定了数据传输的方向。

      读写AT24C02:
      AT24C02的存储空间为2K位(256字节),在对其进行写数据时,最小写入单位为字节(Byte),最大写入单位为页(Page),AT24C02页大小为 16 Byte。

      字节写:
      在字节写模式下,主设备发送初始信号,从设备地址信息(R/W位置零)到设备地址信息,从设备发送响应信号后,主设备发送AT24WC01/02/04/08/16字节地址,从主设备接收响应信号后,然后将数据发送到所指定的存储单元.AT24WC01/02/04/08/16再答覆,在主设备产生停止信号后,开始编写内部数据,在内部擦写过程中,AT24WC01/02/04/08/16不再响应任何来自主机设备的请求。
      .
      -AT24CXX中文手册

      页写:
      用页写,AT24WC01可以同时写8字节数据。AT24WC02/04/08/16数据可以同时写成16字节,页面编写操作的开始与节点编写相同,区别在于,在传输一个字节的数据后,不会产生停止信号,主设备可以发送P(AT24WC01 P=7; AT24WC02/04/08/16 P=15)附加字符。AT24WC01/02/04/08/16在每个消息后生成一个响应位数,并将一个较低位数添加到消息地址1,高位保持不变。
      .
      如果主设备在发送停止信号之前发送超过P+1字节,地址计数器将自动翻转,并覆盖先前写的数据。
      .
      接收P+1位元数据和主设备发送的停止信号后,AT24CXX启动内部写字周期,将数据写入数据区域,所有接收的数据在一次写字周期内被写入AT24WC01/02/04/08/16。
      .
      -AT24CXX中文手册

      当前地址读:
      AT24WC01/02/04/08/16的地址计数器内容添加1到最后的操作字符串的地址。也就是说,如果最后的读写操作地址是N,立即读取的地址由地址N+1开始。如果N=E(for 24WC01 E=127; for 24WC02 E=255; for 24WC04 E=511; for 24WC08 E=1023; for 24WC16 E=2047),计数器转为0并继续输出数据。AT24WC01/02/04/08/16从设备地址(R/W位置1)接收信号后,它首先发送一个响应信号,然后发送8位元数据。主设备不需要发送响应信号,但必须生成一个停止信号。
      .
      -AT24CXX中文手册

      选择读(随机读):
      可选的读取操作允许主机设备读取注册表的任何节点,主要设备首先通过发送设备地址和它想要读取的字节数据的地址发送启动信号来进行伪造操作。AT24WC01/02/04/08/16答复后,主要设备重新发送初始信号和设备地址,此时R/W位置1,AT24WC01/02/04/08/16响应并发送响应信号,然后输出所需的8位数据,主设备不发送响应信号,而是产生停止信号。
      .
      -AT24CXX中文手册

      连续读:
      连续阅读操作可以通过立即阅读或选择性阅读操作开始.AT24WC01/02/04/08/16在完成8位数据字节后,主要设备产生响应信号来响应,通知AT24WC01/02/04/08/16主要设备需要更多数据,每个主机生成的响应信号AT24WC01/02/04/08/16将发送8位数据字节。当主设备不发送响应信号而发送停止位置时,该操作终止。
      .
      从 AT24WC01/02/04/08/16 输出的数据按顺序由 N 到 N+1 输出。整套AT24WC01/02/04/08/16地址增加只读地址计数器,这样整个登记区域在可读操作中读出.当读取字段超过E(24WC01 E=127; 24WC02 E=255; 24WC04 E=511; 24WC08 E=1023; 24WC16 E=2047)时,计数器将变为零并继续输出数据字段。
      .
      -AT24CXX中文手册

      51单片控制器AT24C02
      I2C协议代码:
      下面的代码仅供参考,属于我的实际代码,不能保证没有错误

      大多数51个芯片组没有独立的I2C接口,因此在这里,I2C协议使用纯软件代码实现。

      51单片式AT89C52RC

      延时函数:
      延迟函数是模拟I2C协议的关键。由于I2C的延迟非常短,延迟代码直接用空句子实现:

      /******************************************************************************
      *@功能名称:Delay_10us
      * @ 功  能  : 10us粗略延时
      *@包括:延迟时间-单位10us
      * @ 返回值:不
      ******************************************************************************/
      void Delay_10us(unsigned int time)
      {
      while(time--);
      }

      /******************************************************************************
      *@功能名称:I2c_Delay
      *@电源:I2C延迟功能
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Delay()
      {
      Delay_10us(1);
      }


      1.启动信号,代码:

      /******************************************************************************
      *@功能名称:I2c_Start
      *@电源:I2C启动信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Start()
      {
      sda = 1;
      scl = 1;
      I2c_Delay(); //初始信号构建时间
      sda = 0; //SDA拖下来,沿着下降
      I2c_Delay(); // 启动信号保持时间
      scl = 0;
      }

      2.停止信号,代码:

      /******************************************************************************
      *@功能名称:I2c_Stop
      *@功率:I2C停止信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Stop()
      {
      scl = 0;
      I2c_Delay(); // 前时钟周期的低电压水平
      sda = 0;
      scl = 1;
      I2c_Delay(); // 停止信号建立时间
      sda = 1; //SDA上升,沿着上升
      I2c_Delay(); // 保持公共汽车的空闲时间
      }

      3.响应信号,代码:

      /******************************************************************************
      *@功能名称:I2c_Ack
      *@功率:I2C响应信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Ack()
      {
      scl = 0;
      sda = 0; //SDA拉下并发送响应信号
      I2c_Delay();
      scl = 1;
      I2c_Delay();
      scl = 0;
      }


      4.非响应信号,代码:

      /******************************************************************************
      *@功能名称:I2c_No_Ack
      *@功率: I2C 非 响应信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_No_Ack()
      {
      scl = 0;
      sda = 1; //SDA提高,发出非响应信号
      I2c_Delay();
      scl = 1;
      I2c_Delay();
      scl = 0;
      }


      5.等待响应信号,代码:

      /******************************************************************************
      *@功能名称:I2c_Wait_Ack
      *@电力:I2C等待响应信号
      *@出席者:不
      * @返回值:1:接收响应信号,0:接收非响应信号
      ******************************************************************************/
      unsigned char I2c_Wait_Ack()
      {
      unsigned char ack = 0;
      sda = 1;
      scl = 0;
      I2c_Delay();
      scl = 1;
      I2c_Delay();
      if(sda == 0) // 检测数据线SDA是否拉下来
      ack = 1;
      else
      ack = 0;
      scl = 0;
      return ack;
      }


      6.I2C编写节点,代码:

      /******************************************************************************
      *@功能名称:I2c_Write_Byte
      *@电源:I2C写
      * @ 包括: dat 要写入的字节数据
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Write_Byte(unsigned char dat)
      {
      unsigned char i = 0;


      for(i = 0; i < 8; i++) //读取8位
      {
      scl = 0;
      I2c_Delay();
      if(dat & 0x80)  //发送最高位
      sda = 1;
      else
      sda = 0;
      scl = 1;
      I2c_Delay();
      dat <<= 1;  //左移1位
      }
      scl = 0;
      }


      7.I2C读字节,代码:

      /******************************************************************************
      *@功能名称:I2c_Read_Byte
      *@电源:I2C可读
      *@出席者:不
      * @返回值:读字节数据
      ******************************************************************************/
      unsigned char I2c_Read_Byte()
      {
      unsigned char dat = 0, i = 0;

      for(i = 0; i < 8; i++) //读取8位
      {
      dat <<= 1;   //左移1位
      scl = 0;
      I2c_Delay();
      scl = 1; //SCL高压平原
      I2c_Delay();
      if(sda) // 读SDA状态
      dat |= 0x1;
      }
      scl = 0;
      return dat;
      }


      8.AT24C02写字,代码:
      设备地址为0xA0,表示AT24C02的三个地址都已停用(或停用),而七位地址为10100。

      /******************************************************************************
      *@功能名称:At24c02_Write
      *@电源:AT24C02写
      * @ 包括:
      *              addr 要写数据的地址(存储空间)
      *              dat  要写入的字节数据
      * @ 返回值:不
      ******************************************************************************/
      void At24c02_Write(unsigned char addr, unsigned char dat)
      {
      I2c_Start();
      I2c_Write_Byte(0xA0); // 发送I2C设备地址
      I2c_Wait_Ack(); // 等待机器响应
      I2c_Write_Byte(addr); // 发送写入的内存地址
      I2c_Wait_Ack();
      I2c_Write_Byte(dat); // 写数据
      I2c_Wait_Ack();
      I2c_Stop();
      Delay_10us(100); // 写周期
      }


      8.AT24C02读字节,代码:
      设备地址为0xA0,表示AT24C02的三个地址都已停用(或停用),而七位地址为10100。读数据时,读写位为1,所以8位的I2C地址(设备地址+读写位)为10100001,即0xA1。

      /******************************************************************************
      *@功能名称:At24c02_Read
      *@电源:AT24C02读字节
      * @ 包括: addr 要读数据的地址(存储空间)
      * @返回值:读字节数据
      ******************************************************************************/
      unsigned char At24c02_Read(unsigned char addr)
      {
      unsigned char dat = 0, i = 0;
      I2c_Start();
      I2c_Write_Byte(0xA0); // 发送I2C设备地址
      I2c_Wait_Ack(); // 等待机器响应
      I2c_Write_Byte(addr); // 发送写入的内存地址
      I2c_Wait_Ack();
      I2c_Start();
      I2c_Write_Byte(0xA1); // 发送I2C设备地址(读取数据)
      I2c_Wait_Ack(); // 等待机器响应
      data = I2c_Read_Byte(); // read data
      I2c_Wait_Ack();
      I2c_Stop();
      return dat;
      }


      还有其他AT24C02代码(页面编写、连续阅读),我在这里不编写。

      普羅特斯實驗
      这里是一个模拟上述代码的小型实验,最后请参阅完整的工程代码

      实现功能:先向 AT24C02 的 0 ~ 7 号地址空间写入 8 ~ 1,再向 AT24C02 的 0 ~ 7 号地址空间写入 1 ~ 8。写入后立刻从 AT24C02 中读出,同时显示在数码管上。

      Proteus仿真

      24C02的A0、A1、A2都是基底的,所以它的I2C设备地址是10100,写地址是10100(0xA0),读地址是101001(0xA1):

      完整代码:

      #include <reg52.h>   //此文件中定义了单片机的一些特殊功能寄存器

      sbit scl = P2^1;
      sbit sda = P2^0;
      unsigned char code coding[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
      0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0-F值

      #define LED_TUBE  P3 //P3的8个IO端口对应数码管的8个信号引脚
      /******************************************************************************
      *@功能名称:Delay_10us
      * @ 功  能  : 10us粗略延时
      *@包括:延迟时间-单位10us
      * @ 返回值:不
      ******************************************************************************/
      void Delay_10us(unsigned int time)
      {
      while(time--);
      }

      /******************************************************************************
      *@功能名称:I2c_Delay
      *@电源:I2C延迟功能
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Delay()
      {
      Delay_10us(1);
      }

      /******************************************************************************
      *@功能名称:I2c_Start
      *@电源:I2C启动信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Start()
      {
      sda = 1;
      scl = 1;
      I2c_Delay(); //初始信号构建时间
      sda = 0; //SDA拖下来,沿着下降
      I2c_Delay(); // 启动信号保持时间
      scl = 0;
      }

      /******************************************************************************
      *@功能名称:I2c_Stop
      *@功率:I2C停止信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Stop()
      {
      scl = 0;
      I2c_Delay(); // 前时钟周期的低电压水平
      sda = 0;
      scl = 1;
      I2c_Delay(); // 停止信号建立时间
      sda = 1; //SDA上升,沿着上升
      I2c_Delay(); // 保持公共汽车的空闲时间
      }
      /******************************************************************************
      *@功能名称:I2c_Ack
      *@功率:I2C响应信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Ack()
      {
      scl = 0;
      sda = 0; //SDA拉下并发送响应信号
      I2c_Delay();
      scl = 1;
      I2c_Delay();
      scl = 0;
      }
      /******************************************************************************
      *@功能名称:I2c_No_Ack
      *@功率: I2C 非 响应信号
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/
      void I2c_No_Ack()
      {
      scl = 0;
      sda = 1; //SDA提高,发出非响应信号
      I2c_Delay();
      scl = 1;
      I2c_Delay();
      scl = 0;
      }

      /******************************************************************************
      *@功能名称:I2c_Wait_Ack
      *@电力:I2C等待响应信号
      *@出席者:不
      * @返回值:1:接收响应信号,0:接收非响应信号
      ******************************************************************************/
      unsigned char I2c_Wait_Ack()
      {
      unsigned char ack = 0;
      sda = 1;
      scl = 0;
      I2c_Delay();
      scl = 1;
      I2c_Delay();
      if(sda == 0) // 检测数据线SDA是否拉下来
      ack = 1;
      else
      ack = 0;
      scl = 0;
      return ack;
      }

      /******************************************************************************
      *@功能名称:I2c_Write_Byte
      *@电源:I2C写
      * @ 包括: dat 要写入的字节数据
      * @ 返回值:不
      ******************************************************************************/
      void I2c_Write_Byte(unsigned char dat)
      {
      unsigned char i = 0;
      for(i = 0; i < 8; i++) //读取8位
      {
      scl = 0;
      I2c_Delay();
      if(dat & 0x80)  //发送最高位
      sda = 1;
      else
      sda = 0;
      scl = 1;
      I2c_Delay();
      dat <<= 1;  //左移1位
      }
      scl = 0;
      }
      /******************************************************************************
      *@功能名称:I2c_Read_Byte
      *@电源:I2C可读
      *@出席者:不
      * @返回值:读字节数据
      ******************************************************************************/
      unsigned char I2c_Read_Byte()
      {
      unsigned char dat = 0, i = 0;
      for(i = 0; i < 8; i++) //读取8位
      {
      dat <<= 1;   //左移1位
      scl = 0;
      I2c_Delay();
      scl = 1; //SCL高压平原
      I2c_Delay();
      if(sda) // 读SDA状态
      dat |= 0x1;
      }
      scl = 0;
      return dat;
      }

      /******************************************************************************
      *@功能名称:At24c02_Write
      *@电源:AT24C02写
      * @ 包括:
      *              addr 要写数据的地址(存储空间)
      *              dat  要写入的字节数据
      * @ 返回值:不
      ******************************************************************************/
      void At24c02_Write(unsigned char addr, unsigned char dat)
      {
      I2c_Start();
      I2c_Write_Byte(0xA0); // 发送I2C设备地址
      I2c_Wait_Ack(); // 等待机器响应
      I2c_Write_Byte(addr); // 发送写入的内存地址
      I2c_Wait_Ack();
      I2c_Write_Byte(dat); // 写数据
      I2c_Wait_Ack();
      I2c_Stop();
      Delay_10us(100); // 写周期
      }


      /******************************************************************************
      *@功能名称:At24c02_Read
      *@电源:AT24C02读字节
      * @ 包括: addr 要读数据的地址(存储空间)
      * @返回值:读字节数据
      ******************************************************************************/
      unsigned char At24c02_Read(unsigned char addr)
      {
      unsigned char dat = 0, i = 0;
      I2c_Start();
      I2c_Write_Byte(0xA0); // 发送I2C设备地址
      I2c_Wait_Ack(); // 等待机器响应
      I2c_Write_Byte(addr); // 发送写入的内存地址
      I2c_Wait_Ack();
      I2c_Start();
      I2c_Write_Byte(0xA1); // 发送I2C设备地址(读取数据)
      I2c_Wait_Ack(); // 等待机器响应
      data = I2c_Read_Byte(); // read data
      I2c_Wait_Ack();
      I2c_Stop();
      return dat;
      }
      /******************************************************************************
      *@功能名称:主
      *@功率:主功能
      *@出席者:不
      * @ 返回值:不
      ******************************************************************************/

      int main()
      {
      unsigned char i = 0;
      Delay_10us(100); //AT24C02上时序
      while(1)
      {
      for(i = 0; i < 8; i++)
      {
      //在AT24C02之前写8-i
      At24c02_Write(i, 8 - i);
      //阅读地址数据,并在编码器中显示
      LED_TUBE = coding[At24c02_Read(i)];
      //约500ms延迟
      Delay_10us(50000);
      }
      for(i = 0; i < 8; i++)
      {
      //在AT24C02之前写i+1到8字节空间
      At24c02_Write(i, i + 1);
      //阅读地址数据,并在编码器中显示
      LED_TUBE = coding[At24c02_Read(i)];
      //约500ms延迟
      Delay_10us(50000);
      }
      }
      }

      本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/31185.html

          热门文章

          文章分类