网站建设行业研究,国内网站需要备案,电商网站建设心得体会,网站开发string文件虽然说使用EEPROM保存参数很有效#xff0c;但操作及使用次数均有一下限制。当我们的一些参数需要不定时修改或存储时#xff0c;使用FRAM就更为方便一点。这一节我们就来设计并实现FM24xxx系列FRAM的驱动。
1、功能概述
我们首先说一下铁电随机存取存储器#xff0c;F-RA…虽然说使用EEPROM保存参数很有效但操作及使用次数均有一下限制。当我们的一些参数需要不定时修改或存储时使用FRAM就更为方便一点。这一节我们就来设计并实现FM24xxx系列FRAM的驱动。
1、功能概述
我们首先说一下铁电随机存取存储器F-RAM是非易失性的其读写操作与RAM类似。它提供了151年的可靠数据保存同时消除了由EEPROM和其他非易失性内存引起的复杂性、开销和系统级可靠性问题。
1.1、硬件描述
FM24xxx系列FRAM存储器拥有从4K到1M的各种容量。虽然不同型号的FM24xxx系列FRAM存储器内部存储矩阵存在差异但都采用了相同的封装和引脚排布。具体的引脚分布如下图 FM24xxx系列FRAM存储器与EEPROM不同F-RAM以总线速度执行写操作。没有发生写延迟。数据在每个字节成功传输到设备后立即写入内存数组。下一个总线循环可在不需要进行数据轮询的情况下开始。此外与其他非易失性存储器相比该产品具有较强的写持久性。此外F-RAM在写期间的功耗比EEPROM低得多因为写操作不需要在内部提高写电路的电源电压。F-RAM能够支持10的14次方个读/写周期或比EEPROM多1亿倍的写周期。
这些功能使得FM24xxx系列FRAM存储器非常适合非易失性内存应用程序因为它需要频繁或快速的写操作。例如数据日志记录(写入周期的数量可能至关重要)和严格的工业控制EEPROM的长写入时间可能导致数据丢失。这些特性的组合允许更频繁地编写数据同时减少系统的开销。
1.2、通讯接口
FM24xxx系列FRAM存储器采用I2C通讯接口。其设备地址的前4位固定为1010b后3位则用于设备地址或页面地址所以若后3为均用于设备地址则在同一条I2C总线上最多可以带8个同类设备。其与主机之间的连接是以图如下所示 FM24xxx系列FRAM存储器采用8位或者16位内存地址对于不同的存储容量寻址的最终范围当然是不同的其寻址范围为从512到131072不等。关于设备地址与内存地址的分配不同型号和容量的FM24xxx系列FRAM存储器是不一样的。具体分配如下图所示 从上表我们很容易明白设备地址的低3位的定义决定了在同一条I2C总线上最多可以挂载多少个FM24xxx设备。有3位用于设备地址则最多可挂载8个设备有2位用于设备地址则最多可挂载4个设备有1位用于设备地址则最多可挂载2个设备有0位用于设备地址则最多可挂载1个设备。需要注意的是不同定义的位的设备混用于同一总线时相同的定义位必须一样否则用作寄存器地址的位可能让总线上的总线无法识别。
2、驱动设计与实现
我们在前面一节已经简要的描述了FM24xxx FRAM存储器的读写操作方式在这一节我们将设计并实现FM24xxx FRAM存储器的驱动程序。
2.1、对象的定义
在使用一个对象之前我们需要获得一个对象。同样的我们想要FM24xxx FRAM存储器就需要先定义FM24xxx FRAM存储器的对象。
2.1.1、对象的抽象
我们要得到FM24xxx FRAM存储器对象需要先分析其基本特性。一般来说一个对象至少包含两方面的特性属性与操作。接下来我们就来从这两个方面思考一下FM24xxx FRAM存储器的对象。
先来考虑属性作为属性肯定是用于标识或记录对象特征的东西。我们来考虑FM24xxx FRAM存储器对象属性。首先作为I2C从设备都需要有一个设备地址用以区分总线上的不同设备所以我们将其作为对象的一个属性。FM24xxx FRAM存储器不同的型号在容量、寻址等方面有一些差异为了正确操作不同类型的设备我们将其作为对象的一个属性。不同容量的FM24xxx FRAM存储器因寻址范围不同所使用的地址寄存器位数也不相同为了便于操作我们将地址位长度也作为对象的一个属性。
接着我们还需要考虑FM24xxx FRAM存储器对象的操作问题。我们要对FM24xxx FRAM存储器进行读写但读写都需要同过具体的I2C接口进行这依赖于具体的硬件平台所以我们将针对端口的读写作为对象的操作。FM24xxx FRAM存储器还有一个写保护引脚用于设置内部存储器的写保护问题这同样依赖于硬件平台来实现所以我们也将它作为对象的操作。
根据上述我们对FM24xxx FRAM存储器的分析我们可以定义FM24xxx FRAM存储器的对象类型如下
/*定义FM24XXX对象类型*/
typedef struct FM24Object {uint8_t devAddress; //FM24xxx设备地址FM24ModeType mode; //FM24xxx设备类型FM24MemAddLengthType memAddLength; //存储器地址长度void (*WP)(FM24WPType wp); //写保护操作void (*Read)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize); //读数据操作指针void (*Write)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize); //写数据操作指针void (*Delayms)(volatile uint32_t nTime); //延时操作指针
}FM24ObjectType;
2.1.2、对象初始化
我们知道一个对象仅作声明是不能使用的我们需要先对其进行初始化所以这里我们来考虑FM24xxx FRAM存储器对象的初始化函数。一般来说初始化函数需要处理几个方面的问题。一是检查输入参数是否合理二是为对象的属性赋初值三是对对象作必要的初始化配置。据此我们设计FM24xxx FRAM存储器对象的初始化函数如下
/*FM24XXX对象初始化*/
void Fm24cxxInitialization(FM24ObjectType *fram, //FM24xxx对象实体uint8_t devAddress, //FM24xxx设备地址FM24ModeType mode, //FM24xxx设备类型Fm24WP wp, //FM24xxx写保护Fm24Read read, //读FM24xxx对象操作指针Fm24Write write, //写FM24xxx对象操作指针Fm24Delayms delayms //延时操作指针)
{if((framNULL)||(readNULL)||(writeNULL)||(delaymsNULL)){return;}fram-Readread;fram-Writewrite;fram-Delaymsdelayms;if((0xA0devAddress)(devAddress0xAE)){fram-devAddressdevAddress;}else if((0x50devAddress)(devAddress0x57)){fram-devAddress(devAddress1);}else{fram-devAddress0x00;}if(modeFM24Number){return;}fram-modemode;if((modeFM24CL64B)(mode!FM24C64B)){fram-memAddLengthFM248BitMemAdd;}else{fram-memAddLengthFM2416BitMemAdd;}fram-WPwp;
}
2.2、写对象操作
所有写操作都以从站地址开始然后是内存地址。总线主机通过将从站地址R/W位的LSB设置为“0”来指示写操作。寻址之后总线主机将每个字节的数据发送到FM24xxx FRAM存储器存储器生成一个确认条件。可以写入任意数量的顺序字节如果在内部到达地址范围的末尾地址计数器将从最高地址回滚到最低地址。
2.2.1、写单个字节
FM24xxx FRAM存储器允许在任意地址写一个字节数据。在内部实际的存储器写发生在第8位数据传输之后。它将在确认发送之前完成。因此如果用户希望在不改变存储器内容的情况下中止写操作那么应该在第8个数据位之前使用START或STOP条件来完成。FM24xxx FRAM存储器不使用页面缓冲。具体的操作时序如下 根据上述时序图我们可以设计FM24xxx FRAM存储器写单个字节数据程序如下
/*向FM24XXX写入单个字节*/
void WriteByteToFM24xxx(FM24ObjectType *fram,uint32_t regAddress,uint8_t data)
{uint8_t temp;uint8_t tData[3];uint16_t tSize0;if(fram-memAddLengthFM248BitMemAdd){tData[tSize](uint8_t)(regAddress0xFF);temp(uint8_t)(regAddress8);}else{tData[tSize](uint8_t)(regAddress8);tData[tSize](uint8_t)(regAddress0xFF);temp(uint8_t)(regAddress16);}temp(temp(~(devAddMask[fram-mode]1)))1;fram-devAddress(fram-devAddress devAddMask[fram-mode])|temp;tData[tSize]data;fram-WP(FM24WP_Disable);fram-Write(fram,tData,tSize);fram-WP(FM24WP_Enable);
}
2.2.2、写多个字节
FM24xxx FRAM存储器也允许从一个地址开始顺序写入多个字节。存储器内部地址指针会跟随写操作自动增加其他的过程这与写单个字节相同。具体的操作时序如下 根据上述时序图我们可以设计FM24xxx FRAM存储器写多个字节数据程序如下
/*向FM24XXX写入多个字节从指定地址最多到所在页的结尾*/
void WriteBytesToFM24xxx(FM24ObjectType *fram,uint32_t regAddress,uint8_t *wData,uint16_t wSize)
{uint8_t temp;uint8_t tData[wSize2];uint16_t tSize0;if(fram-memAddLengthFM248BitMemAdd){tData[tSize](uint8_t)(regAddress0xFF);temp(uint8_t)(regAddress8);}else{tData[tSize](uint8_t)(regAddress8);tData[tSize](uint8_t)(regAddress0xFF);temp(uint8_t)(regAddress16);}temp(temp(~(devAddMask[fram-mode]1)))1;fram-devAddress(fram-devAddress devAddMask[fram-mode])|temp;for(int i0;iwSize;i){tData[tSize]wData[i];}fram-WP(FM24WP_Disable);fram-Write(fram,tData,tSize);fram-WP(FM24WP_Enable);
}
2.3、读对象操作 与写操作相对应FM24xxx FRAM存储器允许从当前地址指针位置或任意制定的地址指针位置读取单个或多个字节。为了执行选择性读取总线主机发送最低位R/W设置为0的从站地址。根据写协议总线主机随后发送加载到内部地址锁存器的地址字节。在FM24xxx FRAM存储器确认地址后总线主机发出一个START条件。这将同时中止写操作并允许发出读命令并将从站地址最低为设置为“1”。
2.3.1、读单个字节
FM24xxx FRAM存储器允许从当前地址指针位置或任意制定的地址指针位置读取单个字节。当然从当前地址指针读可以不用设置存储器地址不过我们考虑一般性同意为从任意制定地址读取一个字节。具体的操作时序如下 根据上述时序图我们可以设计FM24xxx FRAM存储器读单个字节数据程序如下
/*从FM24XXX读取单个字节,从随机地址读取*/
uint8_t ReadByteFromFM24xxx(FM24ObjectType *fram,uint32_t regAddress)
{uint8_t wData[2];uint16_t wSize0;uint8_t rData;uint8_t temp;if(fram-memAddLengthFM248BitMemAdd){wData[wSize](uint8_t)regAddress;temp(uint8_t)(regAddress8);}else{wData[wSize](uint8_t)(regAddress8);wData[wSize](uint8_t)regAddress;temp(uint8_t)(regAddress16);}temp(temp(~(devAddMask[fram-mode]1)))1;fram-devAddress(fram-devAddress devAddMask[fram-mode])|temp;fram-Read(fram,wData,wSize,rData,1);return rData;
}
2.3.2、读多个字节
FM24xxx FRAM存储器允许从当前地址指针位置或任意制定的地址指针位置读取多个字节。如果从当前的地址指针开始读可以不用设置存储器地址。但更一般的是从任意地址开始读多个字节所以我们考虑从任意地址开始读取。具体的操作时序如下 根据上述时序图我们可以设计FM24xxx FRAM存储器读多个字节数据程序如下
/*从FM24XXX读取多个字节从指定地址最多到所在页的结尾*/
void ReadBytesFromFM24xxx(FM24ObjectType *fram,uint32_t regAddress,uint8_t *rData,uint16_t rSize)
{uint8_t temp;uint8_t wData[2];uint16_t wSize0;if(fram-memAddLengthFM248BitMemAdd){wData[wSize](uint8_t)regAddress;temp(uint8_t)(regAddress8);}else{wData[wSize](uint8_t)(regAddress8);wData[wSize](uint8_t)regAddress;temp(uint8_t)(regAddress16);}temp(temp(~(devAddMask[fram-mode]1)))1;fram-devAddress(fram-devAddress devAddMask[fram-mode])|temp;fram-Read(fram,wData,wSize,rData,rSize);
}
在传输每个数据字节之后在返回确认之前FM24xxx存储器会递增内部地址锁存器。这允许不使用其它地址访问下一个顺序字节。到达最后一个地址后地址锁存器将滚到0000h。一个读或写操作可以访问的字节数理论上讲是没有限制。
2.4、休眠模式操作
在FM24xxx FRAM存储器的某些型号的设备上实现了一种称为休眠模式的低功耗模式。当休眠命令0x86被锁定时设备将进入这种低功耗状态。进入睡眠模式的操作时序如下 根据上述时序图我们可以设计FM24xxx FRAM存储器进入休眠模式的程序如下
/*FM24XXX对象进入休眠模式*/
void FM24xxxEnterSleepMode(FM24ObjectType *fram)
{uint8_t wData[2];wData[0]fram-devAddress;fram-devAddress0xF8;wData[1]0x86;fram-Write(fram,wData,2);fram-devAddresswData[0];
}
一旦进入休眠模式设备建议低功耗运行但设备会继续监控I2C引脚。一旦主站设备发送一个FM24xxx FRAM存储器标识的从站设备地址它将被“唤醒”并进入正常运行。
3、驱动的使用
FM24xxx FRAM存储器驱动程序我们已经实现了但这一驱动程序是否能正确读写是否能稳定工作还需要验证。所以接下来我们将设计一个简单的应用验证FM24xxx FRAM存储器驱动程序。
3.1、声明并初始化对象
使用基于对象的操作我们需要先得到这个对象所以我们先要使用前面定义的FM24xxx FRAM存储器对象类型声明一个FM24xxx FRAM存储器对象变量具体操作格式如下
FM24ObjectType fm24;
声明了这个对象变量并不能立即使用我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下
FM24ObjectType *framFM24xxx对象实体
uint8_t devAddressFM24xxx设备地址
FM24ModeType modeFM24xxx设备类型
Fm24WP wpFM24xxx写保护
Fm24Read read读FM24xxx对象操作指针
Fm24Write write写FM24xxx对象操作指针
Fm24Delayms delayms延时操作指针
对于这些参数对象变量我们已经定义了。设备地址根据实际的硬件设置。设备类型为枚举根据实际使用的设备类型情况选择就好了。主要的是我们需要定义几个函数并将函数指针作为参数。这几个函数的类型如下
//写保护操作
typedef void (*Fm24WP)(FM24WPType wp); /* 定义读数据操作函数指针类型 */
typedef void (*Fm24Read)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize); /* 定义写数据操作函数指针类型 */
typedef void (*Fm24Write)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize); /* 定义延时操作函数指针类型 */
typedef void (*Fm24Delayms)(volatile uint32_t nTime);
对于这几个函数我们根据样式定义就可以了具体的操作可能与使用的硬件平台有关系。具体函数定义如下
/*读FM24XXX寄存器值*/
static void ReadDataFromFM24(FM24ObjectType *fram,uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize)
{HAL_I2C_Master_Transmit(fm24hi2c,fram-devAddress,wData,wSize,1000);HAL_I2C_Master_Receive(fm24hi2c,fram-devAddress1,rData, rSize, 1000);
}/*写FM24XXX寄存器值*/
static void WriteDataToFM24(FM24ObjectType *fram,uint8_t *wData,uint16_t wSize)
{HAL_I2C_Master_Transmit(fm24hi2c,fram-devAddress,wData,wSize,1000);
}/*写保护操作*/
void FM24WriteProtected(FM24WPType wp)
{if(wpFM24WP_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);}
}
对于延时函数我们可以采用各种方法实现。我们采用的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下
Fm24cxxInitialization(fm24, //FM24XXX对象实体0xAE, //FM24XXX设备地址FM24V10, //FM24XXX对象类型FM24WriteProtected, //写保护操作指针ReadDataFromFM24, //读FM24XXX对象操作指针WriteDataToFM24, //写FM24XXX对象操作指针HAL_Delay //延时操作指针);
3.2、基于对象进行操作
我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。我们在驱动中已经将获取数据并转换为转换值的比例值接下来我们使用这一驱动开发我们的应用实例。
/*FM24XXX数据操作*/
void FM24ReadWriteData(void)
{uint16_t regAddress0x02;uint8_t readByte;uint8_t writeByte0x0A;uint8_t rData[2];uint16_t rSize2;uint8_t wData[]{0x5A,0xA5};uint16_t wSize2;/*从FM24XXX读取单个字节,从随机地址读取*/readByteReadByteFromFM24xxx(fm24,regAddress);/*向FM24XXX写入单个字节*/WriteByteToFM24xxx(fm24,regAddress,writeByte);/*从FM24XXX读取多个字节从指定地址最多到所在页的结尾*/ReadBytesFromFM24xxx(fm24,regAddress,rData,rSize);/*向FM24XXX写入多个字节从指定地址最多到所在页的结尾*/WriteBytesToFM24xxx(fm24,regAddress,wData,wSize);
}
4、应用总结
我们实现了FM24xxx FRAM存储器的驱动程序并基于这一驱动程序设计了简单的验证应用能够写入数据并能将其读出来。这与我们预期的结果是一致的驱动设计基本正确。
FM24xxx FRAM存储器内存数组可以使用WP引脚进行写保护。将WP引脚设置为高状态VDD将写保护所有地址。果试图对这些地址进行写操作地址计数器不会增加。FM24xxx FRAM存储器将不会理会写入受保护地址的数据字节。如将WP设置为低状态VSS将禁用写保护此时整个区域都是可以进行写操作的。
FM24xxx FRAM存储器与其他非易失性内存技术不同F-RAM没有有效的写延迟。由于底层内存的读和写访问时间相同因此用户不会在总线上体验延迟。整个内存周期的时间比一个总线时钟还短。因此任何操作包括读或写都可以在写之后立即执行。
源码下载https://github.com/foxclever/ExPeriphDriver
欢迎关注