1. 程式人生 > 實用技巧 >【ZYNQ Ultrascale+ MPSOC FPGA教程】第十一章 RS232實驗

【ZYNQ Ultrascale+ MPSOC FPGA教程】第十一章 RS232實驗

原創宣告:

本原創教程由芯驛電子科技(上海)有限公司(ALINX)創作,版權歸本公司所有,如需轉載,需授權並註明出處。

適用於板卡型號:

AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4EV-P/AXU5EV-E/AXU5EV-P /AXU9EG/AXU15EG

實驗Vivado工程為“rs232_test”。

本章採用AN3485模組的RS232電路實現UART資料傳輸。

1.模組介紹

AN3845模組專門為工業現場應用設計的RS232/485/422通訊模組。它包含一路RS232介面,2路RS485和2路RS422通訊介面。配合開發板實現RS232、485和422的資料遠端傳輸和通訊。RS232、485和422介面分別採用MAX3232、MAX3485和MAX3490晶片作為電平轉換晶片。模組留有一個40針的排母用於連線開發板,RS232介面為一個標準的DB9串列埠公座,通過串列埠線直接連線電腦或者其他裝置; RS485和RS422介面採用接線端子跟外部連線,超遠距離傳輸可達上千米,另外RS485和RS422介面部分帶有正負15KV的ESD防護功能。

AN3845模組實物照片如下:

AN3845通訊模組正面圖

1.1 模組引數說明

以下為AN3485通訊模組的詳細引數:

RS232介面

    • 一路標準的DB9公座序列介面;
    • 使用MAX3232作為RS232和 TTL電平的轉換;
    • 傳輸率高達120Kbps資料通訊速率

RS485介面

    • 兩路RS485介面,採用3線的接線端子;
    • 使用MAX3485作為RS485和TTL的電平轉換;
    • 工業級設計,抗干擾能力超強,同時採用有效的防雷設計;
    • 具有120歐匹配電阻,插上跳線帽即可使能匹配電阻,長距離傳輸時建議短接。
    • 支援多機通訊,允許接在最多128個裝置的總線上
    • 傳輸率高達500Kbps資料通訊速率。

RS422介面

    • 兩路RS422介面,採用5線的接線端子;
    • 使用MAX3490作為RS422和TTL的電平轉換;
    • 工業級設計,抗干擾能力超強,同時採用有效的防雷設計;
    • 具有120歐匹配電阻,插上跳線帽即可使能匹配電阻,長距離傳輸時建議短接。
    • 支援多機通訊,允許接在最多128個裝置的總線上
    • 傳輸率高達500Kbps資料通訊速率。

1.2 模組功能說明

AN3485模組的RS232介面採用MAX3232晶片實現RS232和+3.3V TTL電平的轉換。TTL電平的串列埠接收和傳送訊號(RXD, TXD)連線到40針的聯結器上跟外面的FPGA晶片或者ARM晶片實現串列埠通訊。RS232串列埠通訊的最高速度為120kbps,RS232介面的原理設計圖如下圖所示。

2. 程式設計

本文所述的串列埠指非同步序列通訊,非同步序列是指UART(Universal Asynchronous Receiver/Transmitter),通用非同步接收/傳送。本實驗程式設計為每秒鐘向串列埠傳送”HELLO ALINX”,如果收到RXD接收的資料,再把接收的資料傳送出去,實現迴環的功能。

2.1 非同步串列埠通訊協議

訊息幀從一個低位起始位開始,後面是7個或8個數據位,一個可用的奇偶位和一個或幾個高位停止位。接收器發現開始位時它就知道資料準備傳送,並嘗試與傳送器時鐘頻率同步。如果選擇了奇偶校驗,UART就在資料位後面加上奇偶位。奇偶位可用來幫助錯誤校驗。在接收過程中,UART從訊息幀中去掉起始位和結束位,對進來的位元組進行奇偶校驗,並將資料位元組從序列轉換成並行。UART 傳輸時序如下圖所示:

從波形上可以看出起始位是低電平,停止位和空閒位都是高電平,也就是說沒有資料傳輸時是高電平,利用這個特點我們可以準確接收資料,當一個下降沿事件發生時,我們認為將進行一次資料傳輸。

2.2 波特率

常見的串列埠通訊波特率有2400 、9600、115200等,傳送和接收波特率必須保持一致才能正確通訊。波特率是指1秒最大傳輸的資料位數,包括起始位、資料位、校驗位、停止位。假如通訊波特率設定為9600,那麼一個數據位的時間長度是1/9600秒,本實驗中的波特率由50MHz時鐘產生。

2.3 接收模組設計

串列埠接收模組uart_rx是個引數化可配置模組,引數“CLK_FRE”定義接收模組的系統時鐘頻率,單位是Mhz,引數“BAUD_RATE”是波特率。接收狀態機狀態轉換圖如下:

“S_IDLE”狀態為空閒狀態,上電後進入“S_IDLE”,如果訊號“rx_pin”有下降沿,我們認為是串列埠的起始位,進入狀態“S_START”,等一個BIT時間起始位結束後進入資料位接收狀態“S_REC_BYTE”,本實驗中資料位設計是8位,接收完成以後進入“S_STOP”狀態,在“S_STOP”沒有等待一個BIT週期,只等待了半個BIT時間,這是因為如果等待了一個週期,有可能會錯過下一個資料的起始位判斷,最後進入“S_DATA”狀態,將接收到的資料送到其他模組。在這個模組我們提一點:為了滿足取樣定理,在接受資料時每個資料都在波特率計數器的時間中點進行取樣,以避免資料出錯的情況:

//receive serial data bit data
always@(posedge clk ornegedge rst_n)
begin
	if(rst_n ==1'b0)
		rx_bits <=8'd0;
	elseif(state == S_REC_BYTE && cycle_cnt == CYCLE/2-1)
		rx_bits[bit_cnt]<= rx_pin;
	else
		rx_bits <= rx_bits;
end

注意:本實驗沒有設計奇偶校驗位

訊號名稱 方向 寬度(bit) 說明
clk in 1 系統時鐘
rst_n in 1 非同步復位,低電平復位
rx_data out 8 接收到的串列埠資料(8位資料)
rx_data_valid out 1 接收到的串列埠資料有效(高有效)
rx_data_ready in 1 表示使用者可以從接收模組接收資料,當rx_data_ready和rx_data_valid都為高時資料送出
rx_pin in 1 串列埠接收資料輸入

串列埠接收模組uart_rx埠

2.4 傳送模組設計

傳送模組uart_tx設計和接收模組相似,也是使用狀態機,狀態轉換圖如下:

上電後進入“S_IDLE”空閒狀態,如果有傳送請求,進入傳送起始位狀態“S_START”,起始位傳送完成後進入傳送資料位狀態“S_SEND_BYTE”,資料位傳送完成後進入傳送停止位狀態“S_STOP”,停止位傳送完成後又進入空閒狀態。在資料傳送模組中,從頂層模組寫入的資料直接傳遞給暫存器‘tx_reg’,並通過‘tx_reg’暫存器模擬串列埠傳輸協議在狀態機的條件轉換下進行資料傳送:

always@(posedge clk ornegedge rst_n)
begin
	if(rst_n ==1'b0)
		tx_reg <=1'b1;
	else
		case(state)
			S_IDLE,S_STOP:
				tx_reg <=1'b1;
			S_START:
				tx_reg <=1'b0;
			S_SEND_BYTE:
				tx_reg <= tx_data_latch[bit_cnt];
			default:
				tx_reg <=1'b1;
		endcase
end
訊號名稱 方向 寬度(bit) 說明
clk in 1 系統時鐘
rst_n in 1 非同步復位,低電平復位
tx_data in 8 要傳送的串列埠資料(8位資料)
tx_data_valid in 1 傳送的串列埠資料有效(高有效)
tx_data_ready out 1 傳送模組已準備好傳送資料,使用者可將tx_data_valid訊號拉高發送資料給傳送模組。當tx_data_ready和tx_data_valid都為高時資料被髮送
tx_pin out 1 串列埠傳送資料傳送

串列埠傳送模組uart_tx埠

2.5 波特率的產生

在傳送和接收模組中,聲明瞭引數CYCLE,也就是UART一個週期的計數值,當然計數是在50MHz時鐘下進行的。使用者只要設定好CLK_FRE和BAUD_RATE這兩個引數即可。

測試程式

測試程式設計FPGA為1秒向串列埠傳送一次“HELLO ALINX\r\n”,不傳送期間,如果接受到串列埠資料,直接把接收到的資料送到傳送模組再返回。“\r\n”,在這裡和C語言中表示一致,都是回車換行。

測試程式分別例化了傳送模組和接收模組,同時將引數傳遞進去,波特率設定為115200。

always@(posedge sys_clk ornegedge rst_n)
begin
	if(rst_n ==1'b0)
	begin
		wait_cnt <=32'd0;
		tx_data <=8'd0;
		state <= IDLE;
		tx_cnt <=8'd0;
		tx_data_valid <=1'b0;
	end
	else
	case(state)
		IDLE:
			state <= SEND;
		SEND:
		begin
			wait_cnt <=32'd0;
			tx_data <= tx_str;

			if(tx_data_valid ==1'b1&& tx_data_ready ==1'b1&& tx_cnt <8'd12)//Send 12 bytes data
			begin
				tx_cnt <= tx_cnt +8'd1;//Send data counter
			end
			elseif(tx_data_valid && tx_data_ready)//last byte sent is complete
			begin
				tx_cnt <=8'd0;
				tx_data_valid <=1'b0;
				state <= WAIT;
			end
			elseif(~tx_data_valid)
			begin
				tx_data_valid <=1'b1;
			end
		end
		WAIT:
		begin
			wait_cnt <= wait_cnt +32'd1;

			if(rx_data_valid ==1'b1)
			begin
				tx_data_valid <=1'b1;
				tx_data <= rx_data;// send uart received data
			end
			elseif(tx_data_valid && tx_data_ready)
			begin
				tx_data_valid <=1'b0;
			end
			elseif(wait_cnt >= CLK_FRE *1000000)// wait for 1 second
				state <= SEND;
		end
		default:
			state <= IDLE;
	endcase
end

//combinational logic
//Send "HELLO ALINX\r\n"
always@(*)
begin
	case(tx_cnt)
		8'd0:  tx_str <="H";
		8'd1:  tx_str <="E";
		8'd2:  tx_str <="L";
		8'd3:  tx_str <="L";
		8'd4:  tx_str <="O";
		8'd5:  tx_str <=" ";
		8'd6:  tx_str <="A";
		8'd7:  tx_str <="L";
		8'd8:  tx_str <="I";
		8'd9:  tx_str <="N";
		8'd10:  tx_str <="X";
		8'd11:  tx_str <="\r";
		8'd12:  tx_str <="\n";
		default:tx_str <=8'd0;
	endcase
end
uart_rx#
(
.CLK_FRE(CLK_FRE),
.BAUD_RATE(115200)
) uart_rx_inst
(
.clk                        (sys_clk                  ),
.rst_n                      (rst_n                    ),
.rx_data                    (rx_data                  ),
.rx_data_valid              (rx_data_valid            ),
.rx_data_ready              (rx_data_ready            ),
.rx_pin                     (uart_rx                  )
);

uart_tx#
(
.CLK_FRE(CLK_FRE),
.BAUD_RATE(115200)
) uart_tx_inst
(
.clk                        (sys_clk                  ),
.rst_n                      (rst_n                    ),
.tx_data                    (tx_data                  ),
.tx_data_valid              (tx_data_valid            ),
.tx_data_ready              (tx_data_ready            ),
.tx_pin                     (uart_tx                  )
);

3. 模擬

這裡我們添加了一個串列埠接收的激勵程式vtf_uart_test.v檔案,用來模擬uart串列埠接收。這裡向串列埠模組的uart_rx傳送0xa3的資料, 每位的資料按115200的波特率傳送,1位起始位,8位資料位和1位停止位。

模擬的結果如下,當程式接收到8位資料的時候,rx_data_valid有效,rx_data[7:0]的資料位a3。

實驗測試

將AN3485模組插到J11擴充套件口上,這裡使用了USB轉RS232/RS485/RS422的裝置,由於很多電腦都沒有9針的序列介面,我們通過串列埠線與USB轉串列埠裝置連線,再通過USB連線到電腦上。如果電腦有串列埠的話,可以直接連線串列埠。

在裝置管理器中找到串列埠號”COM5”

開啟串列埠除錯,埠選擇“COM5”(根據自己情況選擇),波特率設定115200,檢驗位選None,資料位選8,停止位選1,然後點選“開啟串列埠”。此軟體在例程資料夾下。

開啟串列埠以後,每秒可收到“HELLO ALINX”,在傳送區輸入框輸入要傳送的文字,點選“手動傳送”,可以看到接收到自己傳送的字元。