SPI是什么:
SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输。
应用:EEPROM、Flash、RTC、ADC、DSP等。
优缺点:全双工通信,通讯方式较为简单,相对数据传输速率较快;没有应答机制确认数据是否接收,在数据可靠性上有一定缺陷(与I2C相比)。
物理层:
SCK (Serial Clock):时钟信号线,用于同步通讯数据;
MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚;
MISO (Master Input,Slave Output):主设备输入/从设备输出引脚;
𝐶𝑆(CS) ̅ (Chip Select):片选信号线,也称为CS_N。
协议层:
spi通讯协议有四种模式:模式0和模式3,从设备在sck上升沿采样。
模式1和模式2在时钟下降沿采样。
模式0和模式1,在cs_n==1时,sck==0。
模式2和模式3,在cs_n==1时,sck==1。
比较常用的就是模式0和模式3。
flash相关资料:
芯片手册是必须要看的。
模块框图:
状态机:
时序图:
代码:
module spi (
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_start ,
output wire mosi ,
output wire miso ,
output reg cs_n ,
output reg sck
);
// parameter
parameter COMD_W = 8'h06 ,
COMD_B = 8'hc7 ;
parameter IDLE = 4'b0001 ,
WREN = 4'b0010 ,
WEL = 4'b0100 ,
BE = 4'b1000 ;
// wire signal degine
wire IDLEtoWREN;
wire WRENtoWEL ;
wire WRENtoBE ;
wire BEtoIDLE ;
// reg signal define
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [3:0] cnt_20ns ;
reg [3:0] cnt_bit ;
reg flag_bit ;
reg f_b_reg ; // flag_bit_reg的缩写
reg [7:0] comd ;
/****************************************************************************/
// 三段式状态机
// 现态与次态描述
// state_c
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= IDLE ;
else
state_c <= state_n ;
end
// state_n
always @(*) begin
case (state_c)
IDLE :if(IDLEtoWREN)
state_n <= WREN ;
else
state_n <= IDLE ;
WREN :if(WRENtoWEL)
state_n <= WEL ;
else
state_n <= WREN ;
WEL :if(WRENtoBE)
state_n <= BE ;
else
state_n <= WEL ;
BE :if(BEtoIDLE)
state_n <= IDLE ;
else
state_n <= BE ;
default: state_n <= IDLE ;
endcase
end
// 状态转移描述
assign IDLEtoWREN = ( state_c == IDLE) && ( key_start ) ;
assign WRENtoWEL = ( state_c == WREN) && ( f_b_reg ) ;
assign WRENtoBE = ( state_c == WEL ) && ( cnt_20ns == 6 ) ;
assign BEtoIDLE = ( state_c == BE ) && ( f_b_reg ) ;
// 相关信号描述
// reg [3:0] cnt_20ns ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_20ns <= 4'd0 ;
else
case (state_c)
IDLE : cnt_20ns <= 4'd0 ;
WREN : if(cnt_20ns || f_b_reg)
cnt_20ns <= 4'd0 ;
else
cnt_20ns <= cnt_20ns + 1'b1 ;
WEL : if(cnt_20ns == 6) // 60x20ns==120ns
cnt_20ns <= 4'd0 ;
else
cnt_20ns <= cnt_20ns + 1'b1 ;
BE : if(cnt_20ns || f_b_reg)
cnt_20ns <= 4'd0 ;
else
cnt_20ns <= cnt_20ns + 1'b1 ;
default: cnt_20ns <= 4'd0 ;
endcase
end
// reg [3:0] cnt_bit ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_bit <= 4'd0 ;
else
case (state_c)
IDLE : cnt_bit <= 4'd0 ;
WREN : if(!cnt_20ns && sck && cnt_bit == 7)
cnt_bit <= 4'd0 ;
else if(!cnt_20ns && sck)
cnt_bit <= cnt_bit + 1'b1 ;
WEL : cnt_bit <= 4'd0 ;
BE : if(!cnt_20ns && sck && cnt_bit == 7)
cnt_bit <= 4'd0 ;
else if(!cnt_20ns && sck)
cnt_bit <= cnt_bit + 1'b1 ;
default: cnt_bit <= 4'd0 ;
endcase
end
// reg flag_bit ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
flag_bit <= 1'b0 ;
else
case (state_c)
IDLE : flag_bit <= 1'b0 ;
WREN : if(cnt_bit == 7 && sck && !cnt_20ns)
flag_bit <= 1'b1 ;
else
flag_bit <= flag_bit ;
WEL : flag_bit <= 1'b0 ;
BE : if(cnt_bit == 7 && sck && !cnt_20ns)
flag_bit <= 1'b1 ;
else
flag_bit <= flag_bit ;
default: flag_bit <= 1'b0 ;
endcase
end
// reg f_b_reg ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
f_b_reg <= 1'b0 ;
end else begin
f_b_reg <= flag_bit ;
end
end
// reg [7:0] comd ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
comd <= 8'd0 ;
else
case (state_c)
IDLE : comd <= 8'd0 ;
WREN : if(cnt_20ns && sck)
comd <= (COMD_W << cnt_bit) ;
else if(!cnt_bit)
comd <= COMD_W ;
else
comd <= comd ;
WEL : comd <= 8'd0 ;
BE : if(cnt_20ns && sck)
comd <= (COMD_B << cnt_bit) ;
else if(!cnt_bit)
comd <= COMD_B ;
else
comd <= comd ;
default : comd <= 8'd0 ;
endcase
end
// output signal
// wire mosi ,
assign mosi = comd[7] ;
// wire miso ,
assign miso = 1'bz ;
// reg cs_n ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cs_n <= 1'b1 ;
end else begin
case (state_c)
IDLE : if(key_start)
cs_n <= 1'b0 ;
else
cs_n <= 1'b1 ;
WREN : if(f_b_reg)
cs_n <= 1'b1 ;
else
cs_n <= cs_n ;
WEL : if(cnt_20ns == 6)
cs_n <= 1'b0 ;
else
cs_n <= cs_n ;
BE : if(f_b_reg)
cs_n <= 1'b1 ;
else
cs_n <= cs_n ;
default: cs_n <= 1'b1 ;
endcase
end
end
// reg sck
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
sck <= 1'b0 ;
else
case (state_c)
IDLE : sck <= 1'b0 ;
WREN : if(cnt_20ns)
sck <= ~sck ;
else
sck <= sck ;
WEL : sck <= 1'b0 ;
BE : if(cnt_20ns)
sck <= ~sck ;
else
sck <= sck ;
default: sck <= 1'b0 ;
endcase
end
endmodule
module key_filter
#(
parameter MAX_CNT_20MS = 20'd100_0000
)(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_in ,
output wire key_out
);
reg key_r_0 ;
reg key_r_1 ;
wire nege ;
wire pose ;
reg [19:00] cnt_20ms ;
wire add_cnt_20ms ;
wire end_cnt_20ms ;
reg add_cnt_flag ;
// key_r_0 key_r_1
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
key_r_0 <= 1'b1 ;
end else begin
key_r_0 <= key_in ;
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
key_r_1 <= 1'b1 ;
end else begin
key_r_1 <= key_r_0 ;
end
end
// nege pose
assign nege = ~key_r_0 && key_r_1 ;
assign pose = key_r_0 && ~key_r_1 ;
// add_cnt_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
add_cnt_flag <= 1'b0 ;
end else begin
if(nege) begin
add_cnt_flag <= 1'b1 ;
end else begin
if( pose || end_cnt_20ms ) begin
add_cnt_flag <= 1'b0 ;
end else begin
add_cnt_flag <= add_cnt_flag ;
end
end
end
end
// cnt_20ms add_cnt_20ms end_cnt_20ms
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_20ms <= 20'd0 ;
end else begin
if(add_cnt_20ms) begin
if(end_cnt_20ms) begin
cnt_20ms <= 20'd0 ;
end else begin
cnt_20ms <= cnt_20ms + 20'd1 ;
end
end else begin
cnt_20ms <= 20'd0 ;
end
end
end
assign add_cnt_20ms = add_cnt_flag ;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == ( MAX_CNT_20MS - 1'b1 ) ;
// key_out
// always @(posedge sys_clk or negedge sys_rst_n) begin
// // always @(*) begin // 这样的话 会综合成 数据选择器
// if(~sys_rst_n) begin
// key_out <= 1'b0 ;
// end else begin
// if(end_cnt_20ms) begin
// key_out <= 1'b1 ;
// end else begin
// key_out <= 1'b0 ;
// end
// end
// end
assign key_out = end_cnt_20ms ;
endmodule
module top(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_in ,
output wire cs_n ,
output wire sck ,
output wire mosi
);
// 例化间连线
wire key_flag ;
key_filter key_filter_inst(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.key_in ( key_in ) ,
.key_out ( key_flag )
);
spi spi_inst(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.key_start ( key_flag ) ,
.mosi ( mosi ) ,
.miso ( ) ,
.cs_n ( cs_n ) ,
.sck ( sck )
);endmodule
`timescale 1ns/1ns
module test_top();
reg sys_clk ;
reg sys_rst_n ;
reg key_in ;
wire cs_n ;
wire sck ;
wire mosi ;
wire miso ;
top top_inst(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.key_in ( key_in ) ,
.cs_n ( cs_n ) ,
.sck ( sck ) ,
.mosi ( mosi ) ,
.miso ( miso )
);
parameter CYCLE = 20 ;
defparam top_inst.key_filter_inst.MAX_CNT_20MS = 20'd100 ;
initial begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
key_in <= 1'b1 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#(CYCLE * 30) ;
key_in <= 1'b0 ;
#(CYCLE * 130) ;
key_in <= 1'b1 ;
#(CYCLE * 100) ;
$stop;
end
always #(CYCLE/2) sys_clk = ~sys_clk ;
endmodule
仿真波形:
上板验证:
先往板子上固化一个流水灯程序,也就是把生成的.jic文件,写进flash。
重启开发板,流水灯正常工作,说明程序写进了flash。
然后把全擦除程序(.sof),写进开发板。
重启开发板,流水灯效果消失。
说明上板成功。