1. 串口接收原理与思路
1.1 基本原理
- 采样:每位数据采多次,统计高低电平出现的次数,次数多的就是该位的电平值
- 起始位检测:边沿检测,使用两个计数器来判断
Bps_Clk
的下降沿/上升沿(前一个时钟上升沿为高电平/低电平,后一个时钟上升沿为低电平/高电平),两个触发器即可设计得到。 - 新语法:
reg [2:0]R_Data[7:0]
,意为声明8个数的数组,每个数的位数为3位。
1.2 设计
附新语法:reg [2:0]R_Data[9:0]
,二维数组,每个位宽为3位。
-
使用的变量
Clk
时钟Reset_N
复位Baud_Set
波特率Uart_Rx
接收到的数据Data
显示接收到的数据Rx_Done
接收完成
- 两个
D触发器
检测边沿:[1:0]Uart_Rx_R
。用来判断Uart_Rx
的上升沿(Pedge_Uart_Rx
)和下降沿(Nedge_Uart_Rx
)。
reg [1:0]Uart_Rx_R;
always@(posedge Clk)begin
Uart_Rx_R[0] <= Uart_Rx;
Uart_Rx_R[1] <= Uart_Rx_R[0];
end
wire Pedge_Uart_Rx;
assign Pedge_Uart_Rx = (Uart_Rx_R == 2'b01);
wire Nedge_Uart_Rx;
assign Nedge_Uart_Rx = (Uart_Rx_R == 2'b10);
- 把每次接收的信号位分成16份(一次性接收10位则有160份),把每份中间的计数电平作为时钟
Bps_Clk_16x
信号。时钟信号设计:
- 既然每一个电平信号的中间作为计数时刻,那么需要一个计数器
Div_Cnt
循环计数Bps_Dr - 1
(一个位的计数值),每计数到一半产生一个电平信号。 - 计数器开始计数,需要一个控制信号
Rx_En
进行控制,当接收信号的第一个位到来时开始计数。
wire Bps_Clk_16x;
assign Bps_Clk_16x = (Div_Cnt == Bps_Dr / 2);
reg Rx_En;
always@(posedge Clk or negedge Reset_N)
if(!Reset_N)
Rx_En <= 0;
else if(Nedge_Uart_Rx)
Rx_En <= 1'b1;
else if(Rx_Done || (Sta_Bit >= 4))//发送结束和接收的起始电平异常
Rx_En <= 0;
reg [8:0]Div_Cnt;
always@(posedge Clk or negedge Reset_N)
if(!Reset_N)
Div_Cnt <= 0;
else if(Rx_En)begin
if(Div_Cnt == Bps_Dr - 1)
Div_Cnt <= 0;
else
Div_Cnt <= Div_Cnt + 1'b1;
end
else
Div_Cnt <= 0;
- 接收过程的设计
- 首先设计160的计数器
Bps_Cnt
,以时钟Bps_Clk_16x
为计数时钟(接收的10个位综合考虑)。注:计算160个间隔,需要161个点。因此计数值选择0-160。
reg [8:0]Bps_Cnt;
always@(posedge Clk or negedge Reset_N)
if(!Reset_N)
Bps_Cnt <= 0;
else if(Rx_En)
if(Bps_Clk_16x)
if(Bps_Cnt == 160)
Bps_Cnt <= 0;
else
Bps_Cnt <= Bps_Cnt + 1'b1;
else
Bps_Cnt <= Bps_Cnt;
else
Bps_Cnt <= 0;
- 在特定计数值处接收数据。累加每个接收位的中间七个间隔的电平,如果计数值大于4则为高电平。
reg [2:0]R_Data[7:0];
reg [2:0]Sta_Bit;
reg [2:0]Sto_Bit;
always@(posedge Clk or negedge Reset_N)
if(!Reset_N)begin
Sta_Bit <= 0;
R_Data[0] <= 0;
R_Data[1] <= 0;
R_Data[2] <= 0;
R_Data[3] <= 0;
R_Data[4] <= 0;
R_Data[5] <= 0;
R_Data[6] <= 0;
R_Data[7] <= 0;
Sto_Bit <= 0;
end
else
case(Bps_Cnt)
0: begin
Sta_Bit <= 0;
R_Data[0] <= 0;
R_Data[1] <= 0;
R_Data[2] <= 0;
R_Data[3] <= 0;
R_Data[4] <= 0;
R_Data[5] <= 0;
R_Data[6] <= 0;
R_Data[7] <= 0;
Sto_Bit <= 0;
end
5,6,7,8,9,10,11: Sta_Bit <= Sta_Bit + Uart_Rx;
21,22,23,24,25,26,27: R_Data[0] <= R_Data[0] + Uart_Rx;
37,38,39,40,41,42,43: R_Data[1] <= R_Data[1] + Uart_Rx;
53,54,55,56,57,58,59: R_Data[2] <= R_Data[2] + Uart_Rx;
69,70,71,72,73,74,75: R_Data[3] <= R_Data[3] + Uart_Rx;
85,86,87,88,89,90,91: R_Data[4] <= R_Data[4] + Uart_Rx;
101,102,103,104,105,106,107: R_Data[5] <= R_Data[5] + Uart_Rx;
117,118,119,120,121,122,123: R_Data[6] <= R_Data[6] + Uart_Rx;
133,134,135,136,137,138,139: R_Data[7] <= R_Data[7] + Uart_Rx;
149,150,151,152,153,154,155: Sto_Bit <= Sto_Bit + Uart_Rx;
default:;
endcase
always@(posedge Clk or negedge Reset_N)
if(!Reset_N)
Data <= 0;
else if(Bps_Clk_16x && (Bps_Cnt == 160))begin
// Data[0] <= (R_Data[0] >= 4)?1:0;
// Data[1] <= (R_Data[1] >= 4)?1:0;
// Data[2] <= (R_Data[2] >= 4)?1:0;
// Data[3] <= (R_Data[3] >= 4)?1:0;
// Data[4] <= (R_Data[4] >= 4)?1:0;
// Data[5] <= (R_Data[5] >= 4)?1:0;
// Data[6] <= (R_Data[6] >= 4)?1:0;
// Data[7] <= (R_Data[7] >= 4)?1:0;
Data[0] <= R_Data[0][2];
Data[1] <= R_Data[1][2];
Data[2] <= R_Data[2][2];
Data[3] <= R_Data[3][2];
Data[4] <= R_Data[4][2];
Data[5] <= R_Data[5][2];
Data[6] <= R_Data[6][2];
Data[7] <= R_Data[7][2];
end
always@(posedge Clk or negedge Reset_N)
if(!Reset_N)
Rx_Done <= 0;
else if(Bps_Clk_16x && (Bps_Cnt == 160))
Rx_Done = 1;
else
Rx_Done <= 0;
- 仿真文件
- 新语法
task Uart_Tx_Byte;//变量名
input [7:0]Tx_Data;//参数
begin
Uart_Rx = 1;
#20;
Uart_Rx = 0;
#8680;
···
Uart_Rx = 1;
#8680;
end
endtask
`timescale 1ns / 1ns
module Uart_Byte_Rx_tb;
reg Clk;
reg Reset_N;
reg Uart_Rx;
wire [7:0]Data;
wire Rx_Done;
Uart_Byte_Rx Uart_Byte_Rx(
.Clk(Clk),
.Reset_N(Reset_N),
.Baud_Set(4),
.Uart_Rx(Uart_Rx),
.Data(Data),
.Rx_Done(Rx_Done)
);
initial Clk = 1;
always #10 Clk = ~Clk;
initial begin
Reset_N = 0;
Uart_Rx = 1;
#201;
Reset_N = 1;
#200;
Uart_Tx_Byte(8'h5a);
// @(posedge Rx_Done);
#90000;
Uart_Tx_Byte(8'ha5);
// @(posedge Rx_Done);
#90000;
Uart_Tx_Byte(8'h86);
// @(posedge Rx_Done);
#90000;
$stop;
end
task Uart_Tx_Byte;
input [7:0]Tx_Data;
begin
Uart_Rx = 1;
#20;
Uart_Rx = 0;
#8680;
Uart_Rx = Tx_Data[0];
#8680;
Uart_Rx = Tx_Data[1];
#8680;
Uart_Rx = Tx_Data[2];
#8680;
Uart_Rx = Tx_Data[3];
#8680;
Uart_Rx = Tx_Data[4];
#8680;
Uart_Rx = Tx_Data[5];
#8680;
Uart_Rx = Tx_Data[6];
#8680;
Uart_Rx = Tx_Data[7];
#8680;
Uart_Rx = 1;
#8680;
end
endtask
endmodule
1.3 仿真结果
- 仿真中出现的问题
时间无法匹配,tb文件中不要等到Rx_Done
信号到来为结束标志。在仿真文件的接收中,一个位的接收时间为540*16=8640ns
,而接收信号Uart_Rx
位的持续时间则为8680ns
。可能会产生在没有接收完成时,后续Rx_Done
信号已经到来的情况。