FPGA-14-串列埠通訊的原理和傳送模組
瞭解串列埠通訊首先要了解串列埠通訊的原理:
串列埠通訊的概念非常簡單,串列埠按位(bit)傳送和接收位元組。儘管比按位元組(byte)的並行通訊慢,但是串列埠可以在使用一根線傳送資料的同時用另一根線接收資料。它很簡單並且能夠實現遠距離通訊。比如IEEE488定義並行通行狀態時,規定裝置線總長不得超過20米,並且任意兩個裝置間的長度不得超過2米;而對於串列埠而言,長度可達1200米。典型地,串列埠用於ASCII碼字元的傳輸。通訊使用3根線完成:(1)地線,(2)傳送,(3)接收。由於串列埠通訊是非同步的,埠能夠在一根線上傳送資料同時在另一根線上接收資料。其他線用於握手,但是不是必須的。串列埠通訊最重要的引數是波特率、資料位、停止位和奇偶校驗。對於兩個進行通訊的埠,這些引數必須匹配:
a,波特率:這是一個衡量通訊速度的引數。它表示每秒鐘傳送的bit的個數。例如300波特表示每秒鐘傳送300個bit。當我們提到時鐘週期時,我們就是指波特率例如如果協議需要4800波特率,那麼時鐘是4800Hz。這意味著串列埠通訊在資料線上的取樣率為4800Hz。通常電話線的波特率為14400,28800和36600。波特率可以遠遠大於這些值,但是波特率和距離成反比。高波特率常常用於放置的很近的儀器間的通訊,典型的例子就是GPIB裝置的通訊。
b,資料位:這是衡量通訊中實際資料位的引數。當計算機發送一個資訊包,實際的資料不會是8位的,標準的值是5、7和8位。如何設定取決於你想傳送的資訊。比如,標準的ASCII碼是0~127(7位)。擴充套件的ASCII碼是0~255(8位)。如果資料使用簡單的文字(標準 ASCII碼),那麼每個資料包使用7位資料。每個包是指一個位元組,包括開始/停止位,資料位和奇偶校驗位。由於實際資料位取決於通訊協議的選取,術語“包”指任何通訊的情況。
c,停止位:用於表示單個包的最後一位。典型的值為1,1.5和2位。由於資料是在傳輸線上定時的,並且每一個裝置有其自己的時鐘,很可能在通訊中兩臺裝置間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,並且提供計算機校正時鐘同步的機會。適用於停止位的位數越多,不同時鐘同步的容忍程度越大,但是資料傳輸率同時也越慢。
d,奇偶校驗位:在串列埠通訊中一種簡單的檢錯方式。有四種檢錯方式:偶、奇、高和低。當然沒有校驗位也是可以的。對於偶和奇校驗的情況,串列埠會設定校驗位(資料位後面的一位),用一個值確保傳輸的資料有偶個或者奇個邏輯高位。例如,如果資料是011,那麼對於偶校驗,校驗位為0,保證邏輯高的位數是偶數個。如果是奇校驗,校驗位為1,這樣就有3個邏輯高位。高位和低位不真正的檢查資料,簡單置位邏輯高或者邏輯低校驗。這樣使得接收裝置能夠知道一個位的狀態,有機會判斷是否有噪聲干擾了通訊或者是否傳輸和接收資料是否不同步。
傳送模組的設計:
串列埠傳送,通過按鍵控制指令型別。可在PC端顯示相應指令。
指令一:I like FPGA.
指令二:I like Verilog.
指令三:I like 107.
後來加了指令切換功能通過按鍵切換(這裡沒有畫出來)
模組設計:
div.v
module uart_div(clk,rst_n,bps_sel,bps_clk
);
input clk ;//輸入系統時鐘
input rst_n ;//復位訊號
input [1:0] bps_sel ;//波特率選擇
output bps_clk ;//輸出波特率時鐘訊號
reg bps_clk ;
parameter BPS_4800 = 13'd5208 ,
BPS_9600 = 13'd2604 ,
BPS_19200 = 13'd1302 ,
BPS_115200 = 13'd217 ;
reg [12:0] div_cnt ;
reg [12:0] time_div ;
//波特率選擇模組
[email protected](*)begin
if(rst_n==1'b0)begin
time_div=BPS_9600;
end
else begin
case(bps_sel)
2'b00: time_div = BPS_4800;
2'b01: time_div = BPS_9600;
2'b10: time_div = BPS_19200;
2'b11: time_div = BPS_115200;
default:time_div = BPS_9600;
endcase
end
end
//波特率時鐘計數模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
div_cnt<=1'b0;
end
else if(div_cnt==time_div-1'b1)begin
div_cnt<=0;
end
else begin
div_cnt<=div_cnt+1'b1;
end
end
//波特率時鐘輸出模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
bps_clk<=1'b0;
end
else if(div_cnt<(time_div-1'b1))begin
bps_clk<=1'b1;
end
else begin
bps_clk<=1'b0;
end
end
endmodule
串列埠通訊傳送模組:
module uart_txd(clk,rst_n,Date_byte,send_en,txd,txd_finish,uart_state
);
input clk ;//輸入時鐘
input rst_n ;//復位訊號
input [7:0] Date_byte ;//傳送資料
input send_en ;//傳送使能
output txd ;//串列埠傳送
output txd_finish ;//傳送完成位
output uart_state ;//串列埠狀態位
reg txd ;
reg txd_finish ;
reg uart_state ;
reg [7:0] date_buf ;
//資料寄存模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
date_buf<=8'd0;
end
else if(send_en==1'b1)begin
date_buf<=Date_byte;
end
else begin
date_buf<=date_buf;
end
end
//串列埠狀態模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
uart_state<=1'b0;
end
else if(send_en==1'b1)begin
uart_state<=1'b1;
end
else if(txd_finish==1'b1)begin
uart_state<=1'b0;
end
else begin
uart_state<=uart_state;
end
end
reg [3:0] dat_cnt ;
//資料計數模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dat_cnt<=4'd0;
end
else if(txd_finish==1'b1)begin
dat_cnt<=4'd0;
end
else if(send_en==1'b1)begin
dat_cnt<=dat_cnt+1'b1;
end
else begin
dat_cnt<=4'd0;
end
end
//串列埠傳送完成標誌模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
txd_finish<=1'b0;
end
else if(dat_cnt==4'd10)begin
txd_finish<=1'b1;
end
else begin
txd_finish<=1'b0;
end
end
//資料傳送模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
txd<=1'b1;
end
else if(send_en==1'b0)begin
txd<=1'b1;
end
else begin
case(dat_cnt)
4'd0 :txd<=1'b1;
4'd1 :txd<=1'b0;//傳送位
4'd2 :txd<=date_buf[0];
4'd3 :txd<=date_buf[1];
4'd4 :txd<=date_buf[2];
4'd5 :txd<=date_buf[3];
4'd6 :txd<=date_buf[4];
4'd7 :txd<=date_buf[5];
4'd8 :txd<=date_buf[6];
4'd9 :txd<=date_buf[7];
4'd10:txd<=1'b1;//停止位
default:txd<=1'b1;
endcase
end
end
endmodule
資料緩衝模組:
module txd_data_buf(clk,rst_n,txd_finish,sel_dat,dat_buf_en,data_out
);
input clk ;//輸入時鐘
input rst_n ;//復位訊號
input txd_finish;//一位資料傳送完成標誌
input [1:0] sel_dat ;
output [7:0] data_out ;
reg [7:0] data_out ;
output dat_buf_en;//資料傳送使能位
reg dat_buf_en;
reg [4:0] dat_cnt ;
parameter DAT_FULL_1 = 4'd12 ,
DAT_FULL_2 = 4'd15 ,
DAT_FULL_3 = 4'd11 ;
//資料傳送使能模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dat_buf_en<=1'b0;
end
else if(sel_dat==2'b01)begin
if(dat_cnt==dat_ful)begin
dat_buf_en<=1'b0;
end
else begin
dat_buf_en<=1'b1;
end
end
else if(sel_dat==2'b10)begin
if(dat_cnt==dat_ful)begin
dat_buf_en<=1'b0;
end
else begin
dat_buf_en<=1'b1;
end
end
else if(sel_dat==2'b11)begin
if(dat_cnt==dat_ful)begin
dat_buf_en<=1'b0;
end
else begin
dat_buf_en<=1'b1;
end
end
else begin
dat_buf_en<=dat_buf_en;
end
end
//資料計數模組
[email protected](posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dat_cnt<=1'b0;
end
else if(dat_cnt==dat_ful)begin
dat_cnt<=dat_cnt;
end
else if(dat_buf_en==1'b0)begin
dat_cnt<=1'b0;
end
else if(txd_finish==1'b1)begin
dat_cnt<=dat_cnt+1'b1;
end
else begin
dat_cnt<=dat_cnt;
end
end
//資料選擇模組
[email protected](*)begin
if(rst_n==1'b0)begin
data_out=0;
end
else if(dat_buf_en==1'b0)begin
data_out=0;
end
else if(sel_dat==2'b01)begin
case(dat_cnt)
4'd0 :data_out="I";
4'd1 :data_out=" ";
4'd2 :data_out="L";
4'd3 :data_out="i";
4'd4 :data_out="k";
4'd5 :data_out="e";
4'd6 :data_out=" ";
4'd7 :data_out="F";
4'd8 :data_out="P";
4'd9 :data_out="G";
4'd10:data_out="A";
4'd11:data_out=".";
default:data_out=0;
endcase
end
else if(sel_dat==2'b10)begin
case(dat_cnt)
4'd0 :data_out="I";
4'd1 :data_out=" ";
4'd2 :data_out="L";
4'd3 :data_out="i";
4'd4 :data_out="k";
4'd5 :data_out="e";
4'd6 :data_out=" ";
4'd7 :data_out="V";
4'd8 :data_out="e";
4'd9 :data_out="r";
4'd10:data_out="i";
4'd11:data_out="l";
4'd12:data_out="o";
4'd13:data_out="g";
4'd14:data_out=".";
default:data_out=0;
endcase
end
else if(sel_dat==2'b11)begin
case(dat_cnt)
4'd0 :data_out="I";
4'd1 :data_out=" ";
4'd2 :data_out="L";
4'd3 :data_out="i";
4'd4 :data_out="k";
4'd5 :data_out="e";
4'd6 :data_out=" ";
4'd7 :data_out="1";
4'd8 :data_out="0";
4'd9 :data_out="7";
4'd10:data_out=".";
default:data_out=0;
endcase
end
end
reg [3:0] dat_ful;
[email protected](*)begin
if(rst_n==1'b0)begin
dat_ful=4'd15;
end
else if(sel_dat==2'b01)begin
dat_ful=DAT_FULL_1;
end
else if(sel_dat==2'b10)begin
dat_ful=DAT_FULL_2;
end
else if(sel_dat==2'b11)begin
dat_ful=DAT_FULL_3;
end
else begin
dat_ful=dat_ful;
end
end
endmodule
控制電路,控制指令傳送:
module control(clk,rst_n,key_entr,key_down,sel_dat,bps_sel
);
input clk;
input rst_n;
input key_entr,key_down;
output reg [1:0]sel_dat;
output reg [1:0]bps_sel;
//按鍵抖動判斷邏輯
wire key; //所有的按鍵相與的結果,用於按鍵觸發判斷
assign key =key_entr;
reg[3:0]keyr ; //按鍵值key的緩衝暫存器
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
keyr <=4'b1111;
else
keyr <={keyr[2:0],key};
end
wire key_neg =~keyr[2] &keyr [3];//有按鍵被按下
wire key_pos =keyr[2] &~keyr [3];// 有按鍵被釋放
//定時器計數邏輯,用於對按鍵的消抖的判斷
reg [19:0] cnt;
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <=20'd0;
else if(key_pos||key_neg)
cnt <=20'd0;
else if(cnt <20'd999_999)
cnt <= cnt +1'b1;
else
cnt <=20'd0;
end
reg [1:0] key_value_0;
reg [1:0] key_value_1;
//定時採取按鍵值
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
key_value_0 <=2'b11;
key_value_1 <=2'b11;
end
else begin
if(cnt ==20'd999_999)
key_value_0 <={key_entr,key_down};
else
key_value_1 <=key_value_0;
end
end
wire [1:0] key_press=key_value_1 & ~key_value_0;
//切換指令控制
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
sel_dat<=1'b0;
else if(key_press[1]==1'b1)begin
if(sel_dat==2'b11)begin
sel_dat<=2'b01;
end
else begin
sel_dat<=sel_dat+1'b1;
end
end
else begin
sel_dat<=sel_dat;
end
end
//切換波特率控制
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
bps_sel<=2'b01;
else if(key_press[0]==1'b1)begin
if(bps_sel==2'b11)begin
bps_sel<=2'b00;
end
else begin
bps_sel<=bps_sel+1'b1;
end
end
else begin
bps_sel<=bps_sel;
end
end
endmodule
頂層檔案
module top(ext_clk_25m,ext_rst_n,uart_tx,key_down,key_entr,led
);
input ext_clk_25m ;//系統時鐘
input ext_rst_n ;//復位訊號
input key_down ;//波特率選擇
input key_entr ;//指令選擇
output[1:0] led ;//指示bps
output uart_tx ;//串列埠傳送
assign led[1:0]=bps_sel[1:0];
wire bps_clk ;
wire txd_finish ;
wire uart_state ;
wire [7:0] data_out ;
wire dat_buf_en ;
wire [1:0] bps_sel ;
wire [1:0] sel_dat ;
//資料緩衝區
txd_data_buf u_buf(
.clk(bps_clk),
.rst_n(ext_rst_n),
.txd_finish(txd_finish),
.sel_dat(sel_dat),
.dat_buf_en(dat_buf_en),
.data_out(data_out)
);
//波特率選擇模組
uart_div u_div(
.clk(ext_clk_25m),
.rst_n(ext_rst_n),
.bps_sel(bps_sel),
.bps_clk(bps_clk)
);
//串列埠傳送模組
uart_txd u_txd(
.clk(bps_clk),
.rst_n(ext_rst_n),
.Date_byte(data_out),
.send_en(dat_buf_en),
.txd(uart_tx),
.txd_finish(txd_finish),
.uart_state(uart_state)
);
//按鍵控制模組
control u_con(
.clk(ext_clk_25m),
.rst_n(ext_rst_n),
.key_entr(key_entr),
.key_down(key_down),
.sel_dat(sel_dat),
.bps_sel(bps_sel)
);
endmodule
串列埠助手驗證: