串口通信是目前比較重要的一種通信方式,主要是用于計算機和外部的通信。首先簡單的介紹一下串口通信的原理:
串口用于ASCII碼字符的傳輸。通信使用3根線完成:(1)地線,(2)發(fā)送,(3)接收。由于串口通信是異步的,端口能夠在一根線上發(fā)送數(shù)據(jù)同時在另一根線上接收數(shù)據(jù)。其他線用于握手,但是不是必須的。串口通信最重要的參數(shù)是波特率、數(shù)據(jù)位、停止位和奇偶校驗。對于兩個進行通行的端口,這些參數(shù)必須匹配: a,波特率:這是一個衡量通信速度的參數(shù)。它表示每秒鐘傳送的bit的個數(shù)。例如300波特表示每秒鐘發(fā)送300個bit。當我們提到時鐘周期時,我們就是指波特率例如如果協(xié)議需要4800波特率,那么時鐘是4800Hz。這意味著串口通信在數(shù)據(jù)線上的采樣率為4800Hz。通常電話線的波特率為14400,28800和36600。波特率可以遠遠大于這些值,但是波特率和距離成反比。高波特率常常用于放置的很近的儀器間的通信,典型的例子就是GPIB設(shè)備的通信。 b,數(shù)據(jù)位:這是衡量通信中實際數(shù)據(jù)位的參數(shù)。當計算機發(fā)送一個信息包,實際的數(shù)據(jù)不會是8位的,標準的值是5、7和8位。如何設(shè)置取決于你想傳送的信息。比如,標準的ASCII碼是0~127(7位)。擴展的ASCII碼是0~255(8位)。如果數(shù)據(jù)使用簡單的文本(標準 ASCII碼),那么每個數(shù)據(jù)包使用7位數(shù)據(jù)。每個包是指一個字節(jié),包括開始/停止位,數(shù)據(jù)位和奇偶校驗位。由于實際數(shù)據(jù)位取決于通信協(xié)議的選取,術(shù)語“包”指任何通信的情況。 c,停止位:用于表示單個包的最后一位。典型的值為1,1.5和2位。由于數(shù)據(jù)是在傳輸線上定時的,并且每一個設(shè)備有其自己的時鐘,很可能在通信中兩臺設(shè)備間出現(xiàn)了小小的不同步。因此停止位不僅僅是表示傳輸?shù)慕Y(jié)束,并且提供計算機校正時鐘同步的機會。適用于停止位的位數(shù)越多,不同時鐘同步的容忍程度越大,但是數(shù)據(jù)傳輸率同時也越慢。 d,奇偶校驗位:在串口通信中一種簡單的檢錯方式。有四種檢錯方式:偶、奇、高和低。當然沒有校驗位也是可以的。對于偶和奇校驗的情況,串口會設(shè)置校驗位(數(shù)據(jù)位后面的一位),用一個值確保傳輸?shù)臄?shù)據(jù)有偶個或者奇?zhèn)€邏輯高位。例如,如果數(shù)據(jù)是011,那么對于偶校驗,校驗位為0,保證邏輯高的位數(shù)是偶數(shù)個。如果是奇校驗,校驗位位1,這樣就有3個邏輯高位。高位和低位不真正的檢查數(shù)據(jù),簡單置位邏輯高或者邏輯低校驗。這樣使得接收設(shè)備能夠知道一個位的狀態(tài),有機會判斷是否有噪聲干擾了通信或者是否傳輸和接收數(shù)據(jù)是否不同步
串口通信的數(shù)據(jù)傳輸時序如圖:
下面簡單的介紹一下本實驗所實現(xiàn)的功能以及相應(yīng)的程序思想:
驗實現(xiàn)的功能有上位機向開發(fā)板發(fā)送數(shù)據(jù),開發(fā)板接收以后通過數(shù)碼管顯示,并且將接收到的數(shù)據(jù)傳回給上位機,通過串口調(diào)試助手將返回的數(shù)據(jù)顯示出來。
其模塊圖如下:
程序思想:(本程序主要是采用特權(quán)同學(xué)視頻教程的程序,本人只是做一個簡單的思路總結(jié))
1.通過ISE開發(fā)工具,新建4個模塊,分別為串口接收波特率產(chǎn)生模塊,串口接收模塊,串口發(fā)送波特率產(chǎn)生模塊,串口接收模塊,另外再加一個頂層模塊。
2. 頂層模塊中只是做模塊的聲明和端口聲明,不做任何的邏輯處理。
3.波特率產(chǎn)生模塊(兩個模塊都一樣):主要是通過計數(shù)來實現(xiàn)波特率產(chǎn)生,并對數(shù)據(jù)進行采樣。此處采用9600bps,由于1s中有104166個us,系統(tǒng)時鐘為50M,即20us,即需要計數(shù)為104166/20=5208,因此循環(huán)計數(shù)0~5207,并且在計數(shù)到2603時對數(shù)據(jù)進行采樣。
4.串口接收模塊:當接收到接收信號置位時,對數(shù)據(jù)進行采集。
5.串口發(fā)送模塊:當接收到發(fā)送信號置位時,對數(shù)據(jù)進行發(fā)送。
具體程序如下:
/=============================頂層模塊=====================================
module uart_top(clk,,rst_n,rs232_rx,rs232_tx,led);
inputclk; //時鐘信號50M
inputrst_n; //復(fù)位信號,低有效
inputrs232_rx; //數(shù)據(jù)輸入信號
outputrs232_tx; //數(shù)據(jù)輸出信號
output [7:0] led;
wire bps_start1,bps_start2;//
wire clk_bps1,clk_bps2;
wire [7:0]rx_data; //接收數(shù)據(jù)存儲器,用來存儲接收到的數(shù)據(jù),直到下一個數(shù)據(jù)接收
wirerx_int; //接收數(shù)據(jù)中斷信號,接收過程中一直為高,
///////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////子模塊端口申明///////////////////////////////////
speed_select_rx speed_rx( //數(shù)據(jù)接收波特率選擇模塊
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start1),
.clk_bps(clk_bps1)
);
uart_rx uart_rx( //數(shù)據(jù)接收模塊
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start1),
.clk_bps(clk_bps1),
.rs232_rx(rs232_rx),
.rx_data(rx_data),
.rx_int(rx_int),
.led(led)
);
speed_select_tx speed_tx( //數(shù)據(jù)發(fā)送波特率控制模塊
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start2),
.clk_bps(clk_bps2)
);
uart_tx uart_tx(
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start2),
.clk_bps(clk_bps2),
.rs232_tx(rs232_tx),
.rx_data(rx_data),
.rx_int(rx_int)
);
endmodule
/=================================波特率產(chǎn)生模塊=====================================
module speed_select_rx(clk,rst_n,bps_start,clk_bps);//波特率設(shè)定
inputclk; //50M時鐘
inputrst_n; //復(fù)位信號
inputbps_start; //接收到信號以后,波特率時鐘信號置位,當接收到uart_rx傳來的信號以后,模塊開始運行
outputclk_bps; //接收數(shù)據(jù)中間采樣點,
// `defineBPS_PARA 5207;//9600波特率分頻計數(shù)值
// `define BPS_PARA_2 2603;//計數(shù)一半時采樣
reg[12:0] cnt;//分頻計數(shù)器
reg clk_bps_r;//波特率時鐘寄存器
reg[2:0] uart_ctrl;//波特率選擇寄存器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=13'd0;
else if((cnt==5207)||!bps_start)//判斷計數(shù)是否達到1個脈寬
cnt<=13'd0;
else
cnt<=cnt+1'b1;//波特率時鐘啟動
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_bps_r<=1'b0;
else if(cnt==2603)//當波特率計數(shù)到一半時,進行采樣存儲
clk_bps_r<=1'b1;
else
clk_bps_r<=1'b0;
end
assign clk_bps =clk_bps_r;//將采樣數(shù)據(jù)輸出給uart_rx模塊
endmodule
//================================數(shù)據(jù)接收模塊==========================================
module uart_rx(
clk,
rst_n,
bps_start,
clk_bps,
rs232_rx,
rx_data,
rx_int,
led
);
inputclk; //時鐘
inputrst_n; //復(fù)位
input rs232_rx; //接收數(shù)據(jù)信號
inputclk_bps; //高電平時為接收信號中間采樣點
outputbps_start; //接收信號時,波特率時鐘信號置位
output [7:0] rx_data;//接收數(shù)據(jù)寄存器
outputrx_int; //接收數(shù)據(jù)中斷信號,接收過程中為高
output [7:0] led;
reg [7:0] led;
regrs232_rx0,rs232_rx1,rs232_rx2,rs232_rx3;//接收數(shù)據(jù)寄存器
wire neg_rs232_rx;//表示數(shù)據(jù)線接收到下沿
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
rs232_rx0<= 1'b0;
rs232_rx1<= 1'b0;
rs232_rx2<= 1'b0;
rs232_rx3<= 1'b0;
end
else begin
rs232_rx0<= rs232_rx;
rs232_rx1<= rs232_rx0;
rs232_rx2<= rs232_rx1;
rs232_rx3<= rs232_rx2;
end
end
assign neg_rs232_rx = rs232_rx3& rs232_rx2 & ~rs232_rx1& ~rs232_rx0;//串口傳輸線的下沿標志
reg bps_start_r;
reg [3:0] num;//移位次數(shù)
regrx_int; //接收中斷信號
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
bps_start_r<=1'bz;
rx_int<= 1'b0;
end
else if(neg_rs232_rx)begin//
bps_start_r <=1'b1; //啟動串口,準備接收數(shù)據(jù)
rx_int<=1'b1; //接收數(shù)據(jù)中斷使能
end
else if(num==4'd12)begin //接收完有用的信號,
bps_start_r<=1'b0; //接收完畢,改變波特率置位,方便下次接收
rx_int<=1'b0; //接收信號關(guān)閉
end
assign bps_start =bps_start_r;
reg [7:0]rx_data_r;//串口數(shù)據(jù)寄存器
reg [7:0]rx_temp_data;//當前數(shù)據(jù)寄存器
always @(posedge clk or negedgerst_n)
if(!rst_n)begin
rx_temp_data<= 8'd0;
num<= 4'd0;
rx_data_r<= 8'd0;
end
elseif(rx_int) begin //接收數(shù)據(jù)處理
if(clk_bps)begin
num<= num+1'b1;
case(num)
4'd1:rx_temp_data[0] <= rs232_rx;
4'd2:rx_temp_data[1] <= rs232_rx;
4'd3:rx_temp_data[2] <= rs232_rx;
4'd4:rx_temp_data[3] <= rs232_rx;
4'd5:rx_temp_data[4] <= rs232_rx;
4'd6:rx_temp_data[5] <= rs232_rx;
4'd7:rx_temp_data[6] <= rs232_rx;
4'd8: rx_temp_data[7]<= rs232_rx;
default:;
endcase
led<= rx_temp_data;
end
elseif(num==4'd12) begin
num<=4'd0; //數(shù)據(jù)接收完畢
rx_data_r<= rx_temp_data;
end
end
assign rx_data =rx_data_r;
endmodule
//=================================數(shù)據(jù)發(fā)送模塊=========================================
module uart_tx(
clk,
rst_n,
bps_start,
clk_bps,
rs232_tx,
rx_data,
rx_int
);
input clk;
input rst_n;
input clk_bps;//中間采樣點
input [7:0] rx_data;//接收數(shù)據(jù)寄存器
input rx_int;//數(shù)據(jù)接收中斷信號
output rs232_tx;//發(fā)送數(shù)據(jù)信號
output bps_start;//發(fā)送信號置位
reg rx_int0,rx_int1,rx_int2;//信號寄存器,捕捉下降沿
wireneg_rx_int; //下降沿標志
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
rx_int0<= 1'b0;
rx_int1<= 1'b0;
rx_int2<= 1'b0;
end
else begin
rx_int0<= rx_int;
rx_int1<= rx_int0;
rx_int2<= rx_int1;
end
end
assign neg_rx_int = ~rx_int1& rx_int2;//捕捉下沿
reg [7:0] tx_data;//待發(fā)送數(shù)據(jù)
reg bps_start_r;
reg tx_en;//發(fā)送信號使能,高有效
reg [3:0] num;
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
bps_start_r<= 1'bz;
tx_en<= 1'b0;
tx_data<= 8'd0;
end
else if(neg_rx_int)begin//當檢測到下沿的時候,數(shù)據(jù)開始傳送
bps_start_r<= 1'b1;
tx_data<= rx_data;
tx_en<= 1'b1;
end
else if(num==4'd11) begin
bps_start_r<= 1'b0;
tx_en<= 1'b0;
end
end
assign bps_start = bps_start_r;
reg rs232_tx_r;
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
num<=4'd0;
rs232_tx_r<= 1'b1;
end
else if(tx_en) begin
if(clk_bps)begin
num<=num+1'b1;
case(num)
4'd0:rs232_tx_r <= 1'b0;//起始位
4'd1:rs232_tx_r <= tx_data[0];//數(shù)據(jù)位 開始
4'd2:rs232_tx_r <= tx_data[1];
4'd3:rs232_tx_r <= tx_data[2];
4'd4:rs232_tx_r <= tx_data[3];
4'd5:rs232_tx_r <= tx_data[4];
4'd6:rs232_tx_r <= tx_data[5];
4'd7:rs232_tx_r <= tx_data[6];
4'd8:rs232_tx_r <= tx_data[7];
4'd9:rs232_tx_r <= 1'b1;//數(shù)據(jù)結(jié)束位,1位
default:rs232_tx_r <= 1'b1;
endcase
end
elseif(num==4'd11)
num<=4'd0;//發(fā)送完成,復(fù)位
end
end
assign rs232_tx =rs232_tx_r;
endmodule