当前位置: 首页 > news >正文

FPGA_UART

1.UART 概述 (通用异步收发传输器)


1. 基本定义

UART(Universal Asynchronous Receiver/Transmitter)是一种常见的串行通信协议,用于在设备间通过异步串行通信传输数据。它不依赖独立的时钟信号,而是通过预定义的波特率(Baud Rate) 同步数据的收发。 核心特点

  • 异步通信:无需共享时钟信号,仅通过数据线传输。
  • 全双工模式:收发双方可同时发送和接收数据(需独立TX、RX线)。
  • 灵活配置:支持自定义波特率、数据位长度、校验位、停止位等。

2. 工作原理

(1) 数据传输格式

每一帧数据包含以下部分(以典型8N1格式为例):

起始位数据位(8位)校验位(可选)停止位(1位)
1位,低电平LSB先发奇偶校验(可选)1位,高电平
  • 起始位:低电平(0)表示数据传输开始。
  • 数据位:传输的有效数据(5~9位),通常低位(LSB)先发送(如用户之前的案例)。
  • 校验位:用于简单错误检测(奇校验/偶校验/无校验)。
  • 停止位:高电平(1)表示一帧数据结束,并允许接收端校准时序。

(2) 异步同步机制

  • 波特率校准:收发双方必须使用相同的波特率(如115200、9600)。 例如:

复制

波特率115200 → 每位持续时间 ≈ 1/115200 ≈ 8.68μs

  • 数据采样:接收端在起始位下降沿触发,并在数据位中间点采样,抵消时钟偏移影响。

3. 典型应用场景

  • 微控制器与外围模块通信:如ESP8266(Wi-Fi)、GPS模块、传感器(温湿度)。
  • 调试接口:通过UART输出调试信息(常见于嵌入式开发板)。
  • 有线设备互联:旧式打印机、工业设备(如Modbus RTU协议)。
  • 信号转换:结合电平转换芯片(如MAX232)实现RS232、RS485等长距离通信。

4. 优缺点分析

优点缺点
硬件简单(仅需两根数据线)传输距离短(通常<1米)
成本低(无需复杂协议栈)无硬件错误恢复机制(需软件纠错)
广泛兼容性(几乎所有MCU支持)需严格匹配波特率(误差<3%)

5. 硬件实现关键点

  • 发送端(TX)
    • 将并行数据转为串行,按波特率逐位发送。
    • 使用分频器生成波特率时钟(如50MHz主频 → 115200波特率需分频系数:50e6 / 115200 ≈ 434)。
  • 接收端(RX)
    • 检测起始位下降沿,启动同步采样。
    • 通过移位寄存器重组串行数据为并行数据。

6. 常见问题与解决方案

  • 波特率失配: 若收发波特率差超过3%,会导致采样偏移,需重新校准。
  • 电磁干扰: 长距离使用需加屏蔽线或转换为差分信号(如RS485)。
  • 数据冲突: 全双工通信需避免同时发送,可通过流控信号(RTS/CTS)解决。

7. 主流扩展协议

  • RS-232:电平标准(±3~15V),支持更长距离(<15米)。
  • RS-485:差分信号,可多点通信(工业现场总线)。
  • USB转UART:通过芯片(如CH340、CP2102)实现USB与串口无缝衔接。

2.verilog编写

   这里需要讲解下,这里使用到50mhz的时钟, 波特率为115200,这里的50mhz的时钟是在1秒内有50_000_000个周期的数据,波特率115200是在1秒内有115200bit的传输。

   50_000_000/115200  指的是传输1bit需要传输多少个时钟周期

在写测试代码的时候,#8680 是因为 在 50_000_000/115200= 434 个时钟周期传输1bit, 而434个时钟周期每个时钟周期为20ns 434*20= 8680.

1,波形图

接收和发送都根据这个图编写就行

2.1接收模块代码

module uart_rx (
input          clk,
input          rst,
input          rx_en,
input          data_in,
output reg [7:0]  data_out,
output         uart_rx_done
);

localparam   CLK   =  50_000_000,
             BOTE  = 115200,
             CNT   = CLK / BOTE ;

reg             rx_en_d1;
reg             rx_en_d2;
reg             rx_flag ;
reg             rx_valid;
reg   [3:0]     rx_cnt  ;
reg  [15:0]     clk_cnt ;
reg  [7:0]      data_out_r;
reg             uart_rx_done_r;

assign       uart_rx_done = uart_rx_done_r;

always @(posedge clk or negedge rst )begin
    if (rst == 1'b1)begin
        rx_en_d1 <= 1'b0;
        rx_en_d2 <= 1'b0;
    end
    else begin
        rx_en_d1 <= rx_en;
        rx_en_d2 <= rx_en_d1;
    end
end


always @ (posedge clk or negedge rst )begin
    if (rst == 1'b1)
        rx_flag <= 1'b0;
    else if (rx_en_d1 == 1'b1 && rx_en_d2 == 1'b0)
        rx_flag <= 1'b1;
    else 
        rx_flag <= 1'b0;
end

always @ (posedge clk or negedge rst )begin
    if (rst == 1'b1)
        rx_valid <= 1'b0;
    else if (rx_cnt == 4'd9 && clk_cnt == CNT /2 )
        rx_valid <= 1'b0;
    else if (rx_flag == 1'b1)
        rx_valid <= 1'b1;
    else;
end


always @ (posedge clk or negedge rst )begin
    if (rst == 1'b1)
        clk_cnt <= 16'd0;
    else if (clk_cnt == CNT )
        clk_cnt <= 16'd0;
    else if (rx_valid == 1'b1)
        clk_cnt <= clk_cnt +1'b1;
    else 
        clk_cnt <= 16'd0;
end

always @ (posedge clk or negedge rst )begin
    if (rst == 1'b1)
        rx_cnt <= 4'd0;
    else if (rx_en_d1 == 1'b1 && rx_en_d2 == 1'b0)
        rx_cnt <= 4'd0;
    else if (clk_cnt == CNT) 
        rx_cnt <= rx_cnt +1'b1;
    else;
end

always @ (posedge clk or negedge rst )begin
    if (rst == 1'b1)
        uart_rx_done_r <= 1'b0;
    else if (rx_valid == 1'b1) begin
            if (rx_cnt == 4'd9)
                uart_rx_done_r <= 1'b1;
            else 
                uart_rx_done_r <= 4'd0;
    end
    else
        uart_rx_done_r <= 1'b0;
end

always @ (posedge clk or negedge rst )begin
    if (rst == 1'b1)
        data_out_r <= 8'd0;
    else if (rx_valid == 1'b1   ) begin
            if (clk_cnt == CNT /4) 
                   case (rx_cnt )
                        4'd1  : data_out_r[0] = data_in ; 
                        4'd2  : data_out_r[1] = data_in ; 
                        4'd3  : data_out_r[2] = data_in ; 
                        4'd4  : data_out_r[3] = data_in ; 
                        4'd5  : data_out_r[4] = data_in ; 
                        4'd6  : data_out_r[5] = data_in ; 
                        4'd7  : data_out_r[6] = data_in ; 
                        4'd8  : data_out_r[7] = data_in ; 
                       default : ;
                   endcase
            else 
                data_out_r <= data_out_r;
    end
    else 
        data_out_r <= 8'd0;  
      
end

always @ (posedge clk or negedge rst )begin
    if (rst == 1'b1)
        data_out <= 8'd0;
    else if (rx_cnt == 4'd9)
        data_out <= data_out_r;
    else 
        data_out <= 8'd0;
end

endmodule 

 2.2发送模块代码

module uart_tx(
input           clk,
input           rst,
    
input           tx_en,
input   [7:0]   data_din,
output          data_out,
output          uart_tx_done 

);
localparam  CLK  = 50_000_000, //时钟 50_000_000 一秒 50000000个时钟周期 数据 波特率 9600 一秒9600个数据bit  5000000/9600 一个bit需要多少时钟周期 
            BOTE =115200,  
            CNT  = CLK /BOTE ;

reg          tx_en_d1;
reg          tx_en_d2;
reg          start_flag;
reg          tx_valid;
reg [3:0]    tx_cnt;
reg [15:0]   clk_cnt;
reg          uart_done_r;
reg          uart_dout_r;
reg  [7:0]   data_din_r;

assign     data_out  = uart_dout_r;
assign     uart_tx_done = uart_done_r;

always @ (posedge clk or negedge rst) begin
   if (rst == 1'b1) begin
        tx_en_d1 <= 1'b0;
        tx_en_d2 <= 1'b0;
   end
   else begin
        tx_en_d1 <=tx_en;
        tx_en_d2 <= tx_en_d1;
   end
end

always @ (posedge clk or negedge rst) begin
    if (rst == 1'b1)
        data_din_r <= 8'd0;
    else if (tx_cnt ==4'd9 && clk_cnt == CNT /2)
        data_din_r <= 8'd0;
    else if ( tx_en_d1 == 1'b1 && tx_en_d2 == 1'b0 ) 
        data_din_r <= data_din;
    else;
end


always @ (posedge clk or negedge rst) begin
    if (rst == 1'b1)
        start_flag <= 1'b0;
    else if ( tx_en_d1 == 1'b1 && tx_en_d2 == 1'b0 ) 
        start_flag <= 1'b1;
    else 
        start_flag <= 1'b0;
end

always @ (posedge clk or negedge rst) begin
    if (rst == 1'b1)
        tx_valid <= 1'b0;
    else if (start_flag == 1'b1)
        tx_valid <= 1'b1;
    else if (tx_cnt ==4'd9 && clk_cnt == CNT /2 )
        tx_valid <= 1'b0;
    else;
end

always @ (posedge clk or negedge rst) begin
    if (rst == 1'b1)
        clk_cnt <= 16'd0;
    else if (clk_cnt == CNT )
        clk_cnt <= 16'd0;
    else if (tx_valid == 1'b1)
        clk_cnt <= clk_cnt +1'b1;
    else 
        clk_cnt <= 16'd0;
end

always @ (posedge clk or negedge rst) begin
    if (rst == 1'b1)
        tx_cnt <= 4'd0;
    else if (tx_en_d1 == 1'b1 && tx_en_d2 == 1'b0)
        tx_cnt <= 4'd0;
    else if (clk_cnt == CNT)
        tx_cnt <= tx_cnt +1'b1;
    else;
end

always @ (posedge clk or negedge rst) begin
    if (rst == 1'b1)
        uart_done_r <= 1'b0;
    else if (tx_valid == 1'b1) begin
         if (tx_cnt == 4'd9) 
            uart_done_r <= 1'b1;
         else
            uart_done_r <= 1'b0;  
    end
    else
        uart_done_r <= 1'b0;
end

always @ (posedge clk or negedge rst) begin
    if (rst == 1'b1)
        uart_dout_r <= 1'b0;  
    else if (tx_valid == 1'b1) begin
        case (tx_cnt)
            4'd0  : uart_dout_r <= 1'b0;
            4'd1  : uart_dout_r <=data_din_r[0];    
            4'd2  : uart_dout_r <=data_din_r[1];
            4'd3  : uart_dout_r <=data_din_r[2];
            4'd4  : uart_dout_r <=data_din_r[3];
            4'd5  : uart_dout_r <=data_din_r[4];
            4'd6  : uart_dout_r <=data_din_r[5];
            4'd7  : uart_dout_r <=data_din_r[6];
            4'd8  : uart_dout_r <=data_din_r[7];       
            4'd9  : uart_dout_r <=1'b1;
          default  : ;
        endcase
    end
    else
        uart_dout_r <= 1'b1;  
end

   



endmodule 

2.3顶层模块代码

module uart_top (
input          clk,
input          rst,
input          uart_data_rx,
input          rx_en,
output         uart_data_out
);

wire       uart_tx_done;
wire       uart_rx_done;
wire [7:0] data_out;


uart_rx uart_rx (
.clk          (clk           ),      //input                      
.rst          ( rst          ),      //input                      
.rx_en        ( rx_en ),      //input                        
.data_in      ( uart_data_rx      ),      //input                          
.data_out     ( data_out ),      //output reg [7:0]                
.uart_rx_done ( uart_rx_done )       //output                             
);


uart_tx uart_tx(
.clk          ( clk           ),  //input                      
.rst          ( rst           ),  //input                             
.tx_en        ( uart_rx_done  ),  //input                        
.data_din     ( data_out      ),  //input   [7:0]                    
.data_out     ( uart_data_out ),  //output                          
.uart_tx_done ( uart_tx_done  )  //output                              
                      
);



endmodule 

2.4仿真模块代码



module uart_tb(

    );


reg clk;
reg rst;
reg  rx_en;
reg uart_data_rx;
wire uart_data_out;


initial begin
    rst = 1'b1;
    clk = 1'b1;
    uart_data_rx = 1'b1;
    rx_en = 1'b0;
    #20
    rst = 1'b0;

    #4340
    uart_data_rx = 1'b0;
    rx_en = 1'b1;

    #20
    rx_en = 1'b0;

    #8680  uart_data_rx = 1'b1;
    #8680  uart_data_rx = 1'b0;
    #8680  uart_data_rx = 1'b1;
    #8680  uart_data_rx = 1'b0;
    #8680  uart_data_rx = 1'b1;
    #8680  uart_data_rx = 1'b0;
    #8680  uart_data_rx = 1'b1;
    #8680  uart_data_rx = 1'b0;

    #8680  uart_data_rx = 1'b1;

end

always #10 clk = !clk;



uart_top  uart_top_inst (
  .clk(clk),
  .rst(rst),
  .rx_en(rx_en),
  .uart_data_rx(uart_data_rx),
  .uart_data_out(uart_data_out)
);
  

endmodule




3.仿真波形

3.1 接收模块仿真波形

 数据输入先放到低位依次传入

 

3.2 发送模块仿真波形

 发送的时候,把需要发送的数据低位先发

 

3.3 顶层模块仿真波形

相关文章:

  • 刷题记录(3)C语言中的字符
  • LlamaIndex学习
  • Fiddler为什么可以看到一次HTTP请求数据?
  • 项目班——0408
  • 【神经网络】python实现神经网络(四)——误差反向传播的基础理论
  • AI与我共创WEB界面
  • 风丘年度活动:2025年横滨汽车工程展览会
  • java中常用的集合 - 面试篇
  • 【NIO番外篇】之组件 Selector
  • 【Redis】布隆过滤器应对缓存穿透的go调用实现
  • malloc、operator new、new的关系
  • c语言练习4
  • NO.92十六届蓝桥杯备战|图论基础-最小生成树-Prim算法-Kruskal算法|买礼物|繁忙的都市|滑雪(C++)
  • 常见攻击方式及防范措施
  • 基于PHP的酒店网上订房系统(源码+lw+部署文档+讲解),源码可白嫖!
  • Oracle数据库数据编程SQL<9.3 数据库逻辑备份和迁移Data Pump (EXPDP/IMPDP) 导出、导入补充>
  • 视觉slam框架从理论到实践-第一节绪论
  • C语言编译预处理3
  • 展示数据可视化的魅力,如何通过图表、动画等形式让数据说话
  • 面试篇 - GPT-3(Generative Pre-trained Transformer 3)模型
  • 用8年还原曹操墓鉴定过程,探寻曹操墓新书创作分享会举行
  • 被指违反代理协议遭南航暂停售票资格, 去哪儿网:今起恢复
  • 特朗普亲自介入美日关税谈判:以势压人还是给对手“送助攻”
  • 全国登记在册民营企业超过5700万户
  • 中国房地产报:以改促治实现楼市多难并解
  • 黄金投资热,成了“财富焦虑”的贩卖场