文章目录
前言
通过控制FLASH芯片进一步熟悉SPI协议
一、FLASH与EEPROM芯片
Flash 存储器 : Flash 存储器是一种非易失性存储器,它具有 RAM 和 ROM 的一些特点。与 ROM 类似,Flash 存储器的内容在断电时不会丢失,但与 RAM 类似,它可以通过编程来修改存储的内容。Flash 存储器通常用于嵌入式系统中存储程序代码、配置数据等。
EEPROM(电可擦除可编程只读存储器) : EEPROM 是一种可编程的非易失性存储器,可以通过电擦除和编程来修改存储的内容。EEPROM 具有 RAM 的特点,可以随机读写,但也具有 ROM 的特点,存储的内容在断电时不会丢失。EEPROM 通常用于存储配置数据、参数设置等。
Flash 存储器和 EEPROM(Electrically Erasable Programmable Read-Only Memory)之间的主要区别在于它们的擦除和编程方法、速度、寿命和应用范围等方面。
-
擦除和编程方法:
Flash 存储器通常使用扇区擦除(Sector Erase)的方式来擦除数据,并使用编程器(Programmer)来进行编程。扇区擦除意味着需要一次性擦除一整个存储扇区,然后才能进行写入操作。EEPROM 可以通过逐字节擦除和编程来修改数据,因此擦除和编程更加灵活和精细。
-
速度:由于 Flash 存储器通常采用扇区擦除的方式,擦除和编程操作比 EEPROM 慢。EEPROM 在擦除和编程时的速度相对较快,因为它可以逐字节地擦除和编程。
-
寿命:Flash 存储器和 EEPROM 的擦除和编程操作都会导致存储器单元的磨损,从而影响存储器的寿命。由于 Flash 存储器通常需要一次性擦除整个扇区,因此其寿命可能受到擦除次数的限制。EEPROM 的擦除和编程操作更加精细,因此其寿命可能比 Flash 存储器更长。
-
应用范围:Flash 存储器通常用于存储大容量的程序代码、固件更新等。EEPROM 通常用于存储配置数据、参数设置、小容量数据等。
-
总的来说,Flash 存储器和 EEPROM 都是非易失性存储器,但它们在擦除和编程方法、速度、寿命和应用范围等方面存在一些差异,因此在选择存储器时需要根据具体的应用需求进行选择
以下内容为补充的更加详细的参考,来自:https://zhuanlan.zhihu.com/p/27621418rom最初不能编程,出厂什么内容就永远什么内容,不灵活。后来出现了prom,可以自己写入一次,要是写错了,只能换一片,自认倒霉。人类文明不断进步,终于出现了可多次擦除写入的EPROM,每次擦除要把芯片拿到紫外线上照一下,想一下你往单片机上下了一个程序之后发现有个地方需要加一句话,为此你要把单片机放紫外灯下照半小时,然后才能再下一次,这么折腾一天也改不了几次。历史的车轮不断前进,伟大的EEPROM出现了,拯救了一大批程序员,终于可以随意的修改rom中的内容了。
EEPROM的全称是"电可擦除可编程只读存储器",即Electrically Erasable Programmable Read-Only Memory。是相对于紫外擦除的rom来讲的。但是今天已经存在多种EEPROM的变种,变成了一类存储器的统称。
狭义的EEPROM:这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。
flash:flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。**flash做的改进就是擦除时不再以字节为单位,而是以块为单位,一次简化了电路,数据密度更高,降低了成本。**上M字节的rom一般都是flash。
Flash又分NAND Flash和NOR Flash,NOR型存储内容以编码为主,其功能多与运算相关;NAND型主要功能是存储资料,如数码相机中所用的记忆卡。
-
Nor Flash :主要用来执行片上程序
优点:具有很好的读写性能和随机访问性能,因此它先得到广泛的应用;
缺点:单片容量较小且写入速度较慢,决定了其应用范围较窄。
-
NAND Flash :主要用在大容量存储场合
优点:优秀的读写性能、较大的存储容量和性价比,因此在大容量存储领域得到了广泛的应用;
缺点:不具备随机访问性能。
nor flash 数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。
nand flash 同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。(nandflash按块来擦除,按页来读,norflash没有页)由于nandflash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的。小容量的2~12M的flash多是nor型的。
使用寿命上,nand flash的擦除次数是nor的数倍。而且nand flash可以标记坏块,从而使软件跳过坏块。nor flash 一旦损坏便无法再用。因为nor flash可以进行字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。
二、FLASH(W25Q64JV)常见操作
2.1、FLASH常见操作
2.2、W25Q64JV芯片存储大小
2.3、状态寄存器:
BUSY :忙信号,写、擦除操作后自动置0,
WEL :写使能信号,写数据、擦除、写状态寄存器时需要置1.
一般只用这两,有其他需求需要看芯片手册。
2.4、时序图
该芯片存储较大,因此不支持页擦除,最小为块擦除
写使能 :SPI采用模式0或3
写失能:
读状态寄存器 :0h05则返回第一段状态寄存器数值(7-0bit),0h35为第二段,0h15为第三段,第三段时QSPI才有的。
读数据: 读数据时候先传输指令03h,然后地址(24bit分别对应8位扇区地址、8位页地址和8位字节地址),然后FLASH返回读数据
页编程: 写数据之前需要先执行擦除,至少写一个byte数据,最大可写246byte,此时字节地址应该为0,若不是从0开始,当写到当前页最后一个地址时,不会跳到下一页,会返回到当前页0地址开始写起。
扇区擦除 给扇区擦除指令和地址即可,地址当中的页地址、字节地址没关系,只看扇区地址(本人没有验证过,但看到过这样一句话,有兴趣可以自己验证一下,验证好记得戳一戳我hhh)
块擦除(32KB块) :
块擦除(64KB块) :
全片擦除:
读厂商ID :
三、程序设计框图
完整代码参考GiTHub:https://github.com/shun6-6/FLASH_spi
3.1、FLASH_drive模块
FLASH_drive模块与用户模块和FLASH芯片连接
用户模块输入读写操作指令,以及相应的读写数据地址和长度,FLASH_drive模块结果处理后通过SPI将数据写入FLASH或从中读取数据。
以下为接口代码:
c
module Flash_drive#(
parameter P_DATA_WIDTH = 8 ,
parameter P_SPI_CPOL = 0 ,
parameter P_SPI_CPHL = 0 ,
parameter P_READ_DWIDTH = 8 ,
parameter P_OP_LEN = 32
)(
input i_clk ,
input i_rst ,
/*--------user接口--------*/
input [1 :0] i_operation_type ,
input [23:0] i_operation_addr ,
input [8 :0] i_operation_byte_num ,
input i_operation_valid ,
output o_operation_ready ,
input [P_DATA_WIDTH - 1 : 0] i_write_data ,
input i_write_sop ,
input i_write_eop ,
input i_write_valid ,
output [P_DATA_WIDTH - 1 : 0] o_read_data ,
output o_read_sop ,
output o_read_eop ,
output o_read_valid ,
/*--------spi接口--------*/
output o_spi_cs ,
output o_spi_clk ,
output o_spi_mosi ,
input i_spi_miso
);
3.2、FLASH_ctrl模块
FLASH_ctrl模块将用户操作指令以及数据进行处理,并产生相应指令驱动SPI_drive模块。
对于写数据过程 :用户模块会将写指令、写地址以及写数据发送至FLASH_ctrl模块,FLASH_ctrl模块将数据存至本地FIFO,并发送写使能指令和写数据指令,并且根据SPI_drive模块的i_user_write_req信号把FIFO当中存入的用户写数据一个个取出,这是因为用户输入的的数据都是并行的8bit数据,而SPI是穿行传输的,所以需要SPI_drive串行传输完一个byte之后,向FLASH_ctrl模块发起下一个byte请求,也就是i_user_write_req信号。注:在写同一个地址的时候,一定要先擦除,再写,这是因为FLASH芯片只能把1变为0,不可以把0变为1,需要重新擦除,将芯片变为全1,才可以继续写。
对于读数据过程 :用户模块输入读指令、读地址以及要读取的数据长度信息告知FLASH_ctrl模块,FLASH_ctrl模块会产生相应的读指令以控制SPI_drive模块读FLASH,并且将读到的数据先存入FIFO,最后完整的传递给用户模块。
FLASH_ctrl模块接口代码:
c
module Flash_ctrl#(
parameter P_DATA_WIDTH = 8 ,//数据位宽
parameter P_SPI_CPOL = 0 ,//spi时钟极性:0/1表示空闲时钟电平为0/1
parameter P_SPI_CPHL = 0 ,//spi时钟相位:0/1表示数据采集沿为时钟第1/2跳变沿
parameter P_READ_DWIDTH = 8 ,//读数据位宽
parameter P_OP_LEN = 32 //操作数据长度
)(
input i_clk ,
input i_rst ,
/*--------用户接口--------*/
input [1 :0] i_operation_type ,//操作类型 1:read 2:write
input [23:0] i_operation_addr ,//操作地址
input [8 :0] i_operation_byte_num ,//max write 256 byte
input i_operation_valid ,//操作有效信号
output o_operation_ready ,//操作准备信号
input [P_DATA_WIDTH - 1 : 0] i_write_data ,//写数据
input i_write_sop ,//写数据-开始信号
input i_write_eop ,//写数据-结束信号
input i_write_valid ,//写数据有效
output [P_DATA_WIDTH - 1 : 0] o_read_data ,//读数据
output o_read_sop ,//读数据-开始信号
output o_read_eop ,//读数据-结束信号
output o_read_valid ,//读数据有效
/*--------驱动接口--------*/
output [P_OP_LEN - 1 : 0] o_user_op_data ,//操作数据(数据8+地址24)
output [1:0] o_user_op_type ,//操作类型(读写数据,读写指令)
output [15:0] o_user_op_len ,//操作数据长度(读写数据8+24,指令8)
output [15:0] o_user_clk_len ,//时钟周期,读写数据时为8+24+8*字节数
output o_user_op_valid ,//用户数据有效信号
input i_user_op_ready ,//驱动准备信号
output [P_DATA_WIDTH - 1 : 0] o_user_write_data ,//写数据
input i_user_write_req ,//写数据请求
input [P_READ_DWIDTH - 1 : 0] i_user_read_data ,//读数据
input i_user_read_valid //读数据有效
);
FLASH_ctrl模块状态机描述代码:
c
//FSM
localparam P_ST_IDLE = 11'b00000000001,//空闲状态,握手成功后进入运行状态
P_ST_RUN = 11'b00000000010,//运行状态,如果是读指令则进入读数据状态,否则为擦除或者写数据指令,都需要先进入写使能状态
P_ST_W_EN = 11'b00000000100,//写使能状态,若为写数据指令则进入写指令状态,否则进入擦除状态
P_ST_W_INS = 11'b00000001000,//写数据指令状态
P_ST_W_DATA = 11'b00000010000,//写数据状态
P_ST_R_INS = 11'b00000100000,//读数据指令状态
P_ST_R_DATA = 11'b00001000000,//读数据状态
P_ST_CLEAR = 11'b00010000000,//擦除状态
P_ST_BUSY = 11'b00100000000,//读忙状态寄存器
P_ST_BUSY_CHK = 11'b01000000000,//检查返回的忙状态寄存器状态,若为忙则进入P_ST_BUSY_WAIT状态,不忙则说明读数据结束,返回空闲状态
P_ST_BUSY_WAIT = 11'b10000000000;//读忙等待状态,计数256后再次返回P_ST_BUSY读忙状态
FLASH_ctrl模块状态机跳转代码:
c
always @(*)begin
case (r_st_cur)
P_ST_IDLE : r_st_nxt = w_user_op_active ? P_ST_RUN : P_ST_IDLE ;
P_ST_RUN : r_st_nxt = ri_operation_type == P_READ_TYPE ? P_ST_R_INS : P_ST_W_EN;
P_ST_W_EN : r_st_nxt = w_spi_op_active ?
ri_operation_type == P_WRITE_TYPE ? P_ST_W_INS : P_ST_CLEAR
: P_ST_W_EN ;
P_ST_W_INS : r_st_nxt = w_spi_op_active ? P_ST_W_DATA : P_ST_W_INS ;
P_ST_W_DATA : r_st_nxt = i_user_op_ready ? P_ST_BUSY : P_ST_W_DATA;
P_ST_R_INS : r_st_nxt = w_spi_op_active ? P_ST_R_DATA : P_ST_R_INS ;
P_ST_R_DATA : r_st_nxt = i_user_op_ready ? P_ST_BUSY : P_ST_R_DATA;
P_ST_CLEAR : r_st_nxt = w_spi_op_active ? P_ST_BUSY : P_ST_CLEAR ;
P_ST_BUSY : r_st_nxt = w_spi_op_active ? P_ST_BUSY_CHK : P_ST_BUSY ;
P_ST_BUSY_CHK : r_st_nxt = ri_user_read_valid ?
ri_user_read_data[0] ? P_ST_BUSY_WAIT : P_ST_IDLE
: P_ST_BUSY_CHK;
P_ST_BUSY_WAIT : r_st_nxt = r_st_cnt == 255 ? P_ST_BUSY : P_ST_BUSY_WAIT;
default : r_st_nxt = P_ST_IDLE;
endcase
end
3.3、spi_drive模块
该模块则是按照SPI协议和FLASH的相关操作指令,将输入的指令以及数据发送给FLASH。
spi_drive模块接口代码:
c
module spi_drive#(
parameter P_DATA_WIDTH = 8 ,
parameter P_SPI_CPOL = 0 ,
parameter P_SPI_CPHL = 0 ,
parameter P_READ_DWIDTH = 8 ,
parameter P_OP_LEN = 32 //操作数据长度
)(
input i_clk ,
input i_rst ,
output o_spi_cs ,//spi片选信号
output o_spi_clk ,//spi时钟线
output o_spi_mosi ,//spi主机输出
input i_spi_miso ,//spi主机输入
input [P_OP_LEN - 1 : 0] i_user_op_data ,//操作数据(数据8+地址24)
input [1:0] i_user_op_type ,//操作类型(读写数据,读写指令)
input [15:0] i_user_op_len ,//操作数据长度(读写数据8+24,指令8)
input [15:0] i_user_clk_len ,//时钟周期,读写数据时为8+24+8*字节数
input i_user_op_valid ,//用户数据有效信号
output o_user_op_ready ,//主机准备信号
input [P_DATA_WIDTH - 1 : 0] i_user_write_data ,//写数据
output o_user_write_req ,//写数据请求
output [P_READ_DWIDTH - 1 : 0] o_user_read_data ,//读数据
output o_user_read_valid //读数据有效
);
SPI协议的实现过程:
该FLASH芯片支持的SPI是模式0和3,波形图当中是模式0。
run运行信号在片选信号cs拉低时同步拉高,同时开启一个1bit计数器r_spi_cnt,该计数器波形与spi_clk一致,因此以此计数器可以表示当前时钟的上升沿或者下降沿,模式0是在时钟的下降沿改变数据,上升沿采样数据。