基于Verilog实现uart串口回环

一、uart串口通信

1、基本概念

UART,全称Universal Asynchronous Receiver/Transmitter通用异步收发传输器。是电脑硬件的一部分,将资料由串行通信与并行通信间作传输转换。具体实物表现为独立的模块化芯片,或作为集成于微处理器中的周边设备。一般和RS-232C规格的,类似Maxim的MAX232之类的标准信号幅度变换芯片进行搭配,作为连接外部设备的接口。

  • 实现数据的串并转换;
  • 全双工(可以同时进行数据的双向传输)的异步通信;
  • 发送数据时,将并行数据转换为串行数据进行传输;
  • 接收数据时,将串行数据转换为并行数据。
  • UART串口收发的原理与Verilog实现

2、串口回环

流程图
在这里插入图片描述
如图,串口回环流程。
上位机发送数据 ,由串口接收端接收,传到控制模块,而后在传给发送模块,发送模块发送数据后,传给上位机,以此循环,从而实现串口数据收发的回环操作。

3、时序图

串口接收模块
在这里插入图片描述
串口发送模块
在这里插入图片描述

4、FIFO简述

First Input First Output的缩写,先入先出队列,这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。
FIFO阈值设置及深度计算原理

5、相关端口信号说明

端口信号 说明
clk 系统时钟(50Mhz)
rst_n 复位信号
uart_rxd uart接收端口
uart_txd uart发送端口
rx_data 接收端口接收数据
rx_data_vld 接收端口接收数据有效
din 控制模块从接收端的数据输入
ack 应答信号
dout 数据输出
tx_req 发送数据请求
tx_dout 发送端口输出数据

二、工程及实现

1、工程创建

可参考上篇博客,有关创建描述的很清楚,这里不再赘述。
数码管实现秒表计数

2、代码实现

1、串口接收模块

uart_rx.v

`include "cfg.v"

module uart_rx    
    (
        input                       clk                   ,
        input                       rst_n                 ,
        input                       uart_rxd              ,//接收端接收数据

        output    [7:0]             rx_data               ,//接收到的数据
        output                      rx_data_vld            //接收数据有效
    );

    //信号定义
    reg           [12:0]           cnt_baud         ;//波特率计数器 50_000_000/9600
    wire                           add_cnt_baud     ;
    wire                           end_cnt_baud     ;

    reg           [3:0]            cnt_bit          ;//bit计数器 记录传输数据bit
    wire                           add_cnt_bit      ;
    wire                           end_cnt_bit      ;

    reg                            rx_flag          ;//数据接收标志信号

    reg           [9:0]            rx_din           ;//数据采样寄存器 由上位机发送,从机接收到的数据

    /*
    reg                            rx_r0            ;//同步
    reg                            rx_r1            ;//打拍
    */

    reg           [1:0]            rxd_r            ;//同步 打拍

    wire                           nedge            ;//下降沿

    //计数器 
    //cnt_baud
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          cnt_baud <= 0;
        end
        else if(add_cnt_baud)begin
          if(end_cnt_baud)begin
              cnt_baud <= 0;
          end
          else begin
              cnt_baud <= cnt_baud + 1;
          end
        end
    end
    assign add_cnt_baud = rx_flag;//接收数据标志信号开始时,波特率计数器开始计数
    assign end_cnt_baud = add_cnt_baud && cnt_baud == `BAUD/`CLK_FREQ;

    //cnt_bit
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          cnt_bit <= 0;
        end
        else if(add_cnt_bit)begin
          if(end_cnt_bit)begin
              cnt_bit <= 0;
          end
          else begin
              cnt_bit <= cnt_bit + 1;
          end
        end
    end
    assign add_cnt_bit = end_cnt_baud;
    assign end_cnt_bit = add_cnt_bit && (cnt_bit == 10-1 || rx_din[0] == 1'b1);//数据接收完成或者控制模块开始采样数据(数据位 8 加起始位 停止位)

    //同步、打拍 rx_r0/r1
    /*
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          rx_r0 <= 1'b1;
          rx_r1 <= 1'b1;
        end
        else begin
          rx_r0 <= uart_rxd;//同步
          rx_r1 <= rx_r0;//打拍
        end
    end
    assign nedge = ~rx_r0 & rx_r1;//检测下降沿
    */
    always @(posedge clk or negedge rst_n) begin
      if(!rst_n)begin
        rxd_r <= 1'b1;
      end
      else begin
        rxd_r <= {
     
     rxd_r[0],uart_rxd};
      end
    end
    assign nedge = ~rxd_r[0] & rxd_r[1];//检测下降沿

    //rx_flag
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          rx_flag <= 1'b0;
        end
        else if(nedge)begin
          rx_flag <= 1'b1;//检测到下降沿 接收数据开始
        end
        else if(end_cnt_bit)begin
          rx_flag <= 1'b0;//bit计数器计时结束 表明数据传输完成 拉低flag
        end
    end

    //接收数据 rx_din
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          rx_din <= 0;
        end
        else if(add_cnt_baud && cnt_baud == ((`CLK_FREQ>>1) / `BAUD))begin
         // rx_din[cnt_bit] <= rx_r0;//从当前时钟开始采样数据
          rx_din[cnt_bit] <= rxd_r[0];
        end
    end
    assign rx_data = rx_din[8:1];
    assign rx_data_vld = end_cnt_bit && rx_din[0] == 1'b0;

endmodule

2、串口发送模块

uart_tx.v

 `include "cfg.v"
 
 module uart_tx       
    (
        input                                       clk     ,
        input                                       rst_n   ,
        input                                       tx_req  ,//数据发送请求
        input         [7:0]                         tx_din  ,//发送数据输入

        output                                      tx_rdy  ,//准备好数据发送
        output                                      tx_dout  //数据输出

    );

    //信号定义
    reg       [12:0]                      cnt_baud       ;//波特率计数器
    wire                                  add_cnt_baud   ;
    wire                                  end_cnt_baud   ;

    reg       [3:0]                       cnt_bit        ;//bit计数器
    wire                                  add_cnt_bit    ;
    wire                                  end_cnt_bit    ;

    reg                                   tx_flag        ;//数据发送标志

    reg       [9:0]                       tx_data        ;//传输数据寄存
    reg                                   tx_bit         ;//传输bit

    //计数器
    //cnt_baud
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          cnt_baud <= 0;
        end
        else if(add_cnt_baud)begin
          if(end_cnt_baud)begin
              cnt_baud <= 0;
          end
          else begin
              cnt_baud <= cnt_baud + 1;
          end
        end
    end
    assign add_cnt_baud = tx_flag;
    assign end_cnt_baud = add_cnt_baud && cnt_baud == `CLK_FREQ/`BAUD;

    //cnt_bit
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          cnt_bit <= 0;
        end
        else if(add_cnt_bit)begin
          if(end_cnt_bit)begin
              cnt_bit <= 0;
          end
          else begin
              cnt_bit <= cnt_bit + 1;
          end
        end
    end
    assign add_cnt_bit = end_cnt_baud;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == 10-1;

    //tx_flag 
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          tx_flag <= 1'b0;
        end
        else if(tx_req)begin
          tx_flag <= 1'b1;//接收到数据发送请求 开始发送数据
        end
        else if(end_cnt_bit)begin
          tx_flag <= 1'b0;//bit计数结束 拉低flag
        end
    end

    //tx_data
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          tx_data <= 0;
        end
        else if(tx_req)begin//收到请求 开始发送数据
          tx_data <= {1'b1,tx_din,1'b0};//数据拼接 1'b1  停止位  1'b0 起始位
        end
    end

    //tx_bit
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          tx_bit <= 1'b1;
        end
        else if(add_cnt_baud && cnt_baud == 1)begin
          tx_bit <= tx_data[cnt_bit];//将寄存的数据以bit形式传输 循环左移
        end
    end

    //tx_rdy
    assign tx_rdy = ~tx_flag;//发送模块处于非工作状态,可以进行数据发送

    //tx_dout
    assign tx_dout = tx_bit;//输出等于传输的bit数据

endmodule

3、串口控制模块

uart_ctrl.v

module uart_ctrl(
    
    input                  clk         ,
    input                  rst_n       ,
    input    [7:0]         din         ,//接收模块输入数据
    input                  ack         ,//发送数据应答信号
    input                  din_vld     ,//接收模块输入数据有效

    output   [7:0]         dout        ,//发送模块数据输出
    output                 dout_vld     //发送模块输出数据有效
);
    
    //信号定义
    reg                   rd_req   ;
    wire                  wr_req   ;
    wire                  empty    ;
    wire                  full     ;
    wire       [7:0]      q_out    ;
    wire       [4:0]      usedw    ;
            
    reg                   rd_fifo  ;//读fifo标志信号
    //rd_fifo
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          rd_fifo <= 1'b0;
        end
        else if(usedw > 4)begin
          rd_fifo <= 1'b1;
        end
        else if(empty)begin
          rd_fifo <= 1'b0;
        end
    end

    //rd_req
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          rd_req <= 1'b0;
        end
        else if(rd_fifo && ack)begin
          rd_req <= 1'b1;
        end
        else begin
          rd_req <= 1'b0;
        end
    end

    //dout 
    assign dout = q_out;

    //dout_vld
    assign dout_vld = rd_req;

    //fifo例化
    uart_fifo  u_uart_fifo(
	    .aclr          (~rst_n  ),
	    .clock         (clk     ),
	    .data          (din     ),
	    .rdreq         (rd_req  ),
	    .wrreq         (wr_req  ),
	    .empty         (empty   ),
	    .full          (full    ),
	    .q             (q_out   ),
	    .usedw         (usedw   )
    );

    assign wr_req = full == 1'b0 && din_vld;
endmodule

4、顶层模块

uart.v

module uart(
    input                      clk       ,//系统时钟
    input                      rst_n     ,//复位
    input                      uart_rxd  ,//串口接收端数据

    output                     uart_txd   //串口发送端数据
);

    //中间信号定义
    wire      [7:0]      rx_byte       ;
    wire                 rx_byte_vld   ;
     
    wire      [7:0]      tx_byte       ;
    wire                 tx_byte_vld   ;
    wire                 tx_rdy        ;

    //串口接收模块 uart_rx
    uart_rx    u_uart_rx
    (
        .clk                   (clk           ),
        .rst_n                 (rst_n         ),
        .uart_rxd              (uart_rxd      ),//串口接收模块接收端数据
        .rx_data               (rx_byte       ),//接收到的数据 接收的是由上位机发送的数据
        .rx_data_vld           (rx_byte_vld   ) //接收数据有效
    );

    //串口控制模块 uart_ctrl
    uart_ctrl           u_uart_ctrl
    (
        .clk                   (clk           ),
        .rst_n                 (rst_n         ),
        .din                   (rx_byte       ),//串口控制模块的输入 接收模块接收的数据
        .ack                   (tx_rdy        ),//发送数据应答信号 发送数据准备
        .din_vld               (rx_byte_vld   ),
        .dout                  (tx_byte       ),//数据输出 控制模块控制发送模块发送数据
        .dout_vld              (tx_byte_vld   )
    );

    //串口发送模块 uart_tx
    uart_tx        u_uart_tx
    (
        .clk               (clk              ),
        .rst_n             (rst_n            ),
        .tx_req            (tx_byte_vld      ),//数据发送请求 发送数据有效
        .tx_din            (tx_byte          ),
        .tx_rdy            (tx_rdy           ),//准备好数据发送
        .tx_dout           (uart_txd         )

    );

endmodule

5、波特率定义

cfg.v

//波特率定义
//`define BAUD_RATE_9600
//`define BAUD_RATE_19200
//`define BAUD_RATE_38400
//`define BAUD_RATE_57600
`define BAUD_RATE_115200
//时钟频率
`define CLK_FREQ 50_000_000

`ifdef BAUD_RATE_9600
    `define BAUD 9600
`elsif BAUD_RATE_19200
    `define BAUD 19200
`elsif BAUD_RATE_38400
    `define BAUD 38400
`elsif BAUD_RATE_57600
    `define BAUD 57600
`elsif BAUD_RATE_115200
    `define BAUD 115200
`endif 

3、引脚脚本

#时钟复位
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_E15 -to rst_n

#UART
set_location_assignment PIN_M2 -to uart_rxd
set_location_assignment PIN_G1 -to uart_txd

4、相关说明

FIFO例化
本文是调用quartus里面的FIFO,需要自己去配置,利用FIFO先进先出的特点,例化到控制模块。
在这里插入图片描述
两种同步打拍方式
在这里插入图片描述
时钟频率 与波特率关系
在这里插入图片描述

三、效果展示

将工程全编译而后烧录到开发板
在这里插入图片描述
打开串口调试助手
在这里插入图片描述
在串口输入要发送的数据,打开串口
![在这里插入图片描述](https://img-blog.csdnimg.cn/04af9f2b816e4b8ea64195f7ff201fd0.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5aSp5Lya5pm05bCx5Lya5pqX,size_20,color_FFFFFF,t_70,g_se,x_16
点击发送,查看效果
在这里插入图片描述
也可以设置定时发送
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/QWERTYzxw/article/details/121063705