1. 程式人生 > >FPGA作為從機與STM32進行SPI協議通信---Verilog實現

FPGA作為從機與STM32進行SPI協議通信---Verilog實現

src 空間 thumb author 必須 介紹 brush ref 2.0

一.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實現