之前在數電課上也有用NE555+CD4017構成的純數位電路搭過一個baud=4800的RS232協定的串列埠(原理圖見下圖),感覺還是蠻好玩的。於是今天今天用FPGA搭一下試試。
一些客套介紹話
在下面 下麪的RS232字元發生器的設計開始之前,首先介紹RS232協定的規範。因爲這對整個RS232字元發生器的設計非常重要。
RS-232是美國電子工業聯盟(EIA)開發的序列數據通訊介面標準。原始全名爲EIA-RS-232(簡稱232、RS232)。它廣泛應用於計算機序列介面外圍裝置連線.RS-232C標準,其中EIA(電子工業協會)代表美國電子工業協會,RS(推薦標準)代表推薦標準,232爲識別號,C代表RS232(1969)第三次修訂版。有RS232B,RS232A。
同樣,這些介紹對寫程式沒啥用。作爲程式猿,我更希望知道它的時序圖/協定格式。
如上爲RS232協定中一幀數據的格式
以下解釋下圖上的幾個關鍵資訊:
0.每幀數據有1位起始位
1.每幀數據有5/6/7/8位元數據位
2.LSB最低有效位先出
3.1bit/2bit停止位
4.NONE/奇偶校驗停止位
波特率指得是一秒鐘發送bit的個數。
板子上的是50M的晶振,如果要產生9600的波特率。最簡單的計算和處理方法是50M/9600=5208得到 在9600波特率下每發一個bit需要計數5208次。
一般在採樣信號時,我們會採用16倍採樣/64倍採樣來保證信號可靠。經過這樣設計後,採樣率爲5208倍,可以說是非常可靠了QAQ。還有,在採樣的時候,我們一般在信號中點進行採樣。這樣操作的原因,也是爲了保證信號的可靠。
`timescale 1ns / 1ps
module uart_rx_path(
input clk_i,
input uart_rx_i,
output [7:0] uart_rx_data_o,
output uart_rx_done,
output baud_bps_tb //for simulation
);
parameter [12:0] BAUD_DIV = 13'd5208; //波特率時鐘,9600bps,50Mhz/9600=5208 (1bit信號在50M時鐘下計數(採樣)5208次)
parameter [12:0] BAUD_DIV_CAP = 13'd2604; //波特率時鐘中間採樣點,50Mhz/9600/2=2604
reg [12:0] baud_div=0; //波特率設定計數器
reg baud_bps=0; //數據採樣點信號
reg bps_start=0; //波特率啓動標誌
always@(posedge clk_i)
begin
if(baud_div==BAUD_DIV_CAP) //當波特率計數器計數到採樣點時,產生採樣信號baud_bps
begin
baud_bps<=1'b1;
baud_div<=baud_div+1'b1;
end
else if(baud_div<BAUD_DIV && bps_start) //當波特率計數器啓動時,計數器累加
begin
baud_div<=baud_div+1'b1;
baud_bps<=0;
end
else
begin
baud_bps<=0;
baud_div<=0;
end
end
reg [4:0] uart_rx_i_r=5'b11111; //數據接收快取器
always@(posedge clk_i)
begin
uart_rx_i_r<={uart_rx_i_r[3:0],uart_rx_i};
end
//數據接收快取器,當連續接收到五個低電平時,即uart_rx_int=0時,作爲接收到起始信號
wire uart_rx_int=uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0];
reg [3:0] bit_num=0; //接收數據個數計數器
reg uart_rx_done_r=0; //數據接收完成暫存器
reg state=1'b0;
reg [7:0] uart_rx_data_o_r0=0;//數據接收過程中,數據快取器
reg [7:0] uart_rx_data_o_r1=0;//數據接收完成,數據暫存器
always@(posedge clk_i)
begin
uart_rx_done_r<=1'b0;
case(state)
1'b0 :
if(!uart_rx_int)//當連續接收到五個低電平時,即uart_rx_int=0時,作爲接收到起始信號,啓動波特率時鐘
begin
bps_start<=1'b1;
state<=1'b1;
end
1'b1 :
if(baud_bps) //每次等待波特率採樣中心時,接收數據,放入數據快取器中
begin
bit_num<=bit_num+1'b1;
if(bit_num<4'd9) //接收1bit起始信號,8bit有效信號,1bit結束信號
uart_rx_data_o_r0[bit_num-1]<=uart_rx_i;
end
else if(bit_num==4'd10) //接收完成時候,接收數據個數計數器清零,產生接收完成標誌位,並將數據寫入數據暫存器,關閉波特率時候
begin
bit_num<=0;
uart_rx_done_r<=1'b1;
uart_rx_data_o_r1<=uart_rx_data_o_r0;
state<=1'b0;//進入狀態0,再次回圈檢測
bps_start<=0;
end
default:;
endcase
end
assign baud_bps_tb=baud_bps;//for simulation
assign uart_rx_data_o=uart_rx_data_o_r1;
assign uart_rx_done=uart_rx_done_r;
endmodule
`timescale 1ns / 1ps
module uart_tx_path(
input clk_i,
input [7:0] uart_tx_data_i, //待發送數據
input uart_tx_en_i, //發送發送使能信號
output uart_tx_o
);
parameter BAUD_DIV = 13'd5208; //波特率時鐘,9600bps,50Mhz/9600=5208,(1bit信號在50M時鐘下計數(採樣)5208次)
parameter BAUD_DIV_CAP = 13'd2604; //波特率時鐘中間採樣點,50Mhz/9600/2=2604
reg [12:0] baud_div=0; //波特率設定計數器
reg baud_bps=0; //數據發送點信號,高有效
(* MARKDEBUG = "TRUE" *)reg [9:0] send_data=10'b1111111111; //待發送數據暫存器,1bit起始信號+8bit有效信號+1bit結束信號
(* MARKDEBUG = "TRUE" *)reg [3:0] bit_num=0; //發送數據個數計數器
reg uart_send_flag=0; //數據發送標誌位
reg uart_tx_o_r=1; //發送數據暫存器,初始狀態位高
always@(posedge clk_i)
begin
if(baud_div==BAUD_DIV_CAP) //當波特率計數器計數到數據發送中點時,產生採樣信號baud_bps,用來發送數據
begin
baud_bps<=1'b1;
baud_div<=baud_div+1'b1;
end
else if(baud_div<BAUD_DIV && uart_send_flag)//數據發送標誌位有效期間,波特率計數器累加,以產生波特率時鐘
begin
baud_div<=baud_div+1'b1;
baud_bps<=0;
end
else
begin
baud_bps<=0;
baud_div<=0;
end
end
always@(posedge clk_i)
begin
if(uart_tx_en_i) //接收數據發送使能信號時,產生數據發送標誌信號
begin
uart_send_flag<=1'b1;
send_data<={1'b1,uart_tx_data_i,1'b0};//待發送數據暫存器裝填,1bit起始信號0+8bit有效信號+1bit結束信號
end
else if(bit_num==4'd10) //發送結束時候,清楚發送標誌信號,並清楚待發送數據暫存器內部信號
begin
uart_send_flag<=1'b0;
send_data<=10'b1111_1111_11;
end
end
always@(posedge clk_i)
begin
if(uart_send_flag) //發送有效時候
begin
if(baud_bps)//檢測發送點信號
begin
if(bit_num<=4'd9)
begin
uart_tx_o_r<=send_data[bit_num]; //發送待發送暫存器內數據,從低位到高位
bit_num<=bit_num+1'b1;
end
end
else if(bit_num==4'd10)
bit_num<=4'd0;
end
else
begin
uart_tx_o_r<=1'b1; //空閒狀態時,保持發送端位高電平,以備發送時候產生低電平信號
bit_num<=0;
end
end
assign uart_tx_o=uart_tx_o_r;
endmodule
`timescale 1ns / 1ps
module Test05_project_CP2102_UART (
input sys_clk,
input sys_rst_n,
input uart_rx,
output uart_tx
);
wire [7:0] uart_rx_data_o;
wire uart_rx_done;
uart_rx_path uart_rx_path_u (
.clk_i(sys_clk),
.uart_rx_i(uart_rx),
.uart_rx_data_o(uart_rx_data_o),
.uart_rx_done(uart_rx_done)
);
uart_tx_path uart_tx_path_u (
.clk_i(sys_clk),
.uart_tx_data_i(uart_rx_data_o),
.uart_tx_en_i(uart_rx_done),
.uart_tx_o(uart_tx)
);
endmodule