Verilog 学习笔记-urat串口

▼魔方 西西 提交于 2020-02-27 14:18:20

串口:

  串口在嵌入式设备里经常会用到,串口主要包括RS232C串口,RS485串口等,他们只是电平不一样。

  RS232也称标准串口,是一种比较常用的串口,采用标准的DB9接口,RS232采用的是负逻辑电平,即-12~-5V表示逻辑1,+5~+12V表示逻辑0。

  一般的MCU或者FPGA的管脚只能输出TTL电平,所以一般需要采用想MAX232等电平转换芯片将UART的TTL电平转换成RS232电平。

UART:

  UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用于主机与辅助设备通信。

UART传输协议:

  UART一般具有11个bit位,包括一个起始位,8个数据位,一个校验位,一个停止位,校验位可以省掉。

  

verilog代码实现:

  现以将FPGA接收到上位机的数据然后再将这些数据发送回上位机显示为例,整个串口部分可以分别分为接收发送模块。

  首先接收模块:

  

  接收模块主要包含时钟复位输入,以及8个串行数据输入,以及将接收到的串行数据转换成并行数据输出,并输出一个接收完成标志位。

  同时在接收模块内产生串口接收所需要的波特率。

 

接收模块代码如下:

  

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    21:57:52 02/21/2020 
// Design Name: 
// Module Name:    uart_rx 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 


//
//////////////////////////////////////////////////////////////////////////////////
module uart_rx
 #    (
        parameter clk_ref  = 50, //参考时钟50M
        parameter baud_bps = 115200
    )
    (
        input clk_50,
        input Rst_n,
        input rx_data,
        output reg [7:0] uart_rx,
        output reg rx_done
  );
  
  localparam bps_cnt = clk_ref*1000000/baud_bps;
  reg data_temp0;
  reg data_temp1;
  wire start_flag;
  reg rx_flag;
  reg [15:0] baud_cnt;
  reg [3:0]  bit_cnt;
  reg [7:0]  rx_reg;
  
  //======================================================
  //
  //检测RX接收信号的起始位信号,RX信号由高电平变化为低电平
  //
  //======================================================
  always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                begin
                    data_temp0 <= 1'b0;
                    data_temp1 <= 1'b0;
                end
            else
                begin 
                    data_temp0 <= rx_data;
                    data_temp1  <= data_temp0;
                end
        end
        
    assign     start_flag = data_temp1 & (~data_temp0);

    //=======================================================
    //
    //数据接收标志位信号___________
    //                 |           |
    //=======================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                rx_flag<=0;
            else 
                begin 
                    if(start_flag)
                        rx_flag<=1;
                    else if((bit_cnt==4'd9)&(baud_cnt==bps_cnt/2))
                        rx_flag<=0;
                    else 
                        rx_flag<=rx_flag;
                end
        end
        
    //=====================================================
    //
    //波特率产生
    //
    //=====================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                baud_cnt<=16'd0;
            else if(rx_flag)
                begin
                    if(baud_cnt==(bps_cnt-1))
                        baud_cnt<=16'd0;
                    else
                        baud_cnt<=baud_cnt+1'b1;
                end
            else
                baud_cnt<=16'd0;
        end
    
        
    //=======================================================
    //
    //数据接收位数计数
    //
    //=======================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                bit_cnt<=4'd0;
            else if(rx_flag)
                begin
                    if(baud_cnt==(bps_cnt-1))
                        bit_cnt<=bit_cnt+1'b1;
                    else
                        bit_cnt<=bit_cnt;
                end
            else
                bit_cnt<=4'd0;
        end    
    
    //=======================================================
    //
    //数据接收块,将RX信号线上的信号接收并存在一个8位接收寄存器里
    //
    //=======================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)    
                rx_reg<=8'd0;
            else if(rx_flag)
                begin
                    if(baud_cnt==bps_cnt/2)
                        begin
                            case(bit_cnt)
                                4'd1:rx_reg[0]<=rx_data;
                                4'd2:rx_reg[1]<=rx_data;
                                4'd3:rx_reg[2]<=rx_data;
                                4'd4:rx_reg[3]<=rx_data;
                                4'd5:rx_reg[4]<=rx_data;
                                4'd6:rx_reg[5]<=rx_data;
                                4'd7:rx_reg[6]<=rx_data;
                                4'd8:rx_reg[7]<=rx_data;
                                default:;
                            endcase
                        end
                    else
                        rx_reg<=rx_reg;
                end
            else
                rx_reg<=8'd0;
        end
    
    //=======================================================
    //
    //将数据接收寄存器里的数据发送到输出寄存器
    //
    //=======================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                begin
                    rx_done<=1'b0;
                    uart_rx<=8'd0;
                end
            else if(bit_cnt==9)
                begin
                    uart_rx<=rx_reg;
                    rx_done<=1'b1;
                end
            else
                begin
                    uart_rx<=8'd0;
                    rx_done<=1'b0;
                end
        end
endmodule

  发送模块:

  

  发送模块主要包括系统时钟和复位输入,准备需要发送的并行数据输入,发送使能输入,以及串行数据输出。同时发送模块产生数据发送的波特率。

  发送模块检测到发送使能信号以后开始发送,也就是检测到TX_EN的上升沿开始启动发送。

  串口发送部分Verilog代码如下:

  

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    14:16:03 02/22/2020 
// Design Name: 
// Module Name:    uart_tx 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module uart_tx
  #(
        parameter clk_ref  = 50, //参考时钟50M
        parameter baud_bps = 115200//波特率
    )
    (
        input clk_50,
        input Rst_n,
        input  [7:0] uart_tx,
        input  tx_en,
        output reg tx_data
  );
  
  localparam bps_cnt = clk_ref*1000000/baud_bps;
  reg en_temp0;
  reg en_temp1;
  wire start_flag;
  reg tx_flag;
  reg [15:0] baud_cnt;
  reg [3:0]  bit_cnt;
  
  //======================================================
  //
  //数据发送使能信号检测,检测到使能信号后开始发送标志位置1
  //这里的使能信号使用接收模块里的接收完成标志信号
  //
  //======================================================
  always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                begin
                    en_temp0 <= 1'b0;
                    en_temp1 <= 1'b0;
                end
            else
                begin 
                    en_temp0 <= tx_en;
                    en_temp1  <= en_temp0;
                end
        end
        
    assign     start_flag = (~en_temp1) & en_temp0;

    //=======================================================
    //
    //数据发送标志位信号___________
    //                 |           |
    //=======================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                tx_flag<=0;
            else 
                begin 
                    if(start_flag)
                        tx_flag<=1;
                    else if((bit_cnt==4'd9)&(baud_cnt==bps_cnt/2))
                        tx_flag<=0;
                    else 
                        tx_flag<=tx_flag;
                end
        end
        
    //=====================================================
    //
    //波特率产生
    //
    //=====================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                baud_cnt<=16'd0;
            else if(tx_flag)
                begin
                    if(baud_cnt==(bps_cnt-1))
                        baud_cnt<=16'd0;
                    else
                        baud_cnt<=baud_cnt+1'b1;
                end
            else
                baud_cnt<=16'd0;
        end
    
        
    //=======================================================
    //
    //数据发送位数计数
    //
    //=======================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)
                bit_cnt<=4'd0;
            else if(tx_flag)
                begin
                    if(baud_cnt==(bps_cnt-1))
                        bit_cnt<=bit_cnt+1'b1;
                    else
                        bit_cnt<=bit_cnt;
                end
            else
                bit_cnt<=4'd0;
        end    
    
    //=======================================================
    //
    //数据发送块,将需要发送的8位并行数据一位一位的发送到输出
    //
    //=======================================================
    always @(posedge clk_50 or negedge Rst_n)
        begin
            if(!Rst_n)    
                tx_data<=1'b1;
            else if(tx_flag)
                    begin
                        case(bit_cnt)
                            4'd0:tx_data<=1'b0;
                            4'd1:tx_data<=uart_tx[0];
                            4'd2:tx_data<=uart_tx[1];
                            4'd3:tx_data<=uart_tx[2];
                            4'd4:tx_data<=uart_tx[3];
                            4'd5:tx_data<=uart_tx[4];
                            4'd6:tx_data<=uart_tx[5];
                            4'd7:tx_data<=uart_tx[6];
                            4'd8:tx_data<=uart_tx[7];
                            4'd9:tx_data<=1'b1;
                            default:;
                        endcase
                    end
            else
                tx_data<=1'b1;
        end

endmodule

如果需要实现将接收的数据发送,则在顶层模块里进行连接,将接收模块的输出信号连接到发送模块的输入,同时将接收模块的接收完成标志信号作为发送模块的使能信号。

代码如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    21:54:01 02/21/2020 
// Design Name: 
// Module Name:    uart_top 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module uart_top
    (
        input clk_50,
        input Rst_n,
        input  rx_data,
        output tx_data    
    );
    
    wire [7:0] uart_rx;
    wire rx_done;
    wire tx_en;

    uart_rx
        #(
            50,
            115200
        )
        (
            .clk_50(clk_50), 
            .Rst_n(Rst_n), 
            .rx_data(rx_data), 
            .uart_rx(uart_rx), 
            .rx_done(rx_done)
        );
    
    uart_tx
        #(
            50,
            115200
        )
        (
            .clk_50(clk_50), 
            .Rst_n(Rst_n), 
            .uart_tx(uart_tx), 
            .tx_en(rx_done), 
            .tx_data(tx_data)
    );


endmodule

 发送模块的仿真文件:

`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   15:18:38 02/22/2020
// Design Name:   uart_tx
// Module Name:   C:/mydesign/uart/uart_txtb.v
// Project Name:  uart
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: uart_tx
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module uart_txtb;

    // Inputs
    reg clk_50;
    reg Rst_n;
    reg [7:0] uart_tx;
    reg tx_en;

    // Outputs
    wire tx_data;

    // Instantiate the Unit Under Test (UUT)
    uart_tx uut 
    (
        .clk_50(clk_50), 
        .Rst_n(Rst_n), 
        .uart_tx(uart_tx), 
        .tx_en(tx_en), 
        .tx_data(tx_data)
    );

    initial begin
        // Initialize Inputs
        clk_50 = 0;
        Rst_n = 0;
        uart_tx = 0;
        tx_en = 0;

        // Wait 100 ns for global reset to finish
        #10;
        Rst_n = 1;
        #10;
        tx_en = 1;
        #10;
        end
        always #10 clk_50=~clk_50;
        always @(posedge clk_50)
         begin
            uart_tx<=8'b11001011;
         end
        
        
        // Add stimulus here
    
    
      
endmodule

仿真波形:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!