【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