FPGA - 串口发送多字节数据
1,任务要求
关于uart串口接收与发送可以参考我的这篇博客:《FPGA-UART串口》。这篇博客实现了单字节发送与接收。
接下来,来使用串口发送多字节数据到电脑。
2,模块框图以及简单时序分析图
串口发送多字节数据的思路是把写入的数据存到FIFO中,然后读FIFO完成发送。
框架图如下:
整个过程可以用状态机来实现,首先写入FIFO之前为IDLE状态,当FIFO中的empty信号(当FIFO为空时,empty为高电平1,当FIFO中写入数据时,empty拉低)拉低时进入SEND状态(发送数据),当FIFO中的数据发送结束时,uart_tx_done信号拉高进入END状态。
根据框架图分析简单时序图:
3,整体代码设计
3.1 FIFO配置
3.2 发送多字节逻辑代码
然后编写uart_send代码:
`timescale 1ns / 1ps
module uart_send(
input clk ,
input reset ,
input wr_en ,
input [7:0] wr_data ,
output reg uart_tx_en ,
output reg [7:0] uart_tx_data ,
input uart_tx_busy
);
reg [1:0] state ;
localparam IDLE = 2'b00;
localparam SEND = 2'b01;
localparam END = 2'b10;
reg [7:0] din ;
reg wren ;
reg rd_en;
wire [7:0] dout ;
wire full ;
wire empty;
reg uart_tx_busy_d0;
reg uart_tx_busy_d1;
always @(posedge clk ) begin
uart_tx_busy_d0 <= uart_tx_busy ;
uart_tx_busy_d1 <= uart_tx_busy_d0 ;
end
always @(posedge clk ) begin
din <= wr_data;
wren <= wr_en ;
end
always @(posedge clk) begin
if(reset) begin
state <= IDLE;
end
else begin
case(state)
IDLE: begin
if(~empty)
state <= SEND ;
else
state <= state;
end
SEND : begin
if(~uart_tx_busy_d0 && uart_tx_busy_d1)
state <= END ;
else
state <= state;
end
END : begin
state <= IDLE;
end
default : state <= IDLE;
endcase
end
end
always @(posedge clk) begin
if(state == IDLE && ~empty) begin
rd_en <= 1'b1;
end
else
rd_en <= 1'b0;
end
always @(posedge clk or negedge reset) begin
if(reset) begin
uart_tx_en <= 1'b0;
uart_tx_data <= 1'b0;
end
else begin
uart_tx_en <= rd_en;
uart_tx_data <= dout ;
end
end
fifo_w8xd128 fifo_w8xd128 (
.clk(clk), // input wire clk
.srst(reset), // input wire srst
.din(din), // input wire [7 : 0] din
.wr_en(wren), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(dout), // output wire [7 : 0] dout
.full(full), // output wire full
.empty(empty) // output wire empty
);
endmodule
然后设计顶层模块,在顶层模块我们为了上板可以看到接收效果,我们定义一个计数器,在计数器器开始计数时wr_en拉高,在wr_en拉高期间,wr_data自加,计数器计数到49时wr_en拉低,看下图:
3.3 顶层模块
在顶层模块中,其中定义了一个时钟管理单元 (PLL实现)来管理时钟和复位信号(目的使其同源),我们利用ILA逻辑分析仪来控制vio_wr来实现对wr_en的控制。
顶层文件代码如下:
`timescale 1ns / 1ps
module top(
inputwireclkin_50m,
output uart_txd
);
wire reset ;
wire clk ;
parameter CLK_FREQ = 50000000;
parameter BAUD_RATE = 115200 ;
parameter DATA_WIDTH = 8 ;
parameter STOP_WIDTH = 1 ;
parameter CHACK_TYPE = 0 ;
wire uart_tx_en ;
wire [7:0] uart_tx_data;
wire uart_tx_busy;
reg wr_en ;
reg [7:0] wr_data ;
wire vio_wr ;
reg vio_wr_d0 ;
reg vio_wr_d1 ;
reg vio_wr_d2 ;
reg [7:0] cnt ;
always @(posedge clk ) begin
vio_wr_d0 <= vio_wr ;
vio_wr_d1 <= vio_wr_d0;
vio_wr_d2 <= vio_wr_d1;
end
always @(posedge clk ) begin
if(wr_en && cnt == 'd49)
wr_en <= 1'b0;
else if(vio_wr_d1 && ~vio_wr_d2) begin
wr_en <= 1'b1;
end
end
always @(posedge clk) begin
if(reset) begin
cnt <= 1'b0;
end else if(wr_en && cnt == 'd49) begin
cnt <= 1'b0;
end
else if(wr_en) begin
cnt <= cnt + 1'b1;
end
end
always @(posedge clk ) begin
if(reset) begin
wr_data <= 1'b0;
end else if(wr_en) begin
wr_data <= wr_data + 1;
end
end
clock_and_reset clock_and_reset
(
.clkin_50m (clkin_50m),
.clkout_50M(clk),
.reset (reset)
);
uart_send uart_send
(
.clk (clk),
.reset (reset),
.wr_en (wr_en),
.wr_data (wr_data),
.uart_tx_en (uart_tx_en),
.uart_tx_data (uart_tx_data),
.uart_tx_busy (uart_tx_busy)
);
uart_tx #(
.CLK_FREQ(CLK_FREQ),
.BAUD_RATE(BAUD_RATE),
.DATA_WIDTH(DATA_WIDTH),
.STOP_WIDTH(STOP_WIDTH),
.CHACK_TYPE(CHACK_TYPE)
) inst_uart_tx (
.clk (clk),
.reset (reset),
.uart_txd (uart_txd),
.uart_tx_en (uart_tx_en),
.uart_tx_data (uart_tx_data),
.uart_tx_busy (uart_tx_busy)
);
vio_0 vio_0 (
.clk(clk), // input wire clk
.probe_out0(vio_wr) // output wire [0 : 0] probe_out0
);
endmodule
4,上板验证
生成bitstream文件,板级验证,在Hardware Manager界面中添加ILA:
串口接收:
再次切换vio_wr 0/1
原文地址:https://blog.csdn.net/weixin_46897065/article/details/136693318
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!