2021-08-17

FPGA UART通信实现(二)


前言


在上一章简单介绍了FPGA实现与计算机UART通信的几种方式,本章节采用Verilog语言编写UART程序。FPGA UART通信实现(一)


一、顶层模块

顶层模块定义了UART通信的基本协议:波特率、接收位数、奇偶校验以及停止位数,例化接收模块和发送模块。

module Uart_top(
    input clk,
    input rst_p,
    
    input rxd,
    output txd,
    
    input [7:0] tx_data,  //发送数据
    input       tx_ready, //发送数据准备好信号
    output      tx_done,  //数据发送完毕
    
    output [7:0] rx_data,
    output       rx_ready
    
    );
    
    parameter  Clk_freq    =   48000000;   //UART模块工作时钟
    parameter  Uart_Baud   =   115200;    //波特率
    parameter  Data_width  =   8;         //数据位数
    parameter  Parity      =   1;       //奇偶校验位  0:无校验  1:奇校验  2:偶检验
    parameter  Stop_width  =   1;       //停止位
    //奇校验(odd parity):如果数据位中'1'的数目是偶数,则校验位为'1',如果'1'的数目是奇数,校验位为'0'。
    //偶校验(even parity):如果数据为中'1'的数目是偶数,则校验位为'0',如果为奇数,校验位为'1'。
 //   
    Uart_rx                  //UART 接收模块
    #(
       .Clk_freq(Clk_freq),
       .Uart_Baud(Uart_Baud) ,
       .Data_width(Data_width) ,
       .Parity(Parity),
       .Stop_width(Stop_width)
    ) 
    Uart_rx(
        .clk(clk),
        .rxd(rxd),
        .rx_data(rx_data),
        .rx_ready(rx_ready)
    );
    
    
     Uart_tx            //UART  发送模块
    #(
       .Clk_freq(Clk_freq),
       .Uart_Baud(Uart_Baud) ,
       .Data_width(Data_width) ,
       .Parity(Parity),
       .Stop_width(Stop_width)
    ) 
    Uart_tx(
        .clk(clk),
        .txd(rxd),
        .tx_data(tx_data),
        .tx_ready(tx_ready),
        .tx_done(tx_done)
    );
    
    
    
    
endmodule

二、接收模块

module Uart_rx(
    input clk,    
    input rxd,
        
    output [7:0] rx_data,
    output       rx_ready
    );
    
    parameter  Clk_freq    =   48000000;   //时钟48M
    parameter  Uart_Baud   =   115200;    //波特率
    parameter  Data_width  =   8;         //数据位数
    parameter  Parity      =   1;       //奇偶校验位
    parameter  Stop_width  =   1;       //停止位
    
 //   
    parameter  Baud_div    =   Clk_freq/Uart_Baud;
    
    reg     [7:0]    recv_cnt  = 0;    //接收bit计数器
    reg     [15:0]   Baud_cnt  = 0;    //波特率计数器
    
    reg     [7:0]    recv_data = 0;    //接收到的数据
    reg              recv_ready = 0;   //数据有效信号
    
    reg     [1:0]    Parity_bit = Parity;    //奇偶校验
    reg              Odd_bit    = 0;    //接收到的奇偶校验位
    
    reg              rxd_r1     = 1; 
    reg              rxd_r2     = 1;
    reg              rx_negedge = 0; 
    
    reg     [3:0]    rx_state   = 0;
    
    assign  rx_data  = recv_data;
    assign  rx_ready = recv_ready;
    
        
               
    always @ (posedge clk)  begin
        rxd_r1   <=   rxd  ;
        rxd_r2   <=   rxd_r1  ;
        if(rxd_r1==0 && rxd_r2==1)
            rx_negedge<=1;
        else
            rx_negedge<=0;
    end
    
    
    always @ (posedge clk)  begin
        case(rx_state)
        0:begin             //空闲态
            Baud_cnt<=0;
            recv_cnt<=0;
            if(rx_negedge==1)
                rx_state<=1;
            else
                rx_state<=0;
         end
         
         1:begin            //起始位
            if(Baud_cnt == Baud_div-1)
            begin
                 Baud_cnt<=0;
                 rx_state<=2;
            end
            else
            begin
                 Baud_cnt<=Baud_cnt+1;
                 rx_state<=1;
            end         
         end
         
         2:begin            //数据位
            if(Baud_cnt == Baud_div-1)
            begin
                 Baud_cnt<=0;
                 if(recv_cnt==Data_width-1)
                 begin
                    recv_cnt<=0;
                    rx_state<=3;
                 end
                 else
                 begin
                    recv_cnt<=recv_cnt+1;
                 end
            end
            else
            begin
                 Baud_cnt<=Baud_cnt+1;
            end         
         end
         
         3:begin            //校验位
            if(Parity_bit == 0)
                rx_state<=4;
            else
            begin                 
                 if(Baud_cnt==Baud_div-1)
                 begin
                    Baud_cnt<=0;
                    rx_state<=4;
                 end
                 else
                 begin
                    Baud_cnt<=Baud_cnt+1;
                 end
            end        
         end
         
         4:begin            //停止位                
           if(Baud_cnt==Baud_div/2)
           begin
                    Baud_cnt<=0;
                    rx_state<=5;
           end
           else
           begin
                    Baud_cnt<=Baud_cnt+1;
           end
         end        

         default:  rx_state<=0;
         
         endcase
    end
    
    always @ (posedge clk)  begin           //接收数据位
        if(Baud_cnt == Baud_div/2 && rx_state==2)
            recv_data[recv_cnt]<=rxd;
    end
    
    always @ (posedge clk)  begin			//接收校验位
        if(Baud_cnt == Baud_div/2 && Parity_bit>0  && rx_state==3)
            Odd_bit<=rxd;
    end
    
    always @ (posedge clk)  begin			//校验位  校验
        if( rx_state==4)
        begin
            if(Parity_bit==0)
                recv_ready<=1;
            else if(Parity_bit==1 && (Odd_bit!=(^recv_data)))
                recv_ready<=1;
            else if(Parity_bit==2 && (Odd_bit==(^recv_data)))
                recv_ready<=1;
         end
         else
                recv_ready<=0;  
    end
    
endmodule

三、发送模块

module Uart_tx(
    input clk,    
    output txd,
        
    input [7:0] tx_data,
    input       tx_ready,
    output      tx_done
    );
    
    parameter  Clk_freq    =   48000000;   //时钟48M
    parameter  Uart_Baud   =   115200;    //波特率
    parameter  Data_width  =   8;         //数据位数
    parameter  Parity      =   1;       //奇偶校验位
    parameter  Stop_width  =   1;       //停止位
    
 //   
    parameter  Baud_div    =   Clk_freq/Uart_Baud;
    
    reg     [7:0]    send_cnt  = 0;    //发送bit计数器
    reg     [15:0]   Baud_cnt  = 0;    //波特率计数器
    
    reg     [7:0]    send_data = 0;    //需要发送的数据
    reg              send_done = 0;   //发送结束信号
    
    reg     [1:0]    Parity_bit = Parity;    //
    reg              Odd_bit    = 0;         //奇偶校验位
    
    reg              txd_r      = 1;         //
        
    reg     [3:0]    tx_state   = 0;
    
    assign           tx_done    =  (tx_state==4) ? 1 : 0; 
    assign           txd        =   txd_r  ;  
               
   
    always @ (posedge clk)  begin
        case(tx_state)
        0:begin             //空闲态
            Baud_cnt<=0;
            send_cnt<=0;
            if(tx_ready==1)
                tx_state<=1;
            else
                tx_state<=0;
         end
         
         1:begin            //起始位
            send_data<=tx_data;
            if(Baud_cnt == Baud_div-1)
            begin
                 Baud_cnt<=0;
                 tx_state<=2;
            end
            else
            begin
                 Baud_cnt<=Baud_cnt+1;
                 tx_state<=1;
            end         
         end
         
         2:begin            //数据位
            if(Baud_cnt == Baud_div-1)
            begin
                 Baud_cnt<=0;
                 if(send_cnt==Data_width-1)
                 begin
                    send_cnt<=0;
                    tx_state<=3;
                 end
                 else
                 begin
                    send_cnt<=send_cnt+1;
                 end
            end
            else
            begin
                 Baud_cnt<=Baud_cnt+1;
            end         
         end
         
         3:begin            //校验位
            if(Parity_bit == 0)
                tx_state<=4;
            else
            begin                                  
                 if(Baud_cnt==Baud_div-1)
                 begin
                    Baud_cnt<=0;
                    tx_state<=4;
                 end
                 else
                 begin
                    Baud_cnt<=Baud_cnt+1;
                 end
            end        
         end
         
         4:begin            //停止位                
           if(Baud_cnt==Baud_div*Stop_width)
           begin
                    Baud_cnt<=0;
                    tx_state<=5;
           end
           else
           begin
                    Baud_cnt<=Baud_cnt+1;
           end
         end        

         default:  tx_state<=0;
         
         endcase
    end
    
    
    always @ (posedge clk)  begin   //奇偶校验位
        if(Parity_bit == 1)
            Odd_bit <= ~(^send_data);
        else if (Parity_bit == 2) 
            Odd_bit <= ^send_data;
    end
    
    always @ (posedge clk)  begin   //Bit发送
        case(tx_state)
        0: txd_r <=1;
        1: txd_r <= 0;
        2: txd_r <= send_data[send_cnt];
        3: txd_r <= Odd_bit; 
        4: txd_r <=1;
        endcase
    end
   
endmodule

ISim仿真

编写仿真测试TestBench,将UART模块的TXD与RXD连接,形成闭环。发送数据使用递增数,每秒发送8000个字节,按波特率算,总bit数=8000*(起始位1bit+数据位8bit+校验位1bit+停止位1bit)=88000(小于波特率115200)。 仿真代码如下
module Test_Uart(

    );
   reg clk;
   reg  rst_p;
   reg  rxd;
   wire txd;
   reg  [7:0]  tx_data = 0;  
   reg  tx_ready = 0;
   wire tx_done;
    
   wire [7:0] rx_data;
   wire rx_ready;
       
   Uart_top Uart_top(
    .clk(clk),
    .rst_p(rst_p),
    
    .rxd(txd),
    .txd(txd),
    
    .tx_data(tx_data),  //发送数据
    .tx_ready(tx_ready), //发送数据准备好信号
    .tx_done(tx_done),  //数据发送完毕
    
    .rx_data(rx_data),
    .rx_ready(rx_ready)
    
    ); 
    
  / 
  initial
  begin
    clk = 0;
    rst_p = 0;
  end
   
    
   always  #10  clk = ~clk ;  
   
   reg  [15:0]   clk_div = 0;
   
   always @(posedge clk) begin   // 每秒8000个数
    if(clk_div==6000)
        clk_div<=0;
    else
        clk_div<=clk_div+1;
    end
    
    always @(posedge clk) begin  //准备发送数据
    if(clk_div>=3000  && clk_div<=3500)
        tx_ready<=1;
    else
        tx_ready<=0;
    end
    
    always @(posedge clk) begin  //递增数
    if(clk_div==2000)
        tx_data<=tx_data+1;
    end
    
endmodule

UART数据发送仿真波形如下:
UART发送

UART数据接收仿真波形如下:

UART接收
UART数据接收及发送

UART 接收及发送

UART 接收及发送


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mhlicq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值