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 deginewire IDLEtoWREN; wire WRENtoWEL ; wire WRENtoBE ; wire BEtoIDLE ; // reg signal definereg [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_calways @(posedge sys_clk or negedge sys_rst_n) beginif(~sys_rst_n) state_c <= IDLE ;elsestate_c <= state_n ;end// state_nalways @(*) begincase (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 ;endcaseend// 状态转移描述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) beginif(~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==120nscnt_20ns <= 4'd0 ;elsecnt_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 ;endcaseend// reg [3:0] cnt_bit ;always @(posedge sys_clk or negedge sys_rst_n) beginif(~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 ;endcaseend// reg flag_bit ;always @(posedge sys_clk or negedge sys_rst_n) beginif(~sys_rst_n) flag_bit <= 1'b0 ;elsecase (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 ;endcaseend// reg f_b_reg ;always @(posedge sys_clk or negedge sys_rst_n) beginif(~sys_rst_n) beginf_b_reg <= 1'b0 ;end else beginf_b_reg <= flag_bit ;endend// reg [7:0] comd ;always @(posedge sys_clk or negedge sys_rst_n) beginif(~sys_rst_n)comd <= 8'd0 ;elsecase (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) beginif(~sys_rst_n) begincs_n <= 1'b1 ;end else begincase (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 ;endcaseendend// reg sck always @(posedge sys_clk or negedge sys_rst_n) beginif(~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 ;endcaseendendmodule
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) beginif(~sys_rst_n) beginkey_r_0 <= 1'b1 ;end else beginkey_r_0 <= key_in ;endendalways @(posedge sys_clk or negedge sys_rst_n) beginif(~sys_rst_n) beginkey_r_1 <= 1'b1 ;end else beginkey_r_1 <= key_r_0 ;endend// nege poseassign nege = ~key_r_0 && key_r_1 ;assign pose = key_r_0 && ~key_r_1 ;// add_cnt_flagalways @(posedge sys_clk or negedge sys_rst_n) beginif(~sys_rst_n) beginadd_cnt_flag <= 1'b0 ;end else beginif(nege) beginadd_cnt_flag <= 1'b1 ;end else beginif( pose || end_cnt_20ms ) beginadd_cnt_flag <= 1'b0 ;end else beginadd_cnt_flag <= add_cnt_flag ;endend endend// cnt_20ms add_cnt_20ms end_cnt_20msalways @(posedge sys_clk or negedge sys_rst_n) beginif(~sys_rst_n) begincnt_20ms <= 20'd0 ;end else beginif(add_cnt_20ms) beginif(end_cnt_20ms) begincnt_20ms <= 20'd0 ;end else begincnt_20ms <= cnt_20ms + 20'd1 ;endend else begincnt_20ms <= 20'd0 ;endendendassign 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// endassign 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 beginsys_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;endalways #(CYCLE/2) sys_clk = ~sys_clk ;endmodule
仿真波形:
上板验证:
先往板子上固化一个流水灯程序,也就是把生成的.jic文件,写进flash。
重启开发板,流水灯正常工作,说明程序写进了flash。
然后把全擦除程序(.sof),写进开发板。
重启开发板,流水灯效果消失。
说明上板成功。