一.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仿真圖