STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)

文章目录
  • 前言
  • 一、硬件设计
    • 1.E2PROM存储器介绍
    • 2.开发板DAC硬件电路介绍
      • 2.1.主要特征
      • 2.2.I2C地址
      • 2.3.I2C数据传输
      • 2.4.ACK和NACK
      • 2.5.从机地址和R/W位
    • 3.STC8A8K64S4A12系列单片机I2C介绍
  • 二、软件设计
    • 1.I2C相关寄存器汇集
    • 2.寄存器解析
      • 2.1.I2C配置寄存器I2CCFG
      • 2.2.I2C主机控制寄存器I2CMSCR
      • 2.3.I2C主机辅助控制寄存器I2CMSAUX
      • 2.4.I2C主机状态寄存器I2CMSST
      • 2.5.I2C从机控制寄存器I2CSLCR
      • 2.6.I2C从机状态寄存器I2CSLST
      • 2.7.I2C从机地址寄存器I2CSLADR
    • 3.I2C配置步骤
    • 4.外部EEPROM存储器读写单字节实验(模拟I2C)
      • 4.1.工程需要用到的c文件
      • 4.2.头文件引用和路径设置
      • 4.3.编写代码
      • 4.4.硬件连接
    • 5.外部EEPROM存储器读写多字节实验(模拟I2C)
      • 5.1.编写代码
      • 5.2.硬件连接
    • 6.外部EEPROM存储器读写单字节实验(硬件I2C)
      • 6.1.工程需要用到的c文件
      • 6.2.头文件引用和路径设置
      • 6.3.编写代码
      • 6.4.硬件连接
    • 7.外部EEPROM存储器读写多字节实验(硬件I2C)
      • 7.1.编写代码
      • 7.2.硬件连接
  • 总结
前言

今天介绍下STC8A8K64S4A12系列单片机外部E2PROM存储器原理和I2C总线的原理及工作模式,掌握STC8A8K64S4A12系列单片机I2C外设相关的寄存器配置及程序设计。

一、硬件设计1.E2PROM存储器介绍

EEPROM(全称是Electrically Erasable Programmable Read-Only Memory)带电可擦除可编程只读存储器,该类存储器是用户可更改的只读存储器(ROM),其可通过高于普通电压的作用来擦除和重编程(重写)。EEPROM(常写成E2PROM)是一种特殊形式的闪存,其应用通常是个人电脑中的电压来擦写和重编程。

E2PROM存储器可分为片内E2PROM和片外E2PROM,片外E2PROM和单片机之间通过各种通信接口连接,而最为常见的通信接口即是I2C接口。一般情况下,E2PROM可写入或擦除的次数是30~100万次,而读取次数是没有限制的。

24C02芯片是一种常用的基于I2C通信协议的E2PROM元件,例如ATMEL公司的AT24C02、CATALYST公司的CAT24C02和ST公司的ST24C02等芯片。24C02芯片的存储空间是256字节(2048位),当然如果需要更大存储空间的存储器可以选择其他型号的,比如24C04芯片(512字节)、24C08芯片(1024字节)和24C16芯片(2048字节)。

24Cxx系列引脚定义是一致的,这方便用户在项目设计时,如遇到选择的芯片的存储空间不够时,可在该系列中选择存储空间更大的芯片直接替换,而无需改动硬件部分。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(1)

图1:24C02芯片引脚

STC8A8K64S4A12开发板上设计了可供用户使用的AT24C02存储器芯片,其中原理图部分及硬件实物部分如下。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(2)

图2:开发板外扩E2PROM存储器

2.开发板DAC硬件电路介绍

I2C总线(即IIC总线)是集成电路总线(Inter-Integrated Circuit)的缩写,是一种简单、双向二线制同步串行总线。该I2C总线是1982年由荷兰的Philips公司为了解决电视机内的CPU和外围芯片之间连接而开发的,可以说电视机是最早的嵌入式系统之一。

I2C总线是一个真正的多主机总线,如果两个或多个主机同时初始化数据传输,可以通过冲突检测和仲裁防止数据破坏,每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。

2.1.主要特征

典型的I2C应用原理如下图所示,I2C总线通信仅需两根信号线,可以连接多个设备,从设备都有唯一的地址,主设备通过从设备的地址和不同的从设备通信。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(3)

图3:典型I2C总线结构

  1. I2C总线硬件结构简单,仅需一根时钟线(SCL)、一根数据线(SDA)和两个上拉电阻即可实现通信。I2C总线的SCL和SDA均为开漏结构,开漏结构的电路只能输出“逻辑0”,无法输出“逻辑1”,因此SCL和SDA需要连接上拉电阻。上拉电阻的阻值影响传输速率,阻值越大,由于RC影响,会带来上升时间的增大,传输的速率慢,阻值小,传输的速率快,但是会增加电流的消耗,一般情况下,我们会选择4.7K左右的阻值,在从机数量少,信号线短的情况下,可以适当增加阻值,如使用10K的阻值。
  2. I2C总线中的从设备必须有自己的地址,并且该地址在其所处的I2C总线中唯一,主设备通过此唯一的地址即可和该从设备进行数据传输。
  3. I2C总线支持多主机,但是同一时刻只允许有一个主机。I2C总线中存在多个主机时,为了避免冲突,I2C总线通过总线仲裁决定由哪一个主机控制总线。
  4. I2C总线只能传输8位的数据,数据速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s,另外一些变种实现了低速模式(10Kbps)和快速 模式(1Mbps)。
  5. 同时连接到同一个I2C总线上的设备数量受总线最大电容(400pF)的限制。
  6. I2C总线电流消耗很低,抗干扰强,适合应用于低功耗的场合。
2.2.I2C地址

I2C总线中的设备必须要有唯一的地址,这意味着如果在总线中接入两个相同的设备,该设备必须有配置地址的功能,这也是我们经常用的I2C接口的设备会有几个引脚用来配置地址的原因。

对于I2C地址,我们经常看到有的I2C接口设备在规格书中描述的是7位地址,而有的I2C接口设备在规格书中描述的是8位地址,他们有什么区别?(I2C也有10位地址,但用的较少,这里不做介绍,本文档中的内容不涉及到10位地址)。

7位地址和8位地址如下图所示,他们结构上是一样的,都是由7个地址位加一个用来表示读写的位组成,只是描述上有所区别。

  • 规格书中描述I2C地址是7位地址的设备:给出的是7个地址位加R/W位,最低位(R/W位)为0时表示为写地址,最低位为1时为读地址。如果把0和1分别带入R/W位,得到的地址就和8位地址一样了。
  • 规格书中描述I2C地址是8位地址的设备:直接给出写地址和读地址,也就是最低位(R/W位)为0时的地址和最低位为1时的地址。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(4)

图4:I2C地址

☆注:PCF8563时钟芯片手册上给出其I2C从机地址是8位,读地址为0xA3,写地址为0xA2。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(5)

图5:PCF8563芯片手册描述

由此可见,所谓的7位地址和8位地址实际上都是7位地址加上最低位的读写位,本质上是一样的,只是各个I2C接口设备的描述方式不一样。

I2C保留了如下表所示的两组I2C地址,这些地址用于特殊用途。

表1:保留地址

序号

从机地址

R/W位

描述

1

0000 000

0

广播呼叫地址。

2

0000 000

1

起始字节。

3

0000 001

X

CBUS 地址。

4

0000 010

X

保留给不同的总线格式。

5

0000 011

X

保留到将来使用。

6

0000 1XX

X

Hs 模式主机码。

7

1111 1XX

X

保留到将来使用。

8

1111 0XX

X

10 位从机寻址。

2.3.I2C数据传输

■ 起始和停止条件(START and STOP conditions)

所有的I2C事务都是以START开始、STOP结束,起始和停止条件总是由主机产生,如下图所示,当SCL为高电平时,SDA从高电平向低电平转换表示起始条件,当SCL是高电平时,SDA由低电平向高电平转换表示停止条件。如果总线中存在多个主机,先将SDA拉低的主机获得总线控制权。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(6)

图6:起始和停止条件

■ 字节格式(Byte format)

I2C总线发送到SDA上的数据必须为8位,即一次传输一个字节,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位,首先传输的是数据的最高位MSB,如果从机要完成一些其他功能后,例如一个内部中断服务程序才能接收或发送下一个完整的数据字节,那么从机可以将时钟线SCL保持为低电平强制主机进入等待状态,当从机准备好接收下一个字节数据并释放时钟线SCL后数据传输继续。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(7)

图7:I2C总线数据传输

2.4.ACK和NACK

每个字节后会跟随一个ACK信号。接收者通过ACK位告知发送者已经成功接收一字节数据并准备好接收下一字节数据。所有的时钟脉冲包括ACK信号的时钟脉冲都是由主机产生的。

  1. ACK信号:发送者发送完8位数据后,在ACK时钟脉冲期间释放SDA线,接收者可以将SDA拉低并在时钟信号为高时保持低电平,这样就产生了ACK信号,从而使得主机知道从机已成功接收数据并且准备好了接收下一数据。
  2. NACK信号:当SDA在第9个时钟脉冲的时候保持高电平,定义为NACK信号。这时,主机要么产生STOP条件来放弃这次传输,要么重复START条件来启动一个新的传输。

下面的5种情况会导致产生NACK信号:

  1. 发送方寻址的接收方在总线上不存在,因此总线上没有设备应答。
  2. 接收方正在处理一些实时的功能,尚未准备好与主机通信,因此接收方不能执行收发。
  3. 在传输期间,接收方收到不能识别的数据或者命令。
  4. 在传输期间,接收方无法接收更多的数据字节。
  5. 主-接收器要通知从-发送器传输的结束。
2.5.从机地址和R/W位

I2C数据传输如下图所示,在起始条件(S)后,发送从机地址,从机地址是7位,从机地址后紧跟着的第8位是读写位(R/W),读写位为0表示写,读写位为1表示读。数据传输一般由主机产生的停止位P 终止,但是,如果主机仍希望在总线上通信,他可以产生重复起始条件 S和寻址另一个从机而不是首先产生一个停止条件,在这种传输中可能有不同的读写格式结合。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(8)

图8:I2C总线传输时序

可能的数据传输格式有:

  1. 主机发送器发送到从机接收器,传输的方向不会改变,接收器应答每一个字节,如下图所示。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(9)

图9:主机发送器7位地址寻址从机接收器(传输方向不改变)

  1. 在第一个字节后,主机立即读从机,在第一次应答后,主机发送器变成主机接收器,从机接收器变成从机发送器。第一次应答仍由从机生成,主机生成后续应答。之前发送了一个非应答(A)的主机产生STOP条件。

图10:主机发送第一个字节后立即读取从机

  1. 复合格式,如下图所示。传输改变方向的时侯,起始条件和从机地址都会被重复,但R/W位取反。如果主接收器发送重复START条件,他会在重复START条件之前发送一个非应答(A)。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(10)

图11:复合格式

3.STC8A8K64S4A12系列单片机I2C介绍

STC8A8K64S4A12系列单片机片内集成了1个I2C串行总线控制器,与标准I2C总线一样,STC8A8K64S4A12系列单片机的I2C总线支持两种操作模式:主模式和从模式。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(11)

图12:I2C总线工作模式

但需要知道STC8A8K64S4A12系列单片机的I2C协议与标准I2C协议相比较,有以下2种机制被忽略。

  1. 发送起始信号(START)后不进行仲裁。
  2. 时钟信号(SCL)停留在低电平时不进行超时检测。

STC8A8K64S4A12系列单片机的每一组I2C都有2个IO引脚供选择使用,如下表所示。.

表2:单片机I2C外设引脚分配

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(12)

☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。须知同一时刻只能使能一组IO口作为I2C使用。

STC8A8K64S4A12系列单片机I2C使用哪一组IO口由P_SW2外设端口切换寄存器2的B4、B5位决定,如下图所示。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(13)

图13:外设端口切换寄存器2

☆注:一般P_SW2寄存器B4、B5位默认是0,即如果没有对P_SW2寄存器进行操作,则默认选择的I2C是P1.4、P1.5这一组。

二、软件设计1.I2C相关寄存器汇集

STC8A8K64S4A12系列单片机使用I2C外设时会用到10个寄存器,如下表所示:

表3:STC8A8K64S4A12系列I2C使用寄存器汇总

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(14)

2.寄存器解析2.1.I2C配置寄存器I2CCFG

I2C配置寄存器控制I2C外设使能选择、工作模式、总线速度等,详见下图。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(15)

图14:I2C配置寄存器I2CCFG

2.2.I2C主机控制寄存器I2CMSCR

I2CMSCR主机控制寄存器的配置是针对I2C已配置为主机模式才有意义的,该寄存器的B7位用来控制主机模式的中断是否打开,B0~B3位为主机模式下的命令,此部分是重点,详见下图。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(16)

图15:I2C主机控制寄存器I2CMSCR

2.3.I2C主机辅助控制寄存器I2CMSAUX

当配置I2C总线为主机模式时,虽然可以按照I2C标准协议,控制I2CMSCR寄存器发送主机命令,但很多场合还是希望可以简化这个控制流程。I2CMSAUX寄存器的B0位可以帮助用户实现I2C自动发送数据并接收ACK信号。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(17)

图16:I2C主机辅助控制寄存器I2CMSAUX

2.4.I2C主机状态寄存器I2CMSST

I2C主机状态寄存器B7位是可读位,用来读取I2C控制器是否处于忙状态(BUSY),I2CMSST寄存器B6位是I2C控制器执行I2C主机命令后的中断标志位,需软件清零。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(18)

图17:I2C主机状态寄存器I2CMSST

2.5.I2C从机控制寄存器I2CSLCR

I2CSLCR从机控制寄存器可控制处于从机模式下的I2C设备是否使能接收START信号、接收STOP信号、接收1字节数据、发送1字节数据等的中断功能,详见下图。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(19)

图18:I2C从机控制寄存器I2CSLCR

2.6.I2C从机状态寄存器I2CSLST

I2C从机控制寄存器使能了接收START信号、接收STOP信号、接收1字节数据、发送1字节数据等的中断功能后,I2C从机状态寄存器I2CSLST不仅可读取I2C控制器是否处于忙状态(BUSY),还可以对接收START信号、接收STOP信号、接收1字节数据、发送1字节数据等的中断标志位进行读取,但这些位需软件清零。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(20)

图19:I2C从机状态寄存器I2CSLST

2.7.I2C从机地址寄存器I2CSLADR

I2C从机地址寄存器B1~B7位是从机设备地址位,STC8A8K64S4A12系列单片机的I2C外设又配置有B0位(MA位),用来设置是否忽略对从机设备地址的匹配。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(21)

图20:I2C从机地址寄存器I2CSLADR

☆注:前面有介绍I2C设备的地址是7位,此处I2C从机设备地址即是7位。

3.I2C配置步骤

针对STC8A8K64S4A12系列单片机I2C,软件的配置过程如下:

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(22)

图21:I2C中断方式和非中断方式软件配置步骤

4.外部EEPROM存储器读写单字节实验(模拟I2C)

☆注:本节的实验源码是在“实验2-8-1:串口1收发实验(P3.0和P3.1)”的基础上修改。本节对应的实验源码是:“实验2-14-1:外部EEPROM存储器读写单字节实验(模拟I2C)”。

4.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表4:实验需要用到的c文件

序号

文件名

后缀

功能描述

1

uart

.c

包含与用户uart有关的用户自定义函数。

2

i2c

.c

包含与用户i2c通信有关的用户自定义函数。

3

at24cxx

.c

操作AT24C系列存储器相关的用户自定义函数。

4

delay

.c

包含用户自定义延时函数。

4.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h" #include "at24cxx.h" #include "uart.h"

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表5:头文件包含路径

序号

路径

描述

1

…\ Source

uart.h、i2c.h、at24cxx.h和delay.h头文件在该路径,所以要包含。

2

…\Use

STC8.h头文件在该路径,所以要包含。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(23)

图22:添加头文件包含路径

4.3.编写代码

首先,在i2c.c文件中编写模拟i2c总线通信的基本函数,如下表所示。

表6:模拟i2c总线通信的相关函数汇集

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(24)

关于每个模拟i2c总线通信的相关函数,下面给出详细代码。

程序清单:模拟I2C总线产生启动信号

/*************************************************************************** * 描 述 : 模拟I2C总线产生启动信号 * 入 参 : 无 * 返回值 : FALSE 报错 TRUE 启动成功 **************************************************************************/ bit I2C_Start(void) { SDA = 1; //I2C数据线置高电平 SCL = 1; //I2C时钟线置高电平 Delay10us(); if(!SDA) return FALSE; //判断SDA线为低电平则总线忙,退出 SDA = 0; //判断SDA线为高电平则控制SDA为低,即SDA出现一个下降沿表示启动I2C Delay10us(); if(SDA) return FALSE; //判断SDA线为高电平则总线出错,退出 SDA = 0; //判断SDA线为低电平则依然拉低SDA Delay10us(); return TRUE; }

程序清单:模拟I2C总线产生停止信号

/*************************************************************************** * 描 述 : 模拟I2C总线产生停止信号 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_Stop(void) { SDA = 0; //I2C数据线置低电平 Delay10us(); SCL = 1; //I2C时钟线置高电平 Delay10us(); SDA = 1; //当SCL线为高电平时则SDA出现一个上升沿表示停止I2C Delay10us(); }

程序清单:模拟I2C总线发送ACK命令

/*************************************************************************** * 描 述 : 模拟I2C总线发送ACK命令 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_Ack(void) { SCL = 0; //I2C时钟线置低电平 Delay10us(); SDA = 0; //I2C数据线置低电平 Delay10us(); SCL = 1; //I2C时钟线置高电平 Delay10us(); SCL = 0; //I2C时钟线置低电平,完成了在SCL线上产生一个时钟,此时SDA为低电平 Delay10us(); }

程序清单:模拟I2C总线发送NACK应答命令

/*************************************************************************** * 描 述 : 模拟I2C总线发送NACK应答命令 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_NoAck(void) { SCL = 0; //I2C时钟线置低电平 Delay10us(); SDA = 1; //I2C数据线置高电平 Delay10us(); SCL = 1; //I2C时钟线置高电平 Delay10us(); SCL = 0; //I2C时钟线置低电平,完成了在SCL线上产生一个时钟,此时SDA为高电平 Delay10us(); }

程序清单:模拟I2C总线接收ACK命令

/*************************************************************************** * 描 述 : 模拟I2C总线接收ACK命令 * 入 参 : 无 * 返回值 : FALSE 无ACK ; TRUE 有ACK **************************************************************************/ bit I2C_Check_ACK(void) { SCL = 0; //I2C时钟线置低电平 Delay10us(); SDA = 1; //I2C数据线置高电平 Delay10us(); SCL = 1; //I2C时钟线置高电平 Delay10us(); if(SDA) //判断SDA线为高电平 { SCL = 0; //I2C时钟线置低电平,完成了在SCL线上产生一个时钟,此时SDA为高电平 return FALSE; } SCL = 0; //I2C时钟线置低电平,完成了在SCL线上产生一个时钟,此时SDA为低电平 return TRUE; }

程序清单:模拟I2C总线发送数据

/*************************************************************************** * 描 述 : 模拟I2C总线发送数据(数据从高位到低位) * 入 参 : 无 * 返回值 : FALSE 无ACK ; TRUE 有ACK **************************************************************************/ void I2C_SendByte(uint8 SendByte) { uint8 i=8; while(i--) { SCL = 0; Delay10us(); if(SendByte&0x80) SDA = 1; else SDA = 0; SendByte<<=1; Delay10us(); SCL = 1; Delay10us(); } SCL = 0; }

程序清单:模拟I2C总线接收数据

/*************************************************************************** * 描 述 : 模拟I2C总线接收数据 * 入 参 : 无 * 返回值 : I2C总线返回的数据 **************************************************************************/ uint8 I2C_ReceiveByte(void) { uint8 i=8; uint8 ReceiveByte=0; SDA = 1; while(i--) { ReceiveByte<<=1; SCL = 0; Delay10us(); SCL = 1; Delay10us(); if(SDA) { ReceiveByte|=0x01; } } SCL = 0; return ReceiveByte; }

然后,在at24cxx.c文件中编写对E2PROM存储器的基本操作函数,如下表所示。

序号

函数名

功能描述

1

AT24CXX_RcvOneByte

从E2PROM指定地址读取单字节数据。

2

AT24CXX_SendOneByte

向E2PROM指定地址存入单字节数据。

3

AT24CXX_SendLenByte

向E2PROM指定地址存入多字节数据。

4

AT24CXX_RcvLenByte

从E2PROM指定地址读取多字节数据。

5

AT24CXX_EraseOneByte

擦除E2PROM指定地址单字节数据。

6

AT24CXX_EraseAll

擦除整个E2PROM芯片。

关于每个操作外部E2PROM相关用户函数,下面给出详细代码。

程序清单:从指定地址读取单字节数据函数

/*********************************************************************************** * 描 述 : 从芯片AT24CXX指定地址读取单字节数据 * 入 参 : 开始读数据的地址 * 返回值 : 读到的数据 ***********************************************************************************/ uint8 AT24CXX_RcvOneByte(uint16 Addr) { uint8 temp=0; I2C_Start(); //启动总线 if(E2PROM_TYPE > AT24C16) { I2C_SendByte(SLAW); //发送写命令 I2C_Check_ACK(); //等待应答 I2C_SendByte(Addr>>8); //发送高地址 I2C_Check_ACK(); //等待应答 }else I2C_SendByte(SLAW ((Addr/256)<<1)); //发送器件地址,写数据 I2C_Check_ACK(); //等待应答 I2C_SendByte(Addr%6); //发送低地址 I2C_Check_ACK(); //等待应答 I2C_Start(); //启动总线 I2C_SendByte(SLAR); //设置为读模式 I2C_Check_ACK(); //等待应答 temp=I2C_ReceiveByte(); //读字节 I2C_Stop(); //结束总线 return temp; }

程序清单:向指定地址存入单字节数据函数

/*********************************************************************************** * 描 述 : 向芯片AT24CXX指定地址写入单字节数据 * 入 参 : Addr:写入的目的起始地址 Data:要写入的数据 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_SendOneByte(uint16 Addr,uint8 Data) { I2C_Start(); //启动总线 if(E2PROM_TYPE > AT24C16) { I2C_SendByte(SLAW); //发送写命令 I2C_Check_ACK(); //等待应答 I2C_SendByte(Addr>>8); //发送高地址 }else I2C_SendByte(SLAW ((Addr/256)<<1)); //发送器件地址,写数据 I2C_Check_ACK(); //等待应答 I2C_SendByte(Addr%6); //发送低地址 I2C_Check_ACK(); //等待应答 I2C_SendByte(Data); //发送字节数据 I2C_Check_ACK(); //等待应答 I2C_Stop(); //结束总线 delay_ms(10); //该延时保证连续发送字节的稳定性 }

程序清单:向指定地址存入多字节数据函数

/*********************************************************************************** * 描 述 : 向芯片AT24CXX里面的指定地址开始写入长度为Len的数据 * 入 参 : Addr:写入的目的起始地址 Data:要写入的数据 Len:要写入数据的长度 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_SendLenByte(uint16 Addr,uint8 *Data,uint16 Len) { while(Len--) { AT24CXX_SendOneByte(Addr,*Data); Addr ; Data ; } }

程序清单:从指定地址读取多字节数据函数

/*********************************************************************************** * 描 述 : 从芯片AT24CXX里面的指定地址开始读出长度为Len的数据 * 入 参 : Addr:读出的目的起始地址 Data:读出的数据 Len:要读出数据的长度 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_RcvLenByte(uint16 Addr,uint8 *Data,uint16 Len) { while(Len) { *Data = AT24CXX_RcvOneByte(Addr ); Len--; } }

程序清单:从指定地址擦除单字节数据函数

/*********************************************************************************** * 描 述 : 擦除芯片AT24CXX指定地址数据 * 入 参 : Addr:要擦除的目的地址 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_EraseOneByte(uint16 Addr) { AT24CXX_SendOneByte(Addr,0xFF); }

程序清单:擦除整片数据函数

/************************************************************************************ 功能描述:擦除整个芯片AT24CXX(即芯片存储单元数据都是0xFF) 入口参数:无 返回值:无 备注: 不同的存储芯片,存储空间不同,AT24CXX_SIZE数值不同。 *************************************************************************************/ void AT24CXX_EraseAll(void) { uint16 i; // 填充缓冲区 for (i = 0; i < AT24CXX_SIZE; i ) { AT24CXX_SendOneByte(i,0xFF); } }

最后,在主函数中对串口1进行初始化,并通过串口1发送不同的命令实现对单片机片外E2PROM的单字节读、写及擦除等操作。

代码清单:主函数

int main(void) uint8 Temp; P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口 P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出 Uart1_Init(); //串口1初始化 EA = 1; //使能总中断 delay_ms(10); //初始化后延时 while(1) { if(WriteFLAG) //写模式 { WriteFLAG=0; //写标志变量清零,发送一次 AT24CXX_SendOneByte(0x0010,0x33); //在地址0x0010位置写入1个字节数据0x33 SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成 } if(ReadFLAG) //读模式 { ReadFLAG=0; //读标志变量清零,发送一次 Temp=AT24CXX_RcvOneByte(0x0010); //在地址0x0010位置处读取1个字符 SendDataByUart1(Temp); //串口1发送读取的字符 } if(ClearFLAG) //清除模式 { ClearFLAG=0; //清除标志变量清零,发送一次 AT24CXX_EraseOneByte(0x0010); //擦除地址0x0010位置处数据 SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成 } }

4.4.硬件连接

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(25)

图23:开发板连接图

5.外部EEPROM存储器读写多字节实验(模拟I2C)5.1.编写代码

首先,在at24cxx.c文件中编写模拟I2C方式的读写字节函数和对E2PROM存储器的基本操作函数。请参考“实验2-14-1:外部EEPROM存储器读写单字节实验(模拟I2C)”部分。

然后,在主函数中对串口1进行初始化,并通过串口1发送不同的命令实现对单片机片外E2PROM的多字节读、写及擦除等操作。

代码清单:主函数

int main(void) { P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口 P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出 Uart1_Init(); //串口1初始化 EA = 1; //使能总中断 delay_ms(10); //初始化后延时 while(1) { if(WriteFLAG) //写模式 { WriteFLAG=0; //写标志变量清零,发送一次 AT24CXX_SendLenByte(0x0010,scan,E2PROM_Length); //向E2PROM起始地址0x0010中连续写入scan数组中的E2PROM_Length个字节数据 SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成 } if(ReadFLAG) //读模式 { ReadFLAG=0; //读标志变量清零,发送一次 AT24CXX_RcvLenByte(0x0010,buffer,E2PROM_Length); //从E2PROM地址0x0010开始连续读取E2PROM_Length字节数据并存入到buffer数组中 SendStringByUart1_n(buffer,E2PROM_Length); //串口1发送数组buffer中的值(即读取的多字节数据) } if(ClearFLAG) //清除模式 { ClearFLAG=0; //清除标志变量清零,发送一次 AT24CXX_EraseAll(); //擦除整片,需要一定的耗时 SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成 } } }

5.2.硬件连接

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(26)

图24:开发板连接图

6.外部EEPROM存储器读写单字节实验(硬件I2C)6.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表8:实验需要用到的c文件

序号

文件名

后缀

功能描述

1

uart

.c

包含与用户uart有关的用户自定义函数

2

i2c

.c

包含与用户i2c通信有关的用户自定义函数

3

at24cxx

.c

操作AT24C系列存储器相关的用户自定义函数

4

delay

.c

包含用户自定义延时函数

6.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h" #include "at24cxx.h" #include "uart.h"

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表9:头文件包含路径

序号

路径

描述

1

…\ Source

uart.h、i2c.h、at24cxx.h和delay.h头文件在该路径,所以要包含

2

…\User

STC8.h头文件在该路径,所以要包含

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(27)

图25:添加头文件包含路径

6.3.编写代码

首先,在i2c.c文件中对硬件I2C进行初始化,并编写硬件I2C总线通信的基本函数,如下表所示。

表8:实验需要用到的c文件

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(28)

关于每个硬件i2c总线通信的相关函数,下面给出详细代码。

程序清单:硬件I2C初始化函数

/*************************************************************************** * 描 述 : I2C初始化函数 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_init(void) { P_SW2 |= 0x80; //将EAXFR位置1,以访问在XDATA区域的扩展SFR P_SW2 &= 0xCF; //将I2C_S[1:0]置10,以选择I2C硬件功能脚为P7.6 P7.7 P_SW2 |= 0x20; //将I2C_S[1:0]置10,以选择I2C硬件功能脚为P7.6 P7.7 I2CCFG=0xE0; //使能I2C主机模式,I2C总线速度为等待65个时钟数 I2CMSST=0x00; //清零I2C主机状态寄存器各标志位 // P_SW2 &= 0x7F; //将EAXFR位置0,恢复访问XRAM }

程序清单:硬件I2C总线等待中断标志位函数

/*************************************************************************** * 描 述 : I2C总线等待中断标志位 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_Wait(void) { while(!(I2CMSST&0x40)); //等待I2C主机状态寄存器的中断标志位置1 I2CMSST &= 0xBF; //主机模式中断标志位软件清零 }

程序清单:硬件I2C总线产生启动信号函数

/*************************************************************************** * 描 述 : I2C总线产生启动信号 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_Start(void) { I2CMSCR=0x01; //关闭主机模式中断并发送主机命令:0001(起始命令) I2C_Wait(); //等待中断标志位置1 }

程序清单:硬件I2C总线产生停止信号函数

/*************************************************************************** * 描 述 : I2C总线产生停止信号 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_Stop(void) { I2CMSCR=0x06; //关闭主机模式中断并发送主机命令:0110(发送STOP命令) I2C_Wait(); //等待中断标志位置1 }

程序清单:硬件I2C总线发送ACK命令函数

/*************************************************************************** * 描 述 : I2C总线发送ACK命令 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_SendACK(void) { I2CMSST=0x00; //设置ACK信号 I2CMSCR=0x05; //关闭主机模式中断并发送主机命令:0101(发送ACK命令) I2C_Wait(); //等待中断标志位置1 }

程序清单:硬件I2C总线发送NACK应答命令函数

/*************************************************************************** * 描 述 : I2C总线发送NACK应答命令 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_SendNAK(void) { I2CMSST=0x01; //设置NACK信号 I2CMSCR=0x05; //关闭主机模式中断并发送主机命令:0101(发送ACK命令) I2C_Wait(); //等待中断标志位置1 }

程序清单:硬件I2C总线接收ACK命令函数

/*************************************************************************** * 描 述 : I2C总线接收ACK命令 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_RecvACK(void) { I2CMSCR=0x03; //关闭主机模式中断并发送主机命令:0011(接收ACK命令) I2C_Wait(); //等待中断标志位置1 }

程序清单:硬件I2C总线发送数据函数

/*************************************************************************** * 描 述 : I2C总线发送数据 * 入 参 : 无 * 返回值 : 无 **************************************************************************/ void I2C_SendData(uint8 dat) { I2CTXD=dat; //写数据到数据缓冲区 I2CMSCR=0x02; //关闭主机模式中断并发送主机命令:0010(发送数据命令) I2C_Wait(); //等待中断标志位置1 }

程序清单:硬件I2C总线接收数据函数

/*************************************************************************** * 描 述 : I2C总线接收数据 * 入 参 : 无 * 返回值 : I2C总线返回的数据 **************************************************************************/ uint8 I2C_RecvData(void) { I2CMSCR=0x04; //关闭主机模式中断并发送主机命令:0100(接收数据命令) I2C_Wait(); //等待中断标志位置1 return I2CRXD; }

然后,在at24cxx.c文件中编写对E2PROM存储器的基本操作函数,如下表所示。

表11:E2PROM相关用户函数汇集

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(29)

关于每个操作外部E2PROM相关用户函数,下面给出详细代码。

程序清单:从指定地址读取单字节数据函数

/*********************************************************************************** * 描 述 : 在芯片AT24CXX指定地址读出一个数据 * 入 参 : 开始读数据的地址 * 返回值 : 读到的数据 ***********************************************************************************/ uint8 AT24CXX_RcvOneByte(uint16 Addr) { uint8 temp=0; I2C_Start(); //启动总线 if(E2PROM_TYPE > AT24C16) { I2C_SendData(SLAW); //发送写命令 I2C_RecvACK(); //等待应答 I2C_SendData(Addr>>8); //发送高地址 I2C_RecvACK(); //等待应答 }else I2C_SendData(SLAW ((Addr/256)<<1)); //发送器件地址,写数据 I2C_RecvACK(); //等待应答 I2C_SendData(Addr%6); //发送低地址 I2C_RecvACK(); //等待应答 I2C_Start(); //启动总线 I2C_SendData(SLAR); //设置为读模式 I2C_RecvACK(); //等待应答 temp=I2C_RecvData(); //读字节 I2C_Stop(); //结束总线 return temp; }

程序清单:向指定地址存入单字节数据函数

/*********************************************************************************** * 描 述 : 在芯片AT24CXX指定地址写入一个数据 * 入 参 : Addr:写入的目的起始地址 Data:要写入的数据 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_SendOneByte(uint16 Addr,uint8 Data) { I2C_Start(); //启动总线 if(E2PROM_TYPE > AT24C16) { I2C_SendData(SLAW); //发送写命令 I2C_RecvACK(); //等待应答 I2C_SendData(Addr>>8); //发送高地址 I2C_RecvACK(); //等待应答 }else I2C_SendData(SLAW ((Addr/256)<<1)); //发送器件地址,写数据 I2C_RecvACK(); //等待应答 I2C_SendData(Addr%6); //发送低地址 I2C_RecvACK(); //等待应答 I2C_SendData(Data); //发送字节数据 I2C_RecvACK(); //等待应答 I2C_Stop(); //结束总线 delay_ms(10); //该延时保证连续发送字节的稳定性 }

程序清单:向指定地址存入多字节数据函数

/*********************************************************************************** * 描 述 : 向芯片AT24CXX里面的指定地址开始写入长度为Len的数据 * 入 参 : Addr:写入的目的起始地址 Data:要写入的数据 Len:要写入数据的长度 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_SendLenByte(uint16 Addr,uint8 *Data,uint16 Len) { while(Len--) { AT24CXX_SendOneByte(Addr,*Data); Addr ; Data ; } }

程序清单:从指定地址读取多字节数据函数

/*********************************************************************************** * 描 述 : 从芯片AT24CXX里面的指定地址开始读出长度为Len的数据 * 入 参 : Addr:读出的目的起始地址 Data:读出的数据 Len:要读出数据的长度 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_RcvLenByte(uint16 Addr,uint8 *Data,uint16 Len) { while(Len) { *Data = AT24CXX_RcvOneByte(Addr ); Len--; } }

程序清单:从指定地址擦除单字节数据函数

/*********************************************************************************** * 描 述 : 擦除芯片AT24CXX指定地址数据 * 入 参 : Addr:要擦除的目的地址 * 返回值 : 无 ***********************************************************************************/ void AT24CXX_EraseOneByte(uint16 Addr) { AT24CXX_SendOneByte(Addr,0xFF); }

程序清单:擦除整片数据函数

/*********************************************************************************** 功能描述:擦除整个芯片AT24CXX(即芯片存储单元数据都是0xFF) 入口参数:无 返回值:无 备注: 不同的存储芯片,存储空间不同,AT24CXX_SIZE数值不同。 ************************************************************************************/ void AT24CXX_EraseAll(void) { uint16 i; // 填充缓冲区 for (i = 0; i < AT24CXX_SIZE; i ) { AT24CXX_SendOneByte(i,0xFF); } }

最后,在主函数中对串口1和I2C进行初始化,并通过串口1发送不同的命令实现对单片机片外E2PROM的单字节读、写及擦除等操作。

代码清单:主函数

int main(void) { uint8 Temp; P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口 P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出 I2C_init(); //IIC初始化 Uart1_Init(); //串口1初始化 EA = 1; //使能总中断 delay_ms(10); //初始化后延时 while(1) { if(WriteFLAG) //写模式 { WriteFLAG=0; //写标志变量清零,发送一次 AT24CXX_SendOneByte(0x0010,0x33); //在地址0x0010位置写入1个字节数据0x33 SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成 } if(ReadFLAG) //读模式 { ReadFLAG=0; //读标志变量清零,发送一次 Temp=AT24CXX_RcvOneByte(0x0010); //在地址0x0010位置处读取1个字符 SendDataByUart1(Temp); //串口1发送读取的字符 } if(ClearFLAG) //清除模式 { ClearFLAG=0; //清除标志变量清零,发送一次 AT24CXX_EraseOneByte(0x0010); //擦除地址0x0010位置处数据 SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成 } } }

6.4.硬件连接

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(30)

图26:开发板连接图

7.外部EEPROM存储器读写多字节实验(硬件I2C)7.1.编写代码

首先,在at24cxx.c文件中编写硬件I2C方式的读写字节函数和对E2PROM存储器的基本操作函数。请参考“实验2-14-3:外部EEPROM存储器读写单字节实验(硬件I2C)”部分。

然后,在主函数中对串口1和I2C进行初始化,并通过串口1发送不同的命令实现对单片机片外EEPROM的多字节读、写及擦除等操作。

代码清单:主函数

int main(void) P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口 P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出 I2C_init(); //IIC初始化 Uart1_Init(); //串口1初始化 EA = 1; //使能总中断 delay_ms(10); //初始化后延时 while(1) { if(WriteFLAG) //写模式 { WriteFLAG=0; //写标志变量清零,发送一次 AT24CXX_SendLenByte(0x0010,scan,E2PROM_Length); //向E2PROM起始地址0x0010中连续写入scan数组中的E2PROM_Length个字节数据 SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成 } if(ReadFLAG) //读模式 { ReadFLAG=0; //读标志变量清零,发送一次 AT24CXX_RcvLenByte(0x0010,buffer,E2PROM_Length); //从E2PROM地址0x0010开始连续读取E2PROM_Length字节数据并存入到buffer数组中 SendStringByUart1_n(buffer,E2PROM_Length); //串口1发送数组buffer中的值(即读取的多字节数据) } if(ClearFLAG) //清除模式 { ClearFLAG=0; //清除标志变量清零,发送一次 AT24CXX_EraseAll(); //擦除整片,需要一定的耗时 SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成 } }

7.2.硬件连接

STC8A8K64S4A12开发板片外存储器E2PROM讲解(STC8A8K64S4A12开发板片外存储器E2PROM讲解)(31)

图27:开发板连接图

总结

以上是今天要讲的内容,希望对大家有帮助,如果有啥不明白的,欢迎讨论哦!

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页