2021-07-07
作者:互联网
版权声明:本文为博主原创文章,转载请附上原文出处链接。
文章目录
- 前言
- 一、硬件设计
- 二、软件设计
- 总结
前言
今天介绍下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开发板上设计了可供用户使用的AT24C02存储器芯片,其中原理图部分及硬件实物部分如下。
2.开发板DAC硬件电路介绍
I2C总线(即IIC总线)是集成电路总线(Inter-Integrated Circuit)的缩写,是一种简单、双向二线制同步串行总线。该I2C总线是1982年由荷兰的Philips公司为了解决电视机内的CPU和外围芯片之间连接而开发的,可以说电视机是最早的嵌入式系统之一。
I2C总线是一个真正的多主机总线,如果两个或多个主机同时初始化数据传输,可以通过冲突检测和仲裁防止数据破坏,每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。
2.1.主要特征
典型的I2C应用原理如下图所示,I2C总线通信仅需两根信号线,可以连接多个设备,从设备都有唯一的地址,主设备通过从设备的地址和不同的从设备通信。
- I2C总线硬件结构简单,仅需一根时钟线(SCL)、一根数据线(SDA)和两个上拉电阻即可实现通信。I2C总线的SCL和SDA均为开漏结构,开漏结构的电路只能输出“逻辑0”,无法输出“逻辑1”,因此SCL和SDA需要连接上拉电阻。上拉电阻的阻值影响传输速率,阻值越大,由于RC影响,会带来上升时间的增大,传输的速率慢,阻值小,传输的速率快,但是会增加电流的消耗,一般情况下,我们会选择4.7K左右的阻值,在从机数量少,信号线短的情况下,可以适当增加阻值,如使用10K的阻值。
- I2C总线中的从设备必须有自己的地址,并且该地址在其所处的I2C总线中唯一,主设备通过此唯一的地址即可和该从设备进行数据传输。
- I2C总线支持多主机,但是同一时刻只允许有一个主机。I2C总线中存在多个主机时,为了避免冲突,I2C总线通过总线仲裁决定由哪一个主机控制总线。
- I2C总线只能传输8位的数据,数据速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s,另外一些变种实现了低速模式(10Kbps)和快速+模式(1Mbps)。
- 同时连接到同一个I2C总线上的设备数量受总线最大电容(400pF)的限制。
- 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时的地址。
☆注:PCF8563时钟芯片手册上给出其I2C从机地址是8位,读地址为0xA3,写地址为0xA2。
由此可见,所谓的7位地址和8位地址实际上都是7位地址加上最低位的读写位,本质上是一样的,只是各个I2C接口设备的描述方式不一样。
I2C保留了如下表所示的两组I2C地址,这些地址用于特殊用途。
序号 | 从机地址 | 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拉低的主机获得总线控制权。
■ 字节格式(Byte format)
I2C总线发送到SDA上的数据必须为8位,即一次传输一个字节,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位,首先传输的是数据的最高位MSB,如果从机要完成一些其他功能后,例如一个内部中断服务程序才能接收或发送下一个完整的数据字节,那么从机可以将时钟线SCL保持为低电平强制主机进入等待状态,当从机准备好接收下一个字节数据并释放时钟线SCL后数据传输继续。
2.4.ACK和NACK
每个字节后会跟随一个ACK信号。接收者通过ACK位告知发送者已经成功接收一字节数据并准备好接收下一字节数据。所有的时钟脉冲包括ACK信号的时钟脉冲都是由主机产生的。
- ACK信号:发送者发送完8位数据后,在ACK时钟脉冲期间释放SDA线,接收者可以将SDA拉低并在时钟信号为高时保持低电平,这样就产生了ACK信号,从而使得主机知道从机已成功接收数据并且准备好了接收下一数据。
- NACK信号:当SDA在第9个时钟脉冲的时候保持高电平,定义为NACK信号。这时,主机要么产生STOP条件来放弃这次传输,要么重复START条件来启动一个新的传输。
下面的5种情况会导致产生NACK信号:
- 发送方寻址的接收方在总线上不存在,因此总线上没有设备应答。
- 接收方正在处理一些实时的功能,尚未准备好与主机通信,因此接收方不能执行收发。
- 在传输期间,接收方收到不能识别的数据或者命令。
- 在传输期间,接收方无法接收更多的数据字节。
- 主-接收器要通知从-发送器传输的结束。
2.5.从机地址和R/W位
I2C数据传输如下图所示,在起始条件(S)后,发送从机地址,从机地址是7位,从机地址后紧跟着的第8位是读写位(R/W),读写位为0表示写,读写位为1表示读。数据传输一般由主机产生的停止位P 终止,但是,如果主机仍希望在总线上通信,他可以产生重复起始条件 S和寻址另一个从机而不是首先产生一个停止条件,在这种传输中可能有不同的读写格式结合。
可能的数据传输格式有:
- 主机发送器发送到从机接收器,传输的方向不会改变,接收器应答每一个字节,如下图所示。
- 在第一个字节后,主机立即读从机,在第一次应答后,主机发送器变成主机接收器,从机接收器变成从机发送器。第一次应答仍由从机生成,主机生成后续应答。之前发送了一个非应答(A)的主机产生STOP条件。
- 复合格式,如下图所示。传输改变方向的时侯,起始条件和从机地址都会被重复,但R/W位取反。如果主接收器发送重复START条件,他会在重复START条件之前发送一个非应答(A)。
3.STC8A8K64S4A12系列单片机I2C介绍
STC8A8K64S4A12系列单片机片内集成了1个I2C串行总线控制器,与标准I2C总线一样,STC8A8K64S4A12系列单片机的I2C总线支持两种操作模式:主模式和从模式。
但需要知道STC8A8K64S4A12系列单片机的I2C协议与标准I2C协议相比较,有以下2种机制被忽略。
- 发送起始信号(START)后不进行仲裁。
- 时钟信号(SCL)停留在低电平时不进行超时检测。
STC8A8K64S4A12系列单片机的每一组I2C都有2个IO引脚供选择使用,如下表所示。.
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。须知同一时刻只能使能一组IO口作为I2C使用。
STC8A8K64S4A12系列单片机I2C使用哪一组IO口由P_SW2外设端口切换寄存器2的B4、B5位决定,如下图所示。
☆注:一般P_SW2寄存器B4、B5位默认是0,即如果没有对P_SW2寄存器进行操作,则默认选择的I2C是P1.4、P1.5这一组。
二、软件设计
1.I2C相关寄存器汇集
STC8A8K64S4A12系列单片机使用I2C外设时会用到10个寄存器,如下表所示:
2.寄存器解析
2.1.I2C配置寄存器I2CCFG
I2C配置寄存器控制I2C外设使能选择、工作模式、总线速度等,详见下图。
2.2.I2C主机控制寄存器I2CMSCR
I2CMSCR主机控制寄存器的配置是针对I2C已配置为主机模式才有意义的,该寄存器的B7位用来控制主机模式的中断是否打开,B0~B3位为主机模式下的命令,此部分是重点,详见下图。
2.3.I2C主机辅助控制寄存器I2CMSAUX
当配置I2C总线为主机模式时,虽然可以按照I2C标准协议,控制I2CMSCR寄存器发送主机命令,但很多场合还是希望可以简化这个控制流程。I2CMSAUX寄存器的B0位可以帮助用户实现I2C自动发送数据并接收ACK信号。
2.4.I2C主机状态寄存器I2CMSST
I2C主机状态寄存器B7位是可读位,用来读取I2C控制器是否处于忙状态(BUSY),I2CMSST寄存器B6位是I2C控制器执行I2C主机命令后的中断标志位,需软件清零。
2.5.I2C从机控制寄存器I2CSLCR
I2CSLCR从机控制寄存器可控制处于从机模式下的I2C设备是否使能接收START信号、接收STOP信号、接收1字节数据、发送1字节数据等的中断功能,详见下图。
2.6.I2C从机状态寄存器I2CSLST
I2C从机控制寄存器使能了接收START信号、接收STOP信号、接收1字节数据、发送1字节数据等的中断功能后,I2C从机状态寄存器I2CSLST不仅可读取I2C控制器是否处于忙状态(BUSY),还可以对接收START信号、接收STOP信号、接收1字节数据、发送1字节数据等的中断标志位进行读取,但这些位需软件清零。
2.7.I2C从机地址寄存器I2CSLADR
I2C从机地址寄存器B1~B7位是从机设备地址位,STC8A8K64S4A12系列单片机的I2C外设又配置有B0位(MA位),用来设置是否忽略对从机设备地址的匹配。
☆注:前面有介绍I2C设备的地址是7位,此处I2C从机设备地址即是7位。
3.I2C配置步骤
针对STC8A8K64S4A12系列单片机I2C,软件的配置过程如下:
4.外部EEPROM存储器读写单字节实验(模拟I2C)
☆注:本节的实验源码是在“实验2-8-1:串口1收发实验(P3.0和P3.1)”的基础上修改。本节对应的实验源码是:“实验2-14-1:外部EEPROM存储器读写单字节实验(模拟I2C)”。
4.1.工程需要用到的c文件
本例需要用到的c文件如下表所示,工程需要添加下表中的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"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | uart.h、i2c.h、at24cxx.h和delay.h头文件在该路径,所以要包含。 |
2 | …\Use | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
4.3.编写代码
首先,在i2c.c文件中编写模拟i2c总线通信的基本函数,如下表所示。
关于每个模拟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%256); //发送低地址
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%256); //发送低地址
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.硬件连接
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.硬件连接
6.外部EEPROM存储器读写单字节实验(硬件I2C)
6.1.工程需要用到的c文件
本例需要用到的c文件如下表所示,工程需要添加下表中的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"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | uart.h、i2c.h、at24cxx.h和delay.h头文件在该路径,所以要包含 |
2 | …\User | STC8.h头文件在该路径,所以要包含 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
6.3.编写代码
首先,在i2c.c文件中对硬件I2C进行初始化,并编写硬件I2C总线通信的基本函数,如下表所示。
关于每个硬件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存储器的基本操作函数,如下表所示。
关于每个操作外部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%256); //发送低地址
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%256); //发送低地址
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.硬件连接
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.硬件连接
总结
以上是今天要讲的内容,希望对大家有帮助,如果有啥不明白的,欢迎讨论哦!
标签:07,AT24CXX,主机,总线,地址,2021,I2C,E2PROM 来源: https://blog.csdn.net/qq_36139768/article/details/118539234