FPGA-结合协议时序实现UART收发器(三):串口接收模块uart_rx
串口接收模块uart_rx的功能实现
一、功能实现
对照代码,串口接收模块uart_rx实现功能包括:
- r_cnt计数信号,计数数据
- ro_user_rx_data 寄存用户接收数据,采用移位拼接方式依次获取数据
- ro_user_rx_valid 用户接受有效,通过判断是否接收完毕来赋值valid
- r_rx_check 校验位,采用异或方法
二、uart_rx代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/09/09 13:14:22
// Design Name:
// Module Name: uart_rx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_rx#(
//串口可调参数
parameter P_SYSTEM_CLK = 50_000_000,
parameter P_UART_BUADRATE = 9600,
parameter P_UART_DATA_WIDTH = 8,
parameter P_UART_STOP_WIDTH = 1,
parameter P_UART_CHECK = 0 //0未开启校验位,1奇校验,2偶校验
)(
//串口驱动输入输出
input i_clk ,
input i_rst ,
input i_uart_rx,
output [P_UART_DATA_WIDTH - 1 : 0] o_user_rx_data ,//用户输入数据,作为驱动的输出,即先经过驱动输出再输入到用户
output o_user_rx_valid
);
reg [P_UART_DATA_WIDTH - 1 : 0] ro_user_rx_data;
reg ro_user_rx_valid;
reg [1:0] r_uart_rx;
reg [15:0] r_cnt;
reg r_rx_check;
assign o_user_rx_data = ro_user_rx_data;
assign o_user_rx_valid = ro_user_rx_valid;
//处理打两拍情况,即取延迟一个周期的数
//使用时钟动态纠正,此处打两拍没用到
always @(posedge i_clk or posedge i_rst)
begin
if(i_clk)
r_uart_rx <= 2'b11;
else
r_uart_rx <= {r_uart_rx[0] , i_uart_rx};//r_uart_rx[1]就是打两拍的信号,即先读低位再往左移位
end
//处理r_cnt计数信号
always @(posedge i_clk or posedge i_rst)
begin
if(i_rst)//初始值
r_cnt <= 'd0;
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 3 && P_UART_CHECK == 0)//与uart_tx中同理
r_cnt <= 'd0;
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 2 && P_UART_CHECK > 0)
r_cnt <= 'd0;
else if(r_uart_rx[1] == 0 || r_cnt > 0)//打两拍r_uart_rx[1] == 0时为起始位即开始计数,或者已经开始计数了
r_cnt <= r_cnt + 1;//可以计数
else//保持
r_cnt <= r_cnt;
end
//处理用户接受数据ro_user_rx_data
always @(posedge i_clk or posedge i_rst)
begin
if (i_rst)
ro_user_rx_data <= 'd0;
else if(r_cnt > 0 && r_cnt <= P_UART_DATA_WIDTH)//r_cnt判断到数据位结束,此时r_cnt ==1 + P_UART_DATA_WIDTH -1,即r_cnt==起始位+数据位-1
ro_user_rx_data <= {i_uart_rx , ro_user_rx_data[P_UART_DATA_WIDTH - 1 : 1]};//uart先发低位再发高位,即先从高位接收新数据然后向低位移位
//ro_user_rx_data <= {r_uart_rx[1] , ro_user_rx_data[P_UART_DATA_WIDTH - 1 : 1]};//r_uart_rx[1]为打两拍信号
else
ro_user_rx_data <= ro_user_rx_data;
end
//处理用户接收握手ro_user_rx_valid
//i_uart_rx == ~r_rx_check 与 i_uart_rx == r_rx_check不理解可举例说明
//当数据为1001时,进行逐位运算后r_rx_check==0,奇校验时,校验位此时应该为1,即!r_rx_check
//当数据为0001时,进行逐位运算后r_rx_check==1,奇校验时,校验位此时应该为0,即!r_rx_check
//故奇校验时,i_uart_rx == !r_rx_check
//偶校验同理举例
always @(posedge i_clk or posedge i_rst)
begin
if(i_rst)
ro_user_rx_valid <= 'd0;
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 3 && P_UART_CHECK == 0)//计数完毕,没有开启校验位情况,与uart_tx中同理
ro_user_rx_valid <= 'd1;
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 2 && P_UART_CHECK == 1 && i_uart_rx == !r_rx_check)//计数完毕,有开启校验位情况,r_cnt此时为校验位的位置,开启奇校验并且i_uart_rx确实为奇校验
ro_user_rx_valid <= 'd1;
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 2 && P_UART_CHECK == 2 && i_uart_rx == r_rx_check)//计数完毕,有开启校验位情况,r_cnt此时为校验位的位置,开启奇校验并且i_uart_rx确实为奇校验
ro_user_rx_valid <= 'd1;
else
ro_user_rx_valid <= 'd0;//其他情况都是0
end
//处理判断接收的校验位,r_rx_check
always @(posedge i_clk or posedge i_rst)
begin
if(i_rst)
r_rx_check <= 'd0;
else if (r_cnt > 0 && r_cnt <= P_UART_DATA_WIDTH)//r_cnt计数到数据位结束
r_rx_check <= r_rx_check ^ i_uart_rx;//对i_uart_rx接收的数据依次进行异或运算,用来判断校验位使用,异或判断接收的数据里1的个数是奇数还是偶数
else
r_rx_check <= 'd0;
end
endmodule
总结
串口接收模块uart_rx实现,涉及计数器,移位拼接依次获取数据,握手有效判断,判断是否数据接收完毕,校验位计算等功能实现,具体可对照代码详细注释。