1、前言
此博客只为了记录自己的FPGA的学习过程,不完全正确,仅供参考!!!
2、SPI协议的介绍
????????SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。SPI的四根线是指cs(片选信号)、clk(时钟信号)、mosi(主出从入)、miso(主入从出),因为SPI通信是同步通讯协议,所以这边的是时钟信号是由主机产生,通过时钟线传输给从机,在通信时可以通过拉低片选引脚来选中相应的外设,所以SPI总线上可以接上不同的外设,通过拉低片选引脚进行片选。
在使用SPI通的时候,我们需要注意SPI通信有四种工作模式,我们通过设定CPOL和CPHA来选择工作模式
????????CPOL:时钟极性,决定时钟信号空闲时的电平状态。
????????CPHA:时钟相位,决定是奇数沿还是偶数沿进行采样,如果奇数沿进行采样,那么就是偶数沿进行数据更新。
这边两者共同决定工作模式,如果时钟空闲状态是低电平,那么时钟的第一个上升沿也就是奇数沿就是上升沿,那么数据会在上升沿进行采样,这种就是模式0.最常用的就是模式0和模式3,这边选用的就是模式0.
3、片区擦除的介绍及工作时序
????????flash的擦除可分为全部擦除和部分擦除,前者比后者更为简单,不需要传输地址给flash,只需要将全擦除指令写进flash芯片即可,但后者不仅需要写入擦除指令之外还需要写入扇区地址、页地址、字节地址,但两者的工作时序是相同的,所以这边可以放在一起学习一下,只要会了部分扇区的擦除地址,那么全擦除你肯定会了,那么我们先来看一下两种的工作时序。
全擦除时序
?部分擦除时序
????????从上图我们可以看到全擦除和部分扇区的擦除区别就在于在指令写入之后,需要继续写入擦除的地址,这个地址是24位的,分别为8位扇区地址,8位页地址,8位字节地址。
?时间参数表
? ? ? ? 那么我们需要对上面的时序进行一个理解,因为fpga的程序就是严格按照时序来进行的,SPI通信的时候时钟我们可以从上面的表格可以得到,那么上图中读操作的最大时钟频率为20MHZ,进行其他操作的最大时钟频率为50MHZ,为了方便,我们这边定义下所有的时钟频率都不超过20MHZ,那么就选定时钟评率为12.5MHZ,FPGA系统的时钟频率为50MHZ,所以我们进行操作时,只需要进行一个四分频的操作就可以了。所以我们肯定需要提供一个时钟首先进行第一步就是拉低片选信号,,然后我们需要等待一个Tslch的时间(Tsclh的最小时间是5ns),然后写入写使能命令,写使能命令写入时,最后一个bit数据写入到片选信号拉高需要保持一个Tchsh时间(Tchsh时间最小位5ns),然后需要进行一个延时Tshsl时间(最小为100ns)。因为SPI的通信时钟选择为12.5MHZ,时钟周期为80ns,这边传输一个字节需要640ns,那就可以用这个640ns来代替之前的所有的时间,这样代码写起来也比较方便。那么在写代码之前我们需要知道几个操作的指令,写使能指令(0000_0110)、全擦除指令BE(1100_0111)、部分擦除指令SE(1101_1000).
4、波形图绘制
? ? ? ? 越到稍微大一点的工程,越感觉到波形图的重要,因为参数很多,很多的计数器,如果没有一个流程图,那么代码有时真的是非常困难去编写,光靠大脑的逻辑去写真的很容易出错,所以这边劝大家一定要去画。
????????
?????????我这边放的SE的波形图,状态SE的中包括四个数据的写入,第一个是SE指令,然后加上3个字节的地址写入,加上一个Tslch时间和Tchsh时间。正好对应5x640ns,这些都是根据时序图来进行操作的。
5、代码
????????
module spi_flash_se
(
input wire sys_clk,
input wire sys_rst_n,
input wire key_flag,
output reg spi_clk,
output reg spi_csn,
output reg spi_mosi
);
parameter IDLE = 4'b0001,
WREN = 4'B0010,
DELAY = 4'b0100,
SE = 4'b1000;
parameter wren_data = 8'b0000_0110,
se_data = 8'b1101_1000,
s_addr = 8'b0000_0000,
p_addr = 8'b0000_0100,
b_addr = 8'b0010_0101;
reg [3:0] state;
reg [4:0] time_cnt;
reg [3:0] bite_cnt;
reg [2:0] bit_cnt;
reg [1:0] clk_cnt;
//状态机
always@(posedge sys_clk)
begin
if(sys_rst_n ==1'b0)
state <= IDLE;
else
case(state)
IDLE: if(key_flag == 1'b1)
state <= WREN;
WREN: if((bite_cnt == 4'd3)&&(bit_cnt == 5'd31))
state <= DELAY;
DELAY: if((bite_cnt == 4'd4)&&(bit_cnt == 5'd31))
state <= SE;
SE: if((bite_cnt == 4'd10)&&(bit_cnt == 5'd31))
state <= IDLE;
default: state <= IDLE;
endcase
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
time_cnt <= 5'd0;
else if((state = IDLE)&&(time_cnt == 5'd31))
time_cnt <= 5'd0;
else
time_cnt <= time_cnt + 5'd1;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
bite_cnt <= 4'd0;
else if(bite_cnt == 4'd10)&&(time_cnt == 5'd31))
bite_cnt <= 4'd0;
else if(time_cnt == 5'd31)
bite_cnt <= bite_cnt + 5'd1;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
clk_cnt <= 2'd0;
else if(((bite_cnt != 4'd2)&&(bite_cnt >= 4'd3)&&(bite_cnt <= 4'd5)&&(state == IDLE)&&(bite_cnt == 2'd3))
clk_cnt <= 2'd0;
else
clk_cnt <= clk_cnt + 2'd1;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
bit_cnt <= 3'd0;
else if(clk_cnt == 2'd1)
bit_cnt <= bit_cnt + 3'd1;
else
bit_cnt <= bit_cnt;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
spi_clk <= 1'b0;
else if(clk_cnt == 2'b0)
spi_clk <= 1'b0;
else if(clk_cnt == 2'b2)
spi_clk <= 1'b1;
else
spi_clk <= spi_clk;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
spi_csn <=1'b1;
else if((key_flag == 1'b1)||((bite_cnt == 4'd4)&&(time_cnt == 5'd31)))
spi_csn <= 1'b0;
else if(((bite_cnt == 4'd3)&&(time_cnt == 5'd31))||((bite_cnt == 4'd10)&&(time_cnt == 5'd31)))
spi_csn <= 1'b1;
else
spi_csn <= spi_csn;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
spi_mosi <= 1'b0;
else if((bite_cnt == 4'd2)&&(time_cnt == 5'd31))
spi_mosi <= wren_data[7 - bit_cnt];
else if((bite_cnt == 4'd5)&&(time_cnt == 5'd31))
spi_mosi <= se_data[7 - bite_cnt];
else if((bite_cnt == 4'd6)&&(time_cnt == 5'd31))
spi_mosi <= s_addr[7 - bite_cnt];
else if((bite_cnt == 4'd7)&&(time_cnt == 5'd31))
spi_mosi <= p_addr[7 - bite_cnt];
else if((bite_cnt == 4'd8)&&(time_cnt == 5'd31))
spi_mosi <= b_addr[7 - bite_cnt];
else
spi_mosi <= 1'b0;
end
end module
|