FPGA學習筆記03——UART串列埠

ngany發表於2020-10-13

參考內容:王建飛《你好FPGA一本可以聽的書》

正點原子《開拓者FPGA開發指南》

1.介紹

序列通訊分為兩種方式:同步序列通訊和非同步序列通訊。 同步序列通訊需要通訊雙方在同一時鐘的控制下,同步傳輸資料;非同步序列通訊是指通訊雙方使用各自的時鐘控制資料的傳送和接收過程。
基本的UART通訊只需要兩條訊號線(RXD、TXD)就可以完成資料的相互通訊,接收與傳送是全雙工形式。
UART在傳送或接收過程中的一幀資料由4部分組成, 起始位、 資料位、 奇偶校驗位和停止位。

  1. 起始位標誌著一幀資料的開始,
  2. 停止位標誌著一幀資料的結束,
  3. 資料位是一幀資料中的有效資料。
  4. 校驗位分為奇校驗和偶校驗, 用於檢驗資料在傳輸過程中是否出錯。奇校驗時, 傳送方應使資料位中1的個數與校驗位中1的個數之和為奇數;接收方在接收資料時,對1的個數進行檢查,若不為奇數,則說明資料在傳輸過程中出了差錯。 同樣,偶校驗則檢查1的個數是否為偶數。

波特率:UART的傳送速率,用於說明資料傳送的快慢。在序列通訊中,資料是按位進行傳送的,因此傳送速率用每秒鐘傳送資料位的數目來表示,稱之為波特率。如波特率9600=9600bps(位/秒)。

資料位:可選擇為5、 6、 7、 8位,其中8位資料位是最常用的。

2.硬體設計

串列埠USB電平轉換晶片採用CH340C。

FPGA管教分配

訊號名方向管教埠說明
sys_clkinputE1

系統時鐘50M

sys_rst_ninputM1系統復位,低電平有效
uart_rxinputC6串列埠接收
uart_txoutputE6串列埠傳送
LED0outputF3 
LED1outputF5 

 

注意:CH340_TX接FPGA的RX,CH340_RX接FPGA的TX

3.程式設計

功能:計算機串列埠傳送一幀資料,FPGA接收成功後,LED1亮燈,回發給計算機同樣資料。

訊號連線RTL圖:

接收:

module uart_recv(
	input sys_clk,
	input sys_rst_n,
	input uart_rxd, 
	output reg [7:0] recv_data,
	output reg recv_ok
);

// parameter define
parameter  SYSFREQ = 50_000_000;
parameter  BAUD    = 9600;
localparam BPS_CNT = SYSFREQ/BAUD;

// wiew define
wire start_flag/*synthesis keep*/;

// reg define
reg [31:0] clk_cnt;
reg [7:0]  uart_cnt;
reg 	     uart_d0;
reg 	     uart_d1;
reg [7:0]  buf_data;
reg		  recv_en_flag/*synthesis noprune*/;	// uart is receiving

// catch negitive edge of uart_rxd   ---|__
assign start_flag = (!uart_d0) & (uart_d1);

// set uart_d0 and uart_d1
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		uart_d0 <= 1'd0;
		uart_d1 <= 1'd0;
	end
	else begin
		uart_d0 <= uart_rxd;
		uart_d1 <= uart_d0;
	end
end

// receiving flag
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		recv_en_flag <= 1'b0;
	end
	else if(start_flag)begin
		recv_en_flag <= 1'b1;
	end
	else if((uart_cnt == 9) && (clk_cnt == (BPS_CNT/2 + 100)))begin
		recv_en_flag <= 1'b0;
	end
	else
		recv_en_flag <= recv_en_flag;
end

// counter
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		clk_cnt <= 1'b0;
		uart_cnt <= 1'b0;
	end
	else if(recv_en_flag)begin
		if(clk_cnt < BPS_CNT - 1)begin
			clk_cnt  <= clk_cnt + 1'b1;
			uart_cnt <= uart_cnt;
		end
		else begin
			clk_cnt <= 1'b0;
			uart_cnt <= uart_cnt + 1'b1;
		end
	end
	else begin
		clk_cnt <= 1'b0;
		uart_cnt <= 1'b0;
	end
end

// data save
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		buf_data <= 8'd0;
		recv_ok  <= 1'b0;
	end
	else if(recv_en_flag)begin
		if(clk_cnt == BPS_CNT/2)begin
			case(uart_cnt)
				4'd0 : ;
				4'd1 : buf_data[0] <= uart_d1;
				4'd2 : buf_data[1] <= uart_d1;
				4'd3 : buf_data[2] <= uart_d1;
				4'd4 : buf_data[3] <= uart_d1;
				4'd5 : buf_data[4] <= uart_d1;
				4'd6 : buf_data[5] <= uart_d1;
				4'd7 : buf_data[6] <= uart_d1;
				4'd8 : buf_data[7] <= uart_d1;
				4'd9 : begin
						recv_data <= buf_data;
						recv_ok   <= 1'b1;
						end
			endcase
		end
	end
	else begin
		buf_data <= 8'd0;
		recv_ok  <= 1'b0;
	end
end

endmodule

傳送:

module uart_send(	
	input sys_clk,
	input sys_rst_n,
	output reg uart_txd, 
	input [7:0] send_data,
	input recv_ok
);

// parameter define
parameter  SYSFREQ = 50_000_000;
parameter  BAUD    = 9600;
localparam BPS_CNT = SYSFREQ/BAUD;

// reg define
reg [31:0] clk_cnt;
reg [7:0]  uart_cnt;
reg [7:0]  buf_data;
reg		  send_en_flag/*synthesis noprune*/;	// uart is sending

always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		send_en_flag <= 1'b0;
		buf_data 	 <= 8'd0;
	end
	else if(recv_ok)begin
		send_en_flag <= 1'b1;
		buf_data 	 <= send_data;
	end
	else if((uart_cnt == 8'd9) && (clk_cnt == BPS_CNT/2+100))begin
		send_en_flag <= 1'b0;
		buf_data 	 <= 8'd0;
	end
	else begin
		send_en_flag <= send_en_flag;
		buf_data 	 <= buf_data;
	end
end

always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		clk_cnt  <= 32'd0;
		uart_cnt <= 8'd0;
	end
	else if(send_en_flag)begin
		if(clk_cnt < BPS_CNT-1)begin
			clk_cnt  <= clk_cnt + 1'b1;
			uart_cnt <= uart_cnt;
		end
		else begin
			clk_cnt  <= 32'd0;
			uart_cnt <= uart_cnt + 1'b1;
		end
	end
	else begin
		clk_cnt  <= 32'd0;
		uart_cnt <= 8'd0;	
	end
end

always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		uart_txd <= 1'b1;
	end
	else if(send_en_flag)begin
		if(clk_cnt == 32'd0)begin
			case(uart_cnt)
			7'd0:uart_txd <= 1'b0;
			7'd1:uart_txd <= buf_data[0];
			7'd2:uart_txd <= buf_data[1];
			7'd3:uart_txd <= buf_data[2];
			7'd4:uart_txd <= buf_data[3];
			7'd5:uart_txd <= buf_data[4];
			7'd6:uart_txd <= buf_data[5];
			7'd7:uart_txd <= buf_data[6];
			7'd8:uart_txd <= buf_data[7];
			7'd9:uart_txd <= 1'b1;
			endcase
		end
	end
end

endmodule

使用SignalTap抓取波形如下:

原始碼下載:https://download.csdn.net/download/ngany/12921479

相關文章