自学内容网 自学内容网

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)!