異步串行數據的一般格式是:起始位+數據位+停止位,其中起始位1位,8位數據位,奇校驗、偶校驗或無校驗位;停止位可以是1、2位,LSB first:
2.接收原理:
由于UART是異步傳輸,沒有傳輸同步時鐘。為了能保證數據傳輸的正確性,采樣模塊利用16倍數據波特率的時鐘進行采樣,假設波特率為115200,則采樣時鐘為clk16x=115200×16。每個數據占據16個采樣時鐘周期,1bit起始位+8bit數據為+1bit停止位=10bit,因此一幀共占據16×10=160個采樣時鐘,考慮到每個數據為可能有1-2個采樣時鐘周期的便宜,因此將各個數據位的中間時刻作為采樣點,以保證采樣不會滑碼或誤碼。一般UART一幀的數據位數為8,這樣即使每個數據有一個時鐘的誤差,接收端也能正確地采樣到數據。因此,采樣時刻為24(跳過起始位的16個時鐘)、40、56、72、88、104、120、136、152(停止位),如下圖所示:
其中,RX為接收引腳,CNT為對采樣時鐘進行計數的計數器;
3.代碼實現:
/******************************************************************************
*
*
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) /*下降沿使用全局時鐘來捕獲的,其實用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)
/*如果捕獲到下降沿,則開始計數*/
cnt_en<=1'b1;
else if(cnt==8'd152)
cnt_en<=1'b0;
end
//---------------------------------------------
/*
counter module ,對采樣時鐘進行計數
*/
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; /*在各個采樣時刻,讀取接收到的數據*/
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;
//接收到停止位后,給出數據準備好標志位
else
DataReady<=1'b0;
end
endmodule
注:
1)采樣時鐘clk16x必須是波特率的16倍,波特率任意設置如57600、9600等皆可,只要滿足16倍關系;
2)此模塊經過測試上萬字節的接收,從未出錯!
3)引入了最高時鐘對起始信號下降沿進行捕獲,會造成什么不良影響,比如所謂的“時鐘滿天飛”問題,博主還不清楚,若您有好的講解,請您發鏈接給我,在此感謝!
4.如果需要校驗位的朋友,可以參考xilinx 公司的參考設計:
`timescale 1 ns / 1 ns
module rcvr (dout,data_ready,framing_error,parity_error,rxd,clk16x,rst,rdn) ;
input rxd ;
/*數據接收端*/
input clk16x ; /*采樣時鐘*/
input rst ;
/*復位信號,高電平有效*/
input rdn ;
/*數據接收使能,低電平有效*/
output [7:0] dout ; /*數據輸出*/
output data_ready ; /*數據接收完畢標志位*/
output framing_error ; /*幀錯誤標志位*/
output parity_error ;
/*校驗位錯誤標志位*/
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 ; /*在允許接收的情況下,輸出接收到的數據,否則保持高阻態*/
/*下降沿捕獲模塊*/
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)
/*如果捕獲到下降沿,則開始計數*/
clk1x_enable <= 1'b1 ;
else if (no_bits_rcvd == 4'b1100)
clk1x_enable <= 1'b0 ;
end
/*數據準備好標志位的控制模塊*/
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
/*計數模塊,產生一個對clk16x進行16分頻的時鐘信號,用該信號的上升沿進行采樣 */
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進行計數*/
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 ;
/*采樣進程*/
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] ; //數據左移
parity <= parity ^ rsr[7] ; //校驗位
end
else if (no_bits_rcvd == 4'b1010)
begin
rbr <= rsr ; //輸出接收到的數據
end
else if (!parity)
parity_error = 1'b1 ; //判斷校驗位是否正確
else if ((no_bits_rcvd == 4'b1011) && (rxd2 != 1'b1))//判斷是否收到停止位,否則給出幀錯誤信號
framing_error = 1'b1 ;
else framing_error = 1'b0 ;
end
endmodule
評論
查看更多