版权声明:本文为博主原创文章,转载请附上原文出处链接。
文章目录
前言
今天介绍下STC8A8K64S4A12系列单片机FRAM铁电存储器原理和SPI总线的原理及工作模式,掌握STC8A8K64S4A12系列单片机SPI外设相关的寄存器配置及程序设计。
一、硬件设计
1.FRAM铁电存储器介绍
FRAM(全称是Ferroelectric Random Access Memory)铁电存储器,该存储器能兼容RAM的一切功能,并且和ROM技术一样,是一种非易失性的存储器。可以说铁电存储器在这两类存储类型间搭起了一座跨越沟壑的桥梁——一种非易失性的RAM。
铁电存储器的工作原理是:当在铁电晶体材料上加入电场,晶体中的中心原子会沿着电场方向运动,达到稳定状态。晶体中的每个自由浮动的中心原子只有2个稳定状态,一个记为逻辑中的0,另一个记为1。中心原子能在常温、没有电场的情况下,停留在此状态达100年以上。铁电存储器不需要定时刷新,能在断电情况下保存数据。由于整个物理过程中没有任何原子碰撞,铁电存储器有高速读写、超低功耗和无限次写入等优点。
说到FRAM铁电存储器,就必须要介绍下美国Ramtron公司,该公司成立于1984年,是一家研究和开发铁电技术用于半导体存储器的公司。2012年9月,Ramtron公司被美国著名半导体公司赛普拉斯(Cypress)并购。2020年4月,infineon英飞凌完成了总价值90亿欧元(合人民币693亿元)对Cypress赛普拉斯半导体公司的收购。
Ramtron公司的FRAM主要包括两大类:串行FRAM和并行FRAM。其中串行FRAM又分I2C总线方式的FM24xx系列和SPI总线方式的FM25xx系列。艾克姆科技FRAM选择的存储器芯片是FM25CL64B芯片(SPI总线方式)。
☆注:Ramtron公司的商业FRAM产品全部由美国和日本的战略代工厂所制造。
2.SPI总线原理
SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速、全双工、同步的通信总线。SPI是Motorola公司推出的一种同步串行接口技术,SPI由一个主设备和一个或多个从设备组成,在一次数据传输过程中,接口上只能有一个主机和一个从机通信。
SPI总线的优点是操作简单、数据传输速率较高、全双工,缺点是只支持单个主机、没有指定的流控制、没有应答机制确认是否接收到数据。
2.1.接口信号定义
SPI总线接口包括以下四种信号:
- MOSI(Master Output,Slave Input):主器件数据输出,从器件数据输入。
- MISO(Master Input, Slave Output):主器件数据输入,从器件数据输出。
- SCK(Serial Clock):有时也称为SCLK,时钟信号,由主器件产生。
- CS(Chip select):有时也称为SS,从器件使能信号,由主器件控制,实际使用时,经常用GPIO来代替。
SPI总线支持连接多个从机,如下图所示,SPI主机通过连接到从机的片选信号使能/禁止从机,并且同时只能使能一个从机,因此总线里面有多少个从机,就需要多少个片选信号。当SPI主机需要和总线中某个从机进行通信时,主机会拉低对应的CS信号使能该从机,之后发起通信,通信完成后,拉高CS信号,解除总线的占用。
对于SPI总线,我们还需要深刻理解下面几个知识点。
■ 硬件片选和软件片选的区别
所谓硬件片选指的是SPI本身具有片选信号,当我们通过SPI发送数据时,SPI外设自动拉低CS信号使能从机,发送完成后自动拉高CS信号释放从机,这个过程是不需要软件操作的。而软件片选则是需要使用GPIO作为片选信号,SPI在发送数据之前,需要先通过软件设置作为片选信号的GPIO输出低电平,发送完成之后再设置该GPIO输出高电平。
■ SPI总线是回环结构
SPI是一个环形总线结构,如下图所示,主设备和从设备构成一个环形。在时钟SCK的作用下,主设备发送一个位到从设备,因为是环形结构,所以从设备必定会同时传送一个位到主设备。同样,主设备向从设备发送一个字节,从设备也必定会同时传送一个字节到主设备。理解了环形结构,就很容易理解下面几点:
- SPI是全双工,同步的通信总线。
- 主设备向从设备发送数据时,无论我们需不需要从设备返回数据,从设备都会返回数据。
- 主设备从从设备读取一个字节数据时,为什么需要写一个字节数据到从设备:因为是环形结构,不写数据过去,对方的数据就不会被移位过来。
■ SPI主机和从机之间连接时信号不需要交叉
SPI主机和从机连接时,MOSI和MISO信号是不需要交叉连接的,因为MOSI本身就表示了主机输出、从机输入,MISO表示主机输入、从机输出,因此不能交叉连接。
2.2.SPI的4种工作模式
SPI总线共有4种工作模式:模式0~模式3,这4种工作模式是由时钟相位和时钟极性确定的。
1.时钟极性CPOL(Clock polarity):SPI总线空闲时,时钟信号SCLK的电平称为时钟极性,有以下两种模式:
- CPOL=0:SPI总线空闲时,时钟信号为低电平。
- CPOL=1:SPI总线空闲时,时钟信号为高电平。
2.时钟相位CPHA(Clock phase):SPI在时钟信号SCLK第几个边沿开始采样数据,有以下两种模式:
- CPHA=0:在第1个时钟边沿进行数据采样。
- CPHA=1:在第2个时钟边沿进行数据采样。
时钟极性CPOL时钟相位CPHA各有2种模式,他们两两组合就形成了SPI的4种工作模式,如下表所示。
序号 | SPI工作模式 | 描述 | 备注 |
---|---|---|---|
1 | 模式0 | CPOL=0,CPHA=0 | 重要 |
2 | 模式1 | CPOL=0,CPHA=1 | |
3 | 模式2 | CPOL=1,CPHA=0 | |
4 | 模式3 | CPOL=1,CPHA=1 | 重要 |
SPI的4种模式中,最常用的是模式0和模式3。正是由于SPI有4种工作模式,因此当我们使用SPI总线时,需要去查询SPI总线中主机设备(如单片机)和从机设备(如SPI 存储器)的数据手册,确定他们支持什么模式,从而选择适合的工作模式。
SPI的4种模式的时序图如下。
■ 时钟相位CPHA=0时的时序
■ 时钟相位CPHA=1时的时序
3.STC8A8K64S4A12系列单片机SPI介绍
STC8A8K64S4A12系列单片机片内有1个高速串行通信接口SPI接口(以下简称SPI),该接口支持两种操作模式:主模式和从模式。这样,STC8A8K64S4A12系列单片机SPI通信方式可分为3种:单主单从(一个主机设备连接一个从机设备)、互为主从(两个设备连接,设备可互为主机和从机)、单主多从(一个主机设备连接多个从机设备)。
STC8A8K64S4A12系列单片机每一组SPI都有4个IO引脚供选择使用,如下表所示。
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。须知同一时刻只能使能一组IO口作为SPI使用。
STC8A8K64S4A12系列单片机SPI使用哪一组IO口由P_SW1外围设备切换控制寄存器的B2、B3位决定,如下图所示。
☆注:一般P_SW1寄存器B2、B3位默认是0,即如果没有对P_SW1寄存器进行操作,则默认选择的SPI是P1.2、P1.3、P1.4、P1.5这一组。
二、软件设计
1.SPI相关寄存器汇集
STC8A8K64S4A12系列单片机使用SPI外设时会用到6个寄存器,如下表所示:
序号 | 寄存器名 | 读/写 | 功能描述 |
---|---|---|---|
1 | P_SW1 | 读/写 | 外设端口切换控制寄存器1。 |
2 | SPCTL | 读/写 | SPI控制寄存器。 |
3 | SPSTAT | 读/写 | SPI状态寄存器。 |
4 | SPDAT | 读/写 | SPI数据寄存器。 |
5 | IE2 | 读/写 | 中断允许寄存器2。 |
6 | IP2 | 读/写 | 中断优先级控制寄存器2。 |
2.寄存器解析
2.1.SPI控制寄存器SPCTL
SPI控制寄存器控制SPI工作模式、时钟频率、使能选择等,详见下图。
2.2.SPI状态寄存器SPSTAT
SPI状态寄存器SPSTAT的B7位用来标志SPI传输完成,B6位则在SPI对数据寄存器SPDAT写操作时置1,详见下图。
2.3.中断允许寄存器IE2
中断允许寄存器IE2中B1位用于对SPI中断的使能控制,参考下图。
☆注:中断允许寄存器IE2还有对其他外设的中断使能控制位,所以操作该寄存器时要按位操作。
2.4.中断优先级控制寄存器
中断优先级控制寄存器IP2和IP2H中B1位组合起来用于对SPI中断的优先级进行配置,详见下图。
☆注:中断优先级控制寄存器还有对其他外设的中断优先级控制位,所以操作这些寄存器时要按位操作。
3.SPI配置步骤
针对STC8A8K64S4A12系列单片机SPI,软件的配置过程如下:
4.外接FRAM存储器读写单字节实验(模拟SPI)
4.1.工程需要用到的c文件
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | uart | .c | 包含与用户uart有关的用户自定义函数。 |
2 | fm25cl64b | .c | SPI通信及操作FRAM有关的用户自定义函数。 |
3 | delay | .c | 包含用户自定义延时函数。 |
4.2.头文件引用和路径设置
■ 需要引用的头文件
#include "delay.h"
#include "uart.h"
#include " fm25cl64b.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | uart.h、fm25cl64b.h和delay.h头文件在该路径,所以要包含。 |
2 | …\Use | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
4.3.编写代码
首先,在fm25cl64b.c文件中编写模拟SPI方式的读写字节函数,这里的模拟SPI读字节函数与写字节函数是分开的,代码如下:
程序清单:模拟SPI写字节函数
/*****************************************************************************
* 描 述 : 模拟SPI写入一个字节
* 入 参 : uint8 date
* 返回值 : 无
****************************************************************************/
void SPI_WriteByte(uint8 date)
{
uint8 temp,i;
temp = date;
for (i = 0; i < 8; i++)
{
SPI_SCK_0;
delay_us(10);
if((temp&0x80)==0x80)
{ SPI_MOSI_1; }
else
{ SPI_MOSI_0; }
SPI_SCK_1 ;
delay_us(10);
temp <<= 1;
}
SPI_MOSI_0;
}
程序清单:模拟SPI读字节函数
/***********************************************************************
* 描 述 : 模拟SPI读取一个字节
* 入 参 : 无
* 返回值 : 读取uint8数据
***********************************************************************/
uint8 SPI_ReadByte(void)
{
uint8 temp=0;
uint8 i;
for(i = 0; i < 8; i++)
{
temp <<= 1;
SPI_SCK_0 ;
delay_us(10);
if(E_SPI_MISO)
{temp++; }
SPI_SCK_1 ;
delay_us(10);
}
return(temp);
}
然后,编写对FRAM存储器的基本操作函数,如下表所示。
序号 | 函数名 | 功能描述 |
---|---|---|
1 | FramWriteByte | 向FRAM指定地址存入单字节数据。 |
2 | FramReadByte | 从FRAM指定地址读取单字节数据。 |
3 | FRAM_Read_Nbyte | 从FRAM指定地址读取多字节数据。 |
4 | FRAM_Write_Nbyte | 向FRAM指定地址存入多字节数据。 |
5 | FRAM_Erase_Nbyte | 在FRAM指定地址开始擦除指定长度的数据。 |
关于每个操作外部FRAM相关用户函数,下面给出详细代码。
程序清单:向指定地址存入单字节数据函数
/****************************************************************************
* 描 述 : 向指定地址存入数据
* 入 参 : uint16 address写入的地址,uint8 da写入的数据
* 返回值 : 无
**************************************************************************/
void FramWriteByte(uint16 address,uint8 da)
{
uint8 temM,temL;
temM=(uint8)((address&0xff00)>>8);
temL=(uint8)(address&0x00ff);
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_WREN); //写使能FRAM
SPI_CS_1; //取消片选
delay_80us();
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_WRITE); //写数据到FRAM存储器命令
SPI_WriteByte(temM); //写存储器地址高8位
SPI_WriteByte(temL); //写存储器地址低8位
SPI_WriteByte(da); //写入要存入的数据
SPI_CS_1; //取消片选
}
程序清单:从指定地址读取单字节数据函数
/***************************************************************************
* 描 述 : 向指定地址读取数据
* 入 参 : uint16 address要读取的地址
* 返回值 : 返回读取到的数据uint8
***************************************************************************/
uint8 FramReadByte(uint16 address)
{
uint8 temp,temM,temL;
temM=(uint8)((address&0xff00)>>8);
temL=(uint8)(address&0x00ff);
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_READ); //读存储器命令下发
SPI_WriteByte(temM); //待读地址高位下发
SPI_WriteByte(temL); //待读地址低位下发
temp = SPI_ReadByte(); //读存储器数据
SPI_CS_1; //取消片选
return temp;
}
程序清单:从指定地址读取多字节数据函数
/************************************************************************
功能描述:在指定地址开始读取多字节数据
入口参数:pbuf:数据存储区 ReadAddr:读数据首地址 Len:要读取的字节数
返回值:无
*************************************************************************/
void FRAM_Read_Nbyte(uint8 * pbuf,uint16 ReadAddr,uint16 Len)
{
uint16 i;
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_READ); //发送读数据命令
SPI_WriteByte((uint8)((ReadAddr&0xE0)>>8)); //写入高8位址,最大地址13位
SPI_WriteByte((uint8)ReadAddr); //写入低8位址
for(i=0;i<Len;i++) //连续读取数据
{
*pbuf++=SPI_ReadByte(); //读一个字节
}
SPI_CS_1; //取消片选
}
程序清单:向指定地址存入多字节数据函数
/************************************************************************
功能描述:在指定地址开始写入多字节数据
入口参数:pbuf:数据存储区 WriteAddr:写入数据首地址 Len:要写入的字节数
返回值:无
**************************************************************************/
void FRAM_Write_Nbyte(uint8 * pbuf,uint16 WriteAddr,uint16 Len)
{
uint16 i;
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_WREN); //写使能FRAM
SPI_CS_1; //取消片选
delay_80us();
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_WRITE); //发送写数据命令
SPI_WriteByte((uint8)((WriteAddr&0xE0)>>8)); //写入高8位址,最大地址13位
SPI_WriteByte((uint8)WriteAddr); //写入低8位址
for(i=0;i<Len;i++) //连续写入数据
{
SPI_WriteByte(pbuf[i]); //写入要存入的数据
}
SPI_CS_1; //取消片选
}
程序清单:擦除指定长度的数据函数
/*************************************************************************
功能描述:在指定地址开始擦除指定长度的数据
入口参数:EraseAddr:擦除首地址 Len:要擦除的字节数
返回值:无
**************************************************************************/
void FRAM_Erase_Nbyte(uint16 EraseAddr,uint16 Len)
{
uint16 i;
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_WREN); //写使能FRAM
SPI_CS_1; //取消片选
delay_80us();
SPI_CS_0; //使能器件
SPI_WriteByte(FM25CL64_WRITE); //发送写数据命令
SPI_WriteByte((uint8)((EraseAddr&0xE0)>>8)); //写入高8位址,最大地址13位
SPI_WriteByte((uint8)EraseAddr); //写入低8位址
for(i=0;i<Len;i++) //连续擦除数据
{
SPI_WriteByte(0x00); //擦除数据即写入0x00
}
SPI_CS_1; //取消片选
}
最后,在主函数中对串口1进行初始化,并通过串口1发送不同的命令实现对单片机片外FRAM的单字节读、写及擦除等操作。
代码清单:主函数
int main()
{
uint8 Temp;
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
SPI_CS_1; //SPI使能引脚初始化
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while (1)
{
if(WriteFLAG) //写模式
{
WriteFLAG=0; //写标志变量清零,发送一次
FramWriteByte(0x0050,0x33); //向FRAM地址0x0050中写入单字节数据0x33
SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成
}
if(ReadFLAG) //读模式
{
ReadFLAG=0; //读标志变量清零,发送一次
Temp=FramReadByte(0x0050); //从FRAM地址0x0050读取单字节数据并赋值给变量Temp
SendDataByUart1(Temp); //串口1发送Temp变量值(即读取的单字节数据)
}
if(ClearFLAG) //擦除模式
{
ClearFLAG=0; //清除标志变量清零,发送一次
FRAM_Erase_Nbyte(0x0050,1); //擦除FRAM地址0x0050的数据
SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成
}
}
}
4.4.硬件连接
5.外接FRAM存储器读写多字节实验(模拟SPI)
5.1.编写代码
首先,在fm25cl64b.c文件中编写模拟SPI方式的读写字节函数和对FRAM存储器的基本操作函数。请参考“实验2-15-1:外接FRAM存储器读写单字节实验(模拟SPI)”部分。
然后,在主函数中对串口1进行初始化,并通过串口1发送不同的命令实现对单片机片外FRAM的多字节读、写及擦除等操作。
代码清单:主函数
int main()
{
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
SPI_CS_1; //SPI使能引脚初始化
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while(1)
{
if(WriteFLAG) //写模式
{
WriteFLAG=0; //写标志变量清零,发送一次
FRAM_Write_Nbyte(FRAM_Test_Write,0x0000,10); //向FRAM地址0x0000中写入FRAM_Test_Write数组中10个字节数据
SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成
}
if(ReadFLAG) //读模式
{
ReadFLAG=0; //读标志变量清零,发送一次
FRAM_Read_Nbyte(FRAM_Test_Read,0x0000,10) ; //从FRAM地址0x0000读取10个字节数据存放数组FRAM_Test_Read中
SendStringByUart1_n(FRAM_Test_Read,10); //串口1发送数组FRAM_Test_Read中的值(即读取的多字节数据)
}
if(ClearFLAG) //清除模式
{
ClearFLAG=0; //清除标志变量清零,发送一次
FRAM_Erase_Nbyte(0x0000,10); //擦除FRAM地址0x0000开始的10个字节长度的数据
SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成
}
}
}
5.2.硬件连接
6.外接FRAM存储器读写单字节实验(硬件SPI)
6.1.工程需要用到的c文件
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | uart | .c | 包含与用户uart有关的用户自定义函数 |
2 | fm25cl64b | .c | SPI通信及操作FRAM有关的用户自定义函数。 |
3 | delay | .c | 包含用户自定义延时函数。 |
6.2.头文件引用和路径设置
■ 需要引用的头文件
#include "delay.h"
#include "uart.h"
#include " fm25cl64b.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | uart.h、fm25cl64b.h和delay.h头文件在该路径,所以要包含。 |
2 | …\User | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
6.3.编写代码
首先,在fm25cl64b.c文件中对硬件SPI进行初始化,并编写硬件SPI的读写字节函数,代码如下:
程序清单:硬件SPI初始化函数
/**************************************************************************************
* 描 述 : 硬件SPI初始化
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void Init_SPI(void)
{
P_SW1 |=0X08; //将 SPI 调整到 P7.4 P7.5 P7.6 P7.7
P_SW1 &=0XFB; //将 SPI 调整到 P7.4 P7.5 P7.6 P7.7
SPDAT = 0;
SPSTAT = SPIF | WCOL; //清除SPI状态位
SPCTL = SPEN | MSTR | SSIG; //主机模式
}
程序清单:硬件SPI读写字节函数
/***********************************************************************
* 描 述 : 硬件SPI写入一个字节,并返回一个值
* 入 参 : uint8 date
* 返回值 : 无
***********************************************************************/
uint8 SPI_SendByte(uint8 SPI_SendData)
{
SPDAT = SPI_SendData; //触发SPI发送数据
while (!(SPSTAT & SPIF)); //等待发送完成
SPSTAT = SPIF | WCOL; //清除SPI状态位
return SPDAT; //返回SPI数据
}
然后,编写对FRAM存储器的基本操作函数,如下表所示。
序号 | 函数名 | 功能描述 |
---|---|---|
1 | FramWriteByte | 向FRAM指定地址存入单字节数据。 |
2 | FramReadByte | 从FRAM指定地址读取单字节数据。 |
3 | FRAM_Read_Nbyte | 从FRAM指定地址读取多字节数据。 |
4 | FRAM_Write_Nbyte | 向FRAM指定地址存入多字节数据。 |
5 | FRAM_Erase_Nbyte | 在FRAM指定地址开始擦除指定长度的数据。 |
关于每个操作外部FRAM相关用户函数,下面给出详细代码。
程序清单:向指定地址存入单字节数据函数
/**************************************************************************
* 描 述 : 向指定地址存入数据
* 入 参 : uint16 address写入的地址,uint8 da写入的数据
* 返回值 : 无
**************************************************************************/
void FramWriteByte(uint16 address,uint8 da)
{
uint8 temM,temL;
temM=(uint8)((address&0xff00)>>8);
temL=(uint8)(address&0x00ff);
SPI_CS_0; //使能器件
SPI_SendByte(WREN); //写使能FRAM
SPI_CS_1; //取消片选
delay_80us();
SPI_CS_0; //使能器件
SPI_SendByte(WRITEE); //写数据到FRAM存储器命令
SPI_SendByte(temM); //写存储器地址高8位
SPI_SendByte(temL); //写存储器地址低8位
SPI_SendByte(da); //写入要存入的数据
SPI_CS_1; //取消片选
}
程序清单:从指定地址读取单字节数据函数
/**************************************************************************
* 描 述 : 向指定地址读取数据
* 入 参 : uint16 address要读取的地址
* 返回值 : 返回读取到的数据uint8
***************************************************************************/
uint8 FramReadByte(uint16 address)
{
uint8 temp,temM,temL;
temM=(uint8)((address&0xff00)>>8);
temL=(uint8)(address&0x00ff);
SPI_CS_0; //使能器件
SPI_SendByte(READD); //读存储器命令下发
SPI_SendByte(temM); //待读地址高位下发
SPI_SendByte(temL); //待读地址低位下发
temp = SPI_SendByte(0xFF); //读存储器数据
SPI_CS_1; //取消片选
return temp;
}
程序清单:从指定地址读取多字节数据函数
/**************************************************************************
功能描述:在指定地址开始读取多字节数据
入口参数:pbuf:数据存储区 ReadAddr:读数据首地址 Len:要读取的字节数
返回值:无
**************************************************************************/
void FRAM_Read_Nbyte(uint8 * pbuf,uint16 ReadAddr,uint16 Len)
{
uint16 i;
SPI_CS_0; //使能器件
SPI_SendByte(FM25CL64_READ); //发送读数据命令
SPI_SendByte((uint8)((ReadAddr&0xE0)>>8)); //写入高8位址,最大地址13位
SPI_SendByte((uint8)ReadAddr); //写入低8位址
for(i=0;i<Len;i++) //连续读取数据
{
*pbuf++=SPI_SendByte(0xFF); //读一个字节
}
SPI_CS_1; //取消片选
}
程序清单:向指定地址存入多字节数据函数
/*************************************************************************
功能描述:在指定地址开始写入多字节数据
入口参数:pbuf:数据存储区 WriteAddr:写入数据首地址 Len:要写入的字节数
返回值:无
**********************************************************************/
void FRAM_Write_Nbyte(uint8 * pbuf,uint16 WriteAddr,uint16 Len)
{
uint16 i;
SPI_CS_0; //使能器件
SPI_SendByte(FM25CL64_WREN); //写使能FRAM
SPI_CS_1; //取消片选
delay_80us();
SPI_CS_0; //使能器件
SPI_SendByte(FM25CL64_WRITE); //发送写数据命令
SPI_SendByte((uint8)((WriteAddr&0xE0)>>8)); //写入高8位址,最大地址13位
SPI_SendByte((uint8)WriteAddr); //写入低8位址
for(i=0;i<Len;i++) //连续写入数据
{
SPI_SendByte(pbuf[i]); //写入要存入的数据
}
SPI_CS_1; //取消片选
}
程序清单:擦除指定长度的数据函数
/*************************************************************************
功能描述:在指定地址开始擦除指定长度的数据
入口参数:EraseAddr:擦除首地址 Len:要擦除的字节数
返回值:无
****************************************************************************/
void FRAM_Erase_Nbyte(uint16 EraseAddr,uint16 Len)
{
uint16 i;
SPI_CS_0; //使能器件
SPI_SendByte(FM25CL64_WREN); //写使能FRAM
SPI_CS_1; //取消片选
delay_80us();
SPI_CS_0; //使能器件
SPI_SendByte(FM25CL64_WRITE); //发送写数据命令
SPI_SendByte((uint8)((EraseAddr&0xE0)>>8)); //写入高8位址,最大地址13位
SPI_SendByte((uint8)EraseAddr); //写入低8位址
for(i=0;i<Len;i++) //连续擦除数据
{
SPI_SendByte(0x00); //擦除数据即写入0x00
}
SPI_CS_1; //取消片选
}
最后,在主函数中对串口1和SPI进行初始化,并通过串口1发送不同的命令实现对单片机片外FRAM的单字节读、写及擦除等操作。
代码清单:主函数
int main()
{
uint8 Temp;
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
SPI_CS_1; //SPI使能引脚初始化
Init_SPI(); //初始化SPI
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while (1)
{
if(WriteFLAG) //写模式
{
WriteFLAG=0; //写标志变量清零,发送一次
FramWriteByte(0x0050,0x33); //向FRAM地址0x0050中写入单字节数据0x33
SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成
}
if(ReadFLAG) //读模式
{
ReadFLAG=0; //读标志变量清零,发送一次
Temp=FramReadByte(0x0050); //从FRAM地址0x0050读取单字节数据并赋值给变量Temp
SendDataByUart1(Temp); //串口1发送Temp变量值(即读取的单字节数据)
}
if(ClearFLAG) //擦除模式
{
ClearFLAG=0; //清除标志变量清零,发送一次
FRAM_Erase_Nbyte(0x0050,1); //擦除FRAM地址0x0050的数据
SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成
}
}
}
6.4.硬件连接
7.外接FRAM存储器读写多字节实验(硬件SPI)
7.1.编写代码
首先,在fm25cl64b.c文件中编写硬件SPI方式的读写字节函数和对FRAM存储器的基本操作函数。请参考“实验2-15-3:外接FRAM存储器读写单字节实验(硬件SPI)”部分。
然后,在主函数中对串口1和SPI进行初始化,并通过串口1发送不同的命令实现对单片机片外FRAM的多字节读、写及擦除等操作。
代码清单:主函数
int main()
{
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
SPI_CS_1; //SPI使能引脚初始化
Init_SPI(); //初始化SPI
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while(1)
{
if(WriteFLAG) //写模式
{
WriteFLAG=0; //写标志变量清零,发送一次
FRAM_Write_Nbyte(FRAM_Test_Write,0x0000,10); //向FRAM地址0x0000中写入FRAM_Test_Write数组中10个字节数据
SendDataByUart1(0x33); //串口1发送数据0x33表示写操作完成
}
if(ReadFLAG) //读模式
{
ReadFLAG=0; //读标志变量清零,发送一次
FRAM_Read_Nbyte(FRAM_Test_Read,0x0000,10) ; //从FRAM地址0x0000读取10个字节数据存放数组FRAM_Test_Read中
SendStringByUart1_n(FRAM_Test_Read,10); //串口1发送数组FRAM_Test_Read中的值(即读取的多字节数据)
}
if(ClearFLAG) //清除模式
{
ClearFLAG=0; //清除标志变量清零,发送一次
FRAM_Erase_Nbyte(0x0000,10); //擦除FRAM地址0x0000开始的10个字节长度的数据
SendDataByUart1(0x00); //串口1发送数据0x00表示擦除完成
}
}
}
7.2.硬件连接
总结
以上是今天要讲的内容,希望对大家有帮助,如果有啥不明白的,欢迎讨论哦!