串口发送模块
串口发送模块结构框图
发送模块具体实现结构框图如图所示,按照图片的内容一步步实现发送模块的设计。
DR_LUT查找表的作用是选择不同波特率时,得到对应波特率的计数时间。然后将计数时间的装载值给到Div_Cnt模块,目的是通过改变装载值,来改变波特率的计数时间,以实现不同波特率之间的切换。
Div_cnt是一个计数器,用来产生bps_clk信号,也就是串口的波特率时钟信号。再将波特率时钟信号给到bps_cnt中。bps_cnt的作用是产生一个以波特率时钟为基础的计数器。这样就可以计数波特率时钟下的每一位数据。如果得到计数完成信号(Tx_done)计数器进行清零。
计数完成信号由比较器产生,当计数器满11位次时,得到波特率下的十一个数据,这时产生完成信号。
通过波特率计数器(bps_cnt),计数值从0-10循环,不同计数值作为十选一多路器的控制端,选择不同的数据进行输出,从而实现输出串口数据。
下面的send_en始能信号作用是:当始能开始时才发送数据,并且发送一次成功以后由Tx_done信号控制停止发送。通过两个二选一多路器来实现。如果始能按下,这时控制计数器开始工作输出发送信号,在这个过程中如果发送完成信号产生,就停止发送。
参考代码如下
module uart_byte_tx(
Clk,
Rst_n,
data_byte,
send_en,
baud_set,
rs232_tx,
tx_done,
uart_state
);
input Clk;
input Rst_n;
input [7:0]data_byte;
input send_en;
input [2:0]baud_set;
output reg rs232_tx;
output reg tx_done;
output reg uart_state;
reg bps_clk;//波特率时钟
reg [15:0]div_cnt;//分频计数器
reg [15:0]bps_dr;//分频计数最大值
reg [3:0]bps_cnt;//波特率时钟计数器
reg [7:0]r_data_byte;//不直接取data_byte的值,通过r_data_byte寄存器进行寄存,防止直接取data_byte时候,数据改变导致错误。
localparam start_bit = 1'b0;
localparam stop_bit = 1'b1;
//使能控制信号部分,两个二选一多路器,有优先级。
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(tx_done)
uart_state <= 1'b0;
else
uart_state <= uart_state;
//只在en有效的时候,才将寄存器数据进行更新。
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
r_data_byte <= 8'd0;
else if(send_en)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
//dr_lut 查找表设计通常使用case语句实现
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_dr <= 16'd5207;
else begin
case(baud_set)
0:bps_dr <= 16'd5207;//9600
1:bps_dr <= 16'd2603;//19200
2:bps_dr <= 16'd1301;//38400
3:bps_dr <= 16'd867; //57600
4:bps_dr <= 16'd433; //115200
default : bps_dr <= 16'd5207;
endcase
end
//div_counter
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state) begin
if(div_cnt == bps_dr)
div_cnt <= 0;
else
div_cnt <= div_cnt +1'b1;
end
else
div_cnt <= 16'd0;
//bps_clk generate
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//bps_cnt
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 4'd0;
else if(tx_done)
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
//bps_cnt信号和11的比较器,产生发送完成信号
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
tx_done <= 1'b0;
else if(bps_cnt == 4'd11)
tx_done <= 1'b1;
else
tx_done <= 1'b0;
//十选一多路器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
rs232_tx <= 1'b1;
else begin
case (bps_cnt)
0:rs232_tx <= 1'b1;
1:rs232_tx <= start_bit;
2:rs232_tx <= r_data_byte[0];
3:rs232_tx <= r_data_byte[1];
4:rs232_tx <= r_data_byte[2];
5:rs232_tx <= r_data_byte[3];
6:rs232_tx <= r_data_byte[4];
7:rs232_tx <= r_data_byte[5];
8:rs232_tx <= r_data_byte[6];
9:rs232_tx <= r_data_byte[7];
10:rs232_tx <= stop_bit;
default:rs232_tx <= 1'b1;
endcase
end
endmodule
顶层模块
为了实现串口发送模块功能,需要调用写好的串口发送模块来进行测试。在工程中添加之前写好的按键模块。将按键模块中的按键状态,通过assign赋值语句连接到串口发送模块的始能信号(send_en)上。
添加issp模块给测试数据,将data连接到data上。给好指定波特率。并且将串口状态信号连接到led上,这样每次信号发送完成,状态指示灯会亮一下,提示程序是否正常运行。
参考代码
module uart_tx_top(Clk,Rst_n,rs232_tx,key_in0,led);
input Clk;
input Rst_n;
input key_in0;
output rs232_tx;
output led;
wire send_en;
wire [7:0]data_byte;
wire key_flag0;
wire key_state0;
assign send_en = key_flag0 & !key_state0;
uart_byte_tx uart_byte_tx_0(
.Clk (Clk),
.Rst_n (Rst_n),
.data_byte (data_byte),
.send_en (send_en),
.baud_set (3'd4),
.rs232_tx (rs232_tx),
.tx_done (),
.uart_state (led)
);
key_filter key_filter_0(
.Clk (Clk),
.Rst_n (Rst_n),
.key_in (key_in0),
.key_flag (key_flag0),
.key_state (key_state0)
);
issp issp_0(
.probe (),
.source (data_byte)
);
endmodule