FPGA作為從機與STM32進行SPI協議通信---Verilog實現
一.SPI協議簡要介紹
SPI,是英語Serial Peripheral Interface的縮寫,顧名思義就是串行外圍設備接口。SPI,是一種高速的,全雙工,同步的通信總線,並且在芯片的管腳上只占用四根線,節約了芯片的管腳,同時為PCB的布局上節省空間,提供方便,正是出於這種簡單易用的特性,現在越來越多的芯片集成了這種通信協議。
SPI總線是Motorola公司推出的三線同步接口,同步串行3線方式進行通信:一條時鐘線SCK,一條數據輸入線MOSI,一條數據輸出線MISO;用於 CPU與各種外圍器件進行全雙工、同步串行通訊。SPI主要特點有:可以同時發出和接收串行數據;可以當作主機或從機工作;提供頻率可編程時鐘;發送結束中斷標誌;寫沖突保護;總線競爭保護等。
SPI總線有四種工作方式(SP0, SP1, SP2, SP3),其中使用的最為廣泛的是SPI0和SPI3方式。SPI模塊為了和外設進行數據交換,根據外設工作要求,其輸出串行同步時鐘極性和相位可以進行配置,時鐘極性(CPOL)對傳輸協議沒有重大的影響。如果CPOL=0,串行同步時鐘的空閑狀態為低電平;如果CPOL=1,串行同步時鐘的空閑狀態為高電平。時鐘相位(CPHA)能夠配置用於選擇兩種不同的傳輸協議之一進行數據傳輸。如果 CPHA=0,在串行同步時鐘的第一個跳變沿(上升或下降)數據被采樣;如果CPHA=1,在串行同步時鐘的第二個跳變沿(上升或下降)數據被采樣。
SPI主模塊和與之通信的外設時鐘相位和極性應該一致。
以下是SPI時序圖:
主要講解一下廣泛使用的兩種方式設置:
SPI0方式:CPOL=0,CPHA=0;SCK空閑狀態為低電平,第一個跳變沿(上升沿)采樣數據,無論對Master還是Slaver都是如此。
SPI3方式:CPOL=1,CPHA=1;SCK空閑狀態為高電平,第二個跳變沿(上升沿采樣數據,無論對Master還是Slaver都是如此。
其實對於SPI0和SPI1發送與接收數據,可以總結為一句話:上升沿采樣數據,下降沿發送數據。全雙工同時進行,當然,必須在CS拉低使能情況下。
二.FPGA作為Slaver實現SPI3方式與STM32通信
1.STM32方面:用庫函數配置SPI1,設置CPOL=1,CPHA=1.
2.FPGA方面:
(1)通過邊沿檢測技術得出SCK上升沿與下降沿標誌,用於下面狀態機中的數據采樣及發送。
(2)根據時序圖,采用2個狀態機分別在SCK上升沿實現數據采樣,下降沿實現數據發送。無論是采樣還是發送,都是高位在前,從Bit[7]到Bit[0],共8位數據。
(3)最後通過邊沿檢測技術得出數據采樣完成標誌,用於用戶操作。
以下是SPI3的時序圖:
三.Verilog代碼部分
測試工程代碼:實現了STM32每隔200ms發送流水燈數據給FPGA,使FPGA系統板上的4個LED燈實現流水操作;同時,FPGA每隔1s發送計數數據給STM32,並在STM32系統板上的LCD屏出來,即:顯示0-9循環計數。
但下面的代碼只是SPI作為從機的驅動部分,包括SPI發送數據與接收數據。
/*********************************************************************** ****************** name:SPI_Slaver_Driver ************** ********** author:made by zzuxzt ********** ****************** time:2014.4.29 ********************** ***********************************************************************/ //use SPI 3 mode,CHOL = 1,CHAL = 1 module spi(input clk, input rst_n, input CS_N, input SCK, input MOSI, input [7:0] txd_data, output reg MISO, output reg [7:0] rxd_data, output rxd_flag); //-------------------------capture the sck----------------------------- reg sck_r0,sck_r1; wire sck_n,sck_p; [email protected](posedge clk or negedge rst_n) begin if(!rst_n) begin sck_r0 <= 1‘b1; //sck of the idle state is high sck_r1 <= 1‘b1; end else begin sck_r0 <= SCK; sck_r1 <= sck_r0; end end assign sck_n = (~sck_r0 & sck_r1)? 1‘b1:1‘b0; //capture the sck negedge assign sck_p = (~sck_r1 & sck_r0)? 1‘b1:1‘b0; //capture the sck posedge //-----------------------spi_slaver read data------------------------------- reg rxd_flag_r; reg [2:0] rxd_state; [email protected](posedge clk or negedge rst_n) begin if(!rst_n) begin rxd_data <= 1‘b0; rxd_flag_r <= 1‘b0; rxd_state <= 1‘b0; end else if(sck_p && !CS_N) begin case(rxd_state) 3‘d0:begin rxd_data[7] <= MOSI; rxd_flag_r <= 1‘b0; //reset rxd_flag rxd_state <= 3‘d1; end 3‘d1:begin rxd_data[6] <= MOSI; rxd_state <= 3‘d2; end 3‘d2:begin rxd_data[5] <= MOSI; rxd_state <= 3‘d3; end 3‘d3:begin rxd_data[4] <= MOSI; rxd_state <= 3‘d4; end 3‘d4:begin rxd_data[3] <= MOSI; rxd_state <= 3‘d5; end 3‘d5:begin rxd_data[2] <= MOSI; rxd_state <= 3‘d6; end 3‘d6:begin rxd_data[1] <= MOSI; rxd_state <= 3‘d7; end 3‘d7:begin rxd_data[0] <= MOSI; rxd_flag_r <= 1‘b1; //set rxd_flag rxd_state <= 3‘d0; end default: ; endcase end end //--------------------capture spi_flag posedge-------------------------------- reg rxd_flag_r0,rxd_flag_r1; [email protected](posedge clk or negedge rst_n) begin if(!rst_n) begin rxd_flag_r0 <= 1‘b0; rxd_flag_r1 <= 1‘b0; end else begin rxd_flag_r0 <= rxd_flag_r; rxd_flag_r1 <= rxd_flag_r0; end end assign rxd_flag = (~rxd_flag_r1 & rxd_flag_r0)? 1‘b1:1‘b0; //---------------------spi_slaver send data--------------------------- reg [2:0] txd_state; [email protected](posedge clk or negedge rst_n) begin if(!rst_n) begin txd_state <= 1‘b0; end else if(sck_n && !CS_N) begin case(txd_state) 3‘d0:begin MISO <= txd_data[7]; txd_state <= 3‘d1; end 3‘d1:begin MISO <= txd_data[6]; txd_state <= 3‘d2; end 3‘d2:begin MISO <= txd_data[5]; txd_state <= 3‘d3; end 3‘d3:begin MISO <= txd_data[4]; txd_state <= 3‘d4; end 3‘d4:begin MISO <= txd_data[3]; txd_state <= 3‘d5; end 3‘d5:begin MISO <= txd_data[2]; txd_state <= 3‘d6; end 3‘d6:begin MISO <= txd_data[1]; txd_state <= 3‘d7; end 3‘d7:begin MISO <= txd_data[0]; txd_state <= 3‘d0; end default: ; endcase end end endmodule
六.Modelsim仿真圖
FPGA作為從機與STM32進行SPI協議通信---Verilog實現