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傳送資料與接收資料。
1 /*********************************************************************** 2 ****************** name:SPI_Slaver_Driver ************** 3 ********** author:made by zzuxzt ********** 4 ****************** time:2014.4.29 ********************** 5 ***********************************************************************/ 6 //use SPI 3 mode,CHOL = 1,CHAL = 1 7 module spi(input clk, 8 input rst_n, 9 input CS_N, 10 input SCK, 11 input MOSI, 12 input [7:0] txd_data, 13 output reg MISO, 14 output reg [7:0] rxd_data, 15 output rxd_flag); //recieve done,please transmit data 16 17 //-------------------------capture the sck----------------------------- 18 reg sck_r0,sck_r1; 19 wire sck_n,sck_p; 20 always@(posedge clk or negedge rst_n) 21 begin 22 if(!rst_n) 23 begin 24 sck_r0 <= 1'b1; //sck of the idle state is high 25 sck_r1 <= 1'b1; 26 end 27 else 28 begin 29 sck_r0 <= SCK; 30 sck_r1 <= sck_r0; 31 end 32 end 33 34 assign sck_n = (~sck_r0 & sck_r1)? 1'b1:1'b0; //capture the sck negedge 35 assign sck_p = (~sck_r1 & sck_r0)? 1'b1:1'b0; //capture the sck posedge 36 37 //-----------------------spi_slaver read data------------------------------- 38 reg rxd_flag_r; 39 reg [2:0] rxd_state; 40 always@(posedge clk or negedge rst_n) 41 begin 42 if(!rst_n) 43 begin 44 rxd_data <= 1'b0; 45 rxd_flag_r <= 1'b0; 46 rxd_state <= 1'b0; 47 end 48 else if(sck_p && !CS_N) 49 begin 50 case(rxd_state) 51 3'd0:begin 52 rxd_data[7] <= MOSI; 53 rxd_flag_r <= 1'b0; //reset rxd_flag 54 rxd_state <= 3'd1; 55 end 56 3'd1:begin 57 rxd_data[6] <= MOSI; 58 rxd_state <= 3'd2; 59 end 60 3'd2:begin 61 rxd_data[5] <= MOSI; 62 rxd_state <= 3'd3; 63 end 64 3'd3:begin 65 rxd_data[4] <= MOSI; 66 rxd_state <= 3'd4; 67 end 68 3'd4:begin 69 rxd_data[3] <= MOSI; 70 rxd_state <= 3'd5; 71 end 72 3'd5:begin 73 rxd_data[2] <= MOSI; 74 rxd_state <= 3'd6; 75 end 76 3'd6:begin 77 rxd_data[1] <= MOSI; 78 rxd_state <= 3'd7; 79 end 80 3'd7:begin 81 rxd_data[0] <= MOSI; 82 rxd_flag_r <= 1'b1; //set rxd_flag 83 rxd_state <= 3'd0; 84 end 85 default: ; 86 endcase 87 end 88 end 89 90 91 //--------------------capture spi_flag posedge-------------------------------- 92 reg rxd_flag_r0,rxd_flag_r1; 93 always@(posedge clk or negedge rst_n) 94 begin 95 if(!rst_n) 96 begin 97 rxd_flag_r0 <= 1'b0; 98 rxd_flag_r1 <= 1'b0; 99 end 100 else 101 begin 102 rxd_flag_r0 <= rxd_flag_r; 103 rxd_flag_r1 <= rxd_flag_r0; 104 end 105 end 106 107 assign rxd_flag = (~rxd_flag_r1 & rxd_flag_r0)? 1'b1:1'b0; 108 109 //---------------------spi_slaver send data--------------------------- 110 reg [2:0] txd_state; 111 always@(posedge clk or negedge rst_n) 112 begin 113 if(!rst_n) 114 begin 115 txd_state <= 1'b0; 116 end 117 else if(sck_n && !CS_N) 118 begin 119 case(txd_state) 120 3'd0:begin 121 MISO <= txd_data[7]; 122 txd_state <= 3'd1; 123 end 124 3'd1:begin 125 MISO <= txd_data[6]; 126 txd_state <= 3'd2; 127 end 128 3'd2:begin 129 MISO <= txd_data[5]; 130 txd_state <= 3'd3; 131 end 132 3'd3:begin 133 MISO <= txd_data[4]; 134 txd_state <= 3'd4; 135 end 136 3'd4:begin 137 MISO <= txd_data[3]; 138 txd_state <= 3'd5; 139 end 140 3'd5:begin 141 MISO <= txd_data[2]; 142 txd_state <= 3'd6; 143 end 144 3'd6:begin 145 MISO <= txd_data[1]; 146 txd_state <= 3'd7; 147 end 148 3'd7:begin 149 MISO <= txd_data[0]; 150 txd_state <= 3'd0; 151 end 152 default: ; 153 endcase 154 end 155 end 156 157 endmodule
六.Modelsim模擬圖