嵌入式开发--赛普拉斯cypress的铁电存储器FM25CL64B

news2024/11/16 21:40:05

嵌入式开发–赛普拉斯cypress的铁电存储器FM25CL64B

简介

FM25CL64B是赛普拉斯cypress出品的一款铁电存储器,这种存储器最大的优势是可以像RAM一样随机存储,和按字节写入,也可以像ROM一样掉电仍然可以保存数据,是一种相当优秀的新型存储器,但是容量不能做得很大,只适合保存一些重要数据。

重要参数及解读如下:

64K位,即8K字节
100T的读写次数,这意味着即使对于同一单元,每毫秒读取或写入一次,也需要3170年才能消耗完这个次数,而我们对存储器的访问几乎不可能达到这样的频率,也不可能只访问一个数据单元,所以它的读写次数寿命虽然有限,但可以不在考虑之列。
151年的数据保存周期,除了极其特殊用途,基本上是足够的了,毕竟我们之前用的光盘和现在FLASH,数据保存周期也才10年而已。
SPI接口,频率支持到20M,可直接硬件替换SPI口的EEPROM,SPI支持MODO_0 (CPOL = 0, CPHA = 0)即时钟空闲时为低,第1个跳变沿采样数据;和MODO_3(CPOL = 1, CPHA = 1)即时钟空闲时为高,第2个跳变沿采样数据。看手册是自动识别,但我没有测试。
受保护以避免写入的区域为:1/4或1/2或全片保护可选
8脚的SOP或DFN封装
-40~85度温度

芯片框图

封装
在这里插入图片描述

内部工作框图:
在这里插入图片描述

硬件电路图

在这里插入图片描述
我接的是MCU的SP1口,如下:
在这里插入图片描述

CubeMX初始化设置

PA15设置为GPIO输出,速度设为High,由软件控制这个片选引脚。
在这里插入图片描述
Motorola格式,16位数据,MSB,CPOL和CPHA参照之前的说明,CRC校验关闭,软件片选。
软件片选的原因是ST的SPI接口中,NSS不是通常意义上的片选, 一旦开启SPI,这个引脚就只能为低,不能为高。而且,如果SPI总线上还有其他设备,也只能用软件片选。
需要注意的是Baud Rate这里,不能超过20M

芯片操作

控制码

控制码共有6个,如下图
在这里插入图片描述
分别对应的是
WREN 写使能
WRDI 写禁止
RDSR 读状态寄存器
WRSR 写状态寄存器
READ 读操作
WRITE 写操作

其他操作无意义或者说不开放,后面会讲到。

写使能

这是8位命令。
在写入数据之前需要先进行写使能操作,否则无法写入
同时,在SO数据线上,无数据响应,也就是说写使能是单向操作,芯片没有回应。
具体时序如下:
在这里插入图片描述

写禁止WRDI

这是8位命令。
执行本命令后,对芯片的写操作无效。
同样的,在执行本命令时,SO无回应。
在这里插入图片描述

8位操作码

对于前面2个8位操作码,由于我们在CubeMX中,将SPI设置为16位数据,那么无法完成对8位操作码,即1字节的操作。
例如:当发送WRDI(0x04)时,解决方案有3个:
1 发送重复的操作指令,发送0x0404来实现,即2次同样的操作。
2 发送0x0004,或0x0400,由于0x00不是操作码,芯片不会对其响应。
3 发送8位操作码时,修改SPI的寄存器,以实现8位操作。发送完成后再改为16位模式。
我用的是方案1,实测工作正常,就不再折腾寄存器了。

读状态寄存器

16位操作命令
前8位是操作码RDSR(0x05),后8位SI可以没有任何数据,有也不影响。
同时,在后8位时,SO会有数据输出,结束后在SPI的DR寄存器可以看到结果,也就是状态寄存器SR的数据。
如果此时SO上没有观察到波形,说明配置有错误。
在这里插入图片描述

写状态寄存器

16位操作命令,前8位是操作码0x01,后8位是需要写入状态寄存器的值。
SO线没有波形输出。
在这里插入图片描述

写保护

需要注意的是,向SR写入数据时,读取的不一定和写入相同。这与写保护的相关设置有关联,而且只有BIT1,2,3,7位是有效位,其他的位读出来永远是0。
具体请查看写保护的相关部分,我是向状态寄存器写0,然后读到的值应该是0x02,也就是说开启写操作,而且状态寄存器和存储空间均不保护,处于可写的状态。
写入完成后,将WEL位设置为0即可,如下图是相关的位,与保护区域的表格。
在这里插入图片描述

读数据和写数据

这两个操作类似,就放到一块说了。
这是32位操作码,
开始的8位,是操作码
接下来的8位,是地址码的高8位,由于本芯片是64K位,即8K字节,也就是说,地址码的有效位是3+8=11位,所以本区段 只有3个数据位是有效位。当然对于不同容量的芯片来说,有效位数是不一样的。
第3个8位,是地址码的低8位
最后8位,是数据位,写数据时,在SI线上有波形,读数据时,在SO上有波形。
在这里插入图片描述

HOLD引脚

HOLD引脚是用来暂停当前操作的,必须在时钟信号为低期间,HOLD引脚才能为低,
此时时钟,SI这两根线可以任意变化
需要退出HOLD状态前,时钟必须为低,SO可任意。
当HOLD拉高后,继续之前的操作。
个人感觉这个功能不太实用,只有当软件模拟SPI时才用得上。硬件SPI时,有折腾时序的时间,SPI的活都干完了
不用这个功能的话,本引脚接高即可。

WP引脚

硬件写保护,提供对状态寄存器的保护。
该引脚为高时,禁止对状态寄存器写入。可防止误操作。
不用这个功能的话,本引脚接高即可。

写入次数分析

以下是对同一个数据单元进行重复读写的寿命评估。
第1列是SPI时钟频率
第2列是每秒可以访问的次数
第3列是每年的访问次数
第4列是可以不间断写入多少年
在这里插入图片描述
可以看出,即使以本芯片所能支持的最高速度20MHz,来不间断的对同一数据单元进行读写访问,仍然可以维持85年,这个寿命是妥妥的够了。
更何况我们不可能总是读写同一个数据单元,而且不可能以如此高的频率进行数据访问。
所以芯片的寿命是可以放心的。

相关代码

LL_FRAM.h文件的代码

#ifndef __LL_FRAM_H__
#define __LL_FRAM_H__


#include "LL_define.h"
#include "spi.h"

//状态寄存器   X表示无意义,默认值是0
//  BIT7      BIT6      BIT5      BIT4      BIT3      BIT2      BIT1      BIT0
//  WPEN      X(0)      X(0)      X(0)      BP1       BP0       WEL       X(0)
//  WEL:      1为写使能,0为禁止写
//  BP0,BP1:  块保护
//  WPEN:     

#define FRAM_WREN   0x06    //写入使能,即设置WEL位       在写操作(WRSR和WRITE)之前,必须先发本命令
#define FRAM_WRDI   0x04    //写入禁止,即清除WEL位       本操作会禁止写操作(WRSR和WRITE),此后即使发送(WRSR和WRITE)也无效
#define FRAM_RDSR   0x05    //读状态寄存器
#define FRAM_WRSR   0x01    //写状态寄存器,前序操作为FRAM_WREN
#define FRAM_READ   0x03    //读
#define FRAM_WRITE  0x02    //写,前序操作为FRAM_WREN


#define SPI1_NSS(n)  (n?HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET):HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET))


extern u8 spi_read_data;  //spi读取的数值


u8 LL_fram_read_sr(void);
u8 LL_fram_write_sr(u8 value);
u8 LL_fram_read(u16 addr, u8 ret_data);
u8 LL_fram_write(u16 addr, u8 value);

#endif

LL_FRAM.c文件的代码

#include "LL_FRAM.h"

u8 spi_read_data=0;
u8 spi_tx_data[10] = {0x06};


u8 LL_fram_read_sr(void)  //读状态寄存器,其返回值是状态寄存器的内容
{
  u8 data[4];
  u8 ret;
  
  //写使能
  data[0] = FRAM_WREN;
  data[1] = FRAM_WREN;
  SPI1_NSS(0);
  HAL_SPI_Transmit(&hspi1, data, 1, 100);
  SPI1_NSS(1);

  //读状态寄存器,必须先有写使能操作FRAM_WREN  0x05
  data[0] = 0;
  data[1] = FRAM_RDSR;
  SPI1_NSS(0);
  HAL_SPI_TransmitReceive(&hspi1, data, &ret, 1, 10);
  SPI1_NSS(1);
  
  return ret;
}


u8 LL_fram_write_sr(u8 value)   //写状态寄存器,其函数的参数是向状态寄存器写入的内容
{
  u8 data[4];
  u8 ret;
  
 
  //写使能
  data[0] = FRAM_WREN;
  data[1] = FRAM_WREN;
  SPI1_NSS(0);
  HAL_SPI_Transmit(&hspi1, data, 1, 100);
  SPI1_NSS(1);


  //写状态寄存器    0x01
  data[0] = value;
  data[1] = FRAM_WRSR;
  SPI1_NSS(0);
  HAL_SPI_TransmitReceive(&hspi1, data, &spi_read_data, 1, 10);
  SPI1_NSS(1);

  return 0;
}


//读操作
//addr:读取地址
//ret_data: 读取的数据保存在此处
u8 LL_fram_read(u16 addr, u8 ret_data)
{
  u8 data[4];
  u8 ret;
  u8 state;

  //读存储器    0x02
  data[0] = addr>>8;
  data[1] = FRAM_READ;
  data[2] = 0x00;
  data[3] = (u8)(addr&0x00ff);
  
  SPI1_NSS(0);
  state = HAL_SPI_TransmitReceive(&hspi1, data, &spi_read_data, 2, 20);   //(&hspi1, data, &ret, 2, 10);
  SPI1_NSS(1);

  if(state == 0)
  {
    spi_read_data = (u8)hspi1.Instance->DR;
    ret = 0;
  }
  else
  {
    ret = state;
  }
  return ret;
}


//写操作
//addr:写入地址
//ret_data: 写入的数据
u8 LL_fram_write(u16 addr, u8 value)
{
  u8 data[4];
  u8 ret;
  
  //写使能
  data[0] = FRAM_WREN;
  data[1] = FRAM_WREN;
  SPI1_NSS(0);
  HAL_SPI_Transmit(&hspi1, data, 1, 100);
  SPI1_NSS(1);


  //写存储器    0x02
  data[0] = addr>>8;
  data[1] = FRAM_WRITE;
  data[2] = value;
  data[3] = addr&0xff;
  SPI1_NSS(0);
  HAL_SPI_TransmitReceive(&hspi1, data, &ret, 2, 10);
  SPI1_NSS(1);
  
  return ret;

}

测试读写

  while(1)
  {
    LL_fram_write_sr(0x000);    //取消保护,以准备写入
    if(LL_fram_read_sr() == 0x02)         //读取状态寄存器,应该是0x02
    {
      for(i=0; i<8*1024; i++)
      {
        LL_fram_write(i,(u8)i);
        j++;
        temp = LL_fram_read(i, spi_read_data);
        if(temp == 0)
        {
          if(spi_read_data != (u8)i)
          {
            //读出的数据与写入的数据不相等
            LED_ERR(1);
            goto begin;
          }
        }
        else
        {
          //LL_fram_read()函数返回值不是0,表示运行出错,DR寄存器内的数据不能保证正确性
          LED_RUN(1);
          goto begin;
          j++;
        }
      }
    }
  }

begin: 

这一段函数不停的执行写入和读取操作,并进行比对。
如果SPI操作异常,则亮起LED_RUN灯,并跳转到begin处,
如果读取的结果,和写入的数据比对错误,则亮起LED_ERR灯,并跳转到begin处。
经长时间运行(2小时以上),发现正常运行时工作正常,未出现错误跳转。
带上仿真器全速运行时,会不时跳转到LED_RUN(1)处,也就是说SPI函数的运行不正确,显然是由于连接了仿真器造成的。但是不会跳转到LED_ERR(1)处,也就是说不会出现读取的数据不正确的情况。

总结

为了保证数据正确,应当判断LL_fram_read()函数的返回值,应当是0,此时再去读取数据才是正确的,否则会有错误的风险。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1226979.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

宠物信息服务预约小程序的效果如何

宠物的作用越来越重要&#xff0c;因此铲屎官们对自己爱宠的照顾也是加倍提升&#xff0c;而市场围绕宠物展开的细分服务近些年来逐渐增多&#xff0c;且市场规模快速增长。涉及之广&#xff0c;涵盖宠物衣食住行、医疗、美容、婚丧嫁娶等&#xff0c;各品牌争相抢夺客户及抢占…

java游戏制作-拼图游戏

一.制作主界面 首先创建一个Java项目命名为puzzlegame。 再在src中创建一个包&#xff0c;用来制作主界面 代码&#xff1a; 结果&#xff1a; 二.设置界面 代码&#xff1a; 三.初始化界面 代码&#xff1a; 优化代码&#xff1a; 结果&#xff1a; 四.添加图片 先在Java项…

思维模型 留白效应

本系列文章 主要是 分享 思维模型 &#xff0c;涉及各个领域&#xff0c;重在提升认知。因留白而遐想。 1 留白效应的应用 1.1 留白效应在艺术领域的应用 欧洲的艺术和设计领域有很多经典的实际案例&#xff0c;其中荷兰画家文森特梵高的作品《星夜》是一幅非常著名的油画&am…

【沐风老师】3DMAX一键云生成器插件使用教程

3DMAX云生成器插件使用教程 3DMAX云生成器插件&#xff0c;是一款将物体变成云的简单而富有创意的工具。该工具通过在物体周围创建粒子结合材质&#xff0c;最终形成渲染后的云的效果。 【支持版本】 3dMax2018 – 2023 默认的扫描线渲染器 【安装方法】 1.复制“安装文件”…

4、FFmpeg命令行操作10

音视频处理流程 先看两条命令 ffmpeg -i test_1920x1080.mp4 -acodec copy -vcodec libx264 -s 1280x720 test_1280x720.flv ffmpeg -i test_1920x1080.mp4 -acodec copy -vcodec libx265 -s 1280x720 test_1280x720.mkv ffmpeg音视频处理流程

Mysql之单行函数

Mysql之单行函数 单行函数数值类型函数字符串类型的函数日期和时间函数加密与解密函数信息函数 单行函数 函数的定义 函数在计算机语言的使用中贯穿始终&#xff0c;函数的作用是什么呢&#xff1f;它可以把我们经常使用的代码封装起来&#xff0c; 需要的时候直接调用即可。这…

Hive 定义变量 变量赋值 引用变量

Hive 定义变量 变量赋值 引用变量 变量 hive 中变量和属性命名空间 命名空间权限描述hivevar读写用户自定义变量hiveconf读写hive相关配置属性system读写java定义额配置属性env只读shell环境定义的环境变量 语法 Java对这个除env命名空间内容具有可读可写权利&#xff1b; …

MySQL 的执行原理(三)

5.4. InnoDB 中的统计数据 我们前边唠叨查询成本的时候经常用到一些统计数据&#xff0c;比如通过 SHOW TABLE STATUS 可以看到关于表的统计数据&#xff0c;通过 SHOW INDEX 可以看到关于索引 的统计数据&#xff0c;那么这些统计数据是怎么来的呢&#xff1f;它们是以什么方…

4种经典的限流算法

0、基础知识 1000毫秒内&#xff0c;允许2个请求&#xff0c;其他请求全部拒绝。 不拒绝就可能往db打请求&#xff0c;把db干爆~ interval 1000 rate 2&#xff1b; 一、固定窗口限流 固定窗口限流算法&#xff08;Fixed Window Rate Limiting Algorithm&#xff09;是…

pm2在Windows环境中的使用

pm2 进程管理工具可以Windows操作系统上运行&#xff0c;当一台Windows电脑上需要运行多个进程时&#xff0c;或者运维时需要运行多个进程以提供服务时。可以使用pm2&#xff0c;而不再是使用脚本。 1. 使用PM2管理进程 1.1. 启动PM2项目 1.1.1. 直接启动项目 参数说明&…

c++ list容器使用详解

list容器概念 list是一个双向链表容器&#xff0c;可高效地进行插入删除元素。 List 特点&#xff1a; list不可以随机存取元素&#xff0c;所以不支持at.(position)函数与[]操作符。可以对其迭代器执行&#xff0c;但是不能这样操作迭代器&#xff1a;it3使用时包含 #includ…

C++ 运算符重载详解

本篇内容来源于对c课堂上学习内容的记录 通过定义函数实现任意数据类型的运算 假设我们定义了一个复数类&#xff0c;想要实现两个复数的相加肯定不能直接使用“”运算符&#xff0c;我们可以通过自定义一个函数来实现这个功能&#xff1a; #include <iostream> using…

RabbitMQ消息的可靠性

RabbitMQ消息的可靠性 一 生产者的可靠性 生产者重试 有时候由于网络问题&#xff0c;会出现连接MQ失败的情况&#xff0c;可以配置重连机制 注意&#xff1a;SpringAMQP的重试机制是阻塞式的&#xff0c;重试等待的时候&#xff0c;当前线程会等待。 spring:rabbitmq:conne…

MySQL 的执行原理(四)

5.5. MySQL 的查询重写规则 对于一些执行起来十分耗费性能的语句&#xff0c;MySQL 还是依据一些规则&#xff0c;竭尽全力的把这个很糟糕的语句转换成某种可以比较高效执行的形式&#xff0c;这个过程也可以 被称作查询重写。 5.5.1. 条件化简 我们编写的查询语句的搜索条件…

【STM32】ADC(模拟/数字转换)

一、ADC的简介 1.什么是ADC 1&#xff09;将【电信号】-->【电压】-->【数字量】 2&#xff09;ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字量&#xff0c;建立模拟电路到数字电路的桥梁。 3&#xff09;12位逐次逼近型ADC&#xff0c;1us转换时间&#xf…

iOS_折叠展开 FoldTextView

1. 显示效果 Test1&#xff1a;直接使用&#xff1a; Test2&#xff1a;在 cell 里使用&#xff1a; 2. 使用 2.1 直接使用 // 1.1 init view private lazy var mooFoldTextView: MOOFoldTextView {let view MOOFoldTextView(frame: .zero)view.backgroundColor .cyanvie…

Node.js之fs文件系统模块

什么是fs文件系统模块&#xff1f;又如何使用呢&#xff1f;让我为大家介绍一下&#xff01; fs 模块是 Node.js 官方提供的、用来操作文件的模块。它提供了一系列的方法和属性&#xff0c;用来满足用户对文件的操作需求 注意&#xff1a;如果要在JavaScript代码中&#xff0c…

Linux 网络:PMTUD 简介

文章目录 1. 前言2. Path MTU Discovery(PMTUD) 协议2.1 PMTUD 发现最小 MTU 的过程 3. Linux 的 PMTUD 简析3.1 创建 socket 时初始化 PMTUD 模式3.2 数据发送时 PMTUD 相关处理3.2.1 源头主机发送过程中 PMTU 处理3.2.2 转发过程中 PMTUD 处理 4. PMTUD 观察5. 参考链接 1. 前…

k8s的高可用集群搭建,详细过程实战版

kubernetes高可用集群的搭建 前面介绍过了k8s单master节点的安装部署 今天介绍一下k8s高可用集群搭建 环境准备&#xff1a; vip &#xff1a;192.168.121.99 keeplive master01&#xff1a;192.168.121.153 centos7 master02&#xff1a;192.168.121.154 centos7 master03&a…