URAT(通用异步收发串口)

一、uart概念

uart是一种异步收发传输器,在发送数据通过将并行数据转换成串行数据进行传输,在接收数据时将串行数据转换成并行数据

串行通信分为同步串行通信和异步串行通信。同步串行通信即需要时钟的参与,通信双方需要在同一时钟的控制下,同步传输数据;异步串行通信则不需要时钟的干预,通信双方使用各种的时钟来控制数据的发送和接收。

uart属于异步串行通信,即没有时钟信号来同步或验证从发送器发送并由接收器接收的数据,这就要求发送器和接收器必须事先就时序参数达成一致。

特点:

(1)串口通信的信号线只需要两条线就可以完成,TX和RX ,TX为发送端 RX为接收端。

(2) 传输速率较低,传输距离短;

(3)传输速率以波特率为准;
(4) 通过奇偶校验对传输数据进行检测,减少数据错误概率;
      
(5)UART分为协议层和 物理层;协议层包括数据格式和传输速率等。

二、物理层

1、接口标准:目前使用的RS232标准。

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

三、uart协议层

1. 传输速率
1.1 串口通信的速率用波特率表示。
波特率(Baud):是指从一设备发到另一设备的波特率,即每秒钟可以通信的数据比特个数。 
1.2 常用的波特率有 9600 19200 38400 57600 以及 115200 等。
1.3 波特率和比特率
波特率:(码元速率)每秒钟传送的码元(符号)数量,单位 baud (波特)
比特率:(传信率)即每秒传输的比特数,是从二进制信 息的角度来定义的, 其单位是 b/s ( bit/s bps。 故:比特率= 波特率 *log2N b/s )。
例子:以四进制数据为例,码元是: 00 01 10 11 ,四 种状态之一,每种状态含有2 2 进数;
此时 2 比特 =1 波特。
2. 数据格式
a. 空闲位,持续的高电平, 空闲位 持续时间越长,两个数据帧间隔也越长,单位时间传输的 数据就越少。

b.起始位,数据线从高变低,低有效为0,数据传输开始。

c.数据位,起始位传输之后便是数据位开始,一般为8位,传输时低位(LSB) 在前,高位(MSB)在后。

d.校验位,是用来验证数据的正确性,在偶校验中,因为奇偶校验位会被相应的置 1 或 0(一般是最高位或最低位),所以数据会被改变以使得所有传送的数位(含字符的各数位和校验位)中“1”的个数为偶数;在奇校验中,所有传送的数位(含字符的各数位和校验位)中“1”的个数为奇数。奇偶校验可以用于接受方检查传输是否发送生错误,如果某一字节中“1”的个数发生了错误,那么这个字节在传输中一定有错误发生。如果奇偶校验是正确的,那么要么没有发生错误, 要么发生了偶数个的错误。如果用户选择数据长度为 8 位,则因为没有多余的比特可被用来作为奇偶校验位,因此就叫做“无奇偶校验(Non)

e.停止位,停止位高有效为1,他表示这一个个字节传输结束。

帧:从起始位开始到停止位结束的时间间隔称之为一帧。因为UART是一个异步协议,每一帧的开头可以用跳变沿来同 步,但是停止位只能通过波特率来计算相对位置,如果在停止位 的位置识别到一个低电平,则会产生帧错误。在通讯过程中,为了减少波特率的误差导致的问题,可以设置不同的停止位长度来适配。

四、系统设计

系统框架图

说明:

在设计本项目时,将模块划分为:顶层模块、数据发送模块、数据接收模块、控制模块。

顶层模块:

module uart_top#(
    parameter           CHECK_BIT   =   "None"      ,   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    parameter           BPS         =   115200      ,   //UART波特率
    parameter           CLK         =   50000000        //工作时钟
    )(
    input               uart_clk        ,   //50m
    input               rst_n           ,

    input               uart_rxd        ,
    output              uart_txd    
);

    wire    [7:0]   rx_data         ;
    wire            rx_data_vld     ;
    wire    [7:0]   tx_data         ;
    wire            tx_data_vld     ;
    wire            tx_ready        ;

    uart_tx #(
        .CHECK_BIT  ("None"  ),   //"None" 偶校验,"Odd" 奇校验,"Even" 偶校验
        .BPS        (115200        ),   //UART波特率
        .CLK        (50000000        )    //工作时钟
    )uart_tx_inst(
        /* input                */.clk          (uart_clk       ),
        /* input                */.rst_n        (rst_n          ),
        /* input       [7:0]    */.tx_din       (rx_data        ),
        /* input                */.tx_din_vld   (1'b1           ),
        /* output               */.ready        (tx_ready       ),
        /* output  reg          */.tx           (uart_txd       )
    );

    uart_rx #(
        .CHECK_BIT  ("None"  ),   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
        .BPS        (115200        ),   //UART波特率
        .CLK        (50000000        )    //工作时钟
    )uart_rx_inst(
        /* input                */.clk          (uart_clk       ),
        /* input                */.rst_n        (rst_n          ),
        /* output      [7:0]    */.rx_din       (rx_data        ),
        /* output               */.rx_din_vld  (rx_data_vld    ),
        /* output               */.ready        (               ),
        /* input                */.rx           (uart_rxd       )
    );

endmodule

数据发送模块:

module uart_tx#(
    parameter           CHECK_BIT   =   "None"      ,   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    parameter           BPS         =   115200      ,   //UART波特率
    parameter           CLK         =   50000000        //工作时钟
    )( 
    input				clk		    ,
    input				rst_n	    ,
    input		[7:0]	tx_din	    ,
    input		    	tx_din_vld  ,
    output              ready       ,
    output    reg       tx
);								 

    localparam  IDLE   = 0,
                START  = 1,
                DATA   = 2,
                CHECK  = 3,
                STOP   = 4;
    // parameter CHECK_BIT = "NONE";//“NONE”无校验
    // parameter BPS = 115200      ,
    //           CLK = 50000000    ,
    parameter         BPS_MAX = CLK/BPS ;

    reg			[12:0]	cnt_1bit	   	;
    wire				add_cnt_1bit	;
    wire				end_cnt_1bit	;

    reg			[3:0]	cnt_bit	        ;
    wire				add_cnt_bit     ;
    wire				end_cnt_bit     ;
    reg         [3:0]   bit_max         ;
    
    reg 	    [2:0]	state_c     ;
	reg 	    [2:0]	state_n     ;

    reg         [7:0]   tx_din_r   ; //输入寄存

    wire            idle2start  ;
    wire            start2data  ;
    wire            data2stop   ;
    wire            data2check  ;
    wire            check2stop  ;
    wire            stop2idle   ;

//****************************************************************
//--            状态机
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(state_c)
            IDLE  : if(idle2start)
                        state_n = START;
                    else
                        state_n = state_c;
            START : if(start2data)
                        state_n = DATA ;
                    else
                        state_n = state_c;
            DATA  : if(data2stop)
                        state_n = STOP;
                    else if(data2check)
                        state_n = CHECK;
                    else
                        state_n = state_c;
            CHECK : if(check2stop)
                        state_n = STOP;
                    else
                        state_n = state_c;
            STOP  : if(stop2idle)
                        state_n = IDLE;
                    else
                        state_n = state_c;
            default : ;
        endcase
    end

    assign       idle2start  = tx_din_vld && (state_c == IDLE );
    assign       start2data  = end_cnt_bit && (state_c == START);
    assign       data2stop   = end_cnt_bit && (state_c == DATA) && CHECK_BIT == "NONE";
    assign       data2check  = end_cnt_bit && (state_c == DATA);
    assign       check2stop  = end_cnt_bit && (state_c == CHECK);
    assign       stop2idle   = end_cnt_bit && (state_c == STOP); 

//****************************************************************
//--            计数器
//****************************************************************
//波特率计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_1bit <= 'd0;
        end 
        else if(add_cnt_1bit)begin 
            if(end_cnt_1bit)begin 
                cnt_1bit <= 'd0;
            end
            else begin 
                cnt_1bit <= cnt_1bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_1bit = state_c != IDLE;
    assign end_cnt_1bit = add_cnt_1bit && cnt_1bit == BPS_MAX - 1;

//比特计数器  
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = end_cnt_1bit;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1;

    always @(*)begin 
        case (state_c)
                IDLE    : bit_max = 1;
                START   : bit_max = 1;
                DATA    : bit_max = 8;
                CHECK   : bit_max = 1;
                STOP    : bit_max = 1;
                default : bit_max = 1;
        endcase
    end
//****************************************************************
//--        数据寄存
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_din_r <= 'd0;
        end 
        else if(tx_din_vld)begin 
            tx_din_r <= tx_din;
        end 
    end

    wire check_vld;

    // assign check_vld = ~^tx_din_r;//奇校验,缩位同或
    // assign check_vld = ^tx_din_r ;//偶校验,缩位异或

    assign check_vld = (CHECK_BIT == "Odd") ? ~^tx_din_r : ^tx_din_r;
//****************************************************************
//--        串口时序
//****************************************************************
    always @(*)begin 
        case (state_c)
                IDLE  : tx = 1;
                START : tx = 0;
                DATA  : if(tx_din_r[cnt_bit])
                            tx = 1;
                        else
                            tx = 0;
                CHECK : tx = check_vld;
                STOP  : tx = 1;
            default : tx = 1;
        endcase 
    end

    assign ready = state_c == IDLE;

endmodule

数据接收模块:

module uart_rx#(
    parameter           CHECK_BIT   =   "None"      ,   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    parameter           BPS         =   115200      ,   //UART波特率
    parameter           CLK         =   50000000        //工作时钟
    )( 
    input				clk		    ,
    input				rst_n	    ,
    input               rx          ,
    output		[7:0]	rx_din	    ,
    output		    	rx_din_vld  ,
    output              ready       
);								 
    localparam  IDLE   = 0,
                START  = 1,
                DATA   = 2,
                CHECK  = 3,
                STOP   = 4;
    //parameter CHECK_BIT = "NONE";
    // parameter BPS = 115200      ,
    //           CLK = 50000000    ,
    parameter   BPS_MAX = CLK/BPS ;

    

    reg			[12:0]	cnt_1bit	   	;
    wire				add_cnt_1bit	;
    wire				end_cnt_1bit	;

    reg			[3:0]	cnt_bit	    ;
    wire				add_cnt_bit ;
    wire				end_cnt_bit ;
    reg         [3:0]   bit_max     ;
    
    reg 	    [2:0]	state_c     ;
	reg 	    [2:0]	state_n     ;

    // reg         [7:0]   tx_din_r   ;
    reg         [7:0]   rx_temp;//数据寄存

    wire            idle2start  ;
    wire            start2data  ;
    wire            data2stop   ;
    wire            data2check  ;
    wire            check2stop  ;
    wire            stop2idle   ;

//****************************************************************
//--        下降沿
//****************************************************************
    reg     rx_1    ;
    reg     rx_2    ;
    wire    rx_nege ;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_1 <= 1;
            rx_2 <= 1;
        end 
        else begin 
            rx_1 <= rx;
            rx_2 <= rx_1;//同步,打拍
        end 
    end
    assign rx_nege = !rx_1 && rx_2;

//****************************************************************
//--            状态机
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(state_c)
            IDLE  : if(idle2start)
                        state_n = START;
                    else
                        state_n = state_c;
            START : if(start2data)
                        state_n = DATA ;
                    else
                        state_n = state_c;
            DATA  : if(data2stop)
                        state_n = STOP;//没有校验就直接跳转到STOP状态
                    else if(data2check)
                        state_n = CHECK;
                    else
                        state_n = state_c;
            CHECK : if(check2stop)
                        state_n = STOP;
                    else
                        state_n = state_c;
            STOP  : if(stop2idle)
                        state_n = IDLE;
                    else
                        state_n = state_c;
            default : ;
        endcase
    end

    assign       idle2start  = rx_nege && (state_c == IDLE );
    assign       start2data  = end_cnt_bit && (state_c == START);
    assign       data2stop   = end_cnt_bit && (state_c == DATA) && CHECK_BIT == "NONE";
    assign       data2check  = end_cnt_bit && (state_c == DATA);
    assign       check2stop  = end_cnt_bit && (state_c == CHECK);
    assign       stop2idle   = end_cnt_bit && (state_c == STOP); 
    
//****************************************************************
//--            计数器
//****************************************************************
//波特率计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_1bit <= 'd0;
        end 
        else if(add_cnt_1bit)begin 
            if(end_cnt_1bit)begin 
                cnt_1bit <= 'd0;
            end
            else begin 
                cnt_1bit <= cnt_1bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_1bit = state_c != IDLE;
    assign end_cnt_1bit = add_cnt_1bit && cnt_1bit == BPS_MAX - 1;

//比特计数器  
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = end_cnt_1bit;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1;

    always @(*)begin 
        case (state_c)
                IDLE    : bit_max = 1;
                START   : bit_max = 1;
                DATA    : bit_max = 8;
                CHECK   : bit_max = 1;
                STOP    : bit_max = 1;
                default : bit_max = 1;
        endcase
    end

    wire        check_vld   ;//接受到的校验值
    reg         check_rev   ;//计算出的校验值

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            check_rev <= 'd0;
        end 
        else if(state_c == CHECK && (cnt_1bit == BPS_MAX >> 1))begin 
            check_rev <= rx_1;
        end 
    end

    assign check_vld = (CHECK_BIT == "Odd") ? ~^rx_din : ^rx_din;
//****************************************************************
//--        串口时序
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_temp <= 'd0;
        end 
        else if(state_c == DATA && (cnt_1bit == BPS_MAX >> 1))begin //(波特率计数器)中间时刻采样一次,因为最稳定,边沿采样不稳定因为还在变化 
            rx_temp[cnt_bit] <= rx_1;   //移位寄存器,传输顺序LSB
        end 
        else begin
            rx_temp = rx_temp;
        end
    end

    // assign rx_din_vld = data2stop;//停止位和空闲位都可以将有效信号拉高
    assign rx_din = rx_temp;
    assign rx_din_vld = (CHECK_BIT == "NONE") ? data2stop      //无校验方式输出数据有效信号
                        :   (check2stop && (check_vld == check_rev)) ? 1//有校验方式时,当计算的校验值与接受的校验值相同时,则输出数据有效信号,不同则不输出数据有效信号
                        :   0;

    assign ready = state_c == IDLE;

endmodule

测试文件:

module uart_tx_tb();

//激励信号定义 
    reg				tb_clk  	;
    reg				tb_rst_n	;

//输出信号定义	 
    reg     [7:0]   tx_din      ;
    reg             tx_din_vld  ;
    reg     [7:0]   rx_din      ;
    reg             rx_din_vld  ;
    wire            ready       ;

//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   

//模块例化
    uart_tx #(
        .CHECK_BIT("Odd"    ),
        .BPS      (115200   ),
        .CLK      (50000000 )
    )
    u_uart_tx( 
    /*input				    */.clk        (tb_clk       ),
    /*input				    */.rst_n      (tb_rst_n     ),
    /*input		[7:0]	    */.tx_din     (tx_din       ),
    /*input		    	    */.tx_din_vld (tx_din_vld   ),
    /*output                */.ready      (ready        ),
    /*output    reg         */.tx         (rx           )
);

    uart_rx #(
        .CHECK_BIT("Odd"    ),
        .BPS      (115200    ),
        .CLK      (50000000  )
    )
    u_uart_rx( 
    /*input				    */.clk        (tb_clk       ),
    /*input				    */.rst_n      (tb_rst_n     ),
    /*output		[7:0]	*/.rx_din     (             ),
    /*output		    	*/.rx_din_vld (             ),
    /*output                */.ready      (             ),
    /*output                */.rx         (rx           )  
);

//产生时钟
    initial 		tb_clk = 1'b0;
    always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;

//产生激励
    initial  begin 
        tb_rst_n = 1'b1;
        tb_rst_n = 1'b0;
        #(CLOCK_CYCLE*2);
        tb_rst_n = 1'b1;
    end

    initial begin
        tx_din      = 0;
        tx_din_vld  = 0;
        #(CLOCK_CYCLE*5);

        repeat(8) begin
            tx_din_vld = 1;
            tx_din = {$random};
            #20;
            tx_din_vld = 0;
            wait(ready == 1);
            #20;
        end
    end

endmodule 

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值