異步串行數據的一般格式是:起始位+數據位+停止位,其中起始位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 ; /*在允許接收的情況下,輸出接收到的數據,否則保持高阻態*/
評論