概念: UART即數據按一定頻率串行輸出的通信方式,可分為同步串口通信和異步串口通信兩種。在 UA RT 中,數據位是以字符為傳送單位,數據
的前、後要有起始位、停止位,另外可以在停止位的前面加上一個比特(bit)的校驗位。其幀格式如下圖所示:
其基 本 特 點是:
① 在 信 號線上共有兩種狀態,可分別用邏輯1和邏輯。來區分。在發送器空閒時,數據線應該保持在邏輯高電平狀態。
② 起 始 位:該位是一個邏輯0,總是加在每一幀的頭部,提示接受器數據傳輸即將開始,在接收數據位過程中又被分離出去。
③ 數 據 位:在起始位之後就是數據位,一般為8位一個字節的數據,低位在前,高位在後。如字母C在ASCII表中是十進制67,二進制01000011,那麼傳輸的將是110000100
④ 校 驗 位:該位一般用來判斷接收的數據位有無錯誤,常用的校驗方法是奇偶校驗法。
⑤ 停 止位 :停止位總在每一幀的末尾,為邏輯1,用於標志一個字符傳送的結束。
⑥ 幀 :從起始位到停止位為一幀。
在FPGA設計中,其分為波特率產生模塊、發送器模塊、接收器模塊三個部分。
原理設計:發送器和接收器按設定好的波特率分別進行發送和接收串行數據。具體設計如下:
1. 波特率發生器
波特率發送器其實就是一個系統時鐘分頻器。首先,要根據串口波特率和系統時鐘的要求,確定分頻系數,然後根據這個分頻系數確定時鐘計數的位寬(這一點很重要,因為我在設計中,位寬取小了,導致無法產生正確的波特率時鐘)。下面是我設計的波特率發生器的代碼:主要利用50M的系統時鐘,產生8*9600 bits/s 的波特率。注:基於消除信號抖動的考慮,一般波特率發送器產生的頻率是所需波特率的8或者16倍,采用中間值采樣方法。
module REC_Tick(clk,
BTick
);
/***************port define************/
input clk;
output BTick;
/****assign system and object baudRrate frequence******/
parameter sys_freq=50_000_000,
obj_freq=76800;
wire [15:0] step=sys_freq/obj_freq -1;
reg [15:0] count;
always @ (posedge clk)
if (count!=step)count<=count+1;//注:這於平時所做的分頻有區別,因為這是非50%占空比
else count<=0;
wire BTick=(count==step);//這樣設計對後面的數據采集減少了一個計數器操作
endmodule
2 發送器的設計
發送器的主要功能是:在波特率時鐘周期裡,確定該時鐘周期內的應該發送數據的邏輯值。它是根據狀態,決定發送的數據的大小。
設計中考慮的問題:主要的設計在於狀態機的轉換過程,要確定轉換到下一狀態的觸發條件。
給出的實例,按照波特率時鐘轉換狀態,實際中從IDLE到START的轉換,應該是有中斷觸發的,即有發送數據的需求時,產生中斷,使狀態機有idle轉換到start之後按照波特率時鐘依次發送8bit,奇偶校驗,停止,idle,到此,數據發完成,等待下一次的中斷信號。本例中簡化了實際,沒有用中斷,實現的功能是:在波特率時鐘下,每個12個波特率時鐘發送8bits數據A0A0.
/***********************************8
串口發送模塊:
主要是利用波特率節拍,發送數據
***********************************/
module uart_send(clk,BTick,TxD);
/*port define*********************/
input clk,BTick;//本模塊使用的BTICK為波特率產生模塊時鐘再8分頻後得到
output TxD;
/****state definf***************/
parameter idle = 4'b0000,
start = 4'b0001,
bit0 = 4'b1000,
bit1 = 4'b1001,
bit2 = 4'b1010,
bit3 = 4'b1011,
bit4 = 4'b1100,
bit5 = 4'b1101,
bit6 = 4'b1110,
bit7 = 4'b1111,
stop1 = 4'b0010,
stop2 = 4'b0011;
reg [7:0] snd_data;
initial snd_data<=8'b10101010;
/*********timing sequence of state machine***/
reg [3:0] state;
initial state<=idle;
always @(posedge clk)
case(state)
idle: if (BTick) state<=start;
start:if (BTick) state<=bit0;
bit0: if (BTick) state<=bit1;
bit1: if (BTick) state<=bit2;
bit2: if (BTick) state<=bit3;
bit3: if (BTick) state<=bit4;
bit4: if (BTick) state<=bit5;
bit5: if (BTick) state<=bit6;
bit6: if (BTick) state<=bit7;
bit7: if (BTick) state<=stop1;
stop1:if (BTick) state<=stop2;
stop2:if (BTick) state<=idle;
default :state<=idle;
endcase
/***define the logical of bit relating to current state*/
reg mux_bit;
initial mux_bit <=1; /**attention!!!!It is absolutly significant**/
always @(state[3:0])
case(state[3:0])
4'b0001:mux_bit<=0;
4'b1000:mux_bit<=snd_data[0];
4'b1001:mux_bit<=snd_data[1];
4'b1010:mux_bit<=snd_data[2];
4'b1011:mux_bit<=snd_data[3];
4'b1100:mux_bit<=snd_data[4];
4'b1101:mux_bit<=snd_data[5];
4'b1110:mux_bit<=snd_data[6];
4'b1111:mux_bit<=snd_data[7];
default:mux_bit<=1;
endcase
wire TxD=mux_bit;
endmodule
3接收器的設計
接收器的功能:通過不斷采樣RxD線上的數據,將數據為正確的讀取出來。它是根據采樣的數據的大小,確定狀態。和發送器的過程剛好相反。
設計中考慮的幾個問題:
1)如何判斷開始位的到來,及如何根據當前的狀態和采樣的數據,判斷開始位的發生。
2)如何采樣數據位,怎樣做到正確的讀書數據位的邏輯值.
3)開始狀態和數據接收狀態,停止狀態的轉換觸發問題
一般的規則是在一個bit時間內對RxD進行8或16次的采樣,如果連續4次采樣的結果都是1,則認為該bit
為1,同理,如果連續4次采樣的結果都是0,則認為該bit為0.
/*************************************************
接收模塊控制器,主要有3個難點:
1)如何判斷開始位?
2)如何判斷下一位?
3)如何判斷當前為的值?
狀態機的設計原理:
開始是IDLE,當檢測到RxD線為0是,轉到START,之後,數據位,奇偶校驗位,停止位之間,依據下一位標志轉換。
那麼如何檢測RxD的開始為0呢?有技巧,即如果連續4測采樣RxD數據線,都是0,則說明當前的位為0,若連續4次為1,則當前位為1。
判斷下一位:連續8次波特率時鐘,認為是一個bit位的持續時間。
**********************************************************/
module uart_rec(clk,
BaudTick,
RxD,
RxD_data
);
/**port define***************/
input clk,BaudTick,RxD;//BaudTick為波特率產生模塊時鐘
output [7:0] RxD_data;
/****state define**********/
parameter idle = 4'b0000,
start = 4'b0001,
bit0 = 4'b1000,
bit1 = 4'b1001,
bit2 = 4'b1010,
bit3 = 4'b1011,
bit4 = 4'b1100,
bit5 = 4'b1101,
bit6 = 4'b1110,
bit7 = 4'b1111,
stop = 4'b0010;
/****sample process***********************/
reg RxD_bit,current_bit;
reg [7:0] RxD_data;
reg [1:0] count;
initial RxD_bit=1;//attention!!!!
initial current_bit=1;
initial count=2'b00;
always @(posedge clk)
if (BaudTick==1)
RxD_bit<=RxD;
always @ (posedge clk)
if (BaudTick==1)begin
if( RxD_bit&& count!=2'b11) count = count + 2'h1;
else
if(~RxD_bit && count!=2'b00) count = count - 2'h1;
if(count==2'b00) current_bit = 1'b0;
else
if(count==2'b11) current_bit = 1'b1;
end
/***judge a bit*****************************************/
reg [3:0] Tick_num;
always @ (posedge clk)
if (state==idle)
Tick_num<=4'b0000;
if(BaudTick==1)
Tick_num <= Tick_num[2:0]+4'b0001;
wire next_bit =Tick_num[3];
/********state reverse*********************************/
reg [3:0] state;
always @(posedge clk)
if (BaudTick)
case(state)
idle:if(~current_bit) state<=start;
start:if(next_bit)state<=bit0;
bit0:if(next_bit)state<=bit1;
bit1:if(next_bit)state<=bit2;
bit2:if(next_bit)state<=bit3;
bit3:if(next_bit)state<=bit4;
bit4:if(next_bit)state<=bit5;
bit5:if(next_bit)state<=bit6;
bit6:if(next_bit)state<=bit7;
bit7:if(next_bit)state<=stop;
stop:if(next_bit)state<=idle;
default:state<=idle;
endcase
/*****output data received from RxD line**************/
always @(posedge clk)
if (next_bit && state[3] && BaudTick)
RxD_data<={current_bit,RxD_data[7:1]};
//else if (state==start)
//RxD_data<=0;
endmodule
-----貌似上發送器時鐘有些問題,待修改