UART

1、理论部分

  • UART全称为通用异步收发传输器(Universal Asynchronous Receiver/Transmitter)。异步的意思是指发送模块和接收模块之间没有同步时钟来同步数据,因此需要发送和接收模块提前约定好传输的速率。特点是通信线路简单,但传输速率低、传输距离有限。

  • 数据传输速率以波特(baud)为单位,指每秒钟能传输的bit的个数。波特率115200即代表每秒钟最多传115200bit,则每bit需要维持的时间为1/115200s≈8.7us

  • 常见波特率有:1200、2400、4800、19200、38400、57600等,最常用的是9600和115200。因为起始位、停止位的存在,实际传输速率不等于波特率。

  • 包格式

  • 空闲状态总线处于高电平,当数据开始发送时,发送端将总线拉低作为起始位。然后发送数据位,数据位位宽可变。先发送最低位再发送最高位。

  • 停止位是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢

2、思路

发送模块

输入:clk、rst_n、tx_data[7:0]、data_valid

输出: tx

使用计数器按照约定的波特率产生脉冲,代码中baud_pulse相当于分频后的时钟上升沿。在data_valid为高电平时,将输入的数据载入缓存寄存器。将baud_pulse作为状态机的跳变条件,在不同的寄存器状态分别对信号进行描述。
代码中baud_valid相当于一个门控时钟,可以使模块只在数据信号有效时工作,降低功耗。

接收模块

待定

3、实现

发送模块

代码

module uart_tx
#(
    parameter   CLK_FRE = 50 ,
    parameter   BAUD_RATE = 115200 
)(
    input           clk     ,
    input           rst_n   ,
    input   [7:0]   tx_data ,
    input           data_valid  ,
    output  reg        tx      
);
    //define for count
    localparam  CYCLE = CLK_FRE * 1000000 / BAUD_RATE   ;
    reg         baud_valid  ;
    reg [15:0]  baud_cnt    ;
    reg         baud_pulse  ;
    //define for FSM
    localparam  IDLE        = 2'b00  ;
    localparam  START       = 2'b01 ;
    localparam  SEND_BYTE   = 2'b10 ;
    localparam  STOP        = 2'b11 ;
    reg [1:0]   state,next_state   ;
    reg [2:0]   bit_cnt         ;
    reg [7:0]   tx_data_temp    ;

    //baud rate counter
    always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                baud_cnt <= 16'd0;
            else if(!baud_valid)
                baud_cnt <= 16'd0;
            else if(baud_cnt == CYCLE - 1)
                baud_cnt <= 16'd0;
            else
                baud_cnt <= baud_cnt + 1;
        end
    // registe the posedge  of baud pulse
    always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                baud_pulse <= 0;
            else if(baud_cnt == CYCLE/2 -1)
                baud_pulse <= 1'b1 ;
            else 
                baud_pulse <= 1'b0 ;
        end

    //FSM
    always @(*)
        begin
            case (state)
                IDLE : next_state = START       ;
                START: next_state = SEND_BYTE   ;
                SEND_BYTE   :   next_state = (bit_cnt == 3'd7) ? STOP : SEND_BYTE   ;
                STOP : next_state = IDLE        ;
                default: next_state = IDLE      ;
            endcase
        end

    always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                state <= IDLE   ;
            else if(!baud_valid)
                state <= IDLE   ;
            else if(baud_valid && baud_pulse)
                state <= next_state ;
        end
    
    always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                begin
                    baud_valid <= 1'b0 ;
                    tx_data_temp <= 8'd0;
                    tx  <=  1'b1 ;
                end
            else
                begin
                  case (state)
                      IDLE :
                          begin
                              tx      <= 1'b1 ;
                              bit_cnt <= 3'd0 ;
                              if(data_valid)
                                  begin
                                      tx_data_temp <= tx_data ;
                                      baud_valid   <= 1'b1    ;
                                  end
                          end
                      START:
                          begin
                          if(baud_pulse)
                              tx <= 1'b0;
                          end
                      SEND_BYTE : 
                              begin
                              if(baud_pulse)
                                  begin
                                      bit_cnt <= bit_cnt + 1      ;
                                      tx      <= tx_data_temp[0]  ;
                                      tx_data_temp <= {1'b0,tx_data_temp[7:1]};
                                      //tx_data_temp <= {tx_data_temp[6:0],1'b0};
                                  end
                          end
                      STOP :
                          begin
                            if(baud_pulse)
                                begin
                                    tx <= 1'b1;
                                    baud_valid <= 1'b0;
                                end
                          end
                      default: ;
                  endcase
                end
        end
endmodule

仿真结果

测试用例8’b01100110 8‘b01011010

posted @   骑猪上树的少年  阅读(451)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
回到顶部
点击右上角即可分享
微信分享提示

目录导航