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時序圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicGcq5yN0QzMwQzNwMDMwYzNvw1My8CX1AzLcRTMwIzLcd2bsJ2LcNXZnFWbp9CXt92YuQXZhFmbph2YuMXZslmZvw1LcpDc0RHaiojIsJye.jpg)
主要講解一下廣泛使用的兩種方式設定:
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仿真圖