上一篇文章里,我们介绍了rx模块在参数固定时后的写法,其实主要工作已经完成,剩下的就是对模块的扩展和完善。扩展和完善工作也不复杂,只要对uart几个可变参数进行变量化或参数化即可。在之前的文章中我们也曾这样做过,感兴趣的朋友可以也可以看一下之前tx端的写法(https://ed.2743.com/archives/3478)。下面我们按照同样的做法,完善一下rx模块。

图1
uart 字符帧结构
module uart_rx_all #(
parameter BAUD = 9600,
parameter CLK_F = 50_000_000,
parameter PARITY = 0,
parameter DATA_BITS = 8,
parameter STOPBIT = 1
)
(
input wire clk,
input wire rst,
input wire rx,
output reg [ DATA_BITS - 1: 0 ] data,
output reg parity_err, //接收校验错误
output reg done //接收完成提示
);
localparam NONE = 0;
localparam ODD = 1;
localparam EVEN = 2;
localparam MARK = 3;
localparam SPACE = 4;
localparam STOPBIT_ONE = 0;
localparam STOPBIT_ONEHALF = 1;
localparam STOPBIT_TWO = 2;
//【1】检测起始位
reg [ 1: 0 ] rx_r;
always@( posedge clk or posedge rst )
begin
if ( rst )
rx_r <= 0;
else
rx_r <= { rx_r[ 0 ], rx };
end
wire en; //起始位信号
assign en = ( rx_r == 2'b10 ) ? 1'b1 : 1'b0;
//【2】使用状态机来接收rx线上的数据
localparam [31:0] CNT_MAX = CLK_F / BAUD; //接收bit位计数(时钟周期个数)上限值
//[1] 定义状态空间
localparam FSM_IDLE = 0; //空闲状态
localparam FSM_START = 1; //接收起始位状态
localparam FSM_DATA = 2; //接收数据位
localparam FSM_PARITY = 3; //接收校验位
localparam FSM_STOP = 4; //接收停止位
//校验位
reg ifparity; //是否有检验位
reg parity_value; //校验位的值
//[2] 定义状态变量
reg [ 2: 0 ] cstate;
//[3] 实现状态机
//每个状态做状态判断和跳转,以及根据状态输出
reg [ DATA_BITS - 1: 0 ] data_r; //暂存接收到的数据
reg [ 31: 0 ] cnt; //时钟周期计数,上限就是CNT_MAX
reg startbit; //接收到的起始位值
reg [ 3: 0 ] num; //接收到的数据位个数
wire baud_tick = (cnt == CNT_MAX[31:0] - 1) ? 1'b1 : 1'b0;
always@( posedge clk or posedge rst )
begin
if ( rst )
begin
cstate <= 0;
data_r <= 0;
num <= 0;
done <= 0;
cnt <= 0;
startbit <= 0;
parity_err <= 0;
end
else
case ( cstate )
FSM_IDLE :
begin
data_r <= 0;
done <= 0;
if ( en )
begin
cstate <= FSM_START;
cnt <= 0;
end
else
cstate <= FSM_IDLE;
end
FSM_START :
begin
cnt <= cnt + 1;
startbit <= rx_r[ 1 ];
if ( cnt == CNT_MAX[31:1] && startbit ) //在bit位中点时进行采样
cstate <= FSM_IDLE ; //收到开始位为1 ,说明前面是个干扰信号
// else if ( startbit == 0 && cnt == CNT_MAX - 1 )
else if ( cnt == CNT_MAX - 1 )
begin
cstate <= FSM_DATA; //收到开始位为0 ,是起始位
cnt <= 0;
end
end
FSM_DATA :
begin
cnt <= cnt + 1 ;
if ( cnt == CNT_MAX[31:1] ) //在bit位中点时进行采样
data_r[ num ] <= rx_r[ 1 ];
if ( cnt == CNT_MAX - 1 && num <= DATA_BITS - 1 ) //接收bit位少于DATA_BITS时继续接收
begin
num <= num + 1 ;
cnt <= 0;
end
else if ( num == DATA_BITS ) //接收bit位大于DATA_BITS时跳转
//写的明明是==,为何说大于?因为bit位数为8,计数应该是0-7
begin
cstate <= ifparity ? FSM_PARITY : FSM_STOP; //有无校验位的判断
cnt <= 0;
num <= 0;
end
end
FSM_PARITY :
begin
cnt <= cnt + 1;
if ( cnt == CNT_MAX[31:1] )
begin
if ( parity_value == rx_r[ 1 ] ) //计算出的校验位的值 和 发送过来的校验位是否相等?
parity_err <= 1'b0;
else
parity_err <= 1'b1; //保证奇偶校验错误也只发单周期的高电平
end
else if ( cnt == CNT_MAX - 1 )
begin
cstate <= FSM_STOP;
cnt <= 0;
end
end
FSM_STOP :
begin
cnt <= cnt + 1;
case ( STOPBIT ) //不同长度停止位的处理
STOPBIT_ONE : //1停止位
begin
if ( cnt == CNT_MAX[31:1] )
begin
cstate <= FSM_IDLE;
data <= data_r;
done <= 1;
cnt <= 0;
end
end
STOPBIT_ONEHALF: //1.5停止位
begin
if( cnt == ( CNT_MAX[31:0] - 1) )
begin
cstate <= FSM_IDLE;
data <= data_r;
done <= 1;
cnt <= 0;
end
end
STOPBIT_TWO : //2停止位
begin
if( cnt == ( CNT_MAX[31:0] + CNT_MAX[31:1] - 1) )
begin
cstate <= FSM_IDLE;
data <= data_r;
done <= 1;
cnt <= 0;
end
end
default :
begin
if ( cnt == CNT_MAX - 1 )
begin
cstate <= FSM_IDLE;
data <= data_r;
done <= 1;
cnt <= 0;
end
end
endcase
end
default :
begin
cstate <= 0;
data_r <= 0;
num <= 0;
done <= 0;
cnt <= 0;
startbit <= 0;
parity_err <= 0;
end
endcase
end
always@( * )
begin
if ( rst )
begin
ifparity = 0 ;
parity_value = 0 ;
end
else
case ( PARITY )
NONE :
begin
ifparity = 0 ;
parity_value = 0 ;
end
ODD :
begin
ifparity = 1 ;
parity_value = ~( ^ data_r ) ;
end
EVEN :
begin
ifparity = 1 ;
parity_value = ^ data_r ;
end
MARK :
begin
ifparity = 1 ;
parity_value = 1 ;
end
SPACE :
begin
ifparity = 1 ;
parity_value = 0 ;
end
default :
begin
ifparity = 0 ;
parity_value = 0 ;
end
endcase
end
endmodule
仿真工程:
`timescale 1ns/1ps
module tb();
reg clk = 0;
always #10 clk = ~clk;
reg rst = 0;
initial
begin
rst = 1;
#20;
rst = 0;
#10;
#800000;
$stop;
end
wire txrx;
uart_core #
(
.SIM_DEBUG ("TRUE"),
.BAUD (115200),
.CLK_F (50_000_000)
)
uart_core_inst
(
.clk (clk),
.rx (txrx),
.tx (txrx),
.rst (rst)
);
endmodule
仿真波形:

图2
工程代码(quartus)
[content_control]
[/content_control]
老师您好,麻烦老师将最终的uart项目代码上传到文章,谢谢老师!