日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

fpga的spi的编程_UASP协议

發布時間:2023/12/19 综合教程 38 生活家
生活随笔 收集整理的這篇文章主要介紹了 fpga的spi的编程_UASP协议 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面

SPI協議系列文章:

FPGA實現的SPI協議(一)—-SPI驅動

FPGA實現的SPI協議(二)—-基于SPI接口的FLASH芯片M25P16的使用


1、什么是SPI協議

SPI(Serial Peripheral Interface,串行外圍設備接口)通訊協議,是 Motorola 公司提出的一種同步串行接口技術,是一種高速、全雙工、同步通信總線,在芯片中只占用四根管腳用來控制及數據傳輸,廣泛用于 EEPROM、Flash、RTC(實時時鐘)、ADC(數模轉換 器)、DSP(數字信號處理器)以及數字信號解碼器上,是常用的、重要的低速通訊協議之一。

SPI 通訊協議的優點是支持全雙工通信,通訊方式較為簡單,且相對數據傳輸速率較快;缺點是沒有指定的流控制,沒有應答機制,在數據可靠性上有一定缺陷。

2、SPI協議詳述

2.1、SPI協議物理層

SPI 通訊設備的通訊模式是主從通訊模式,通訊雙方有主從之分,根據從機設備的數量,SPI 通訊設備之間的連接方式可分為一主一從和一主多從。

SPI總線傳輸只需要4根線就能完成,這四根線的作用分別如下:

  • SCK (Serial Clock):時鐘信號線,用于同步通訊數據。由通訊主機產生,決定了通訊的速率,不同的設備支持的最高時鐘頻率不同
  • MOSI (Master Output, Slave Input):主設備輸出/從設備輸入引腳。主機的數據從這條信號線輸出,從機由這條信號線讀入主機發送的數據,數據方向由主機到從機
  • MISO (Master Input,Slave Output):主設備輸入/從設備輸出引腳。主機從這條信號線讀入數據,從機的數據由這條信號線輸出到主機,數據方向由從機到主機
  • CS(Chip Select):片選信號線。當有多個 SPI 從 設備與 SPI 主機相連時,設備的其它信號線 SCK、MOSI 及 MISO 同時并聯到相同的 SPI 總線上,即無論有多少個從設備,都共同使用這 3 條總線;而每個從設備都有獨立的片選信號線,即有多少個從設備,就有多少條片選信號線。相當于由SPI構成的通信系統中,通過CS片選信號來決定通信的從機設備是哪一臺。通信期間低電平有效,表示對應從機被選中

2.2、SPI 協議層

SPI總線傳輸一共有4種模式,這4種模式分別由時鐘極性(CPOL,Clock Polarity)時鐘相位(CPHA,Clock Phase)來定義,其中CPOL參數規定了SCK時鐘信號空閑狀態的電平,CPHA規定了數據是在SCK時鐘的上升沿被采樣還是下降沿被采樣。

SPI總線的極性–時鐘極性

時鐘極性決定SPI總線空閑時的時鐘信號是高電平還是低電平。CPOL = 1:表示空閑時是高電平;CPOL = 0:表示空閑時是低電平。

SPI總線的相位–時鐘相位

時鐘相位決定SPI總線從哪個跳變沿開始采樣數據CPHA = 0:在時鐘信號SCK的第1個跳變沿采樣;CPHA = 1:在時鐘信號SCK的第2個跳變沿采樣。

這四種模式的時序圖如下圖所示:

  • 模式0:CPOL= 0,CPHA=0。SCK串行時鐘線空閑是為低電平,數據在SCK時鐘的上升沿被采樣,數據在SCK時鐘的下降沿切換
  • 模式1:CPOL= 0,CPHA=1。SCK串行時鐘線空閑是為低電平,數據在SCK時鐘的下降沿被采樣,數據在SCK時鐘的上升沿切換
  • 模式2:CPOL= 1,CPHA=0。SCK串行時鐘線空閑是為高電平,數據在SCK時鐘的下降沿被采樣,數據在SCK時鐘的上升沿切換
  • 模式3:CPOL= 1,CPHA=1。SCK串行時鐘線空閑是為高電平,數據在SCK時鐘的上升沿被采樣,數據在SCK時鐘的下降沿切換

經常用到的是模式0和模式3(畢竟在下降沿采集數據的還是少)。下圖描述了4種模式數據線MOSI和MISO的數據切換(Toggling)位置和數據采樣位置的關系。

2.3、SPI協議通信過程

下面以模式 0 為例,講解一下 SPI 基本的通訊過程:

SCK、MOSI、CS_N 信號均由主機控制產生, SCK 是時鐘信號,用以同步數據,MOSI 是主機輸出從機輸入信號,主機通過此信號線傳輸數據給從機,CS_N 為片選信號,用以選定從機設備,低電平有效;而 MISO 的信號由 從機產生,主機通過該信號線讀取從機的數據。MOSI 與 MISO 的信號只在 CS_N 為低電平的時候才有效,在 SCK 的每個時鐘周期 MOSI 和 MISO 傳輸一位數據。

在圖中的標號1處,CS_N 信號線由高變低,是 SPI 通訊的起始信號。CS_N 是每 個從機各自獨占的信號線,當從機在自己的 CS_N 線檢測到起始信號后,就知道自己被主 機選中了,開始準備與主機通訊。在圖中的標號6處,CS_N 信號由低變高,是 SPI 通訊的停止信號,表示本次通訊結束,從機的選中狀態被取消。

SPI 使用 MOSI 及 MISO 信號線來傳輸數據,使用 SCK 信號線進行數據同步。MOSI 及 MISO 數據線在 SCK 的每個時鐘周期傳輸一位數據,且數據輸入輸出是同時進行的。數據傳輸時,MSB 先行或 LSB 先行并沒有作硬性規定,但要保證兩個 SPI 通訊設備之間使用同樣的協定,一般都會采用MSB 先行模式。 MOSI 及 MISO 的數據在 SCK 的下降沿期間變化輸出, 在 SCK 的上升沿時被采樣。即在 SCK 的上升沿時刻,MOSI 及 MISO 的數據有效,高電平時表示數據“1”,為低電平時表示數據“0”。在其它時刻,數據無效,MOSI 及 MISO 為下一次表示數據做準備。

SPI 每次數據傳輸可以 8 位或 16 位為單位,每次傳輸的單位數不受限制。

2.4、SPI協議的特性

  • SPI協議是一主多從的架構,通過片選信號CS來區分不同的從機(尋址方式)
  • SPI協議是一種同步(Synchronous)傳輸協議,通信雙方通過主機生成的時鐘信號SCK來作為數據交換的基準信號
  • SPI協議是一種全雙工的串行通信協議,通信過程中主從雙方均可進行數據交換
  • SPI協議具有4中通信模式,依據雙方約定好的模式進行通信

2.5、SPI協議的優勢、劣勢

優勢:

  • 全雙工串行通信
  • 簡單的硬件結構
  • 高速數據傳輸速率(相比UART、IIC)
  • 靈活的數據傳輸方式,不限于8位,可以是任意大小的字

劣勢

  • 僅支持一個主設備
  • 引腳略多(相比UART、IIC)
  • 沒有硬件從機應答信號(主機可能在不知情的情況下無處發送)

3、驅動代碼的設計實現

接下來實現的SPI驅動代碼特性如下:MSB 先行;僅限模式0;每次傳輸8位(1個BYTE)。

3.1、接口定義與整體設計

SPI驅動的整體框圖、輸入輸出信號如下所示:

其中信號描述如下:

該模塊的使用方法如下:

  • 拉高SPI傳輸開始信號spi_start一個周期,同時發送要傳輸的數據給data_send,等待數據發送完成后,該模塊會將發送完成標志信號send_done拉高一個周期,標志一個BYTE的數據通過SPI總線發送給了從機
  • 同樣的,當接收完成標志信號rec_done被該模塊拉高后,則意味著,主機成功接收了一個BTYE從機發送過來的數據
  • 當主機希望結束這次傳輸時,可將SPI結束信號spi_end拉高一個周期,則該模塊會在發送最后一個模塊后結束SPI傳輸,這也意味著,如果沒有結束到SPI結束信號,則SPI傳輸會一直進行,以便實現多個BYTE的SPI傳輸

3.2、Verilog代碼

Verilog代碼并不復雜,結合下圖的SPI通信過程,可以發現以下要點:

  • SCK很適合使用系統時鐘的4分頻時鐘,因為在一個SCK內需要對其進行4次操作
  • 分別使用生成的SCK的上升沿、下降沿對其移位發送數據、接收數據即可
  • 此外從下圖可知,SPI的驅動非常適合使用狀態機編寫,有興趣可以自己嘗試一下

`timescale 1ns/1ns		//時間單位/精度	
// 模式0
module spi_drive
(
// 系統接口
    input               sys_clk		, 			// 全局時鐘50MHz
    input               sys_rst_n	, 			// 復位信號,低電平有效
// 用戶接口	
    input               spi_start	,			// 發送傳輸開始信號,一個高電平
    input              	spi_end		,			// 發送傳輸結束信號,一個高電平
    input        [7:0]  data_send   , 			// 要發送的數據
    output  reg  [7:0]  data_rec  	, 			// 接收到的數據
    output  reg         send_done	, 			// 主機發送一個字節完畢標志位    
    output  reg         rec_done	, 			// 主機接收一個字節完畢標志位    
// SPI物理接口
    input               spi_miso	, 			// SPI串行輸入,用來接收從機的數據
    output  reg         spi_sclk	, 			// SPI時鐘
    output  reg         spi_cs    	, 			// SPI片選信號,低電平有效
    output  reg         spi_mosi				// SPI輸出,用來給從機發送數據          
);

reg	[1:0]	cnt;								//4分頻計數器
reg	[3:0]	bit_cnt_send;						//發送計數器
reg	[3:0]	bit_cnt_rec;						//接收計數器
reg			spi_end_req;						//結束請求

//4分頻計數器
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		cnt <= 2'd0;						
	else if(!spi_cs)begin
		if(cnt == 2'd3)
			cnt <= 2'd0;
		else
		cnt <= cnt + 1'b1;		
	end
	else 
		cnt <= 2'd0;	
end
// 生成spi_sclk時鐘
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_sclk <= 1'b0;			//模式0默認為低電平					
	else if(!spi_cs)begin			//在SPI傳輸過程中
		if(cnt == 2'd0 )
			spi_sclk <= 1'b0;
		else if (cnt == 2'd2)
			spi_sclk <= 1'b1;
		else 
			spi_sclk <= spi_sclk;	
	end
	else 
		spi_sclk <= 1'b0;			//模式0默認為低電平		
end
// 生成片選信號spi_cs
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_cs <= 1'b1;				//默認為高電平						
	else if(spi_start)				//開始SPI準備傳輸,拉低片選信號
		spi_cs <= 1'b0;
	//收到了SPI結束信號,且結束了最近的一個BYTE
	else if(spi_end_req && (cnt == 2'd1 && bit_cnt_rec == 4'd0))
		spi_cs <= 1'b1;				//拉高片選信號,結束SPI傳輸
end
// 生成結束請求信號(捕捉spi_end信號)
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_end_req <= 1'b0;		//默認不使能					
	else if(spi_cs)					
		spi_end_req <= 1'b0;		//結束SPI傳輸后拉低請求
	else if(spi_end)				
		spi_end_req <= 1'b1;		//接收到SPI結束信號后就把結束請求拉高
end
// 發送數據過程--------------------------------------------------------------------

// 發送數據
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		spi_mosi <= 1'b0;						//模式0空閑
		bit_cnt_send <= 4'd0;
	end
	else if(cnt == 2'd0 && !spi_cs)begin		//模式0的上升沿
		spi_mosi <= data_send[7-bit_cnt_send];	//發送數據移位
		if(bit_cnt_send == 4'd7)				//發送完8bit
			bit_cnt_send <= 4'd0;
		else
			bit_cnt_send <= bit_cnt_send + 1'b1;	
	end
	else if(spi_cs)begin						//非傳輸時間段
		spi_mosi <= 1'b0;						//模式0空閑
		bit_cnt_send <= 4'd0;
	end
	else begin
		spi_mosi <= spi_mosi;
		bit_cnt_send <= bit_cnt_send;
	end
end
// 發送數據標志
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		send_done <= 1'b0;			
	else if(cnt == 2'd0 && bit_cnt_send == 4'd7)		//發送完了8bit數據
		send_done <= 1'b1;								//拉高一個周期,表示發送完成	
	else 
		send_done <= 1'b0;			
end

// 接收數據過程--------------------------------------------------------------------

// 接收數據spi_miso
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		data_rec <= 8'd0;		
		bit_cnt_rec <= 4'd0;
	end
	else if(cnt == 2'd2 && !spi_cs)begin				//模式0的上升沿
		data_rec[7-bit_cnt_rec] <= 	spi_miso;			//移位接收
		if(bit_cnt_rec == 4'd7)							//接收完了8bit
			bit_cnt_rec <= 4'd0;
		else
			bit_cnt_rec <= bit_cnt_rec + 1'b1;	
	end
	else if(spi_cs)begin								
		bit_cnt_rec <= 4'd0;
	end
	else begin
		data_rec <= data_rec;
		bit_cnt_rec <= bit_cnt_rec;
	end
end
// 接收數據標志
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		rec_done <= 1'b0;									
	else if(cnt == 2'd2 && bit_cnt_rec == 4'd7)			//接收完了8bit
		rec_done <= 1'b1;								//拉高一個周期,表示接收完成			
	else 
		rec_done <= 1'b0;					
end

endmodule

4、Testbench及仿真結果

4.1、單個BYTE的仿真

使用該SPI驅動,向從機發送單個BYTE數據8‘b01010101,觀察其仿真時序是否正確:

//------------------------------------------------
//--SPI驅動仿真(模式0,1個BYTE)
//------------------------------------------------
`timescale 1ns/1ns		//時間單位/精度

//------------<模塊及端口聲明>----------------------------------------
module tb_spi_drive();
//系統接口
reg				sys_clk		;			// 全局時鐘50MHz
reg				sys_rst_n	;   		// 復位信號,低電平有效
//用戶接口                      		
reg				spi_start 	;   		// 發送傳輸開始信號,一個高電平
reg				spi_end   	;   		// 發送傳輸結束信號,一個高電平
reg		[7:0]  	data_send   ;   		// 要發送的數據
wire  	[7:0]  	data_rec  	;   		// 接收到的數據
wire         	send_done	;   		// 主機發送一個字節完畢標志位    
wire         	rec_done	;   		// 主機接收一個字節完畢標志位    
//SPI物理接口                   		
reg				spi_miso	;   		// SPI串行輸入,用來接收從機的數據
wire         	spi_sclk	;   		// SPI時鐘
wire			spi_cs    	;   		// SPI片選信號
wire         	spi_mosi	;   		// SPI輸出,用來給從機發送數據
//仿真用
reg		[3:0]  	cnt_send 	;			//發送數據計數器,0-15      

//------------<例化SPI驅動模塊(模式0)>----------------------------------------
spi_drive	spi_drive_inst(
	.sys_clk		(sys_clk	), 			
	.sys_rst_n		(sys_rst_n	), 			
		
	.spi_start		(spi_start	), 			
	.spi_end		(spi_end	),
	.data_send		(data_send	), 			
	.data_rec  		(data_rec	), 			
	.send_done		(send_done	), 			
	.rec_done		(rec_done	), 			
				
	.spi_miso		(spi_miso	), 			
	.spi_sclk		(spi_sclk	), 			
	.spi_cs    		(spi_cs		), 			
	.spi_mosi		(spi_mosi	)			
);

//------------<設置初始測試條件>----------------------------------------
initial begin
	sys_clk = 1'b0;						//初始時鐘為0
	sys_rst_n <= 1'b0;					//初始復位
	spi_start <= 1'b0;	
	data_send <= 8'd0;	
	spi_miso <= 1'bz;	
	spi_end <= 1'b0;	
	#80									//80個時鐘周期后
	sys_rst_n <= 1'b1;					//拉高復位,系統進入工作狀態
	#30									//30個時鐘周期后拉高SPI開始信號,開始SPI傳輸
	spi_start <= 1'b1;data_send <= 8'b01010101;
	#20	
	spi_start <= 1'b0;
	@(posedge send_done)				//一個BYTE發送完成
	spi_end <= 1'b1;	#20	spi_end <= 1'b0;	//拉高一個周期結束信號	
	
end
	
//------------<設置時鐘>----------------------------------------------
always #10 sys_clk = ~sys_clk;					//系統時鐘周期20ns

endmodule

仿真結果如下:

可以看到,在拉高了spi_start開始傳輸信號后,主機開始發送數據,MOSI上的數據分別是01010101,發送完一個BYTE的數據后,send_done拉高。此時拉高結束信號spi_end,就終結了這次SPI傳輸,完成了單個BYTE的SPI傳輸。

4.2、多個BYTE的仿真

使用該SPI驅動,依次向從機發送數據8‘d0~8‘d10,觀察其仿真時序是否正確:

//------------------------------------------------
//--SPI驅動仿真(模式0)
//------------------------------------------------
`timescale 1ns/1ns		//時間單位/精度

//------------<模塊及端口聲明>----------------------------------------
module tb_spi_drive();
//系統接口
reg				sys_clk		;			// 全局時鐘50MHz
reg				sys_rst_n	;   		// 復位信號,低電平有效
//用戶接口                      		
reg				spi_start 	;   		// 發送傳輸開始信號,一個高電平
reg				spi_end   	;   		// 發送傳輸結束信號,一個高電平
reg		[7:0]  	data_send   ;   		// 要發送的數據
wire  	[7:0]  	data_rec  	;   		// 接收到的數據
wire         	send_done	;   		// 主機發送一個字節完畢標志位    
wire         	rec_done	;   		// 主機接收一個字節完畢標志位    
//SPI物理接口                   		
reg				spi_miso	;   		// SPI串行輸入,用來接收從機的數據
wire         	spi_sclk	;   		// SPI時鐘
wire			spi_cs    	;   		// SPI片選信號
wire         	spi_mosi	;   		// SPI輸出,用來給從機發送數據
//仿真用
reg		[3:0]  	cnt_send 	;			//發送數據計數器,0-15      

//------------<例化SPI驅動模塊(模式0)>----------------------------------------
spi_drive	spi_drive_inst(
	.sys_clk		(sys_clk	), 			
	.sys_rst_n		(sys_rst_n	), 			
		
	.spi_start		(spi_start	), 			
	.spi_end		(spi_end	),
	.data_send		(data_send	), 			
	.data_rec  		(data_rec	), 			
	.send_done		(send_done	), 			
	.rec_done		(rec_done	), 			
				
	.spi_miso		(spi_miso	), 			
	.spi_sclk		(spi_sclk	), 			
	.spi_cs    		(spi_cs		), 			
	.spi_mosi		(spi_mosi	)			
);

//------------<設置初始測試條件>----------------------------------------
initial begin
	sys_clk = 1'b0;						//初始時鐘為0
	sys_rst_n <= 1'b0;					//初始復位
	spi_start <= 1'b0;	
	data_send <= 8'd0;	
	spi_miso <= 1'bz;	
	spi_end <= 1'b0;	
	#80									//80個時鐘周期后
	sys_rst_n <= 1'b1;					//拉高復位,系統進入工作狀態
	#30									//30個時鐘周期后拉高SPI開始信號,開始SPI傳輸
	spi_start <= 1'b1;	#20	spi_start <= 1'b0;
end

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		data_send <= 8'd0;			
		spi_end <= 1'b0;			
		cnt_send <= 4'd0; 		
	end
	else if(send_done)begin						//數據發送完成		
		if(cnt_send == 4'd10)begin		
			cnt_send <= 4'd0; 
			spi_end <= 1'b1;					//拉高結束標志,結束SPI傳輸過程	
			data_send <= 8'd0;
		end
		else begin
			cnt_send <= cnt_send + 4'd1; 
			spi_end <= 1'b0;					
			data_send <= data_send + 4'd1;		//發送數據累加	
		end
	end
	else begin
		data_send <= data_send;
		spi_end <= 1'b0;						//其他時候保持SPI傳輸(不結束)	
	end
end
	
//------------<設置時鐘>----------------------------------------------
always #10 sys_clk = ~sys_clk;					//系統時鐘周期20ns

endmodule

仿真結果如下:

可以看到,在拉高了spi_start開始傳輸信號后,主機一直在發送數據,MOSI上的數據分別是8‘d0~8‘d10,每次發送一個BYTE的數據后,send_done即拉高一次。當結束信號spi_end被拉高后,就終結了這次SPI傳輸。

5、其他

  • 需要注意的是,由于沒有從機響應,所以MISO都是高阻態(藍色)
  • 下篇文章再結合從機(FLASH芯片)進行仿真驗證接收數據功能
  • 想要整個工程的朋友可以在評論區留下郵箱

總結

以上是生活随笔為你收集整理的fpga的spi的编程_UASP协议的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。