UART
通用異步收發傳輸器(Universal Asynchronous Receiver/Transmitter),通常稱作UART。它将要傳輸的資料在串行通信與并行通信之間加以轉換。作為把并行輸入信号轉成串行輸出信号的晶片,UART通常被內建于其他通訊接口的連結上。
本文詳細文檔和完整的代碼會在後面文章以文檔的形式發出來
1. 硬體連接配接
硬體連接配接比較簡單,僅需要3條線,注意連接配接時兩個裝置UART電平,如電平範圍不一緻請做電平轉換後再連接配接,如下圖所示:
TX:發送資料端,要接對面裝置的RX
RX:接收資料端,要接對面裝置的TX
GND:保證兩裝置共地,有統一的參考平面
2. 軟體通信協定
空閑位:
UART協定規定,當總線處于空閑狀态時信号線的狀态為‘1’即高電平
起始位:
開始進行資料傳輸時發送方要先發出一個低電平’0’來表示傳輸字元的開始。因為空閑位一直是高電平是以開始第一次通訊時先發送一個明顯差別于空閑狀态的信号即為低電平。
資料位:
起始位之後就是要傳輸的資料,資料可以是5,6,7,8,9位,構成一個字元,一般都是8位。先發送最低位最後發送最高位。
奇偶校驗位:
資料位傳送完成後,要進行奇偶校驗,校驗位其實是調整個數,序列槽校驗分幾種方式:
1.無校驗(no parity)
2.奇校驗(odd parity):如果資料位中’1’的數目是偶數,則校驗位為’1’,如果’1’的數目是奇數,校驗位為’0’。
3.偶校驗(even parity):如果資料為中’1’的數目是偶數,則校驗位為’0’,如果為奇數,校驗位為’1’。
4.mark parity:校驗位始終為1
5.space parity:校驗位始終為0
停止位:
資料結束标志,可以是1位,1.5位,2位的高電平。
波特率:
資料傳輸速率使用波特率來表示,機關bps(bits per second),常見的波特率9600bps,115200bps等等,其他标準的波特率是1200,2400,4800,19200,38400,57600。舉個例子,如果序列槽波特率設定為9600bps,那麼傳輸一個比特需要的時間是1/9600≈104.2us。
3. 發送具體子產品介紹
(1)發送的框圖:
(2)發送的狀态轉移圖:
(3)時鐘産生子產品:
(4)計數器子產品:
(5)奇偶校驗子產品:
(6)移位寄存器子產品:
4.接收具體子產品介紹:
(1)接收子產品的框圖:
(2)接收子產品的狀态轉移圖:
5.發送子產品的代碼
(1)狀态機代碼
module FSM(
input SysClk,
input rst_n,
input Send,
input NextBit,
input Count_finish,
output reg Busy,
output reg ResetTimer,
output reg Increment,
output reg ResetCounter,
output reg Shift,
output reg Load
);
//-----------------------------------
//狀态變量、狀态編碼(初始值為0的獨熱碼)
//-----------------------------------
reg [3:0]CS,NS;//CS:目前狀态;NS:下一狀态
parameter [3:0]
IDLE = 4'b0000,
LOAD = 4'b0001,
COUNT = 4'b0010,
SHIFT = 4'b0100,
WAIT = 4'b1000;
//-----------------------------------
//時序邏輯描述狀态轉移
//-----------------------------------
always @(posedge SysClk or negedge rst_n)
if(!rst_n) CS <= IDLE;
else CS <= NS;
//-----------------------------------
//組合邏輯判斷狀态轉移條件,描述狀态轉移規律
//-----------------------------------
always @(rst_n or CS or Send or NextBit or Count_finish)//敏感清單中為複位信号、目前狀态和輸入條件
begin
NS = 4'bx;//預設狀态為不定态X
case(CS)//敏感清單為目前狀态
IDLE: begin
if(!Send) NS = IDLE;
if(Send) NS = LOAD;
end
LOAD: NS = COUNT;
COUNT: begin
if(!NextBit) NS = COUNT;
if(NextBit) NS = SHIFT;
end
SHIFT: begin
if(!Count_finish) NS = COUNT;
if(Count_finish) NS = WAIT;
end
WAIT: begin
if(Send) NS = WAIT;
if(!Send) NS = IDLE;
end
default: NS = IDLE;
endcase
end
//-----------------------------------
//時序邏輯描述每個狀态的輸出
//-----------------------------------
always @(posedge SysClk or negedge rst_n)
if(!rst_n) begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;//複位信号高有效
Load <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
else begin
case(NS)//敏感清單為下一狀态
IDLE: begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Load <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
LOAD: begin
ResetCounter <= 1'b1;
ResetTimer <= 1'b1;
Load <= 1'b1;
Shift <= 1'b0;
Busy <= 1'b1;
Increment <= 1'b0;
end
COUNT: begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Load <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b1;
Increment <= 1'b0;
end
SHIFT: begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Load <= 1'b0;
Shift <= 1'b1;
Busy <= 1'b1;
Increment <= 1'b1;
end
WAIT: begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Load <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
default:begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Load <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
endcase
end
endmodule
(2)計數器子產品:
module Mod10_Counter(
input SysClk,
input ResetCounter,//高電平有效
input Increment,
input [1:0] mode,
output reg Count_finish
);
reg [3:0] count;
reg [3:0] count_preload;
// 模式選擇
always @(posedge SysClk or negedge ResetCounter) begin
if(ResetCounter) begin
count_preload <= 0;
end else begin
case (mode)
0: begin
count_preload <= 7;
end
1: begin
count_preload <= 8;
end
2: begin
count_preload <= 9;
end
3: begin
count_preload <= 10;
end
endcase
end
end
// 計數
always @(posedge SysClk or posedge ResetCounter)
if(ResetCounter) count <= 4'b0;
else begin
if(Increment) begin
if(count == count_preload) count <= 4'b0;
else count <= count + 1'b1;
end
else count <= count;
end
// 産生Count10
always @(posedge SysClk or posedge ResetCounter)
if(ResetCounter) Count_finish <= 1'b0;
else begin
if(count == count_preload) Count_finish <= 1'b1;
else Count_finish <= 1'b0;
end
endmodule
(3)奇偶校驗
module Parity_Generator(
input [7:0] Din,
input ParitySelect,
output ParityBit
);
assign ParityBit = ParitySelect?(~(^Din)):(^Din);//ParitySelect=1,偶校驗;ParitySelect=0,奇校驗
//^按位異或
endmodule
(4)移位子產品
module Shift_Register(
input SysClk,
input rst_n,
input [7:0] Din,
input [1:0] mode, //模式選擇 5-8位情況 0-5 1-6 2-7 3-8
input Shift,
input Load,
input ParityBit,//æ ¡éªŒä½?
output wire Dout
);
wire Start_bit;//起始�
wire Stop_bit; //åœæ¢ä½?
assign Start_bit = 1'b0;
assign Stop_bit = 1'b1;
reg [10:0] Data_Pack;//ä¸?帧数æ?
// æ•°æ®ç§»ä½
always @(posedge SysClk or negedge rst_n)
if(!rst_n) begin
Data_Pack <= 10'b1111111111; //複位值全為1,避免誤觸發
end
else begin
if(Load == 1'b1) begin
case (mode)
0: begin
Data_Pack <= {3'b111,Stop_bit,ParityBit,Din[4:0],Start_bit};
end
1: begin
Data_Pack <= {2'b11,Stop_bit,ParityBit,Din[5:0],Start_bit};
end
2: begin
Data_Pack <= {1'b1,Stop_bit,ParityBit,Din[6:0],Start_bit};
end
3: begin
Data_Pack <= {Stop_bit,ParityBit,Din[7:0],Start_bit};
end
endcase
end
else if(Shift == 1'b1) begin
Data_Pack <= {1'b1,Data_Pack[9:1]};
end
else Data_Pack <= Data_Pack;
end
// æ•°æ®è¾“出
assign Dout = (!rst_n)?1'b1:Data_Pack[0];
endmodule
(5)時鐘子產品
//輸入50MHZ
//preload 5207 3471 2603 1301
//波特率 9600 14400 19200 38400
module Timer_300Hz(
input SysClk,//50MHz
input ResetTimer,//高電平有效
input [13:0] preload,
output reg NextBit
);
reg [13:0] count;
// assign preload = 3;//1MHz--->300Hz,每隔333個系統時鐘周期發送一次NextBit
//計數
always @(posedge SysClk or posedge ResetTimer)
if(ResetTimer) count <= 14'b0;
else begin
if(count == preload) count <= 14'b0;
else count <= count + 1'b1;
end
// 産生NestBit
always @(posedge SysClk or posedge ResetTimer)
if(ResetTimer) NextBit <= 1'b0;
else begin
if(count == preload) NextBit <= 1'b1;
else NextBit <= 1'b0;
end
endmodule
6.接收子產品的代碼
(1)狀态轉移
module RSM(
input SysClk,
input rst_n,
input Start_rx,
input NextBit,
input Count_finish,
output reg Busy,
output reg ResetTimer,
output reg Increment,
output reg ResetCounter,
output reg Shift
);
//-----------------------------------
//狀态變量、狀态編碼(初始值為0的獨熱碼)
//-----------------------------------
reg [3:0]CS,NS;//CS:目前狀态;NS:下一狀态
parameter [3:0]
IDLE = 4'b0000,
COUNT = 4'b0001,
SHIFT = 4'b0010,
Finish = 4'b0100;
// WAIT = 4'b1000;
//-----------------------------------
//時序邏輯描述狀态轉移
//-----------------------------------
always @(posedge SysClk or negedge rst_n)
if(!rst_n) CS <= IDLE;
else CS <= NS;
//-----------------------------------
//組合邏輯判斷狀态轉移條件,描述狀态轉移規律
//-----------------------------------
always @(rst_n or CS or Start_rx or NextBit or Count_finish)//敏感清單中為複位信号、目前狀态和輸入條件
begin
NS = 4'bx;//預設狀态為不定态X
case(CS)//敏感清單為目前狀态
IDLE: begin
if(!Start_rx) NS = IDLE;
if(Start_rx) NS = COUNT;
end
COUNT: begin
if(!NextBit) NS = COUNT;
if(NextBit) NS = SHIFT;
end
SHIFT: begin
if(!Count_finish) NS = COUNT;
if(Count_finish) NS = Finish;
end
Finish: begin
NS = IDLE;
end
default: NS = IDLE;
endcase
end
//-----------------------------------
//時序邏輯描述每個狀态的輸出
//-----------------------------------
always @(posedge SysClk or negedge rst_n)
if(!rst_n) begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;//複位信号高有效
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
else begin
case(NS)//敏感清單為下一狀态
IDLE: begin
ResetCounter <= 1'b1;
ResetTimer <= 1'b1;
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
COUNT: begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b1;
Increment <= 1'b0;
end
SHIFT: begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Shift <= 1'b1;
Busy <= 1'b1;
Increment <= 1'b1;
end
Finish: begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
default:begin
ResetCounter <= 1'b0;
ResetTimer <= 1'b0;
Shift <= 1'b0;
Busy <= 1'b0;
Increment <= 1'b0;
end
endcase
end
endmodule
(2)移位子產品
module Shift_Register_rx(
input Sysclk,
// input [8:0] D_in,
input rst_n,
input Shift,
input Count_finish,
input ParitySelect,
input [1:0] mode,
input rx_bit,
output [7:0] D_out,
output D_out_valid,
output ParityBit,
output error
);
wire Sysclk;
reg [10:0] reg_D_out;
reg [7:0] reg_D_data;
reg Shift_1;
reg ParityBit;
wire error;
reg D_out_valid;
//将shift信号延遲一個周期
always @(posedge Sysclk or negedge rst_n) begin
if(~rst_n) begin
Shift_1 <= 0;
end else begin
Shift_1 <= Shift;
end
end
// 資料移位
always @(posedge Sysclk or negedge rst_n)
if(!rst_n) begin
reg_D_out <= {11{1'b0}};
end
else begin
if(Shift == 1'b1) begin
reg_D_out <= {rx_bit,reg_D_out[10:1]};
end
else reg_D_out <= reg_D_out;
end
always @(posedge Sysclk or negedge rst_n) begin
if(~rst_n) begin
reg_D_data <= 0;
ParityBit <= 0;
D_out_valid <=0 ;
end else begin
if( Shift_1 & Count_finish) begin
case (mode)
0: begin
reg_D_data <= {3'b0,reg_D_out[8:4]};
ParityBit <= reg_D_out[9];
end
1: begin
reg_D_data <= {2'b0,reg_D_out[8:3]};
ParityBit <= reg_D_out[9];
end
2: begin
reg_D_data <= {1'b0,reg_D_out[8:2]};
ParityBit <= reg_D_out[9];
end
3: begin
reg_D_data <= reg_D_out[8:1];
ParityBit <= reg_D_out[9];
end
endcase
D_out_valid <= 1;
end
else
D_out_valid <= 0;
end
end
assign error = D_out_valid?(ParityBit^(ParitySelect?(~(^reg_D_data)):(^reg_D_data))):0;
assign D_out = reg_D_data;
endmodule
(3)時鐘子產品
//輸入50MHZ
//preload 5207 3471 2603 1301
//波特率 9600 14400 19200 38400
module Timer_rx(
input SysClk,//50MHz
input ResetTimer,//高電平有效
input [13:0] preload,
output reg NextBit
);
reg [13:0] count;
// assign preload = 3;//1MHz--->300Hz,每隔333個系統時鐘周期發送一次NextBit
//計數
always @(posedge SysClk or posedge ResetTimer)
if(ResetTimer) count <= 14'b0;
else begin
if(count == preload) count <= 14'b0;
else count <= count + 1'b1;
end
// 産生NestBit
always @(posedge SysClk or posedge ResetTimer)
if(ResetTimer) NextBit <= 1'b0;
else begin
if(count == preload/2) NextBit <= 1'b1;
else NextBit <= 1'b0;
end
endmodule
(4)計數子產品
module Mod_Counter_rx(
input SysClk,
input ResetCounter,//高電平有效
input Increment,
input [1:0] mode,
output reg Count_finish
);
reg [3:0] count;
reg [3:0] count_preload;
// 模式選擇
always @(posedge SysClk or negedge ResetCounter) begin
if(ResetCounter) begin
count_preload <= 0;
end else begin
case (mode)
0: begin
count_preload <= 7;
end
1: begin
count_preload <= 8;
end
2: begin
count_preload <= 9;
end
3: begin
count_preload <= 10;
end
endcase
end
end
// 計數
always @(posedge SysClk or posedge ResetCounter)
if(ResetCounter) count <= 4'b0;
else begin
if(Increment) begin
if(count == count_preload) count <= 4'b0;
else count <= count + 1'b1;
end
else count <= count;
end
// 産生Count10
always @(posedge SysClk or posedge ResetCounter)
if(ResetCounter) Count_finish <= 1'b0;
else begin
if(count == count_preload) Count_finish <= 1'b1;
else Count_finish <= 1'b0;
end
endmodule
7.整體的UART子產品
(1)發送的APB子產品
// Programmer's model
// 0x00 DATA R RXD[7:0] Received Data
// DATA W TXD[7:0] Transmit data
module apb_uart(
input [7:0] Din,
input Send,
input SysClk,
input rst_n,
input [1:0] mode,
input [13:0] preload,
input ParitySelect,
output Busy,
output Dout
);
wire SysClk,Send,ParitySelect,rst_n,NextBit;
wire [7:0] Din;
wire [13:0] preload;
reg [7:0] Din_inside;
always @(posedge SysClk or negedge rst_n) begin
if(~rst_n) begin
Din_inside <= 0;
end else begin
case (mode)
0: begin
Din_inside <= {3'b000,Din[4:0]};
end
1: begin
Din_inside <= {2'b00,Din[5:0]};
end
2: begin
Din_inside <= {1'b0,Din[6:0]};
end
3: begin
Din_inside <= {Din[7:0]};
end
endcase
end
end
Mod10_Counter inst_Mod10_Counter
(
.SysClk (SysClk),
.ResetCounter (ResetCounter),
.Increment (Increment),
.mode (mode),
.Count_finish (Count_finish)
);
Timer_300Hz inst_Timer_300Hz (.SysClk(SysClk), .ResetTimer(ResetTimer), .preload(preload),.NextBit(NextBit));
Parity_Generator inst_Parity_Generator (.Din(Din_inside), .ParitySelect(ParitySelect), .ParityBit(ParityBit));
Shift_Register inst_Shift_Register
(
.SysClk (SysClk),
.rst_n (rst_n),
.Din (Din_inside),
.mode (mode),
.Shift (Shift),
.Load (Load),
.ParityBit (ParityBit),
.Dout (Dout)
);
FSM inst_FSM (
.SysClk (SysClk),
.rst_n (rst_n),
.Send (Send),
.NextBit (NextBit),
.Count_finish (Count_finish),
.Busy (Busy),
.ResetTimer (ResetTimer),
.Increment (Increment),
.ResetCounter (ResetCounter),
.Shift (Shift),
.Load (Load)
);
endmodule
(2)接收的APB子產品
// Programmer's model
// 0x00 DATA R RXD[7:0] Received Data
// DATA W TXD[7:0] Transmit data
module apb_uart_rx(
input Din,
input SysClk,
input rst_n,
input [1:0] mode,
input [13:0] preload,
input ParitySelect,
output Busy,
output error,
output D_out_valid,
output [7:0] D_out
);
wire ParitySelect,rst_n,NextBit;
wire [1:0] mode;
wire Din,error;
reg Din_1;
reg Din_2; //兩級同步後的資料
reg Din_3;
wire Start_rx;
wire [13:0] preload;
wire [7:0] D_out;
//為避免亞穩态,使用兩級同步
always @(posedge SysClk or negedge rst_n) begin
if(~rst_n) begin
Din_1 <= 0;
Din_2 <= 0;
Din_3 <= 0;
end else begin
Din_1 <= Din;
Din_2 <= Din_1;
Din_3 <= Din_2;
end
end
//取Din的下降沿信号
assign Start_rx = (~Din_2)&Din_3 ;
Mod_Counter_rx inst_Mod_Counter_rx
(
.SysClk (SysClk),
.ResetCounter (ResetCounter),
.Increment (Increment),
.mode (mode),
.Count_finish (Count_finish)
);
Timer_rx inst_Timer_rx (.SysClk(SysClk), .ResetTimer(ResetTimer), .preload(preload),.NextBit(NextBit));
Shift_Register_rx inst_Shift_Register_rx
(
.Sysclk (SysClk),
.rst_n (rst_n),
.mode (mode),
.rx_bit (Din_2),
.Count_finish (Count_finish),
.ParitySelect (ParitySelect),
.ParityBit (ParityBit),
.Shift (Shift),
.D_out_valid (D_out_valid),
.error (error),
.D_out (D_out)
);
RSM inst_RSM (
.SysClk (SysClk),
.rst_n (rst_n),
.Start_rx (Start_rx),
.NextBit (NextBit),
.Count_finish (Count_finish),
.Busy (Busy),
.ResetTimer (ResetTimer),
.Increment (Increment),
.ResetCounter (ResetCounter),
.Shift (Shift)
);
endmodule
(2)整個子產品
`timescale 1ns/1ns
//定義寄存器的位址
//`define reg_enable 8'h00
//`define reg_mode 8'h04
//`define reg_preload 8'h08
//`define reg_counter 8'h0c
module apb_uart_tx_rx(
input PCLK, //時鐘
input PRESETn, //複位
input [15:0] PADDR, //位址信号
input PWRITE, //控制信号 1:寫 0:讀
input [31:0] PWDATA, //寫資料
input PENABLE, //使能信号
input PSEL, //片選信号
input RXD,
output [31:0] PRDATA, //讀資料
output TXD,
output wire PREADY, // Device ready
output wire PSLVERR, // Device error response
output uart_intr //中斷輸出
);
assign PREADY = 1'b1; // Always ready
assign PSLVERR = 1'b0; // Always okay
reg Send_reg;
reg [1:0] mode_reg;
reg ParitySelect_reg;
reg [7:0] Din_reg;
reg [13:0] preload_reg;
reg [31:0] prdata_r;
wire Busy_rx;
wire [7:0] D_out_reg;
寫寄存器 //
always @(posedge PCLK or negedge PRESETn) begin
if(~PRESETn) begin
Send_reg <= 0;
mode_reg <= 0;
preload_reg <= 0;
ParitySelect_reg <= 0;
Din_reg <= 0;
end
else if(PSEL && PENABLE && PWRITE)
case (PADDR[4:2])
3'b000: Send_reg <= PWDATA[0];
3'b001: mode_reg <= PWDATA[1:0];
3'b010: preload_reg <= PWDATA[13:0];
3'b011: ParitySelect_reg <= PWDATA[0];
3'b100: Din_reg <= PWDATA[7:0];
default : begin
Send_reg <= 0;
mode_reg <= 0;
preload_reg <= 0;
ParitySelect_reg <= 0;
Din_reg <= 0;
end
endcase
end
讀寄存器 //
always @(posedge PCLK or negedge PRESETn) begin
if(~PRESETn) begin
prdata_r <= 32'b0;
end
else if(PSEL && !PENABLE && !PWRITE)
case (PADDR[4:2])
3'b000: prdata_r <= {31'b0,Send_reg};
3'b001: prdata_r <= {30'b0,mode_reg};
3'b010: prdata_r <= {18'b0,preload_reg};
3'b011: prdata_r <= {31'b0,Busy_reg};
3'b100: prdata_r <= {31'b0,Busy_rx};
3'b101: prdata_r <= {31'b0,D_out_valid};
3'b110: prdata_r <= {24'b0,D_out_reg};
default : begin
prdata_r <= 32'b0;
end
endcase
end
assign PRDATA = prdata_r;
apb_uart inst_apb_uart_tx
(
.Din (Din_reg),
.Send (Send_reg),
.SysClk (PCLK),
.rst_n (PRESETn),
.mode (mode_reg),
.preload (preload_reg),
.ParitySelect (ParitySelect_reg),
.Busy (Busy_reg),
.Dout (TXD)
);
apb_uart_rx inst_apb_uart_rx
(
.Din (RXD),
.SysClk (PCLK),
.rst_n (PRESETn),
.mode (mode_reg),
.preload (preload_reg),
.ParitySelect (ParitySelect_reg),
.Busy (Busy_rx),
.error (error),
.D_out_valid (D_out_valid),
.D_out (D_out_reg)
);
endmodule