当前位置:   article > 正文

FPGA入门 —— FPGA UART 串口通信_uart通信 延时

uart通信 延时

串口简介

UART 通用异步收发传输器( Universal Asynchronous Receiver/Transmitter) ,通常称作 UART。 UART 是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。 它包括了ch340、 RS232、 RS499、 RS423、 RS422 和 RS485 等接口标准规范和总线标准规范

串口作为常用的三大低速总线(UART、 SPI、 IIC)之一,在设计众多通信接口和调试时占有重要地位。但 UART 和 SPI、 IIC 不同的是,它是异步通信接口,异步通信中的接收方并不知道数据什么时候会到达,所以双方收发端都要有各自的时钟,在数据传输过程中是不需要时钟的,发送方发送的时间间隔可以不均匀,接受方是在数据的起始位和停止位的帮助下实现信息同步的。而 SPI、 IIC 是同步通信接口,同步通信中双方使用频率一致的时钟,在数据传输过程中时钟伴随着数据一起传输,发送方和接收方使用的时钟都是由主机提供的

UART 通信只有两根信号线,一根是发送数据端口线叫 tx(Transmitter),一根是接收数据端口线叫 rx(Receiver),对于 PC 来说它的 tx 要和对于 FPGA 来说的 rx 连接,同样 PC 的 rx 要和 FPGA 的 tx 连接,如果是两个 tx 或者两个 rx 连接那数据就不能正常被发送出去和接收到,所以不要弄混,记住 rx 和 tx 都是相对自身主体来讲的。UART 可以实现全双工,即可以同时进行发送数据和接收数据

串口回环模块如下图所示,使用电脑上位机串口模块完成对 FPGA 串口模块的环回实验

串口时序

下面我们来看一下 RS232 协议:

  1. RS232 是 UART 的一种,没有时钟线,只有两根数据线,分别是 rx 和 tx,这两根线都是 1bit 位宽的。其中 rx 是接收数据的线, tx 是发送数据的线

  2. rx 位宽为 1bit, PC 机通过串口调试助手往 FPGA 发 8bit 数据时, FPGA 通过串口线rx 一位一位地接收,从最低位到最高位依次接收,最后在 FPGA 里面位拼接成 8 比特数据,也就是我们常说的串转并

  3. tx 位宽为 1bit, FPGA 通过串口往 PC 机发 8bit 数据时, FPGA 把 8bit 数据通过 tx线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照RS232 协议把这一位一位的数据位拼接成 8bit 数据,并行数据转换成串行数据进行发送

  4. 串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有10bit。在不发送或者不接收数据的情况下, rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输

rs232时序图入下图所示:

  1. 波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进行传输的,所以其码元就是代表一个二进制数), 每秒钟通过信号传输的码元数称为码元的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有 4800、9600、 115200 等

  2. 比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为“每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是 115200 的波特率,其串口的比特率为: 115200Bps *1bit= 115200bps

  3. 由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/9600 秒,如果用 50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1s * 10^9)ns /115200bit)ns / 20ns ≈434 个系统时钟周期,即每个 bit 数据之间的间隔要在 50MHz 的时钟频率下计数 434 次

串口模块设计

我们先绘制一下串口回环的整个模块图:

由整个回环模块图,我们可以看出在整个回环实验中,我们需要再设计两个模块,一个接收模块和一个发送模块。

接收模块

先看一下接收模块的波形图:

输入信号为系统时钟、复位信号以及接收信号;为了消除跨时钟域所带来的的亚稳态现象,需要对 rx 信号进行打 3 拍操作,跨时钟域处理推荐是 3 拍,当然有些人觉得 2 拍足矣,只要验证没问题,这个你随意。

进行完打拍操作后,需要取第三拍的下降沿操作,也就是 ~rx_reg2 & rx_reg3 ,至于为何这样,可以参考边沿检测部分。

得到 start_flag 信号之后,就可以进行一个 rx 的接收了,本次接收模块使用的波特率为 115200,系统时钟为 50M,所以波特率为 50_000_000/115200=434,当然这里波特率也可以根据需要进行修改

为了每位数据的接收的稳定,所以可以数据的标志信号可以在波特率信号的中间位置进行拉高。然后将每 bit 的数据都拼接到我们接收数据的最高位,以此类推,接收到的第8位数据,就拼接在了 rx_data 的最高位,最先接收的数据就存储在了 rx_data 的最低位

接收完8位数据之后,我们的接收标志信号拉高,表示已经完成一次8位数据的接收。在接收完成标志位拉高后,将我们寄存的串转并的数据赋值 po_data,同样 po_flag 拉高一个周期

根据波形图与文字分析,可以编写代码如下:

UART_recv.v

module UART_recv 
#(
    parameter  CLK         =   26'd50000000    ,    // 时钟频率
    parameter  BAUD        =   17'd115200           // 波特率
)
(
    input   wire            clk         ,
    input   wire            rstn        ,
    input   wire            UART_rx     ,

    output  reg             flag_out    ,   // 数据接收完成标志位,既发送开始标志位
    output  reg    [7 : 0]  data_out        // 接收的数据
);

    localparam Baud_Clk     =   CLK/BAUD       ;    // 传输每个 Baud 需要的时钟数

    reg             rx_en       ;   // 接收使能
    reg             start_flag  ;   // 开始接收标志
    reg             flag_rx     ;   // 接收标志位,半个时钟周期为 1 ,用于判断数据已经全部接收完成
    reg             flag_bit    ;   // 比特标志位,采用下降沿发送
    reg             rx_reg1     ;   // 接收寄存器1,同步打拍(打一拍延时一个时钟周期)
    reg             rx_reg2     ;   // 接收寄存器2
    reg             rx_reg3     ;   // 接收寄存器3,取第三拍下降沿进行边缘检测
    reg [8 : 0]     cnt_baud    ;   // 波特率计数器
    reg [7 : 0]     data_rx     ;   // 接收数据寄存器
    reg [3 : 0]     cnt_bit     ;   // 比特计数器

    // 打三拍
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            rx_reg1 <= 1'b1;
            rx_reg2 <= 1'b1; 
            rx_reg3 <= 1'b1;
        end
        else begin
            rx_reg1 <= UART_rx;
            rx_reg2 <= rx_reg1;
            rx_reg3 <= rx_reg2;
        end
    end

    // 检测第三拍下降沿,用作数据接收信号
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            start_flag <= 1'b0;
        end
        // 判断第三拍下降沿
        else if(rx_reg3 && ~rx_reg2) begin
            start_flag <= 1'b1;
        end
        else begin
            start_flag <= 1'b0;
        end
    end

    // 接收使能
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            rx_en <= 1'b0;
        end
        // 下降沿接收
        else if(start_flag == 1'b1) begin
            rx_en <= 1'b1;
        end
        // 接收完成,输入只需要判断到数据位最后一位,输出则需要判断完整输出
        else if(cnt_bit == 4'd8 && flag_bit == 1'b1) begin
            rx_en <= 1'b0;
        end
    end

     // 波特计数器
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_baud <= 9'd0;
        end
        // 传输完成所有波特或者使能失效,表示接收结束
        e
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/467794
推荐阅读
相关标签
  

闽ICP备14008679号