闲来无事,写一个uart发送端的一段式状态机。uart写法很多,一段,两段,三段状态机,甚至不用状态机,直接用计数器都可以。
远程仓库,需要请自取:https://gitee.com/mikoxxx/FPGA_UART_TX
闲话少叙直接上代码:
module uart_tx_yiduan (
input clk , // 系统时钟
input rst_n , // 复位信号
input [7:0] uart_tx_data, // 需要发送的数据
input tx_en , // 发送使能
output reg uart_txd // 发送的数据
);
parameter BAUT_RATE = 115200; // 波特率--1秒钟发送多少个bit
parameter CLOCK_PERIOD = 50_000_000; // 系统时钟 20ns
parameter BAUT_CNT_MAX = CLOCK_PERIOD / BAUT_RATE; // 发送一个bit的数据需要多少个clk
reg [3:0] i; // 状态机变量
reg [8:0] baut_cnt; // 波特率计数器
reg tx_busy; // 发送中的标识信号
always @(posedge clk or negedge rst_n) begin
if (~rst_n)
tx_busy <= 1'b0;
else if (i == 11) // 发送完之后,拉低忙信号,代表现在可以发送下一数据
tx_busy <= 1'b0;
else if (tx_en)
tx_busy <= 1'b1; // 使能信号拉高,然后进入忙状态
end
// 波特计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
baut_cnt <= 9'd0;
else if (~tx_busy || baut_cnt == BAUT_CNT_MAX - 1) // 计数到波特计数最大值,归0.
baut_cnt <= 9'd0;
else if (tx_busy) // 需要在发送状态时,才开始计数
baut_cnt <= baut_cnt + 1;
end
// 一段式状态机
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
i <= 0;
uart_txd <= 1'b1;
end
else begin
case (i)
0: begin
if (tx_en) // 使能信号到了,进入下面的发送状态
i <= 1;
else
i <= 0;
end
1,2,3,4,5,6,7,8,9,10: begin
if (i == 1)
uart_txd <= 1'b0; // 发送起始位
else if (i > 1 && i <= 9)
uart_txd <= uart_tx_data[i - 2]; // 发送数据段
else if (i == 10) begin
uart_txd <= 1'b1; // 发送停止位
end
if (baut_cnt == BAUT_CNT_MAX - 1) begin
i <= i + 1;
end
end
11: begin // 这里因为最后的停止位少了一个时钟,所以多用了一个状态,相当于打了一拍。
i <= 0;
end
default: i <= 0;
endcase
end
end
endmodule
这里还设计了一个top模块,一秒钟发送一次8‘h55。
代码如下:
module top_uart_tx (
input clk,
input rst_n,
output uart_txd // 发送的数据
);
parameter CNT_MAX_1S = 50_000_000; // 1秒钟计数的最大值
parameter BAUT_RATE = 115200; // 波特率--1秒钟发送多少个bit
reg [25:0] cnt_1s ;
reg tx_en ;
reg [7:0] uart_tx_data;
// 1秒钟计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt_1s <= 0;
else if (cnt_1s == CNT_MAX_1S - 1)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s + 1;
end
// 每隔1秒拉高一次tx_en
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
tx_en <= 0;
else if (cnt_1s == CNT_MAX_1S - 1) // && ~tx_busy
tx_en <= 1'b1;
else
// 使能信号一般是一个脉冲,维持一个时钟,所以这里记得拉低
tx_en <= 1'b0;
end
uart_tx_yiduan #(
.BAUT_RATE (BAUT_RATE)
) u_uart_tx(
.clk (clk),
.rst_n (rst_n),
.uart_tx_data (8'h55), // 固定发送8'h55
.tx_en (tx_en),
.uart_txd (uart_txd)
);
endmodule
仿真的代码也一起贴了吧:
`timescale 1ns/1ns
module tb_uart_tx ();
parameter T = 20;
reg sys_clk;
reg sys_rst_n;
wire uart_txd;
// 这里为了仿真,增加了波特率,减少了发送间隔时间
parameter BAUT_RATE = 11520000; // 波特率--1秒钟发送多少个bit
parameter CNT_MAX_1S = 50;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0; //复位
#(T+1)
sys_rst_n = 1'b1;
end
always #(10) sys_clk = ~sys_clk;
top_uart_tx #(
.BAUT_RATE (BAUT_RATE),
.CNT_MAX_1S (CNT_MAX_1S)
) u_top_uart_tx(
.clk (sys_clk),
.rst_n (sys_rst_n),
.uart_txd (uart_txd)
);
endmodule
需要注意一点,在写XDC文件的时候,需要找到uart_txd对应的管脚进行约束,否则无效。
然后打开串口助手:
一秒钟可以到一个55显示。
然后自己也可以根据需要修改间隔时间和发送的数据。
如有错误,欢迎评论或私聊指出,谢谢~~~~~
更多嵌入式,FPGA资料可以参考:天津大学四川院FPGA实训 -- www.sxfpga.cn