• 外设驱动库开发笔记40:AT25xxx外部存储器驱动


      我们在前面开发过AT24CXX系列EEPROM存储器,它使用的是I2C接口。不过有时候我们也会使用SPI接口的EEPROM存储器。在这一篇我们将来讨论AT25XXX系列EEPROM存储器的驱动设计、实现及使用。

    1、功能概述

      AT25XXX系列EEPROM存储器采用SPI接口,因其操作简单且性价比高,常用于数据保存。出于开发面向AT25XXX系列EEPROM存储器操作的驱动目标,我们先来了解一下AT25XXX系列EEPROM存储器的基本情况。

    1.1、硬件描述

      AT25XXX系列EEPROM存储器拥有从1K到2M的多种容量。AT25XXX系列EEPROM存储器采用SPI通讯接口。尽管容量跨度很大,但都采用相同的封装形式。具体的引脚排布及定义如下:

      为了更好地理解AT25XXX系列EEPROM存储器的操作过程,我们对CS、WP以及HOLD引脚做一个简单说明。
      首先来看一看CS引脚,CS为芯片选择引脚,低电平有效,AT25XXX系列EEPROM存储器被选中。当设备未被选中时,串行数据输入(SI)引脚将不接受数据,串行输出(SO)引脚将保持高阻抗状态。为了确保稳定的操作,CS引脚应在电源启动时跟随VCC。因此建议使用小于或等于10 kΩ的上拉电阻器连接到VCC。在电源启动后,要实现对AT25XXX系列EEPROM存储器的任何操作,都需要先将CS下拉到低电平。
      接下来我们看一看WP引脚,WP引脚为AT25XXX系列EEPROM存储器的写保护引脚。当写保护(WP)引脚保持高电位时,AT25XXX系列EEPROM存储器允许正常的读/写操作。当写保护(WP)引脚为低电平时,WPEN位设置为逻辑“1”时,所有对状态寄存器的写操作都被禁止。但如果内部写周期已经启动,那么写保护(WP)引脚变为低电平对状态寄存器的任何写操作都没有影响。当状态寄存器中的WPEN位设置为逻辑“0”时,写保护(WP)引脚的功能被屏蔽。
      最后我们来看一看HOLD引脚,暂停串行输入(HOLD)引脚低电平有效。暂停串行输入(HOLD)引脚与芯片选择(CS)引脚一起使用来暂停AT25XXX系列EEPROM存储器。当设备被选中,一个串行序列正在进行中,HOLD可以用来暂停与主设备的串行通信,而不需要重新设置串行序列。此时,串行数据输入(SI)引脚的输入将被忽略,而串行数据输出(SO)引脚将处于高阻抗状态。

    1.2、通讯接口

      AT25XXX系列EEPROM存储器采用SPI通讯接口。AT25XXX系列EEPROM存储器由主机控制器(通常称为SPI主机)发送的一组指令控制。与AT25XXX系列EEPROM存储器的通信必须由SPI主设备(如微控制器)发起。SPI主设备必须在串行数据时钟(SCK)引脚上为AT25XXX系列EEPROM存储器生成串行时钟。AT25XXX系列EEPROM存储器总是作为一个从属操作,因为SCK总是一个输入。主机与AT25XXX系列EEPROM存储器通讯的拓扑图如下所示:

      SPI主机通过SPI总线与AT25XXX系列EEPROM存储器通信,SPI总线由四条信号线组成:芯片选择(CS)、串行数据时钟(SCK)、串行数据输入(SI)和串行数据输出(SO)。SPI协议定义了总共四种操作模式(模式0、模式1、模式2或模式3),每种模式在SCK极性和相位以及极性和相位如何控制SPI总线上的数据流方面有所不同。AT25XXX系列EEPROM存储器支持两种最常见的模式,SPI模式0和3。

    1.3、命令操作

      AT25XXX系列EEPROM存储器被设计成直接与同步串行外围接口(SPI)接口。AT25XXX系列EEPROM存储器使用一个8位指令寄存器。所有的指令、地址和数据首先由高位开始传送,然后由高到低依次进行。指令列表及其操作代码如下:

      从上表我们知道,除了操作存储区域外,还可以操作状态寄存器。AT25XXX系列EEPROM存储器包括一个8位状态寄存器。状态寄存器位调节设备的各种特性。这些位可以通过指令进行更改。具体的结构如下:

      状态寄存器除了反应当前的状态外,实际上有些位还有配置功能。关于致谢为的属性及具体含义如下:

      通过写状态寄存器(WRSR)指令可以配置写保护使能和写保护的区域。块写保护位(BP1、BP0)决定了存储阵列的写保护区域。两位决定了阵列保护的四个级别,分别是:没有一个内存阵列被保护;上四分之一地址范围内存阵列被保护;上半部分地址范围内存阵列被保护;所有的内存阵列都是写保护的,这意味着所有的地址位都是只读的。块写保护级别和相应的状态寄存器控制位关系如下:

      而写保护使能 (WPEN)位用于启用或禁用写保护 (WP) 引脚。当WPEN位设置为逻辑“0”时,写入EEPROM数组的能力取决于块写保护(BP1、BP0)位的值。写入状态寄存器的权限是由WEL位控制的。当WPEN位设置为逻辑“1”时,状态寄存器是只读的。当WP引脚低且WPEN位设置为逻辑“1”时,硬件写保护就启用了。当设备被硬件写保护时,对状态寄存器的写操作,包括块写保护、WEL和WPEN位,以及对块写保护位所选择的内存阵列中的段的写操作被禁用。当启用硬件写保护时,只允许对未受块保护的内存段进行写。当WP引脚为高电平或WPEN位逻辑为“0”时,硬件写保护被禁用。当硬件写保护被禁用时,只允许对未被块保护的内存段进行写。当WPEN位被硬件写保护时,只要WP引脚保持低,它就不能被设置回逻辑“0”。写保护的关系如下所示:

      AT25XXX系列EEPROM存储器拥有从1K到2M的不同容量,寻址范围的不同所需的地址位数也不相同。地址位数根据容量从7位到18位不等,分别对应1到3个字节。具体的容量与地址位关系如下:

      需要注意的是,4K(512x8)容量的AT25XXX系列EEPROM存储器需要9为地址,但在实际操作时只用了1个字节来装载地址,最高位(第9位)地址借用了操作码的第4位来传送。

    2、驱动设计与实现

      我们已经了解了AT25XXX存储器的基本功能及读写方式,接下来我们将开发操作AT25XXX系列EEPROM存储器的驱动程序。

    2.1、对象定义

      在使用一个对象之前我们需要获得一个对象。同样的我们想要AT25XXX系列EEPROM存储器就需要先定义AT25XXX系列EEPROM存储器的对象。

    2.1.1、对象的抽象

      我们要得到AT25XXX系列EEPROM存储器对象,需要先分析其基本特性。一般来说,一个对象至少包含两方面的特性:属性与操作。接下来我们就来从这两个方面思考一下AT25XXX系列EEPROM存储器的对象。
      先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑AT25XXX系列EEPROM存储器对象属性。首先AT25XXX系列EEPROM存储器有多种型号,不同型号在容量、地址位数等方面都有较大差异。为了区别不同类型的AT25XXX系列EEPROM存储器,我们将类型作为对象的属性。另外每一个AT25XXX对象都有一个状态寄存器,它标识了AT25XXX对象的当前状态,所以我们也将它作为对象的属性。
      接着我们还需要考虑AT25XXX系列EEPROM存储器对象的操作问题。我们总是要对AT25XXX对象进行数据读写,但读写操作使用SPI接口依赖于具体的硬件平台,所以我们将数据读写作为对象的操作。片选信号、写保护以及hold信号均依赖于具体的硬件定义来实现,所以我们将其作为对象的操作。还有用于时序控制的延时,其也要根据具体的软硬件平台来实现,所以我们也将其定义为对象的操作。
      根据上述我们对AT25XXX系列EEPROM存储器的分析,我们可以定义AT25XXX系列EEPROM存储器的对象类型如下:

    /*定义AT25XXX对象类型*/
    typedef struct At25Object {
    	uint8_t status;				//状态寄存器
    	At25ModeType mode;		//设备类型
    	void (*Read)(uint8_t *rData,uint16_t rSize);       //读数据操作指针
    	void (*Write)(uint8_t *wData,uint16_t wSize);    //写数据操作指针
    	void (*Delayms)(volatile uint32_t nTime);       //毫秒延时操作指针
    	void (*ChipSelect)(AT25xxxCSType cs);	//使用SPI接口时,片选操作
    	void (*WP)(AT25WPType wp);						//写保护操作
    	void (*Hold)(AT25HoldType hold);			//保持信号
    }At25ObjectType;
    

    2.1.2、对象初始化

      我们知道,一个对象仅作声明是不能使用的,我们需要先对其进行初始化,所以这里我们来考虑AT25XXX系列EEPROM存储器对象的初始化函数。一般来说,初始化函数需要处理几个方面的问题。一是检查输入参数是否合理;二是为对象的属性赋初值;三是对对象作必要的初始化配置。据此我们设计AT25XXX系列EEPROM存储器对象的初始化函数如下:

    /* 初始化AT25XXX对象 */
    void At25xxxInitialization(At25ObjectType *at,	//AT25XXX对象实体
    					At25ModeType mode,		//AT25XXX对象类型
    					AT25Read read,				//读AT25XXX对象操作指针
    					AT25Write write,			//写AT25XXX对象操作指针
    					AT25Delayms delayms,		//延时操作指针
    					AT25ChipSelect cs			//片选操作函数指针
    				)
    {
    	if((at==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL))
    	{
    		return;
    	}
    	at->Read=read;
    	at->Write=write;
    	at->Delayms=delayms;
    	
    	if(cs!=NULL)
    	{
    		at->ChipSelect=cs;
    	}
    	else
    	{
    		at->ChipSelect=AT25ChipSelectDefault;
    	}
    
    	if(mode>=AT25Number)
    	{
    		return;
    	}
    	at->mode=mode;
    	
    	if(mode<AT25080B)
    	{
    		at->memAddLength=AT258BitMemAdd;
    	}
    	else if(mode<AT25M01)
    	{
    		at->memAddLength=AT2516BitMemAdd;
    	}
    	else
    	{
    		at->memAddLength=AT2524BitMemAdd;
    	}
    	
    	ReadStatusForAT25xxx(at);
    	
    	//写允许
    	SetWriteEnableLatchForAT25xxx(at);
    	
    	uint8_t cmd;
    	//使能写保护,保护全部区域
    	cmd=at->status|AT25_WPEN|AT25_BPALL;
    	WriteStatusForAT25xx(at,cmd);
    	
    	ReadStatusForAT25xxx(at);
    }
    

    2.2、对象操作

      我们已经完成了AT25XXX系列EEPROM存储器对象类型的定义和对象初始化函数的设计。但我们的主要目标是获取对象的信息,接下来我们还要实现面向AT25XXX系列EEPROM存储器的各类操作。

    2.2.1、读数据操作

      读取AT25XXX系列EEPROM存储器需要先将CS线拉低以选择设备,尔后发送READ(0x03)指令,在后发送要读的寄存器地址。一旦接收完寄存器地址,后续的信号将被忽略。然后返回指定地址的数据。读AT25XXX系列EEPROM存储器数据的操作时序如下:

      根据上述时序图,我们可以编写读AT25XXX系列EEPROM存储器数据的程序如下:

    /*从AT25xxx读取数据*/
    void ReadDatasFromAT25xxx(At25ObjectType *at,uint32_t regAddress,uint8_t *rData,uint16_t rSize)
    {
      uint8_t data[4];
    	uint16_t index=0;
    	uint8_t temp;
    	uint16_t size=0;
    	
    	data[index++]=AT25_READ;
    	
    	if(at->memAddLength==AT258BitMemAdd)
    	{
    		data[index++]=(uint8_t)regAddress;
    		
    		if(at->mode==AT25040B)
    		{
    			temp=(uint8_t)(regAddress>>8);
    			data[0]|=((temp&0x01)<<3);
    		}
    	}
    	else if(at->memAddLength==AT2516BitMemAdd)
    	{
    		data[index++]=(uint8_t)(regAddress>>8);
    		data[index++]=(uint8_t)regAddress;
    	}
    	else
    	{
    		data[index++]=(uint8_t)(regAddress>>16);
    		data[index++]=(uint8_t)(regAddress>>8);
    		data[index++]=(uint8_t)regAddress;
    	}
    
    	temp=(uint8_t)(regAddress&regAddMask[at->mode]);
    	if((rSize<=pageBytes[at->mode])&&(rSize<=(pageBytes[at->mode]-temp)))
    	{
    		size=rSize;
    	}
    	else
    	{
    		size=pageBytes[at->mode]-temp;
    	}
    	
    	at->ChipSelect(AT25CS_Enable);
    	at->Write(data,index);
    	at->Delayms(1);
    	at->Read(rData,size);
    	at->ChipSelect(AT25CS_Disable);
    }
    

      如果只需要读取一个字节,那么在读取一个字节后CS信号需在读取后恢复高电平。如果想读取多个字节,那么CS信号必须持续低电平,在存储器内部字节地址将自动递增,数据将继续移位。当到达最高地址时,地址计数器将滚动到最低阶地址,从而允许在一个连续的读取循环中读取整个内存,而不管起始地址是什么。

    2.2.2、写数据操作

      写AT25XXX系列EEPROM存储器,必须执行两条单独的指令。首先,必须通过写使能(WREN)指令使设备能够写。然后,可以执行写序列。如果设备没有启用写(WREN),设备将忽略写指令。在内部写周期中,除了RDSR指令外,所有命令都将被忽略。写AT25XXX系列EEPROM存储器的时序如下:

      根据上述时序图,我们可以编写写AT25XXX系列EEPROM存储器数据的程序如下:

    /*向AT25xxx写入数据*/
    void WriteDatasToAT25xxx(At25ObjectType *at,uint16_t regAddress,uint8_t *wData,uint16_t wSize)
    {
      uint8_t data[wSize+4];
    	uint16_t index=0;
    	uint8_t temp;
    	uint16_t size=0;
    	
    	data[index++]=AT25_WRITE;
    	
    	if(at->memAddLength==AT258BitMemAdd)
    	{
    		data[index++]=(uint8_t)regAddress;
    		
    		if(at->mode==AT25040B)
    		{
    			temp=(uint8_t)(regAddress>>8);
    			data[0]|=((temp&0x01)<<3);
    		}
    	}
    	else if(at->memAddLength==AT2516BitMemAdd)
    	{
    		data[index++]=(uint8_t)(regAddress>>8);
    		data[index++]=(uint8_t)regAddress;
    	}
    	else
    	{
    		data[index++]=(uint8_t)(regAddress>>16);
    		data[index++]=(uint8_t)(regAddress>>8);
    		data[index++]=(uint8_t)regAddress;
    	}
    
    	temp=(uint8_t)(regAddress&regAddMask[at->mode]);
    	if((wSize<=pageBytes[at->mode])&&(wSize<=(pageBytes[at->mode]-temp)))
    	{
    		size=wSize;
    	}
    	else
    	{
    		size=pageBytes[at->mode]-temp;
    	}
    
    	for(int i;i<size;i++)
    	{
    		data[index++]=wData[i];
    	}
    	
    	if(((at->status)&0x02)!=0x02)
    	{
    		SetWriteEnableLatchForAT25xxx(at);
    	}
    	
    	if(((at->status)&0x0C)!=0x00)
    	{
    		WriteStatusForAT25xx(at,at->status|AT25_BPNONE);
    	}
    	
    	at->ChipSelect(AT25CS_Enable);
    	at->Write(data,index);
    	at->ChipSelect(AT25CS_Disable);
    	
    	WriteStatusForAT25xx(at,at->status|AT25_BPALL);
    }
    

    2.2.3、状态寄存器操作

      前面我们已经详细描述过状态寄存器的格式以及每一位的定义。有一些位是只读的,有一些位是可读写的,记下来我们实现针对状态寄存器中的操作。

    (1)、读状态寄存器

      读状态寄存器(RDSR)指令提供对状态寄存器的访问。RDSR指令可以确定设备的状态以及块写保护(BP1, BP0)位表示所使用的内存阵列保护的范围。读取状态寄存器的方法是拉低CS信号,然后发送0x05操作码。操作码完成后,设备将返回8位状态寄存器值。具体时序如下:

      根据上述时序图,我们可以编写读AT25XXX系列EEPROM存储器状态寄存器的程序如下:

    /*读AT25xxx状态寄存器*/
    void ReadStatusForAT25xxx(At25ObjectType *at)
    {
      uint8_t opCode=AT25_RDSR;
    	uint8_t status;
    	
    	at->ChipSelect(AT25CS_Enable);
    	at->Write(&opCode,1);
    	at->Delayms(1);
    	at->Read(&status,1);
    	at->ChipSelect(AT25CS_Enable);
    	
    	at->status=status;
    }
    

    (2)、写状态寄存器

      写状态寄存器(WRSR)指令使SPI主机能够更改状态寄存器的选定位。在WRSR指令开始之前,必须执行一条WREN指令,将WEL位设置为逻辑“1”。WREN指令完成后,可以执行WRSR指令。在WRSR指令之后,AT25XXX系列EEPROM存储器将不会响应除RDSR以外的命令,直到自动计时的内部写周期完成。写周期结束后,状态寄存器中的WEL位复位为逻辑“0”。具体的时序图如下:

      根据上述时序图,我们可以编写写AT25XXX系列EEPROM存储器状态寄存器的程序如下:

    /*写AT25xxx状态寄存器*/
    void WriteStatusForAT25xx(At25ObjectType *at,uint8_t cmd)
    {
      uint8_t data[2];
    	
    	data[0]=AT25_WRSR;
    	data[1]=cmd;
    	
    	if(((at->status)&0x02)!=0x02)
    	{
    		SetWriteEnableLatchForAT25xxx(at);
    	}
    	
    	if((((at->status)&AT25_WPEN)!=AT25_WPEN)&&(at->WP!=NULL))
    	{
    		at->WP(AT25WP_Disable);
    	}
    	
    	at->ChipSelect(AT25CS_Enable);
    	at->Write(data,2);
    	at->ChipSelect(AT25CS_Disable);
    	
    	ReadStatusForAT25xxx(at);
    	
    	if(at->WP!=NULL)
    	{
    		at->WP(AT25WP_Enable);
    	}
    }
    

      WRSR指令对状态寄存器的第6位、第5位、第4位、第1位和第0位没有影响。只有第7位、第3位和第2位可以通过WRSR指令进行更改。这些可修改的位是写保护使能(WPEN)和块保护(BP1, BP0)位。这三个位元是非易失性位元,具有与常规EEPROM单元相同的特性和功能。当电源从设备中移除时,它们的值被保留。

    2.2.4、写操作使能与失能操作

      通过写使能(WREN)指令和写失能(WRDI)指令实现对状态寄存器和EEPROM阵列的写操作的启用和禁用。这些功能改变了状态寄存器中WEL位的状态。

    (1)写操作启用

      状态寄存器的写能门闩(WEL)位必须在每个写状态寄存器(WRSR)和写入内存阵列(Write)指令之前设置为逻辑“1”。这是通过向AT25XXX系列EEPROM存储器发送一条WREN(0x06)指令来完成的。首先,CS引脚被拉低以选择设备,然后发送WREN指令。然后CS信号被拉高,并将状态寄存器中的WEL位更新为逻辑“1”。具体的操作时序如下:

      根据上述时序图,我们可以编写AT25XXX系列EEPROM存储器操作启用的程序如下:

    /* AT25XXX设置写使能所存器*/
    void SetWriteEnableLatchForAT25xxx(At25ObjectType *at)
    {
    	uint8_t opCode=AT25_WREN;
    	
    	at->ChipSelect(AT25CS_Enable);
    	at->Write(&opCode,1);
    	at->ChipSelect(AT25CS_Enable);
    	
    	ReadStatusForAT25xxx(at);
    }
    

    (2)、写操作禁用

      为了防止误写,写禁用(WRDI)指令(0x04)通过将WEL位设置为逻辑“0”来禁用所有编程模式。WRDI指令与WP引脚的状态无关。具体的操作时序如下图所示:

      根据上述时序图,我们可以编写AT25XXX系列EEPROM存储器操作禁用的程序如下:

    /* AT25XXX复位写使能所存器*/
    void ResetWriteEnableLatchForAT25xxx(At25ObjectType *at)
    {
    	uint8_t opCode=AT25_WRDI;
    	
    	at->ChipSelect(AT25CS_Enable);
    	at->Write(&opCode,1);
    	at->ChipSelect(AT25CS_Enable);
    	
    	ReadStatusForAT25xxx(at);
    }
    

    3、驱动的使用

      在上一节我们设计并实现了AT25XXX系列EEPROM存储器的驱动程序,而这一节我们将设计一个简单的应用来验证这一驱动程序。

    3.1、声明并初始化对象

      使用基于对象的操作我们需要先得到这个对象,所以我们先要使用前面定义的AT25XXX系列EEPROM存储器对象类型声明一个AT25XXX系列EEPROM存储器对象变量,具体操作格式如下:
      At25ObjectType at25;
      声明了这个对象变量并不能立即使用,我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下:

    At25ObjectType *at,AT25XXX对象实体
    At25ModeType mode,AT25XXX对象类型
    AT25Read read,读AT25XXX对象操作指针
    AT25Write write,写AT25XXX对象操作指针
    AT25Delayms delayms,延时操作指针
    AT25ChipSelect cs,片选操作函数指针
    

      对于这些参数,对象变量我们已经定义了。而对象类型为枚举,根据实际使用的AT25XXX系列EEPROM存储器来选择就好了。主要的是我们需要定义几个函数,并将函数指针作为参数。这几个函数的类型如下:

    /* 定义读数据操作函数指针类型 */
    typedef void (*AT25Read)(uint8_t *rData,uint16_t rSize);
    /* 定义写数据操作函数指针类型 */
    typedef void (*AT25Write)(uint8_t *wData,uint16_t wSize);
    /* 定义延时操作函数指针类型 */
    typedef void (*AT25Delayms)(volatile uint32_t nTime);
    /* 定义使用SPI接口时,片选操作函数指针类型 */
    typedef	void (*AT25ChipSelect)(AT25xxxCSType cs);
    

      对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。片选操作函数用于多设备需要软件操作时,如采用硬件片选可以传入NULL即可。具体函数定义如下:

    /*读AT25寄存器值*/
    static void ReadDataFromAT25(uint8_t *rData,uint16_t rSize)
    {
    	HAL_SPI_Receive (&at25hspi,rData,rSize,1000);
    }
    
    /*写AT25寄存器值*/
    static void WriteDataToAT25(uint8_t *wData,uint16_t wSize)
    {
      HAL_SPI_Transmit (&at25hspi,wData,wSize,1000);
    }
    
    /*片选操作*/
    void ChipSelectForAT25(AT25xxxCSType cs)
    {
    	if(cs==AT25CS_Enable)
      {
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
      }
      else
      {
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
      }
    }
    

      对于延时函数我们可以采用各种方法实现。我们采用的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下:

    At25xxxInitialization(&at25,		//AT25XXX对象实体
    					AT25M01,	//AT25XXX对象类型
    					ReadDataFromAT25,		//读AT25XXX对象操作指针
    					WriteDataToAT25,		//写AT25XXX对象操作指针
    					HAL_Delay,		//延时操作指针
    					ChipSelectForAT25		//片选操作函数
    						   );
    

    3.2、基于对象进行操作

      我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。我们在驱动中已经将获取数据并转换为转换值的比例值,接下来我们使用这一驱动开发我们的应用实例。

    /*AT25XXX数据读写操作*/
    void AT25ReadWriteData(void)
    {
    	uint16_t regAddress=0x02;
    	uint8_t readByte;
    	uint8_t writeByte=0x0A;
    	uint8_t rData[2];
    	uint16_t rSize=2;
    	uint8_t wData[]={0x5A,0xA5};
    	uint16_t wSize=2;
    	
    	/*从AT25XXX读取单个字节,从随机地址读取*/
    	readByte=ReadByteFromAT25xxx(&at25,regAddress);
    
    	/*向AT25XXX写入单个字节*/
    	WriteByteToAT25xxx(&at25,regAddress,writeByte);
    
    	/*从AT25XXX读取多个字节,从指定地址最多到所在页的结尾*/
    	ReadBytesFromAT25xxx(&at25,regAddress,rData,rSize);
    
    	/*向AT25XXX写入多个字节,从指定地址最多到所在页的结尾*/
    	WriteBytesToAT25xxx(&at25,regAddress,wData,wSize);
    }
    

    4、应用总结

      在本片中我们讨论并设计了AT25XXX系列EEPROM存储器的驱动程序,并据此设计了一个简单的验证应用。无论是写数据还是读数据均可顺利执行,说明我们的驱动设计是正确的。
      需要注意的是,4K(512x8)容量的AT25XXX系列EEPROM存储器需要9为地址,但在实际操作时只用了1个字节来装载地址,最高位(第9位)地址借用了操作码的第4位来传送。
      在使用驱动时需注意,采用SPI接口的器件需要考虑片选操作的问题。如果片选信号是通过硬件电路来实现的,我们在初始化时给其传递NULL值。如果是软件操作片选则传递我们编写的片选操作函数。
      在这一驱动设计的过程中我们并未验证读写数据的正确性,事实上如果是比较重要的数据我们可以为其添加验证,如CRC验证等。

    欢迎关注:

  • 相关阅读:
    python基础——返回函数
    python基础——sorted()函数
    python基础——filter函数
    python基础——map/reduce
    python基础——高阶函数
    python基础——迭代器
    python——生成器
    轻量级文本编辑器,Notepad最佳替代品:Notepad++
    python网络编程
    如何检测远程主机上的某个端口是否开启?
  • 原文地址:https://www.cnblogs.com/foxclever/p/16102109.html
Copyright © 2020-2023  润新知