Uart串口学习

目录

学习目标:

一、UART通信原理:

             1.概念:

             2.结构:

             3.标准:

              3.1协议层:

                3.2物理层:

二、UART发送端设计

        1.需求分析

       2.系统框架

        3.信号列表

         4.描述预期功能时序图

           5.发送端设计代码 

                6.接收端设计代码

                7.顶层文件描述

                8.仿真代码

                9.仿真截图

                10.RTL视图

 三、总结


学习目标:

                了解uart基本概念,尝试描述出uart数据发送端与接收端并将其连接,随后使用上位机测试uart模块能否正常收发数据。

一、UART通信原理:

             1.概念

                UART(universal asynchronous receiver-transmitter)是一种采用异步串行通信方式的通用异步收发传输器;它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

             2.结构

                串口通信需要两根信号线来实现,一根用于发送,另外一根接收。在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据;LSB低位;MSB高位。

             3.标准

                UART通信需满足协议层与物理层的标准。协议层主要为通信协议(包括数据格式、传输速率等)。物理层主要为接口类型、电平标准等。

              3.1协议层:

数据格式:1帧数据由四部分组成
起始位(1bit)
数据位(6\7\8bit)
奇偶校验位(1bit)
停止位(1\1.5\2bit)
传输速率:
串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,
单位是 bit/s(位/秒),简称 bps;
常用的波特率有 9600、19200、38400、57600 以及 115200 等。

                3.2物理层:

                     3.2.1接口标准

   

3.2.2电平标准
TTL 电平的串口(3.3V)
RS232 电平的串口(+5~+12V 为低电平,-12~-5V 为高电平)
按电气标准可分为
RS-232-C:TXD/RXD/GND、15 米/9600bps
RS-422:TX+/TX-/RX+/RX-/GND
RS485:A/B/G 、1200 米/9600bps

二、UART发送端设计

        1.需求分析

        使用FPGA和PC进行串口通信(数据收发)

        以9600波特率发送1byte数据,即8bit。故需要1bit起始位,1bit停止位。

       2.系统框架

        3.信号列表

         4.描述预期功能时序图

           5.发送端设计代码 

/*************======================*****************\
filename:uart_tx
description:uart串口,发送端
up file:
reversion:
    v1.0:2022-8-4 16:03  uart练习
    
tips:
author:106年
\*************======================*****************/

module uart_tx (
    input               clk      , //系统时钟
    input               rst_n    , //复位信号,低有效
    input               send_en  , //
    input  [07:00]      data_in  ,

    output reg          txd      , //串行数据发送信号
    output wire         tx_done    //一帧数据发送完成信号
    


);
    
//参数定义
    parameter CLK_FRE = 26'd50_000_000 ;
    
    parameter 
                BAUD_9600    = CLK_FRE / 9600    , //波特率为9600
                BAUD_19200   = CLK_FRE / 19200   , //波特率为19200
                BAUD_57600   = CLK_FRE / 57600   , //波特率为57600
                BAUD_115200  = CLK_FRE / 115200  ; //波特率为115200

//内部信号定义
    reg  [12:00]        cnt_bps     ; //产生波特周期
    wire                add_bps     ;
    wire                end_bps     ;

    reg  [03:00]        cnt_bit     ; //统计一帧数据发送计数器
    wire                add_bit     ;
    wire                end_bit     ;

    reg  [07:00]        tx_data     ; //欲发送的数据
    reg                 work_state  ; //系统工作使能信号
//逻辑描述
    //_______功能描述
       /*__系统工作使能信号__功能描述=============================================*/
        always@(posedge clk or negedge rst_n)begin
              if(!rst_n)begin
                    work_state <= 1'b0;
              end
              else if(send_en)begin
                    work_state <= 1'b1; //发送使能信号到来时系统工作
              end
              else if(tx_done)begin
                    work_state <= 1'b0; //传输完成时系统停止工作
              end
              else begin
                    work_state <= work_state;
              end        
        end
       /*================================================================*/

//产生写入数据
    //_产生写入数据___功能描述
       /*__产生写入数据__功能描述=============================================*/
        always@(posedge clk or negedge rst_n)begin
              if(!rst_n)begin
                 tx_data <= 8'd0;
              end
              else begin
                 tx_data <= data_in; //待传输数据,有空时改成标准化模块
              end     
        end
       /*================================================================*/


    /*__波特周期计数器________计数器============================================*/
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n)begin
                cnt_bps <= 13'd0 ;
            end
            else if(add_bps)begin
                   if(end_bps)begin
                      cnt_bps <= 13'd0;
                   end
                   else begin
                     cnt_bps <= cnt_bps +13'd1;
                   end
            end
            else begin
                cnt_bps <= 13'd0;
            end
        end
    
    
        assign add_bps = work_state  ;
        assign end_bps = add_bps && (cnt_bps >= BAUD_9600 - 1)    ;
    /*=================================================================*/
    
    /*___数据位置___计数器============================================*/
    
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n)begin
                cnt_bit <= 1'd0 ;
            end
            else if(add_bit)begin
                   if(end_bit)begin
                      cnt_bit <= 1'd0;
                   end
                   else begin
                     cnt_bit <= cnt_bit +1'd1;
                   end
            end
            else begin
                cnt_bit <= cnt_bit;
            end
        end
    
    
        assign add_bit = end_bps  ;
        assign end_bit = add_bit && (cnt_bit >= 4'd9)   ;
    /*=================================================================*/
    
//描述输出
    //发送数据
       /*========================================================*/
        always@(posedge clk or negedge rst_n)begin
              if(!rst_n)begin
                txd <= 1'b1;
              end
              else if(work_state)begin
                case (cnt_bit)
                0   : txd <= 1'b0       ; //起始位                       
                1   : txd <= tx_data[0] ; //发送数据第 1 位  
                2   : txd <= tx_data[1] ; //发送数据第 2 位  
                3   : txd <= tx_data[2] ; //发送数据第 3 位  
                4   : txd <= tx_data[3] ; //发送数据第 4 位  
                5   : txd <= tx_data[4] ; //发送数据第 5 位  
                6   : txd <= tx_data[5] ; //发送数据第 6 位  
                7   : txd <= tx_data[6] ; //发送数据第 7 位  
                8   : txd <= tx_data[7] ; //发送数据第 8 位  
                9   : txd <= 1'b1       ; //停止位                       
                    default: ; 
                endcase
              end
              else begin
                txd <= 1'b1;
              end        
        end

        assign tx_done = end_bit;       //结束传输

       /*================================================================*/
    

endmodule

                6.接收端设计代码

/*************======================*****************\
filename:uart_rx
description:uart串口,接收端端
up file:
reversion:
    v1.0:2022-8-5 09:45  uart练习
    
tips:
author:106年
\*************======================*****************/
module uart_rx (
    input               clk         ,
    input               rst_n       ,
    input               rxd         ,

    output reg [07:00]  rx_data     ,
    output reg          rx_done
);
// 参数定义
    parameter CLK_FRE = 26'd50_000_000 ;
    
    parameter 
                BAUD_9600    = CLK_FRE / 9600    ,
                BAUD_19200   = CLK_FRE / 19200   ,
                BAUD_57600   = CLK_FRE / 57600   ,
                BAUD_115200  = CLK_FRE / 115200  ;

    parameter   //采样信号位置
                CPD_9600    =  2604     ,
                CPD_19200   =  1302     ,
                CPD_57600   =  434      ,
                CPD_115200  =  217      ;


//内部信号定义
  //输入延迟信号
    reg             reg1;       //输入信号延迟,避免亚稳态
    reg             reg2;       //输入信号延迟,避免亚稳态
    reg             reg3;       //输入信号延迟,避免亚稳态
    wire            key_neg;    //起始信号下降沿
  //工作状态信号  
    reg             work_state; //系统工作状态
  //计数器信号
    reg   [12:00]   cnt_bps   ; //波特率计数器
    wire            add_bps   ; //波特率计数器
    wire            end_bps   ; //波特率计数器

    reg   [03:00]   cnt_bit   ; //记录接收数据位置
    wire            add_bit   ; //记录接收数据位置
    wire            end_bit   ; //记录接收数据位置
    
    reg             cpd       ; //采样标志信号

    reg   [07:00]   keep_data ; //数据保持

//逻辑描述
  //_______功能描述
   /*___输入信号延迟采样___功能描述=============================================*/
    always@(posedge clk or negedge rst_n)begin
          if(!rst_n)begin
             reg1 <= 1'b1;
             reg2 <= 1'b1;
             reg3 <= 1'b1;
          end
          else begin
             reg1 <= rxd;
             reg2 <= reg1;
             reg3 <= reg2;
          end        
    end
    assign key_neg = ~reg2 && reg3; //起始位下降沿检测
    //  assign rx_en   = key_neg;       //使能信号在起始位下降沿到来时为1
    /*================================================================*/
    
    
    
    /*___系统工作状态___功能描述=============================================*/
        always@(posedge clk or negedge rst_n)begin
              if(!rst_n)begin
                  work_state <= 1'b0;
              end
              else if(key_neg)begin
                  work_state <= 1'b1;
              end
              else if(rx_done)begin
                  work_state <= 1'b0;
              end
              else begin
                  work_state <= work_state;
              end        
        end
    /*================================================================*/

    /*__波特周期计数器________计数器============================================*/
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n)begin
                cnt_bps <= 13'd0 ;
            end
            else if(add_bps)begin
                   if(end_bps)begin
                      cnt_bps <= 13'd0;
                   end
                   else begin
                     cnt_bps <= cnt_bps +13'd1;
                   end
            end
            else begin
                cnt_bps <= 13'd0;
            end
        end
    
    
        assign add_bps = work_state  ;
        assign end_bps = add_bps && (cnt_bps >= BAUD_9600 - 1)    ;
    /*=================================================================*/

    /*___数据位置___计数器============================================*/
    
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n)begin
                cnt_bit <= 1'd0 ;
            end
            else if(add_bit)begin
                   if(end_bit)begin
                      cnt_bit <= 1'd0;
                   end
                   else begin
                     cnt_bit <= cnt_bit +1'd1;
                   end
            end
            else begin
                cnt_bit <= cnt_bit;
            end
        end
    
    
        assign add_bit = end_bps  ;
        assign end_bit = add_bit && (cnt_bit >= 4'd9)   ;
    /*=================================================================*/

    /*___采样标志信号___功能描述=============================================*/
        always@(posedge clk or negedge rst_n)begin
              if(!rst_n)begin
                cpd <= 1'b0;
              end
              else if((cnt_bit >= 1) && (cnt_bit <=9)) begin
                       if(cnt_bps == CPD_9600)begin
                              cpd <= 1'b1;
                        end
                        else begin
                              cpd <= 1'b0;
                        end
              end
              else begin
                cpd <= 1'b0;
              end        
        end
    /*================================================================*/
    
    /*================================================================*/
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)begin
                keep_data <= 8'd0;
            end
            else if(cpd)begin
                case(cnt_bit)
                    0   : keep_data     <= reg3 ;
                    1   : keep_data[0]  <= reg3 ;
                    2   : keep_data[1]  <= reg3 ;
                    3   : keep_data[2]  <= reg3 ;
                    4   : keep_data[3]  <= reg3 ;
                    5   : keep_data[4]  <= reg3 ;
                    6   : keep_data[5]  <= reg3 ;
                    7   : keep_data[6]  <= reg3 ;
                    8   : keep_data[7]  <= reg3 ;
                    9   : keep_data     <= keep_data;
                    default:;
                endcase
            end
            else begin
                keep_data <= keep_data;
            end
        end
    /*================================================================*/

    /*================================================================*/
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)begin
                rx_done <= 0;
                rx_data <= 8'd0;
            end
            else if(end_bit)begin
                rx_data <= keep_data;
                rx_done <= 1;
            end
            else begin
                rx_data <= rx_data;
                rx_done <= 0;
            end
        end
    /*================================================================*/



endmodule

                7.顶层文件描述

module top_uart(
    input               clk     ,   //系统时钟,50MHz
    input               rst_n   ,   //复位信号,低有效  
    input               data_in ,   //接收线

    output              txd         //发送线     

);

//内部信号定义
    wire [07:00]        data_link1  ;
    wire                rx_line     ;
//模块调用
  //数据接收端
    uart_rx U_uart_rx 
        (
            .clk       (clk)        ,
            .rst_n     (rst_n)      ,
            .rxd       (data_in)    ,

            .rx_data   (data_link1) ,
            .rx_done   (rx_line)
        );
    //数据发送端
    uart_tx U_uart_tx
        (
            .clk      (clk)         ,
            .rst_n    (rst_n)       ,
            .send_en  (rx_line)     ,
            .data_in  (data_link1)  ,
            .txd      (txd)            
        );


endmodule

                8.仿真代码

`timescale 1ns/1ns
module tb_top_uart();

//参数重定义
    defparam        U_top_uart.U_uart_tx.BAUD_9600 = 520 ,
                    U_top_uart.U_uart_rx.BAUD_9600 = 520 ,
                    U_top_uart.U_uart_rx.CPD_9600  = 260 ; 
//激励信号
    reg                 tb_clk      ;
    reg                 tb_rst_n    ;
    
    reg                tb_rxd        ;
//观测信号
    wire                tb_txd      ;
top_uart U_top_uart(
    .clk     (tb_clk),
    .rst_n   (tb_rst_n),
    .data_in (tb_rxd),

    .txd     (tb_txd)     

);

//系统初始化
    initial tb_clk = 1'b0;
    always #10 tb_clk = ~tb_clk;

    initial begin
        tb_rst_n = 1'b0;
        tb_rxd   = 1'b0;
        #33
        tb_rst_n = 1'b1;
        #20
        tb_rxd   = 1'b0;
        #10400
        tb_rxd   = 1'b1;
        #10400
        tb_rxd   = 1'b1;
        #10400
        tb_rxd   = 1'b0;
        #10400
        tb_rxd   = 1'b1;
        #10400
        tb_rxd   = 1'b0;
        #10400
        tb_rxd   = 1'b1;
        #10400
        tb_rxd   = 1'b0;
        #10400
        tb_rxd   = 1'b1;
        #10400
        tb_rxd   = 1'b1;
        #2_000_000
        $stop(2);
    end



endmodule

                9.仿真截图

                10.RTL视图

 三、总结

        使用Verilog HDL描述UART串口通信编码难度不高,重点需注意波特率的计数周期以及起始位和停止位的判断。本次完成UART数据回环并未加入缓存模块及按键控制模块,读者可根据自己需要自行添加。

        在描述接收端时需注意该问题,本问题为作者遇到的,初学者可留意自己是否遇到同样问题。

        

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值