• UART协议及串口回环


    一、异步通信的分类

    1、UART(通用异步收发器)

    一般是指元器件之间的通信,用一位低电平作为起始位,再来8位数据位后面加校验和高电平的停止位,信号一般对地电压5V、3.3V等等都可以,这就是uart通信,

    2、RS422

    后来人们发现这种通信方案传输距离不长,于是将这种电平信号改成差分信号,每个差分信号需要两条线,那rx和tx就一共需要四条线,不需要gnd了,这种通信就叫RS422,

    3、RS485

    但人们发现一般情况下发送和接收很少一起于是就掐掉一路变成一对差分,这就是RS485。

    4、Modbus

    在实践中人们又发现在这每家都通信协议都不一样很难集成到一起,于是就统一定义一下发送的协议,每个设备有一个地址,在一个网络中主机首先发送从机地址然后再发送功能码字节长度和实际数据以及CRC校验,这样不仅稳定而且还能兼容很多产品,于是modbus就诞生了,也就是说modbus是建立在uart或者422或者485之上的一种协议,其实modbus还可以建立在tcp/ip上面

    5、接口标准

    在这里插入图片描述

    二、UART协议要求

    1、空闲状态

    保持高电平

    2、起始位

    master设备由空闲状态的高电平拉为低电平,且持续一个bit数据的保持时间(跟波特率有关),用于告知slave设备准备接受数据

    3、数据位

    支持5~8bit的有效数据传输,由通信双方约定好通信的格式。先发送数据的低位,LSB表示低位,MSB表示高位
    格式:起始位,bit0~bit7,校验位,停止位

    4、校验位

    用于数据校验的,校验的方式分为奇偶校验,校验位的作用是平衡数据中高电平数据的个数是奇数个/偶数个。
    缩位异或(^A) 用于判断1的个数,0:偶数 1:奇数
    缩位同或(~^A) 用于判断0的个数,0:奇数 1:偶数
    奇校验(odd):保证数据(数据位+校验位)中奇数个逻辑高电平
    偶校验(even):保证数据中偶数个逻辑高电平
    例如:传输的数据为0100_0011
    奇校验的校验位:0
    偶校验的校验位:1

    5、停止位

    两台设备通信可能会存在小段时间的不同步情况,从而影响数据的传输结果,因此master必须保证有停止位,即数据线拉高一段时间。停止位不仅仅表示数据传输结束,也是矫正时钟同步的机会,让slave设备能够正确的识别下一轮数据的起始位。

    6、波特率

    码元:携带数据信息的信号单元(uart是单根线传输,所以码元就是一个二进制数)
    波特率:每秒钟信道传输的码元数(二进制数据),符号Baud,单位波特每秒(Bps)
    计算方法:50M时钟,周期为20ns
    (1_000_000_000/115200)/20 = 434
    传输1bit信号,在115200的波特率下,需要持续434个时钟周期

    7、比特率

    比特率:每秒钟信道传输的信息量,单位每秒比特数(bps)
    公式:比特率 = 波特率*码元(对于一次传输一个bit数据而言,波特率=比特率)

    三、汉字发送

    UART每次只能发送8bit的数据,发送汉字信息需要连续发送16bit的信息
    (一个汉字2个字节)发送的时ASCII码,需要使用软件查询汉字的ASCII码。发送汉字,先发送汉字对应16bit的ASCII码中的高位8bit,再发送低8bit

    四、串口回环

    uart_tx
    `include "param.v"
    
    module  uart_tx(       //发送数据  并串转换
        input               clk     ,
        input               rst_n   ,
        input   [1:0]       baud_sel,
        input   [7:0]       din     ,
        input               din_vld ,
        output              tx_dout ,
        output              tx_busy  //发送状态指示   
    );
    
    //信号定义
        reg     [12:0]      cnt0        ;//波特率计数器
        wire                add_cnt0    ;
        wire                end_cnt0    ;
        reg     [3:0]       cnt1        ;//bit计数器
        wire                add_cnt1    ;
        wire                end_cnt1    ;
        reg                 add_flag    ;
        
        reg     [`DATA_W+1:0] tx_data   ;
    
        reg     [12:0]      baud        ;//选择分频系数
        reg                 tx_bit      ;
    
    //计数器
    
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt0 <= 0; 
            end
            else if(add_cnt0) begin
                if(end_cnt0)
                    cnt0 <= 0; 
                else
                    cnt0 <= cnt0+1 ;
           end
        end
        assign add_cnt0 = (add_flag);
        assign end_cnt0 = add_cnt0  && cnt0 == (baud)-1 ;
    
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt1 <= 0; 
            end
            else if(add_cnt1) begin
                if(end_cnt1)
                    cnt1 <= 0; 
                else
                    cnt1 <= cnt1+1 ;
           end
        end
        assign add_cnt1 = (end_cnt0);
        assign end_cnt1 = add_cnt1  && cnt1 == (`DATA_W)+1;
    
    //add_flag  计数器使能信号
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                add_flag <= 1'b0;
            end
            else if(din_vld)begin
                add_flag <= 1'b1;
            end
            else if(end_cnt1)begin 
                add_flag <= 1'b0;
            end
        end
    
        always  @(*)begin
            case(baud_sel)
                0:baud = `BAUD_RATE_115200;
                1:baud = `BAUD_RATE_57600;
                2:baud = `BAUD_RATE_38400;
                3:baud = `BAUD_RATE_9600;
                default:baud = `BAUD_RATE_115200;
            endcase 
        end
    
    //输出
        `ifdef PARITY_ENABLE    //使能校验功能
            //reg     [`DATA_W+1:0] tx_data     ;
            wire            parity_bit  ;
            assign parity_bit = (`PARITY_TYPE == 1'b1) ? (~^din) : (^din);
            always  @(posedge clk or negedge rst_n)begin
                if(rst_n==1'b0)begin
                    tx_data <= 0;
                end
                else if(din_vld)begin   
                    //收到请求时,将 停止位 校验位 数据 起始位 拼接
                    tx_data <= {1'b1,parity_bit,din,1'b0};
                end
            end
        `else      //未使能校验功能
            //reg     [`DATA_W+1:0] tx_data     ;
            always  @(posedge clk or negedge rst_n)begin
                if(rst_n==1'b0)begin
                    tx_data <= 0;
                end
                else if(din_vld)begin   
                    //收到请求时,将 校验位 数据 起始位 拼接
                    tx_data <= {1'b1,din,1'b0};
                end
            end
        `endif 
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                tx_bit <= 1'b1;
            end
            else if(add_cnt0 && cnt0 == 1-1)begin
                tx_bit <= tx_data[cnt1];
            end
        end
    
        assign tx_busy = din_vld | add_flag;
        assign tx_dout = tx_bit;
    
    endmodule 
    
    
    • 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
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    uart_rx
    `include "param.v"
    
    module uart_rx (       //接收串行数据 串并转换
        input               clk     ,
        input               rst_n   ,
        input   [1:0]       baud_sel,
        input               rx_din  ,
        output  [7:0]       rx_dout ,
        output              rx_vld   
    );
    
    //定义信号
        reg     [12:0]      cnt0        ;//波特率计数器
        wire                add_cnt0    ;
        wire                end_cnt0    ;
        reg     [3:0]       cnt1        ;//bit计数器
        wire                add_cnt1    ;
        wire                end_cnt1    ;
        reg                 add_flag    ;
        
        reg     [12:0]      baud        ;//选择分频系数
    
        reg     [2:0]       rx_din_r    ;//同步、打拍寄存器
        wire                n_edge      ;//下降沿检测
    
        reg     [`DATA_W:0] rx_data     ;//采样数据寄存器
    
    //计数器
    
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt0 <= 0; 
            end
            else if(add_cnt0) begin
                if(end_cnt0)
                    cnt0 <= 0; 
                else
                    cnt0 <= cnt0+1 ;
           end
        end
        assign add_cnt0 = (add_flag);
        assign end_cnt0 = add_cnt0  && cnt0 == (baud)-1 ;
    
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt1 <= 0; 
            end
            else if(add_cnt1) begin
                if(end_cnt1)
                    cnt1 <= 0; 
                else
                    cnt1 <= cnt1+1 ;
           end
        end
        assign add_cnt1 = (end_cnt0);
        assign end_cnt1 = add_cnt1  && (cnt1 == (`DATA_W) || rx_data[0]);
    
    //add_flag  计数器使能信号
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                add_flag <= 1'b0;
            end
            else if(n_edge)begin
                add_flag <= 1'b1;
            end
            else if(end_cnt1)begin
                add_flag <= 1'b0;
            end
        end
    
        always  @(*)begin
            case(baud_sel)
                0:baud = `BAUD_RATE_115200;
                1:baud = `BAUD_RATE_57600;
                2:baud = `BAUD_RATE_38400;
                3:baud = `BAUD_RATE_9600;
                default:baud = `BAUD_RATE_115200;
            endcase 
        end
    
    //下降沿检测  同步打拍
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                rx_din_r <= 3'b111;
            end
            else begin
                rx_din_r <= {rx_din_r[1:0],rx_din};
                //rx_din_r[0] <= rx_din;      //同步
                //rx_din_r[1] <= rx_din_r[0]; //打拍
                //rx_din_r[2] <= rx_din_r[1]; //打拍
            end
        end
    
        assign n_edge = rx_din_r[2] & ~rx_din_r[1];
    
    //采样数据
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                rx_data <= 0;
            end
            else if(add_flag && cnt0 == (baud >> 1))begin
                //rx_data <= {rx_din,rx_data[`DATA_W:1]};//右移
                rx_data[cnt1] <= rx_din;
            end
        end
    
    //校验
        `ifdef PARITY_ENABLE       //开启校验
            wire        parity_result  ;
            assign parity_result = ^rx_data[`DATA_W:1];
            assign rx_dout = rx_data[`DATA_W-1:1];
            assign rx_vld  = end_cnt1 & ~rx_data[0] & parity_result == `PARITY_TYPE;
        `elsif          //不开启校验
            assign rx_dout = rx_data[`DATA_W:1];
            assign rx_vld  = end_cnt1 & ~rx_data[0];
        `endif 
    
    //输出
    
    //    assign rx_dout = rx_data[8:1];
    //    assign rx_vld  = end_cnt0 & cnt1 == (9)-1 & ~rx_data[0];
    
    endmodule 
    
    
    
    • 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
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    ctrl
    module control (     //缓存
        input               clk     ,
        input               rst_n   ,
        input   [7:0]       din     ,
        input               din_vld ,
        input               busy    ,
        output  [7:0]       dout    ,
        output              dout_vld    
    );
    
    //信号定义
        reg                 rd_flag     ;
        reg     [7:0]       tx_data     ;
        reg                 tx_data_vld ;
    
        wire                fifo_rdreq  ;
        wire                fifo_wrreq  ;
        wire                fifo_empty  ;
        wire                fifo_full   ;
        wire    [7:0]       fifo_qout   ;
        wire    [3:0]       fifo_usedw  ;
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                rd_flag <= 1'b0;
            end
            else if(fifo_usedw >= 8)begin
                rd_flag <= 1'b1;
            end
            else if(fifo_empty)begin 
                rd_flag <= 1'b0;
            end 
        end
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                tx_data <= 0;
                tx_data_vld <= 1'b0; 
            end
            else begin
                tx_data <= fifo_qout;
                tx_data_vld <= fifo_rdreq;
            end
        end
    
    //FIFO例化
    
        fifo u_fifo(
    	.aclr   (~rst_n     ),
    	.clock  (clk        ),
    	.data   (din        ),
    	.rdreq  (fifo_rdreq ),
    	.wrreq  (fifo_wrreq ),
    	.empty  (fifo_empty ),
    	.full   (fifo_full  ),
    	.q      (fifo_qout  ),
    	.usedw  (fifo_usedw )
        );
        
        assign fifo_wrreq = din_vld & ~fifo_full;
        assign fifo_rdreq = rd_flag & ~busy;  //发送模块 非忙状态下 给发送请求
        
    //    assign dout_vld = fifo_rdreq;
    //    assign dout = fifo_qout;
    
        assign dout_vld = tx_data_vld;  //时序逻辑输出
        assign dout = tx_data;
    
    endmodule 
    
    
    
    • 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
    top
    module top(
        input               clk     ,
        input               rst_n   ,
        
        input               uart_rxd,
        output              uart_txd    
    );
    
    //信号定义
    
        wire        [7:0]       rx_byte     ;
        wire                    rx_byte_vld ;
        wire        [7:0]       tx_byte     ;
        wire                    tx_byte_vld ;
        wire                    tx_busy     ;
        wire        [1:0]       baud_sel    ;
        
        assign baud_sel = 2'd0; //选择波特率
    
    //模块例化
        
        uart_rx u_rx(       //接收串行数据 串并转换
        /*input               */.clk     (clk           ),
        /*input               */.rst_n   (rst_n         ),
        /*input   [1:0]       */.baud_sel(baud_sel      ),
        /*input               */.rx_din  (uart_rxd      ),
        /*output  [7:0]       */.rx_dout (rx_byte       ),
        /*output              */.rx_vld  (rx_byte_vld   ) 
        );
    
        control u_ctrl(     //缓存
        /*input               */.clk     (clk           ),
        /*input               */.rst_n   (rst_n         ),
        /*input   [7:0]       */.din     (rx_byte       ),
        /*input               */.din_vld (rx_byte_vld   ),
        /*input               */.busy    (tx_busy       ),
        /*output  [7:0]       */.dout    (tx_byte       ),
        /*output              */.dout_vld(tx_byte_vld   )    
        );
    
        uart_tx u_tx(       //发送数据  并串转换
        /*input               */.clk     (clk           ),
        /*input               */.rst_n   (rst_n         ),
        /*input   [1:0]       */.baud_sel(baud_sel      ),
        /*input   [7:0]       */.din     (tx_byte       ),
        /*input               */.din_vld (tx_byte_vld   ),
        /*output              */.tx_dout (uart_txd      ),
        /*output              */.tx_busy (tx_busy       ) //发送状态指示   
        );
    
    endmodule 
    
    
    
    • 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

    五、参考

    UART的fpga实现

    六、源码

    https://github.com/IvanXiang/FPGA_UART

  • 相关阅读:
    美国夏威夷州禁止某些产品中使用PFAS
    【Swift 60秒】58 - Capturing values
    公平锁与非公平锁的核心原理
    Promise 解决阻塞式同步,将异步变为同步
    Centos - 磁盘快照
    基于vue+node.js+zigbee的蔬菜大棚温湿度物联网监控系统
    debian安装Elasticsearch 8单节点
    【客观赋权法1】熵权法(MATLAB全代码)
    harbor-db restarting问题
    嘉泰实业举行“互联网金融知识社区”“安全理财风险讲座”等活动
  • 原文地址:https://blog.csdn.net/xyf_fate/article/details/126858400