FPGA-结合协议时序实现UART收发器(二):串口发送模块实现uart_tx
实现架构框图中的uart_tx串口发送模块功能。
一、功能实现
对照代码,串口发送模块uart_tx实现功能包括:
- ro_user_tx_ready,握手,控制发送数据
- r_cnt,计数信号,计数数据
- r_tx_data ,寄存数据
- ro_uart_tx ,发送寄存数据
- r_tx_check ,计算校验位
二、uart_tx代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/09/09 13:06:58
// Design Name:
// Module Name: uart_tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_tx#(
//串口可调参数
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
)(
//串口驱动输入输出
input i_clk ,
input i_rst ,
output o_uart_tx,
input [P_UART_DATA_WIDTH - 1 : 0] i_user_tx_data ,//用户输出数据,作为驱动的输入,即先输入到驱动处理再输出
input i_user_tx_valid ,//握手
output o_user_tx_ready
);
//只要有output信号,就要有reg,因为需要寄存器进行输出
reg ro_uart_tx;
reg ro_user_tx_ready;
reg [15:0] r_cnt;//计数器位宽高于16bit时,组合逻辑的逻辑级数过高,谨慎使用。
reg [P_UART_DATA_WIDTH - 1 : 0] r_tx_data;
reg r_tx_check;
//发送激活信号
wire w_tx_active;
assign o_uart_tx = ro_uart_tx;
assign o_user_tx_ready = ro_user_tx_ready;
assign w_tx_active = i_user_tx_valid & o_user_tx_ready;//valid和ready都为1时才激活发送,达成握手
//处理ready信号
//重点处理可以握手的情况,已经达成激活握手时ro_user_tx_ready == 0,即此时没有ready即不需要握手,因为正在握手传数据
//待数据处理完毕时,ro_user_tx_ready == 1,即可以再次握手了,可以再次发送数据了
always @(posedge i_clk or posedge i_rst)
begin
if (i_clk)
ro_user_tx_ready <= 'd1;//协议初始状态是高位
else if (w_tx_active)
ro_user_tx_ready <= 'd0;//已经激活了,正在发送数据,暂时不需要进行握手
else if(r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 3 && P_UART_CHECK == 0)//2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 3 表示计数到校验位位置(根据uart协议来看),并且没有开启校验位,即此时也计数完毕了,可以再次握手了
ro_user_tx_ready <= 'd1;
else if(r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 && P_UART_CHECK > 0)//2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 表示计数到校验位位置,并且开启了校验位,即此时也计数完毕了,可以再次握手了
ro_user_tx_ready <= 'd1;
else
ro_user_tx_ready <= ro_user_tx_ready;
end
//处理r_cnt计数信号
always @(posedge i_clk , posedge i_rst)
begin
if (i_rst)
r_cnt <= 'd0;
else if(r_cnt == 1 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH + P_UART_CHECK -1)//计数完毕
r_cnt <= 'd0;
else if(!ro_user_tx_ready)//ro_user_tx_ready==0说明发送激活,可以发送数据
r_cnt <= r_cnt + 1;
else
r_cnt <= r_cnt;
end
//处理用户发送数据,寄存在r_tx_data
//进行用户发送数据寄存操作
always @(posedge i_clk or posedge i_rst)
begin
if(i_rst)
r_tx_data <= 'd0;
else if(w_tx_active)
r_tx_data <= i_user_tx_data;
else if(!ro_user_tx_ready)
r_tx_data <= r_tx_data >> 1;//ro_user_tx_ready==0,可发送数据,移位后发送r_tx_data的低位给ro_uart_tx
else
r_tx_data <= r_tx_data;
end
//处理串口发送输出操作
//将用户寄存的数据发送出去
always @(posedge i_clk or posedge i_rst)
begin
if(i_rst)
ro_uart_tx <= 'd1;//uart协议空闲状态为1,所以最初值就是1
else if(w_tx_active)
ro_uart_tx <= 'd0;//起始位为0
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 3 && P_UART_CHECK > 0)//当r_cnt计数到校验位,且开启校验位时
ro_uart_tx <= P_SYSTEM_CLK == 1 ? ~r_tx_check : r_tx_check;
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 3 && P_UART_CHECK == 0)//当r_cnt计数到校验位,且不开启校验位时,直接发送停止位高电平
ro_uart_tx <= 'd1;//直接拉高
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 2 && P_UART_CHECK > 0)//有校验位时,且发送完了校验位,此时改发送停止位了,停止位发送高电平
ro_uart_tx <= 'd1;
else if(!ro_user_tx_ready)
ro_uart_tx <= r_tx_data[0];//ro_user_tx_ready==0可以发送数据,进行移位发送,uart先发送低位再发送高位
else
ro_uart_tx <= 'd1;
end
//处理校验位r_tx_check,计算校验位
always @(posedge i_clk or posedge i_rst)
begin
if(i_rst)
r_tx_check <= 'd0;
else if(r_cnt == 3 + P_UART_DATA_WIDTH - 1)
r_tx_check <= 'd0;
else
r_tx_check <= r_tx_check ^ r_tx_data[0];
end
endmodule
总结
串口发送模块uart_tx的功能实现,代码根据协议和时序进行实现,采用了握手来控制发送数据,具体详情可参考代码注释内容。