異步串行數(shù)據(jù)的一般格式是:起始位+數(shù)據(jù)位+停止位,其中起始位1位,8位數(shù)據(jù)位,奇校驗(yàn)、偶校驗(yàn)或無校驗(yàn)位;停止位可以是1、2位,LSB first:
2.接收原理:
由于UART是異步傳輸,沒有傳輸同步時鐘。為了能保證數(shù)據(jù)傳輸?shù)恼_性,采樣模塊利用16倍數(shù)據(jù)波特率的時鐘進(jìn)行采樣,假設(shè)波特率為115200,則采樣時鐘為clk16x=115200×16。每個數(shù)據(jù)占據(jù)16個采樣時鐘周期,1bit起始位+8bit數(shù)據(jù)為+1bit停止位=10bit,因此一幀共占據(jù)16×10=160個采樣時鐘,考慮到每個數(shù)據(jù)為可能有1-2個采樣時鐘周期的便宜,因此將各個數(shù)據(jù)位的中間時刻作為采樣點(diǎn),以保證采樣不會滑碼或誤碼。一般UART一幀的數(shù)據(jù)位數(shù)為8,這樣即使每個數(shù)據(jù)有一個時鐘的誤差,接收端也能正確地采樣到數(shù)據(jù)。因此,采樣時刻為24(跳過起始位的16個時鐘)、40、56、72、88、104、120、136、152(停止位),如下圖所示:
其中,RX為接收引腳,CNT為對采樣時鐘進(jìn)行計數(shù)的計數(shù)器;
3.代碼實(shí)現(xiàn):
/******************************************************************************
*
*
Module :
rx_module
*
File Name :
rx_module.v
*
Author :
JC_Wang
*
Version :
1.0
*
Date
:
2012/12/5
*
Description :
UART接收模塊
*
*
********************************************************************************/
module rx_module(
input
GClk,
/*
system topest clock
*/
input
clk16x,
/*
sample clock,16×115200
*/
input
rst_n,
/*
glabol reset signal
*/
input
rx,
/*
serial data in
*/
output reg
DataReady, /*
a complete byte has been received
*/
output reg[7:0]
DataReceived /*
Bytes received
*/
);
/* 捕獲rx的下降沿,即起始信號 */
reg trigger_r0;
wire neg_tri;
always@(posedge GClk or negedge rst_n) /*下降沿使用全局時鐘來捕獲的,其實(shí)用clk16x來捕獲也可以*/
begin
if(!rst_n)
begin
trigger_r0<=1'b0;
end
else
begin
trigger_r0<=rx;
end
end
assign neg_tri = trigger_r0 & ~rx;
//----------------------------------------------
/*
counter control
*/
reg cnt_en;
always@(posedge GClk or negedge rst_n)
begin
if(!rst_n)
cnt_en<=1'b0;
else if(neg_tri==1'b1)
/*如果捕獲到下降沿,則開始計數(shù)*/
cnt_en<=1'b1;
else if(cnt==8'd152)
cnt_en<=1'b0;
end
//---------------------------------------------
/*
counter module ,對采樣時鐘進(jìn)行計數(shù)
*/
reg [7:0] cnt;
always@(posedge clk16x or negedge rst_n)
begin
if(!rst_n)
cnt<=8'd0;
else if(cnt_en)
cnt<=cnt+1'b1;
else
cnt<=8'd0;
end
//---------------------------------------------
/*
receive module
*/
reg StopBit_r;
always@(posedge clk16x or negedge rst_n)
begin
if(!rst_n)
begin
DataReceived<=8'b0;
end
else if(cnt_en)
case(cnt)
8'd24: DataReceived[0] <= rx; /*在各個采樣時刻,讀取接收到的數(shù)據(jù)*/
8'd40: DataReceived[1] <= rx;
8'd56: DataReceived[2] <= rx;
8'd72: DataReceived[3] <= rx;
8'd88: DataReceived[4] <= rx;
8'd104: DataReceived[5] <= rx;
8'd120: DataReceived[6] <= rx;
8'd136: DataReceived[7] <= rx;
endcase
end
always@(posedge clk16x or negedge rst_n)
begin
if(!rst_n)
DataReady<=1'b0;
else if (cnt==8'd152)
DataReady<=1'b1;
//接收到停止位后,給出數(shù)據(jù)準(zhǔn)備好標(biāo)志位
else
DataReady<=1'b0;
end
endmodule
注:
1)采樣時鐘clk16x必須是波特率的16倍,波特率任意設(shè)置如57600、9600等皆可,只要滿足16倍關(guān)系;
2)此模塊經(jīng)過測試上萬字節(jié)的接收,從未出錯!
3)引入了最高時鐘對起始信號下降沿進(jìn)行捕獲,會造成什么不良影響,比如所謂的“時鐘滿天飛”問題,博主還不清楚,若您有好的講解,請您發(fā)鏈接給我,在此感謝!
4.如果需要校驗(yàn)位的朋友,可以參考xilinx 公司的參考設(shè)計:
`timescale 1 ns / 1 ns
module rcvr (dout,data_ready,framing_error,parity_error,rxd,clk16x,rst,rdn) ;
input rxd ;
/*數(shù)據(jù)接收端*/
input clk16x ; /*采樣時鐘*/
input rst ;
/*復(fù)位信號,高電平有效*/
input rdn ;
/*數(shù)據(jù)接收使能,低電平有效*/
output [7:0] dout ; /*數(shù)據(jù)輸出*/
output data_ready ; /*數(shù)據(jù)接收完畢標(biāo)志位*/
output framing_error ; /*幀錯誤標(biāo)志位*/
output parity_error ;
/*校驗(yàn)位錯誤標(biāo)志位*/
reg rxd1 ;
reg rxd2 ;
reg clk1x_enable ;
reg [3:0] clkdiv ;
reg [7:0] rsr ;
reg [7:0] rbr ;
reg [3:0] no_bits_rcvd ;
reg data_ready ;
reg parity ;
reg parity_error ;
reg framing_error ;
wire clk1x ;
assign dout = !rdn ? rbr : 8'bz ; /*在允許接收的情況下,輸出接收到的數(shù)據(jù),否則保持高阻態(tài)*/
/*下降沿捕獲模塊*/
always @(posedge clk16x or posedge rst)
begin
if (rst)
begin
rxd1 <= 1'b1 ;
rxd2 <= 1'b1 ;
end
else
begin
rxd1 <= rxd ;
rxd2 <= rxd1 ;
end
end
always @(posedge clk16x or posedge rst)
begin
if (rst)
clk1x_enable <= 1'b0;
else if (!rxd1 && rxd2)
/*如果捕獲到下降沿,則開始計數(shù)*/
clk1x_enable <= 1'b1 ;
else if (no_bits_rcvd == 4'b1100)
clk1x_enable <= 1'b0 ;
end
/*數(shù)據(jù)準(zhǔn)備好標(biāo)志位的控制模塊*/
always @(posedge clk16x or posedge rst or negedge rdn)
begin
if (rst)
data_ready = 1'b0 ;
else if (!rdn)
data_ready = 1'b0 ;
else
if (no_bits_rcvd == 4'b1011)
data_ready = 1'b1 ;
end
/*計數(shù)模塊,產(chǎn)生一個對clk16x進(jìn)行16分頻的時鐘信號,用該信號的上升沿進(jìn)行采樣 */
always @(posedge clk16x or posedge rst)
begin
if (rst)
clkdiv = 4'b0000 ;
else if (clk1x_enable)
clkdiv = clkdiv +1 ;
end
assign clk1x = clkdiv[3] ;
/*對clk1x進(jìn)行計數(shù)*/
always @(posedge clk1x or posedge rst or negedge clk1x_enable)
if (rst)
no_bits_rcvd = 4'b0000;
else
if (!clk1x_enable)
no_bits_rcvd = 4'b0000 ;
else
no_bits_rcvd = no_bits_rcvd + 1 ;
/*采樣進(jìn)程*/
always @(posedge clk1x or posedge rst)
if (rst)
begin
rsr <= 8'b0 ;
rbr <= 8'b0 ;
parity <= 1'b1 ;
parity_error = 1'b0 ;
end
else
begin
if (no_bits_rcvd >= 4'b0001 && no_bits_rcvd <= 4'b1001)
begin
rsr[0] <= rxd2 ;
rsr[7:1] <= rsr[6:0] ; //數(shù)據(jù)左移
parity <= parity ^ rsr[7] ; //校驗(yàn)位
end
else if (no_bits_rcvd == 4'b1010)
begin
rbr <= rsr ; //輸出接收到的數(shù)據(jù)
end
else if (!parity)
parity_error = 1'b1 ; //判斷校驗(yàn)位是否正確
else if ((no_bits_rcvd == 4'b1011) && (rxd2 != 1'b1))//判斷是否收到停止位,否則給出幀錯誤信號
framing_error = 1'b1 ;
else framing_error = 1'b0 ;
end
endmodule
評論
查看更多