一、在串口模块的基础上加一点修改
1. 这一段讲做什么
我的博客是连续剧,觉得有参考/纠错/鼓励的价值的话,我是非常期待能与大家多多交流的呀
小梅哥的视频P14集,后半部分讲,能不能通过在大模块中操作一个控制信号send_go,进而影响子模块(就是串口模块,以下称串口模块)种的send_en信号,进而来操控串口发送数据的节奏呢?↓↓↓
想要在大模块中操纵send_go信号,那就需要在大模块中给send_go信号赋值。而send_go信号的变化凭借的是:信号发生器希望在哪一个时刻发送数据,这完全取决于信号发生器的设置。当然,由于我还没学到写信号发生器的模块,所以假设不随信号发生器的喜好,随我的喜好。我选择每隔10ms通过串口发送8bit数据。那么回到“send_go应该何时发送的问题”,通过分析,认为send_go每隔10ms来一个脉冲挺合适。发送流程是这样的:每隔10mssend_en拉高一个时钟周期,然后把send_go当做串口模块的输入端口,send_go一拉高,send_en就拉高,起始位、数据位、停止位,就是在send_en拉搞的时段没发送数据的。直到停止位的到来,send_en拉低。下一个send_go的到来将再次激发串口传输数据。
2. 这一段讲为什么要在这次的代码中加入r_data
大模块把data传输给串口模块,串口模块会分时刻把data传给uart_tx。这个传输的过程是one by one,说不准就会有一位受到干扰由0变1,因此,把要传的数据寄存到寄存器里面,保证传输尽量少出错。r_data就是那个寄存器。我是这样理解的。
二. 代码
1. 较之前代码的区别
大模块里加入下面这几句话
/*--------------send_en--------------*/
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
send_go<=0;
end
else if (cnt_10ms == 1) begin
send_go<=1'b1;
end
else
send_go<=0;
end
在串口模块里面要加入send_go如何驱动send_en:
/*-----------------------send_en-------------------------------*/
always @(posedge sys_clk or negedge rst_n) begin
if(!rst_n)
send_en<=32'd0;
else if(send_go)
send_en<=1;
else if (tx_done)
send_en<=0;
else
send_en<=send_en;
end
以及r_data
/*-----------------------r_data-------------------------------*/
always @(posedge sys_clk) begin
if(!rst_n)
r_data<=1'b1;
else if (send_go)
r_data<=data;
else
r_data<=r_data;
end
/*-----------
2. 完整的代码
2.1 设计代码
- 大模块代码
module byte_send(
input sys_clk,
input rst_n,
output uart_tx
);
reg [7:0] data ;
reg send_go ;
wire tx_done ;
send_byte s1(
.sys_clk (sys_clk ) ,
.rst_n (rst_n ) ,
.time_set (2) ,
.data (data ) ,//之前是在tb文件里面给data,send_en,现在是在顶层模块给
.send_go (send_go ) ,//顶层模块就是负责给子模块赋值的
.uart_tx (uart_tx ) ,
.tx_done (tx_done)
);
/*--------------变量的声明-------------------*/
reg [31:0] cnt_10ms;
parameter time_10ms = 500_000;
/*-------------- 1 0 ms ---------------*/
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
cnt_10ms<=0;
end
else if (cnt_10ms==time_10ms-1) begin
cnt_10ms<=0;
end
else
cnt_10ms<=cnt_10ms+1;
end
/*--------------send_en--------------*/
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
send_go<=0;
end
else if (cnt_10ms == 1) begin
send_go<=1'b1;
end
else
send_go<=0;
end
/*--------------data--------------*/
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
data<=0;
end
else if (tx_done) begin
data<=data+1;
end
else
data<=data;
end
endmodule
- 串口模块代码
module send_byte (
input sys_clk ,
input rst_n ,
input [2:0] time_set , //基础计数器的设置
input [7:0] data ,
input send_go ,
output reg uart_tx ,
output reg tx_done
);
/*-----------------------变量的声明-----------------------------*/
reg [31:0] cnt;//基本计数器
reg [3:0] cnt2;//2级定时器
reg [31:0] time_cnt;
reg send_en;
reg [7:0] r_data;
/*-----------------------设置时间间隔-----------------------------*/
always@(*)
if(!rst_n)
time_cnt<=434;
else
case(time_set)
0:time_cnt<=10416; //4800;
1:time_cnt<=5208; //9600;
2:time_cnt<=434; //115200;
default:time_cnt<=434; //115200;
endcase
/*-----------------------r_data-------------------------------*/
always @(posedge sys_clk) begin
if(!rst_n)
r_data<=1'b1;
else if (send_go)
r_data<=data;
else
r_data<=r_data;
end
/*-----------------------send_en-------------------------------*/
always @(posedge sys_clk or negedge rst_n) begin
if(!rst_n)
send_en<=32'd0;
else if(send_go)
send_en<=1;
else if (tx_done)
send_en<=0;
else
send_en<=send_en;
end
/*-----------------------基本计数器-----------------------------*/
always@(posedge sys_clk or negedge rst_n)
if(!rst_n)
cnt<=32'd0;
else if(send_en)
if(cnt==time_cnt-1)
cnt<=32'd0;
else
cnt<=cnt+1;
else//!send_en
cnt<=32'd0;
/*-----------------------2级计数器-----------------------------*/
always@(posedge sys_clk or negedge rst_n)
if(!rst_n)
cnt2<=4'd0;//默认发start
else if(send_en)begin
if((cnt2>=0)&&(cnt2<10))begin
if(cnt==time_cnt-1)
cnt2<=cnt2+1;
else
cnt2<=cnt2;
end
else if(cnt2==10)
cnt2<=0;//cnt2的清0
else
cnt2<=cnt2;
end
else //!send_en
cnt2<=4'd0;
/*-----------------------uart_tx-----------------------------*/
always@(posedge sys_clk or negedge rst_n)
if(!rst_n)
uart_tx<=0;
else if(send_en)
case(cnt2)
0: begin uart_tx<=0; end
1: uart_tx<=r_data[0] ;
2: uart_tx<=r_data[1] ;
3: uart_tx<=r_data[2] ;
4: uart_tx<=r_data[3] ;
5: uart_tx<=r_data[4] ;
6: uart_tx<=r_data[5] ;
7: uart_tx<=r_data[6] ;
8: uart_tx<=r_data[7] ;
9: uart_tx<=1 ;
default:uart_tx<=1;
endcase
else//!send_en
uart_tx<=uart_tx;
/*-----------------------tx_done-----------------------------*/
always@(posedge sys_clk or negedge rst_n)
if(!rst_n)
tx_done<=0;
else if(cnt2==9 && cnt == time_cnt-1)
tx_done<=1;
else if(send_en)
tx_done<=0;
else
tx_done<=0;
endmodule
2.2 tb文件代码
`timescale 1ns/1ps
module tb ;
reg sys_clk;
reg rst_n;
wire uart_tx ;
byte_send b1(
. sys_clk(sys_clk),
. rst_n (rst_n ),
. uart_tx(uart_tx)
);
/*-----------------sys_clk-----------------*/
initial
sys_clk=0;
always #10 sys_clk=~sys_clk;
/*-------------------rst_n-----------------*/
initial begin
rst_n=0;
#201
rst_n=1;
#500_000_0;
$stop;
end
endmodule
三、仿真效果
-
一次串口发送,显示uart_tx为
-
所以除去最左边的start位、最右边的stop位,发送的数据为:0000_1100
-
一个send_go脉冲时间为一个时钟周期: