FPGA常用接口協議--UART


前言

    UART接口協議是一種比較簡單、非常常用的一種接口協議,使用它的場景很常見,是我們學習FPGA一定要會的接口協議;

UART協議

    通用異步收發器(Universal Asynchronous Receiver/Transmitter),通常稱作UART,是一種串行、異步、全雙工的通信協議,在嵌入式領域應用的非常廣泛。其數據通信格式如下圖:
在這里插入圖片描述

UART 數據傳輸格式

LSB:
least significant bit 表示二進制數據的最低位。

MSB :
most significant bit 表示二進制數據的最高位。

起始位: 
    每開始一次通信時發送方先發出一個邏輯”0”的信號(低電平),表示傳輸字符的開始。因為總線空閑時為高電平所以開始一次通信時先發送一個明顯區別於空閑狀態的信號即低電平。

數據位: 
    起始位之后就是我們所要傳輸的數據,數據位可以是5、6、7、8,9位等,構成一個字符(一般都是8位)。先發送最低位,最后發送最高位,使用低電平表示‘0’高電平表示‘1’完成數據位的傳輸。

奇偶校驗位: 
    數據位加上這一位后,使得“1”的位數應為偶數(偶校驗)或奇數(奇校驗),以此來校驗數據傳送的正確性。

停止位: 
    它是一個字符數據的結束標志。可以是1位、1.5位、2位的高電平。 由於數據是在傳輸線上定時的,並且每一個設備有其自己的時鍾,很可能在通信中兩台設備之間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,並且提供計算機校正時鍾的機會。停止位個數越多,數據傳輸越穩定,但是數據傳輸速度也越慢。

空閑位: 
     UART協議規定,當總線處於空閑狀態時信號線的狀態為‘1’即高電平,表示當前線路上沒有數據傳輸。

UART協議實現(verilog)

     為了更加簡單的描述串口協議,我們這里使用常用的1位起始位,1位停止位,8位數據位,無奇偶校驗位,波特率9600(本次波特率以參數形式提供,如需測試其他波特率,計算后更改即可)。

     UART發送端代碼如下

module uart_rx
#(
	parameter max_baud = 5208,
	parameter max_samp = 2604
)
(
	input	wire		sclk			,
	input	wire		rst_n			,
	input	wire		rx				,
	
	output	reg			rx_data_en		,
	output	reg[7:0]	rx_data
);	
	reg				rx1					;//延時一個時鍾
	reg				rx2					;//延時兩個時鍾
	reg				rx_reg				;//延時三個時鍾
	reg				rx_en				;//計數標志
	reg[12:0]		cnt_baud			;//波特率
	reg				bit_flag			;
	reg[3:0]		bit_cnt				;
	reg				rx_data_en_tmp		;
	reg[7:0]		rx_data_tmp			;


//消除亞穩態
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx1 <= 0;
	else 
		rx1 <= rx;
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx2 <= 0;
	else 
		rx2 <= rx1;
		
//邊沿檢測
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_reg <= 0;
	else 
		rx_reg <= rx2;	
		
//產生計數標志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_en <= 0;
	else if(rx2 == 0 && rx_reg == 1)
		rx_en <= 1;	
	else if(bit_cnt==9)
		rx_en <= 0;	
		
//波特率產生
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		cnt_baud <= 0;
	else if(cnt_baud==max_baud)
		cnt_baud <= 0;
	else if(rx_en==0)
		cnt_baud <= 0;
	else if(rx_en==1)
		cnt_baud <= cnt_baud + 1'b1;
		
//采樣產生bit標志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		bit_flag <= 0;
	else if(cnt_baud==max_samp)
		bit_flag <= 1;
	else
		bit_flag <= 0;
		
//bit計數
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		bit_cnt <= 0;
	else if(bit_cnt==9)
		bit_cnt <= 0;
	else if(bit_flag==1)
		bit_cnt <= bit_cnt + 1'b1;
		
//接收數據
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_tmp <= 0;	
	else if(bit_flag==1 && bit_cnt!=0)
		rx_data_tmp <= {rx_reg,rx_data_tmp[7:1]}; //右移
				
//傳輸完標志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_en_tmp <= 0;
	else if(bit_cnt==8 && bit_flag==1)
		rx_data_en_tmp <= 1;	
	else
		rx_data_en_tmp	<= 0;
		
//接收數據輸出
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data <= 0;	
	else if(rx_data_en_tmp==1)
		rx_data <= rx_data_tmp; 		
		
//接收數據標志輸出
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_en <= 0;	
	else
		rx_data_en <= rx_data_en_tmp;				
endmodule

     UART接收端代碼如下

module uart_rx
#(
	parameter max_baud = 5208,
	parameter max_samp = 2604
)
(
	input	wire		sclk			,	//50MHZ
	input	wire		rst_n			,
	input	wire		rx				,
	
	output	reg			rx_data_en		,
	output	reg[7:0]	rx_data
);	
	reg				rx1					;//延時一個時鍾
	reg				rx2					;//延時兩個時鍾
	reg				rx_reg				;//延時三個時鍾
	reg				rx_en				;//計數標志
	reg[12:0]		cnt_baud			;//波特率
	reg				bit_flag			;
	reg[3:0]		bit_cnt				;
	reg				rx_data_en_tmp		;
	reg[7:0]		rx_data_tmp			;


//消除亞穩態
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx1 <= 0;
	else 
		rx1 <= rx;
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx2 <= 0;
	else 
		rx2 <= rx1;
		
//邊沿檢測
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_reg <= 0;
	else 
		rx_reg <= rx2;	
		
//產生計數標志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_en <= 0;
	else if(rx2 == 0 && rx_reg == 1)
		rx_en <= 1;	
	else if(bit_cnt==9)
		rx_en <= 0;	
		
//波特率產生
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		cnt_baud <= 0;
	else if(cnt_baud==max_baud)
		cnt_baud <= 0;
	else if(rx_en==0)
		cnt_baud <= 0;
	else if(rx_en==1)
		cnt_baud <= cnt_baud + 1'b1;
		
//采樣產生bit標志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		bit_flag <= 0;
	else if(cnt_baud==max_samp)
		bit_flag <= 1;
	else
		bit_flag <= 0;
		
//bit計數
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		bit_cnt <= 0;
	else if(bit_cnt==9)
		bit_cnt <= 0;
	else if(bit_flag==1)
		bit_cnt <= bit_cnt + 1'b1;
		
//接收數據
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_tmp <= 0;	
	else if(bit_flag==1 && bit_cnt!=0)
		rx_data_tmp <= {rx_reg,rx_data_tmp[7:1]}; //右移
				
//傳輸完標志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_en_tmp <= 0;
	else if(bit_cnt==8 && bit_flag==1)
		rx_data_en_tmp <= 1;	
	else
		rx_data_en_tmp	<= 0;
		
//接收數據輸出
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data <= 0;	
	else if(rx_data_en_tmp==1)
		rx_data <= rx_data_tmp; 		
		
//接收數據標志輸出
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_en <= 0;	
	else
		rx_data_en <= rx_data_en_tmp;		
			
endmodule

     再寫一個頂層文件將發送端和接收端例化(當然,使用時也可以直接使用發送代碼和接收代碼),代碼如下

module	uart_top(
	input	wire		sclk			,//50MHZ
	input	wire		rst_n			,
	input	wire		rx_data			,
	
	output	wire	 	tx_data		
);

	wire		rx_flag		;
	wire[7:0]	data_net	;

uart_rx
#(
	.max_baud 	(5208-1		),		//波特率 50M/9600 ~5208
	.max_samp 	(2604-1		)		//在中間采樣
)
uart_rx_inst(
	.sclk		(sclk		),
	.rst_n		(rst_n		),
	.rx			(rx_data	),
                                	
	.rx_data_en	(rx_flag	),
	.rx_data    (data_net	)
);	

uart_tx 
#(
	.max_baud 	(5208-1		),		//波特率 50M/9600 ~5208
	.max_samp 	(2604-1		)		//在中間采樣
)
uart_tx_inst(
	.sclk		(sclk		),		//50MHZ
	.rst_n		(rst_n		),
	.tx_data_en	(rx_flag	),
	.tx_data	(data_net	),

	.uart_tx	(tx_data	)
);  
endmodule

    這里實現UART的方式是通過計數器的方式,當然也可以通過其他的方式實現(比如狀態機等),主要還是要理解UART協議,再理解的基礎上,實現起來就會比較輕松。

仿真

    給對應top寫一個testbench(仿真激勵),如下:

`timescale	1ns/1ns
module	tb_uart_top()		;
	reg		sclk			;
	reg		rst_n			;
	reg		rx_data			;
							
  	wire	tx_data			;
	
	
	
initial
	begin
		sclk = 0			;
		rst_n <= 0			;
		#20		
		rst_n <= 1			;
	end
	
always #5 sclk = ~sclk		;	//50MHZ

initial
	begin
		rx_data <= 1		;
		#200
		rx_byte()	;
	end

// defparam	uart_top_inst.uart_rx_inst.max_baud	= 51;
// defparam	uart_top_inst.uart_rx_inst.max_samp	= 25;
// defparam	uart_top_inst.uart_tx_inst.max_baud	= 51;
// defparam	uart_top_inst.uart_tx_inst.max_samp	= 25;


task	rx_bit(
	input	[7:0]	data	
);
integer	i;
for(i=0;i<10;i=i+1)
begin
	case(i)
		0:rx_data <= 0 ;
		1:rx_data <= data[0] ;
		2:rx_data <= data[1] ;
		3:rx_data <= data[2] ;
		4:rx_data <= data[3] ;
		5:rx_data <= data[4] ;
		6:rx_data <= data[5] ;
		7:rx_data <= data[6] ;
		8:rx_data <= data[7] ;
		9:rx_data <= 1'b1	;
	endcase
	#53000;
end
endtask

task	rx_byte();
integer	j;
for(j=0;j<24;j=j+1)
	rx_bit(j);
endtask


uart_top	uart_top_inst(
	.sclk		(sclk		),
	.rst_n		(rst_n		),
	.rx_data	(rx_data	),

	.tx_data	(tx_data	)	
);
endmodule

    這里通過task發送0-23到UART接收端,接收端接收到數據再通過發送端發送出去。
    截取其中仿真波形如下:

在這里插入圖片描述
    以發送23(8‘h17)為例,從圖上可以看出接收的二進制數為8‘b11101000,因為在仿真時(testbench)我們先發送的低位,所以需要將接收的數據倒置,即8’b00010111=8’h17,轉化為十進制即為23。
    仿真結果正確。
    實際上本段代碼可以直接拿來使用,下板測試通過。

    若有相關問題可以互相討論


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM