FPGA-UART串口

发布时间:2024年01月18日

1,什么是UART?

UART:全称为Universal Asynchronous Receiver/Transmitter,通用异步收发器。是一种串行异步的通信协议,该协议规定了传输数据时数据的传输方式以及所使用的信号,在嵌入式领域中有着非常广泛的应用。

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART)是一种异步 收发传输器,其在数据发送时将并行数据转换成串行数据来传输,在数据接收时将接收到的 串行数据转换成并行数据,可以实现全双工传输和接收。它包括了RS232、RS449、RS423、 RS422 和 RS485 等接口标准规范和总线标准规范。换句话说,UART 是异步串行通信的总 称。而RS232、RS449、RS423、RS422和RS485等,是对应各种异步串行通信口的接口标 准和总线标准,它们规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内 容。

RS232? 其中+0-+15V表示高电平? 0 - -15V表示低电平

RS485接口? 采用差分信号(幅值相等 极性相反 的两根吸信号)传输数据

电压差 D+ - D- : +2V-+6V 低电平? -2V - -6V 高电平

UART串口接受和发送都是TTL电平信号, 那什么是TTL电平信号呢?

TTL由双极晶体管构成,只能工作在5V的电压下,TTL器件输出低电平要小于0.5V,高电平要大于2.4V,对于一个接收器来说,如果电压小于0.8V,那么就认为其是逻辑0;高于2V就是逻辑1;

但是2.4V和5V之间由较大的差距,不利于改变噪声,而且会增加系统的功耗,还会影响信号变化的速度;后买就发展了LVTTL(2种 3.3V? 2.5V)

USB转TTL串口电路(FT232 CP2104 CH340系列等等)

串口细节梳理

数据位可选择为:5、6、7或者 8(默认)。

在bit7和停止位之间 可以插入1位校验位?

2,实现UART-发送控制器

UART发送逻辑和UART接受逻辑

起始位? bit1 bit2 ... bit7?停止位? ?(8位数据 10位时间)

位时间? -----> 波特率又称码元率,是指每秒传输码元的数目,单位波特(Band)?

码元:在数字通信中常常用时间间隔相同的符号来表示一个二进制数字二进制数字,这样的时间间隔内的信号称为(二进制)码元。 而这个间隔被称为码元长度

2.1UART串口发送逻辑分析

实现设计的模块端口

输入:

Data[7:0] :? 8位输入 对应8个拨码开关? ?;

时序电路,所以有CLK时钟,以及Reset_n复位;

输出:

uart_tx :? 需要发送的数据;

LED :? ?发送完LED闪烁;

细节梳理 :

9600波特率:计数器实现

起始位 bit1,bit2....bit7 停止位(一共10位)----->定义一个4位计数器 在发送数据时在0-9计数,

然后根据计数器的值 来决定发起始位还是8位数据还是停止位。

其中,每1秒发送一次数据? (每秒都能发送)

当发送到bit3时? 我飞快的改变bit4,bit5,bit6,bit7,使其为1,那我们究竟发0 还是发1呢?

答案是肯定发送0 ?因为在启动发送的时候是0,即使之后变为了1,我们也只能发送启动发送的值 那就得想办法在启动发送的时候把拨码开关的值,存储下来,只发生存储的值。而不管拨码开关当前的值。

那怎么存呢? ----->D触发器 存储特性

其实就是找到发送完成的时刻。

思路:通过线性序列机,当串口完成发送时,翻转LED状态

2.2编写代码分析

输入 : data;Clk;Reset;

输出:uart_tx; LED;

首先 波特率? ---> 波特率计数器? ?

然后 每一位发送 -----> 位计数器

之后? 位发送逻辑

最后 控制LED翻转

开发流程可参考(FPGA_Vivado开发流程

代码如下

module uart_tx0(
    data,
    Clk,
    Reset,
    uart_tx,
    LED
    );
    input [7:0]data;
    input Clk;
    input Reset;
    output reg uart_tx;
    output LED;
    reg LED;
    
    parameter DELAY = 50000000-1; //1000000000/20;
    parameter MCNT_DIV = 5208 - 1; //1000000000/9600/20; 
    parameter MCNT_BIT = 10-1;
    
    reg [3:0]bit_count;
    reg en_bud;
    reg [25:0]delay_count;
    //reg [11:0]delay_cnt;
    
    //波特率计数
    reg [12:0]div_count;
    always@(posedge Clk or negedge Reset)
    if(!Reset)
        div_count <= 0;
    else if(en_bud)begin
        if(div_count >= MCNT_DIV)
            div_count <= 0;
        else
            div_count <= div_count + 1;
        end
    else
        div_count <= 0;
        
    //使能    
    always@(posedge Clk or negedge Reset)
    if(!Reset)
        en_bud <= 0;
    else if(delay_count == DELAY)
        en_bud <= 1;
    else if(div_count ==MCNT_DIV && bit_count ==  MCNT_BIT)
        en_bud <= 0;
        
    //位计数
    always@(posedge Clk or negedge Reset)
    if(!Reset)
        bit_count <= 0;
    else if(div_count == MCNT_DIV)begin
        if(bit_count == MCNT_BIT)
            bit_count <= 0;
        else
            bit_count <= bit_count + 1'd1;
    end
    
    // 1S 延时计数器
    always@(posedge Clk or negedge Reset)
    if(!Reset)
        delay_count <= 0;
    else if (delay_count == DELAY)
        delay_count <= 0;
    else
        delay_count <= delay_count + 1'd1;
        
    reg[7:0]temp_data;
    always@(posedge Clk or negedge Reset)
    if(!Reset)
       temp_data <= 0;
    else if(delay_count == DELAY)
        temp_data <= data;
    else
        temp_data <= temp_data;

    always@(posedge Clk or negedge Reset)
    if(!Reset)
        uart_tx <= 0;
    else if(en_bud==0)
        uart_tx <= 1;
    else begin
        case (bit_count)
            0 : uart_tx <= 0 ;
            1 : uart_tx <= temp_data[0];
            2 : uart_tx <= temp_data[1];
            3 : uart_tx <= temp_data[2];
            4 : uart_tx <= temp_data[3];
            5 : uart_tx <= temp_data[4];
            6 : uart_tx <= temp_data[5];
            7 : uart_tx <= temp_data[6];
            8 : uart_tx <= temp_data[7];
            9 : uart_tx <= 1; 
            default: uart_tx <= 0; 
        endcase
    end
    
    //控制LED翻转
    always@(posedge Clk or negedge Reset)
    if(!Reset)
        LED <= 0;
    else if(div_count ==MCNT_DIV && bit_count ==  MCNT_BIT)
        LED <= ~LED;
    else
        LED <= 0;

endmodule

然后编写testbench测试文件?

代码如下(在测试中我们将时间参数减小便于调试仿真)

`timescale 1ns / 1ps
module uart_tx0_tb();
    reg [7:0]data;
    reg Clk;
    reg Reset;
    wire uart_tx;
    wire LED;
    uart_tx0 uart_tx00(
        .data(data),
        .Clk(Clk),
        .Reset(Reset),
        .uart_tx(uart_tx),
        .LED(LED)
        );

    //时间计数参数调小 便于测试仿真
    defparam uart_tx0.DELAY = 500000 -1;
        
    initial Clk = 0;
    always #10 Clk = ~Clk;
    
    initial begin
        Reset = 0;
        data = 0;
        #201
        Reset = 1;
        #100
        data = 8'h57;
        #20000000;
        data = 8'h75;
        #20000000;
        $stop;
    end
    
endmodule

仿真波形如下:

对uart发送进行优化? 加入Send_GO信号??Baud_Set信号波特率设置,支持 6种,baud set为 3 位

以及发送完成信号

代码如下

module uart_tx_3(
    Clk,
    Reset_n,
    Data,
    Send_G0,
    Baud_Set,
    uart_tx,
    Tx_Done
    );
    input Clk;           //模块全局时钟 50MHz 
    input Reset_n;       //模块全局复位信号
    input [7:0]Data;     //待传输 8bit 数
    input Send_G0;        //发送使能信号
    input [2:0]Baud_Set;  //baud set 波特率设置,支持 6种,baud set为 3 位
    output reg uart_tx;   //串口发送信号输出
    output reg Tx_Done;   //发送结束信号,一个时钟周期高电平
    
    parameter MCNT_BIT = 10-1;

    //波特率
    reg [15:0]bps_DR;
    reg [15:0]div_count;  //波特率计数器
    reg [3:0]bit_count;
    reg [7:0]temp_data;
    
    always@(*)
    if(!Reset_n)
        bps_DR <= 1000000000/9600/20;
    else begin
        case(Baud_Set)
            0:bps_DR = 1000000000/4800/20;
            1:bps_DR = 1000000000/9600/20;
            2:bps_DR = 1000000000/19200/20;
            3:bps_DR = 1000000000/38400/20;
            4:bps_DR = 1000000000/57600/20;
            5:bps_DR = 1000000000/115200/20;
            default:bps_DR <= 1000000000/9600/20;  
        endcase
    end
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        div_count <= 0;
    else if (Send_G0) begin
        if(div_count == bps_DR -1)
            div_count <= 0;
        else
            div_count <= div_count + 1'd1;
    end
    else
        div_count <= 0;
        
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        bit_count <= 0;
    else if(div_count == bps_DR -1)begin
        if(bit_count == MCNT_BIT)
            bit_count <= 0;
        else
            bit_count <= bit_count + 1'd1;   
    end
    
    always@(posedge Clk )
    if(Send_G0)
        temp_data <= Data;
    else
        temp_data <= temp_data;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        uart_tx <= 0;
    else begin
        case (bit_count)
            0 : uart_tx <= 0 ;
            1 : uart_tx <= temp_data[0];
            2 : uart_tx <= temp_data[1];
            3 : uart_tx <= temp_data[2];
            4 : uart_tx <= temp_data[3];
            5 : uart_tx <= temp_data[4];
            6 : uart_tx <= temp_data[5];
            7 : uart_tx <= temp_data[6];
            8 : uart_tx <= temp_data[7];
            9 : uart_tx <= 1; 
            default: uart_tx <= uart_tx; 
        endcase
    end
    
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Tx_Done <= 0;
    else if ((bit_count == MCNT_BIT)&& (div_count == bps_DR -1))
        Tx_Done <= 1;
    else 
        Tx_Done <= 0;
    
endmodule

testbench代码:

`timescale 1ns / 1ps
module uart_tx_3_tb();
    reg [7:0]Data;
    reg Clk;
    reg Reset_n;
    reg Send_G0;
    reg [3:0]Baud_Set;
    wire uart_tx;
    wire Tx_Done;
    uart_tx_3 uart_tx_31(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Data(Data),
        .Send_G0(Send_G0),
        .Baud_Set(Baud_Set),
        .uart_tx(uart_tx),
        .Tx_Done(Tx_Done)
        );
        
        
    initial Clk =1;
    always#10 Clk = ~Clk;
    initial begin
        Reset_n = 0;
        Data =0;
        Send_G0 = 0;
        Baud_Set = 5;
        #201
        Reset_n = 1;
        #100
        
        Data = 8'h57;
        Send_G0 = 1;
        #20;
        @(posedge Tx_Done);
        Send_G0 = 0;
        #20000;
        
        Data = 8'h75;
        Send_G0 = 1;
        #20;
        @(posedge Tx_Done);
        Send_G0 = 0;
        #20000;
        $stop;
    end 
endmodule

其中在testbench中? 新语法 @(posedge TX_done),死循环,一直等待TX_done的到来,再执行下一语句

仿真结果

3,实现UART-接收控制器

3.1UART串口接收逻辑分析

把接收到的8位串行数据还原出8位并行数据

uart串口来说 u有可能不是一个系统 ,两者之间仅通过一根信号线进行通信,那接收方如何恰好接收到数据呢?uart作为异步通信协议,没有同步信号来实现这些功能的。所以在通信之前,收发双方必须提前约定好各种参数(波特率,数据位个数,校验位,停止位位宽等等)

要点1:对于串口接收来说,我们应该怎么样从串行数据中准确的获取到每一位数据?要点1:对于串口接收来说,我们应该怎么样从串行数据中准确的获取到每一位数据?

每一位数据的中点

?当对于数据线上的每一位进行采样时,一般情 况下认为每一位数据的中间点是最稳定的。因此一般应用中,采集中间时刻时的电平即认为 是此位数据的电平,如下图所示。

但是在实际工业应用中,现场往往有非常强的电磁干扰,只采样一次就作为该数据的电 平状态是不可靠的。很有可能恰好采集到被干扰的信号而导致结果出错,因此这里提出以下 改进型的单bit数据接收方式示意图,使用多次采样求概率的方式进行状态判定,如下图所 示。

?在上图中,将每一位数据再平均分成了16小段。对于Bit_x这一位数据,考虑到数据 在刚刚发生变化和即将发生变化的这一时期,数据极有可能不稳定的(用深灰色标出的两 段),在这两个时间段采集数据,很有可能得到错误的结果,因此判定这两段时间的电平无 效,采集时直接忽略。而中间这一时间段(用浅灰色标出),数据本身是比较稳定的,一般都代表了正确的结果。也就是前面提到的中间测量方式,但是也不排除该段数据受强电磁干 扰而出现错误的电平脉冲。因此对这一段电平,进行多次采样,并求高低电平发生的概率, 6 次采集结果中,取出现次数多的电平作为采样结果。例如,采样 6 次的结果分别为 1/1/1/1/0/1/,则取电平结果为 1,若为0/0/1/0/0/0,,则取电平结果为0,当 6次采样结果中1 和0各占一半(各3次),则可判断当前通信线路环境非常恶劣,数据不具有可靠性,不进行处理。

FPGA中有专门的边沿检测电路:

检测到uart信号下降沿开始? ?

在什么时候停止呢?

使用位计数器

3.2编写代码分析?

//波特率计数器逻辑

//UART信号边沿检测逻辑

//波特率计数器使能逻辑

//位计数器逻辑

//位接收逻辑

//接收完成标志信号

单 bit 异步信号同步设计

这里串口接收的信号uart_rx相对于FPGA内部信号来说是一个异步信号,如不进行处理直接将其输入使用,容易出现时序违例导致亚稳态。uart_rx信号由外界输入得到,在什么时候得到,我们无法预知和控制,?而其他信号都是由时钟信号驱动,所以uart_rx信号有可能在Clk信号上升沿很近的位置发生变化,甚至同步发生变化,就会导致D触发器无法正确存储D端口数据,无法正确有效的判断存储这个时刻的uart状态,就会引起D触发器输出震荡---->从而产生亚稳态。

因此这里就需要先将信号同步到 FPGA的时钟域内才可以供后续模块使用,常见的同步方法即使用两级触发器,也就是使 用触发器对信号打两拍的方式进行与系统时钟进行同步,参考电路即如下图所示。其中 uart_rx 为异步串口输入信号,uart_rx_sync2为同步后的信号。

设计代码如下:

module uart_rx_0(
    Clk,
    Reset_n,
    uart_rx,
    Baud_Set,
    Data,
    Rx_done
    );
    input Clk;
    input Reset_n;
    input uart_rx;
    input [2:0]Baud_Set;
    output reg [7:0]Data;
    output reg Rx_done;
    
    reg [1:0]uart_rx_r;
    wire posedge_uart_rx;
    wire negedge_uart_rx;   
    reg [9:0]Bps_DR; 
    reg [9:0]div_count;
    reg [7:0]bit_count;  //每位分成16次采样
    reg RX_EN;    //检测到下降沿----发送使能   
    reg uart_rx_sync1;
    reg uart_rx_sync2;
    reg [2:0]r_data[7:0];
    reg [2:0]start_bit;
    reg [2:0]stop_bit;
    
    //异步信号转同步
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        uart_rx_sync1 <= 0;
        uart_rx_sync2 <= 0;
    end
    else begin
        uart_rx_sync1 <= uart_rx;
        uart_rx_sync2 <= uart_rx_sync1;
    end
         
              
    //边沿检测                                 
    always@(posedge Clk)begin
         uart_rx_r[0] <= uart_rx_sync2;
         uart_rx_r[1] <= uart_rx_r[0];
    end
    assign posedge_uart_rx = (uart_rx_r == 2'b01);
    assign negedge_uart_rx = (uart_rx_r == 2'b10);
    //波特率设置
    always@(*)
        case(Baud_Set)
            0: Bps_DR = 1000000000/4800/20/16 - 1;
            1: Bps_DR = 1000000000/9600/20/16 - 1;
            2: Bps_DR = 1000000000/19200/20/16 - 1;
            3: Bps_DR = 1000000000/38400/20/16 - 1;
            4: Bps_DR = 1000000000/57600/20/16 - 1;
            5: Bps_DR = 1000000000/115200/20/16 - 1;
            default: Bps_DR = 1000000000/9600/20/16 - 1;
        endcase
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        RX_EN <= 0;
    else if(negedge_uart_rx)
        RX_EN <= 1;
//    else if(Rx_done || (stop_bit >= 4))
    else if((div_count == 1)&&(bit_count ==0)&&(uart_rx_sync2 == 1))
        RX_EN <= 0;
    else if(Rx_done)
        RX_EN <= 0; 
           
    
    //分频计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        div_count <= 0;
    else if(RX_EN) begin
        if(div_count == Bps_DR)
             div_count <= 0;
        else
            div_count <= div_count+ 1'b1;
    end
    else
        div_count <= 0;
     //位计数器   
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)  
        bit_count <= 0;
    else if(RX_EN) begin
        if(div_count == 1)begin
        //if(bit_count == 160 -1 | bit_count == 12 && (start_bit >2))
            if(bit_count == 159 )
                bit_count <= 0;
            else
                bit_count <= bit_count + 1'b1; 
            end 
    end
    else
        bit_count <= bit_count;
        
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        start_bit <= 0;
        r_data[0] <= 0;
        r_data[1]<= 0;
        r_data[2]<= 0;
        r_data[3]<= 0;
        r_data[4]<= 0;
        r_data[5]<= 0;
        r_data[6]<= 0;
        r_data[7]<= 0;
        stop_bit <= 0;
        end
    else if(div_count == 1)begin
        case(bit_count)
            0:begin
                start_bit <= 0;
                r_data[0] <= 0;
                r_data[1]<= 0;
                r_data[2]<= 0;
                r_data[3]<= 0;
                r_data[4]<= 0;
                r_data[5]<= 0;
                r_data[6]<= 0;
                r_data[7]<= 0;
                stop_bit <= 0;
            end
            6,7,8,9,10,11 :start_bit <= start_bit + uart_rx_sync2;
            22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx_sync2;
            38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx_sync2;
            54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx_sync2;
            70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx_sync2;
            86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx_sync2;
            102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx_sync2;
            118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx_sync2;
            134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx_sync2;
            150,151,152,153,154,155:stop_bit <= stop_bit + uart_rx_sync2;
            default:begin
                start_bit <= start_bit;
                r_data[0] <= r_data[0];
                r_data[1] <= r_data[1];
                r_data[2] <= r_data[2];
                r_data[3] <= r_data[3];
                r_data[4] <= r_data[4];
                r_data[5] <= r_data[5];
                r_data[6] <= r_data[6];
                r_data[7] <= r_data[7];
                stop_bit  <= stop_bit ;
            end
       endcase    
    end
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Data <= 0;
    else if((div_count == 1) && (bit_count == 159)) begin
        Data[0] <= (r_data[0] >= 4) ? 1 : 0;
        Data[1] <= (r_data[1] >= 4) ? 1 : 0;
        Data[2] <= (r_data[2] >= 4) ? 1 : 0;
        Data[3] <= (r_data[3] >= 4) ? 1 : 0;
        Data[4] <= (r_data[4] >= 4) ? 1 : 0;
        Data[5] <= (r_data[5] >= 4) ? 1 : 0;
        Data[6] <= (r_data[6] >= 4) ? 1 : 0;
        Data[7] <= (r_data[7] >= 4) ? 1 : 0;
    end  
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)  
        Rx_done <= 0;
    else if((div_count == 1) && (bit_count == 159)) 
        Rx_done <= 1;
    else
        Rx_done <= 0;
        
       
endmodule

编写测试代码:

`timescale 1ns / 1ps
module uart_rx_0_tb();
    reg Clk;
    reg Reset_n;
    reg uart_rx;
    reg [2:0]Baud_Set;
    wire [7:0]Data;
    wire Rx_done;

    uart_rx_0 uart_rx_0(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .uart_rx(uart_rx),
        .Baud_Set(Baud_Set),
        .Data(Data),
        .Rx_done(Rx_done)
        );
        
    initial Clk = 1;
    always #10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        uart_rx = 1;
        #201;
        Reset_n = 1;
        Baud_Set = 5;
        #200;
        uart_tx(8'b01110101);
        #90000;
        uart_tx(8'b10111010);
        #90000;
        uart_tx(8'b11100110);
        #90000;
        #2000;
        $stop;
    end
    
    task uart_tx;
        input [7:0]tx_data;
        begin
            uart_rx = 1;
            #20;
            uart_rx = 0;
            #8680;
            uart_rx = tx_data[0];
            #8680;
            uart_rx = tx_data[1];
            #8680;
            uart_rx = tx_data[2];
            #8680;
            uart_rx = tx_data[3];
            #8680;
            uart_rx = tx_data[4];
            #8680;
            uart_rx = tx_data[5];
            #8680;
            uart_rx = tx_data[6];
            #8680;
            uart_rx = tx_data[7];
            #8680;
            uart_rx = 1;
            #8680;
        end
    endtask

    
endmodule

仿真波形如下:

以上实现了Uart接收模块,然后设计顶层模块----->控制LED

代码如下:

module uart_rx_LED(
    Clk,
    Reset_n,
    uart_rx,
    LED,
    Rx_Data
    );
    
    input Clk;
    input Reset_n;
    input uart_rx;
    output reg LED;
    output Rx_Data;
    wire Rx_Done;
    uart_rx_0  uart_rx_00(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .uart_rx(uart_rx),
        .Baud_Set(5),
        .Data(Rx_Done),
        .Rx_done(Rx_Done)
        );
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        LED <= 0;
    else if(Rx_Done)
        LED <= ~LED;
endmodule

至此,uart串口发送-接收逻辑到此结束!

文章来源:https://blog.csdn.net/weixin_46897065/article/details/135586405
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。