I2C通信之EEPROM

发布者:乐呵的挑最新更新时间:2018-07-15 来源: eefocus关键字:I2C通信  EEPROM 手机看文章 扫描二维码
随时随地手机看文章

1 EEPROM及其背景知识

1.1 EEPROM

(1)一些概念:ROM、RAM、PROM(可编程ROM)、EPROM(可擦除ROM)、EEPROM(电可擦除ROM)。

(2)为什么需要EEPROM(单片机内部的ROM只能在程序下载时进行擦除和改写,但是程序运行本身是不能改写的。单片机内部的RAM中的数据程序运行时可以改,但是掉电就丢失了。有时候我们有一些数据要存在系统中,要求掉电不丢失,而且程序还要能改。所以内部ROM和RAM都不行。这时候系统中就需要一块EEPROM)。

(3)EEPROM(按照功能命名)和flash(按照工艺进行命名)的区别与联系,EEPROM可能是用flash工艺做出来的(flash可以做成EEPROM)。

(4)EEPROM存在系统中的2种形式:内置在单片机内部,外部扩展。

1.2 EEPROM如何编程

(1)I2C接口底层时序。

(2)器件定义的寄存器读写时序。

2 原理图和数据手册

2.1 原理图和接线确定

这里写图片描述

(1)关键性引脚定义及其接法(SCL对应P2.1,SDA对应P2.0)。

(2)接线。

2.2 数据手册浏览

(1)芯片基本信息。

(2)I2C从地址确定。

(3)I2C低层时序:起始信号、停止信号、发送字节、读取字节。

2.3 I2C总结

(1)主CPU和其附属芯片之间最常用的接口,尤其是各种传感器,因此在物联网时代非常重要。

(2)三根线:GND、SCL、SDA,串行、电平式。

(3)总线式结构,可以一对多,总线上可以挂上百个器件,用从地址来区分。

(4)主从式,由主设备来发起通信及总线仲裁,从设备被动响应。

(5)通信速率一般(kbps级别),不适合语音、视频等信息类型。

3 I2C低层时序图和程序

3.1 起始信号和结束信号

(1)起始信号:SCL保持高时,SDA有一个从高到低(下降沿)。

(2)结束信号:SCL保持高时,SDA有一个从低到高(上升沿)。

这里写图片描述

3.2 I2C发送一个字节

(1)I2C发送和接收字节时,都是从高位开始的。

这里写图片描述

3.3 应答位处理

这里写图片描述

3.4 I2C接收一个字节

概念:释放总线。在51单片机中,SDA=1就是释放总线;在其他更高级的单片机(譬如STM32等)这里的处理还会有点不一样。为什么SDA=1就是释放总线,是因为当51单片机把引脚拉高时,从设备可以选择再把这个引脚拉高或者拉低;但是当51单片机把这个引脚拉低(接地)后,从设备再也没办法把这个引脚拉高了。

4 EEPROM读写测试

4.1 24C02读写高层时序

(1)从器件的地址是由从器件自身定义的,不同的从器件的地址定义方式是不同的,要查具体的芯片数据手册来确定。 
(2)同一个I2C网络中只有一个主设备,但是从设备可以有多个。这多个从设备的从地址不能相同(硬件设计工程师必须保证这一点。因为从地址是不能通过软件设定的)。 
(3)通过分析原理图和24C02的地址定义,可以得出: 
从设备地址是:读地址:0xa1,写地址:0xa0

4.2 写时序

这里写图片描述

这里写图片描述

这里写图片描述

4.3 读时序

这里写图片描述

这里写图片描述

这里写图片描述

4.4 工程建立与文件导入

4.5 加入串口输出代码

4.6 测试EEPROM

4.7 程序问题解决

(1)通过调试发现程序跑飞了,经过检查发现uart中没有关中断。

(2)读出内容不对,怀疑是EEPROM经不起快速的连续读写,所以在读和写之间加入20ms的delay,测试后发现读写正确了。

(3)定义了局部变量没有初始化,程序中直接去通过串口输出,结果导致程序出现错误。

自己写代码中出现的问题:1.引脚定义错误。2.发送数据和接收数据时移位运算出错。

4.8 代码

i2c.h

#ifndef __I2C_H_

#define __I2C_H_


#include


//---重定义关键词---//

#ifndef uchar

#define uchar unsigned char

#endif


#ifndef uint 

#define uint unsigned int

#endif


//--定义使用的IO口--//

sbit I2C_SCL = P2^1;

sbit I2C_SDA = P2^0;


//--声明全局变量--//

void I2C_Delay10us();

void I2C_Start();

void I2C_Stop();

uchar I2C_SendByte(uchar dat, uchar ack);

uchar I2C_ReadByte();


#endif


i2c.c


#include "i2c.h"


/*******************************************************************************

* 函 数 名         : Delay1us()

* 函数功能         : 延时

* 输    入         : 无

* 输    出         : 无

*******************************************************************************/


void I2C_Delay10us()

{

    uchar a, b;

    for(b=1; b>0; b--)

    {

        for(a=2; a>0; a--);

    }

}

/*******************************************************************************

* 函 数 名         : I2C_Start()

* 函数功能         : 起始信号:在I2C_SCL时钟信号在高电平期间I2C_SDA信号产生一个下降沿

* 输    入         : 无

* 输    出         : 无

* 备    注         : 起始之后I2C_SDA和I2C_SCL都为0

*******************************************************************************/


void I2C_Start()

{

    I2C_SDA = 1;

    I2C_Delay10us();

    I2C_SCL = 1;

    I2C_Delay10us();//建立时间是I2C_SDA保持时间>4.7us

    I2C_SDA = 0;

    I2C_Delay10us();//保持时间是>4us

    I2C_SCL = 0;            

    I2C_Delay10us();        

}

/*******************************************************************************

* 函 数 名           : I2C_Stop()

* 函数功能           : 终止信号:在I2C_SCL时钟信号高电平期间I2C_SDA信号产生一个上升沿

* 输    入           : 无

* 输    出             : 无

* 备    注           : 结束之后保持I2C_SDA和I2C_SCL都为1;表示总线空闲

*******************************************************************************/


void I2C_Stop()

{

    I2C_SDA = 0;

    I2C_Delay10us();

    I2C_SCL = 1;

    I2C_Delay10us();//建立时间大于4.7us

    I2C_SDA = 1;

    I2C_Delay10us();    


}

/*******************************************************************************

* 函 数 名           : I2cSendByte(uchar num)

* 函数功能           : 通过I2C发送一个字节。在I2C_SCL时钟信号高电平期间,

*                    * 保持发送信号I2C_SDA保持稳定

* 输    入           : num ,ack

* 输    出             : 0或1。发送成功返回1,发送失败返回0

* 备    注           : 发送完一个字节I2C_SCL=0, 需要应答则应答设置为1,否则为0

*******************************************************************************/


uchar I2C_SendByte(uchar dat, uchar ack)

{

    uchar a = 0,b = 0;//最大255,一个机器周期为1us,最大延时255us。

    // 为了保证时序正确,这里应该加一句 SCL = 0;        

    for(a=0; a<8; a++)//要发送8位,从最高位开始

    {

        I2C_SDA = dat >> 7;  //起始信号之后I2C_SCL=0,所以可以直接改变I2C_SDA信号

        dat = dat << 1;

        I2C_Delay10us();

        I2C_SCL = 1;

        I2C_Delay10us();//建立时间>4.7us

        I2C_SCL = 0;

        I2C_Delay10us();//时间大于4us       

    }


    I2C_SDA = 1;            // 主设备释放SDA线给从设备去操作

    I2C_Delay10us();

    I2C_SCL = 1;            // 主设备开始了第9个周期

    while(I2C_SDA && (ack == 1))//等待应答,也就是等待从设备把I2C_SDA拉低

    {

        b++;

        if(b > 200)  //如果超过200us没有应答发送失败,或者为非应答,表示接收结束

        {

            I2C_SCL = 0;

            I2C_Delay10us();

            return 0;

        }

    }


    I2C_SCL = 0;

    I2C_Delay10us();

    return 1;       

}

/*******************************************************************************

* 函 数 名           : I2cReadByte()

* 函数功能           : 使用I2c读取一个字节

* 输    入           : 无

* 输    出             : dat

* 备    注           : 接收完一个字节I2C_SCL=0

*******************************************************************************/


uchar I2C_ReadByte()

{

    uchar a = 0,dat = 0;

    I2C_SDA = 1;            //起始和发送一个字节之后I2C_SCL都是0

    I2C_Delay10us();

    // 按道理这里应该有一个SCL = 0的

    for(a=0; a<8; a++)//接收8个字节

    {

        I2C_SCL = 1;        // 通知从设备我要开始读了,可以放1bit数据到SDA了

        I2C_Delay10us();

        dat <<= 1;          // 读取的时候是高位在前的

        dat |= I2C_SDA;

        I2C_Delay10us();

        I2C_SCL = 0;        // 拉低,为下一个bit的周期做准备

        I2C_Delay10us();

    }

    return dat;     

}


at24c02.h


#ifndef __AT24C02_H__

#define __AT24C02_H__







void At24c02Write(unsigned char addr,unsigned char dat);

unsigned char At24c02Read(unsigned char addr);





#endif


at24c02.c


#include "at24c02.h"

#include "i2c.h"


/*******************************************************************************

* 函 数 名         : void At24c02Write(unsigned char addr,unsigned char dat)

* 函数功能         : 往24c02的一个地址写入一个数据

* 输    入         : 无

* 输    出         : 无

*******************************************************************************/


void At24c02Write(unsigned char addr,unsigned char dat)

{

    I2C_Start();

    I2C_SendByte(0xa0, 1);//发送写器件地址

    I2C_SendByte(addr, 1);//发送要写入内存地址

    I2C_SendByte(dat, 0);   //发送数据

    I2C_Stop();

}

/*******************************************************************************

* 函 数 名         : unsigned char At24c02Read(unsigned char addr)

* 函数功能         : 读取24c02的一个地址的一个数据

* 输    入         : 无

* 输    出         : 无

*******************************************************************************/


unsigned char At24c02Read(unsigned char addr)

{

    unsigned char num;

    I2C_Start();

    I2C_SendByte(0xa0, 1); //发送写器件地址

    I2C_SendByte(addr, 1); //发送要读取的地址

    I2C_Start();

    I2C_SendByte(0xa1, 1); //发送读器件地址

    num=I2C_ReadByte(); //读取数据

    I2C_Stop();

    return num; 

}


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

main.c


#include

#include "at24c02.h"

#include "uart.h"




void delay20ms(void)   //误差 -0.000000000005us

{

    unsigned char a,b,c;

    for(c=1;c>0;c--)

        for(b=222;b>0;b--)

            for(a=40;a>0;a--);

}


/*******************************************************************************

* 函 数 名         : main

* 函数功能         : 主函数

* 输    入         : 无

* 输    出         : 无

*******************************************************************************/


void main()

{

    unsigned char i;

    unsigned char addr; 

    unsigned char src_data[] = "()ab#cde!fg1234567";    

    unsigned char buf[8] = "ABCDEFGH";


    uart_init();

/*

    for (i=0; i<128; i++)

    {

        uart_send_byte(i);

    }

    while (1);

*/

    // 先随便找一堆数据,譬如"abcdefg1234567-_-*&%@/\"

    // 把这些写入EEPROM的特定地址中

    // 然后读EEROM的这些地址,读出后通过串口打印出来看是不是我们写入的


    uart_send_byte('%');


    addr = 0;

    for (i=0; i<8; i++)

    {

        At24c02Write(addr, src_data[i]);

        delay20ms();

        addr++;

    }


    for (i=0; i<8; i++)

    {

        uart_send_byte(buf[i]);

    }



    for (i=0; i<20; i++)

    {

        uart_send_byte('-');

    }


    // 读出测试

    addr = 0;

    for (i=0; i<8; i++)

    {

        buf[i] = At24c02Read(addr);

        delay20ms();

        addr++;

    }


    for (i=0; i<8; i++)

    {

        uart_send_byte(buf[i]);

    }


    while (1);


    // 进一步测试

    // 先写入一些特定内容,然后关机断电;然后改代码为读出并打印显示看内容


}           


关键字:I2C通信  EEPROM 引用地址:I2C通信之EEPROM

上一篇:I2C总线EEPROM实现
下一篇:标志寄存器及其标志位

推荐阅读最新更新时间:2024-03-16 16:08

STC89C52单片机内部EEPROM程序
实验外部存储期间不方便 看了内部有2K的数据 代码如下 .C文件可直接移植 #include STC89C5xRC.H #include intrins.h #define Read 0x01 //读指令 #define Write 0x02 //写指令 #define Erase 0x03 //擦除指令 #define ENABLE_IAP 0x81 //速度20Mhz //操作完成一次之后 进入待机模式 等待下一次命令 无ISP操作 void ISP_IAP_disable(v
[单片机]
STC89C52单片机内部<font color='red'>EEPROM</font>程序
适合工业应用的鲁棒SPI/I2C通信
Robust SPI/I2C Communications for Industrial Applications 适合工业应用的鲁棒SPI/I2C通信 评估和设计支持 电路评估板 CN0564参考设计板(EVAL-CN0564-ARDZ) 设计和集成文件 原理图、布局文件、物料清单 电路功能与优势 对于控制器和外设之间的短距离电路板内连接,串行外设接口(SPI)和Inter-Integrated Circuit (I2C)接口是流行的事实上的通信标准。由于存在广泛的硬件和软件支持,SPI和I2C已被传感器、执行器和数据转换器制造商广泛采用。当控制器和外设位于同一电路板上、共享同一接地层且相距
[电源管理]
适合工业应用的鲁棒SPI/<font color='red'>I2C</font><font color='red'>通信</font>
I2C总线通信接口的CPLD实现
    摘要: 介绍采用ALTERA公司的可编程器件,实现I2C总线的通信接口的基本原理;给出部分VHDL语言描述。该通信接口号专用的接口芯片相比,具有使用灵活,系统配置方便的特点。     关键词: I2C总线 CPLD VHDL I2C总线是PHILIPS公司开发的一种简单、双向二线制同步串行总线。它只需要两根线(串行时钟线和串行数据线)即可在连接于总线上的器件之间传送信息。该总线是具备多主机系统所需要的包括裁决和高低速设备同步等功能的高性能串行总线,应用极为广泛。 目前,虽然市场上有专用I2C总线接口芯片,但是地址可选范围小,性能指标固定,功能单一,而且使用不方便。针对I2C总线的电气特性及其通信协
[半导体设计/制造]
模拟I2C总线多主通信研究与软件设计
摘要:介绍模拟I2C总线的多主节点通信原理,并提出一种新的实现方法。这种采用延时接收比较来实现仲裁的方法,可使不具有I2C接口的普通微控制器(MCU)能够实现模拟I2C总线的多主通信,同时对I2C总线的推广起到了积极作用。 关键词:模拟I2C总线 仲裁 多主通信   I2C总线(Inter IC BUS)是Philips公司推出的双向两线串行通信标准。由于它具有接口少、通信效率高等优点,现已得到广泛的应用\ 。它除了可以进行简单的单主节点通信外,还可以应用在多主节点的通信系统中。在多主节点通信系统中,如果两个或者更多的主节点同时启动数据传输,总线具有冲突检测和仲裁功能,保证通信正常进行并防止数据破坏。现在许多微控制器(MCU)都
[网络通信]
EEPROM IIC
1. 数据位的有效性规定 I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化 2. 起始和终止信号 SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号 起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态 接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好
[单片机]
<font color='red'>EEPROM</font> IIC
STM32单片机内部EEPROM的读写
STM32L系列单片机内部提供了EEPROM存储区域,但实质上,其FLASH也是EEPROM类型,只不过有一块区域被开放出来专门用作EEPROM操作而已。STM32L的EEPROM使用寿命设计为100000次擦写以上,容量为2K-4K,这对于一般设备的参数存储来说是非常理想的。但从EEPROM使用方式看,其不适用于被反复修改的数据存储使用,一般作为配置参数,其修改次数往往是比较少量的。 STM32L的EEPROM和FLASH是统一编址,操作共用同一个读写电路,所以在EEPROM读写的时候STM32L核对于FLASH的一切访问和操作都将暂停,只有当EEPROM的操作完成后,才继续执行后续代码,在这期间只有EEPROM的读写电路工
[单片机]
新唐M051 ISP(LDROM、FMC,相当于8位机EEPROM操作)程序
发一个M051单片机的 ISP操作程序(LDROM、FMC,相当于8位机EEPROM操作) #include "SmartM_M0.h" #define DEBUGMSG printf #define PAGE_SIZE 512 #define DATAFLASH_START_ADDRESS 0x0001F000 #define DATAFLASH_SIZE 0x00001000 #define RW_SIZE 0x20 STATIC UINT8 g_unDataFlashWRBuf ; //全局读写缓冲区 /*************************************
[单片机]
MSP430F5438学习笔记 FLAHS操作
1.前言 MSP430F5438的片内FLASH可以当做EEPROM使用,该部分FLASH称为INFO FLASH,总共有4块每块128Byte。虽然INFO FLASH容量比较少,但是多数情况还是够用的。 2.代码实现 // 时钟默认情况 // FLL时钟 FLL选择 XT1 // 辅助时钟 ACLK选择 XT1 32768Hz // 主系统时钟 MCLK选择 DCOCLKDIV 8000000Hz // 子系统时钟 SMCLK选择 DCOCLKDIV 8000000Hz // UART时钟选择 ACLK // 低频波特率产生 9600-8-N-1 #include msp430.h #incl
[单片机]
MSP430F5438学习笔记 FLAHS操作
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved