UART协议以及verilog实现

uart介绍

UART (Universal Asynchronous Receiver/Transmitter) 是一种常用的串行通讯协议,用于数据在电子设备之间的传输和通讯。它是一种异步数据传输协议,在通讯时不需要发送方和接收方之间的时钟信号同步。

uart的工作过程

UART 协议通常使用两条线路进行通讯,一条用于数据传输(TX),一条用于数据接收(RX)。uart的传输格式如下:(TX跟RX均为一位)

 空闲状态:空闲状态时,uart传输位始终保持高电平状态

 起始位:uart传输位拉低一个低电平并且持续一个周期,传输开始

 数据传输状态:uart实际要传输的数据位数,可以是 5、6、7 或 8 位。

 奇偶校验状态:是可选的,可以选择奇校验还是偶校验,用于数据的验证,通常为 0 (即不使用奇偶校验)或 1 位。

      奇校验:数据1个数为奇时,校验为0,反之为1;

      偶校验:数据0个数为奇时,校验为0,反之为1;

停止位状态:用于标识数据传输的结束位置,通常为 1 或 2 位。uart传输位恢复高电平

uart的verilog实现 

本次的实验代码使用的数据传输位数为8位,奇偶校验位数为1位,停止位为1位

uart_tx模块

uart_tx模块主要是负责将要传输的八位数据进行并串转化,输出八个单bit数据,优先输出高位uart的发送端模块分为空闲状态,开始状态,传输数据状态,奇偶校验状态和停止模块。状态图如下:

verilog代码如下: 

module uart_tx(
    input            sysclk ,
    input            rst_n  ,
    input      [7:0] tx_data,
    input            tx_en  ,
    output reg       tx     ,
    output reg       tx_done
);

parameter  CLK_FREQ  = 50000000         ;            //
parameter  UART_BPS  = 9600             ;                
localparam BPS_CNT   = CLK_FREQ/UART_BPS;//每个数据位占用的周期 
parameter  EVEN_ODD_CHECK = 1           ;

parameter IDLE =5'b00001;
parameter START=5'b00010;
parameter DATA =5'b00100;
parameter CHECK=5'b01000;
parameter DONE =5'b10000;

reg [4:0]  state      ;
reg        tx_flag    ;//数据正在传输
reg [3:0]  tx_cnt     ;//数据传输位计数器,
reg [15:0] data_cnt   ;
reg check_flag;

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
    data_cnt<=16'd0        ;
  else if(data_cnt==BPS_CNT)
    data_cnt<=16'd0        ;
  else if(tx_en==1'b1)
    data_cnt<=data_cnt+1'b1;
  else
    data_cnt<=data_cnt     ;    
end

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
    tx_cnt<=4'd0       ;
  else if(tx_cnt==4'd11)
    tx_cnt<=4'd0       ;
  else if ((tx_en==1'b1)&&(data_cnt==BPS_CNT))
    tx_cnt<=tx_cnt+1'b1;
  else
    tx_cnt<=tx_cnt     ;    
    end

always @(posedge sysclk or negedge rst_n ) 
    if(rst_n==1'b0) begin
        state<=IDLE;
    end
    else  
    case (state)
        IDLE: begin
          if((tx_en==1'b1)&&(tx_cnt==4'd1))
            state<=START;
            else 
            state<=IDLE;
        end
        START :begin
          if(tx_cnt==4'd2)
          state<=DATA;
          else
          state<=START;
        end
        DATA:begin
          if(tx_cnt==4'd10)
          state<=CHECK;
          else
          state<=DATA;
             end
        CHECK:begin
          if(check_flag)
          state<=DONE;
          else 
          state<=CHECK;
              end   
        DONE:begin    
          if(tx_done==1'b1)
          state<=IDLE;
          else
          state<=DONE;     
        end  
        default  begin
            state<=IDLE;
        end
        endcase

always @(posedge sysclk or negedge rst_n) begin
   if(rst_n==1'b0)
   tx_flag<=1'b0;
   else if (state==DATA)
   tx_flag<=1'b1;
   else 
   tx_flag<=tx_flag;
   end

always@(posedge sysclk or negedge rst_n) begin
 if (rst_n==1'b0)
   tx_done<=1'b0;
 else if ((state==DONE)&&(data_cnt==BPS_CNT))
   tx_done<=1'b1;
 else 
   tx_done<=1'b0;
end

reg [7:0] data;
reg [2:0] cnt;
reg check;

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
  tx<=1'b1;
  else if(state==IDLE)        
  tx<=1'b1;
  else if(state==START)
  tx<=1'b0;
  else if(state==DATA)
  tx<=data[7-cnt];
  else if(state==CHECK)
  tx<=check;
  else if(state==DONE)
  tx<=1'b1;
  else 
  tx<=tx;
                                     end

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
  data<=8'd0;
  else if (state==START)
  data<=tx_data;
  else
  data<=data;
end

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
    cnt<=3'd0;
  else 
    case(tx_cnt)
    4'd0:cnt<=3'd0;
    4'd1:cnt<=3'd0;
    4'd2:cnt<=3'd1;
    4'd3:cnt<=3'd2;
    4'd4:cnt<=3'd3;
    4'd5:cnt<=3'd4;
    4'd6:cnt<=3'd5;
    4'd7:cnt<=3'd6;
    4'd8:cnt<=3'd7;
    4'd9:cnt<=3'd0;
    4'd10:cnt<=3'd0;
    default begin
      cnt<=cnt;
    end
    endcase
                                  end

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
    check_flag<=1'b0;
   else if ((data_cnt==BPS_CNT)&&(state==CHECK))
   check_flag<=1'b1;
   else
   check_flag<=1'b0;
   end  

reg even_check;
reg odd_check;
always@(posedge sysclk or negedge rst_n) 
  if(rst_n==1'b0) begin
    even_check<=1'b0;
    odd_check <=1'b0;
  end
  else if(tx==1'b1) begin
    even_check<=^tx_data;
    odd_check<=~^tx_data;  
  end
  else begin
  even_check<=even_check;
  odd_check<=odd_check;
end  

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
    check<=1'b0;
  else if(tx_en==1'b1&&EVEN_ODD_CHECK==1'b1)  
    check<=even_check;
  else if(tx_en==1'b1&&EVEN_ODD_CHECK==1'b0)
    check<=odd_check;
  else 
    check<=check;
end    
endmodule

uart_rx模块

uart接收模块负责将接收到的单bit数据进行串并转化,输出一个8位的数据,先传输过来的数据放在高位。uart的发送端模块分为空闲状态,开始状态,传输数据状态,奇偶校验状态和停止模块。状态图如下:

verilog代码如下: 

module uart_rx(
    input           sysclk ,
    input           rst_n  ,
    input           tx     ,
    input           tx_done,
    output reg[7:0] rx_data,
    output reg      error  ,
    output reg      rx_done
);

parameter   CLK_FREQ       = 50000000         ;            //
parameter   UART_BPS       = 9600             ;                
localparam  BPS_CNT        = CLK_FREQ/UART_BPS;//每个数据位占用的周期 
parameter   EVEN_ODD_CHECK = 1                ;

parameter IDLE =5'b00001;
parameter START=5'b00010;
parameter DATA =5'b00100;//接收数据
parameter CHECK=5'b01000;
parameter DONE =5'b10000;

reg [4:0]state;
reg [15:0] data_cnt   ;//计数到BPS_CNT

always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
    data_cnt<=16'd0        ;
  else if(data_cnt>BPS_CNT)
    data_cnt<=16'd0        ;
  else if(data_cnt<BPS_CNT)
    data_cnt<=data_cnt+1'b1;
  else
    data_cnt<=data_cnt     ;    
    end

reg [3:0] rx_cnt;//每到bps_cnt计数
always@(posedge sysclk or negedge rst_n)begin
  if(rst_n==1'b0)
    rx_cnt<=4'd0       ;
  else if(rx_cnt==4'd11)
    rx_cnt<=4'd0       ;
  else if (data_cnt==BPS_CNT)
    rx_cnt<=rx_cnt+1'b1;
  else
    rx_cnt<=rx_cnt     ;    
    end

reg start_end;
always@(posedge sysclk or negedge rst_n) begin
  if(rst_n==1'b0)
  start_end<=1'b0;
  else if((state==START)&&(data_cnt==BPS_CNT))
  start_end<=1'b1;
  else 
  start_end<=start_end;  
  end

reg check_flag;
always@(posedge sysclk or negedge rst_n) begin
    if(rst_n==1'b0)
      check_flag<=1'b0;
     else if ((data_cnt==BPS_CNT)&&(state==CHECK))
     check_flag<=1'b1;
     else
     check_flag<=1'b0;
     end  
     
reg [2:0]cnt;
always@(posedge sysclk or negedge rst_n) begin
    if(rst_n==1'b0)
      cnt<=3'd0;
    else 
      case(rx_cnt)
      4'd0:cnt<=3'd0;
      4'd1:cnt<=3'd0;
      4'd2:cnt<=3'd1;
      4'd3:cnt<=3'd2;
      4'd4:cnt<=3'd3;
      4'd5:cnt<=3'd4;
      4'd6:cnt<=3'd5;
      4'd7:cnt<=3'd6;
      4'd8:cnt<=3'd7;
      4'd9:cnt<=3'd0;
      4'd10:cnt<=3'd0;
      default begin
        cnt<=cnt;
      end
      endcase
      end

reg data_done;//当state==data,cnt=7时拉�?
always@(posedge sysclk or negedge rst_n) begin
    if(rst_n==1'b0)
      data_done<=1'b0;
     else if (cnt==3'd7&&state==DATA)
      data_done<=1'b1;
     else
      data_done<=1'b0;
     end  

 //state
reg check;
always @(posedge sysclk or negedge rst_n ) 
    if(rst_n==1'b0) begin
        state<=IDLE;
    end
    else  
    case (state)
        IDLE: begin
          if(tx_done)
            state<=START;
            else 
            state<=IDLE;
        end
        START :begin
          if(start_end)
          state<=DATA;
          else
          state<=START;
        end
        DATA:begin
          if(data_done)
          state<=CHECK;
          else
          state<=DATA;
             end
        CHECK:begin
          if(check_flag)
          state<=DONE;
          else 
          state<=CHECK;
              end   
        DONE:begin    
          if(rx_done==1'b1)
          state<=IDLE;
          else
          state<=DONE;     
        end  
        default  begin
            state<=IDLE;
        end
        endcase

reg [7:0]shift_reg;     
always@(posedge sysclk or negedge rst_n) begin
    if(rst_n==1'b0)
    shift_reg<=8'd0;
    else if((state==DATA)&&(data_cnt==BPS_CNT))
    shift_reg<={tx,shift_reg[7:1]};
    else
    shift_reg<=shift_reg;    
end    
    
always@(posedge sysclk or negedge rst_n) 
    if(rst_n==1'b0) begin
     rx_data<=8'd0;
     rx_done<=1'b0;
    end
    else if(state==DATA&&rx_cnt==3'd7) begin
     rx_data<=shift_reg;
     rx_done<=1'b1;
    end
    else begin
     rx_data<=rx_data;
     rx_done<=rx_done;
    end 
    
reg even_check;
reg odd_check;
    always@(posedge sysclk or negedge rst_n) 
      if(rst_n==1'b0) begin
        even_check<=1'b0;
        odd_check <=1'b0;
      end
      else if(tx==1'b1) begin
        even_check<=^rx_data;
        odd_check<=~^rx_data;  
      end
      else begin
      even_check<=even_check;
      odd_check<=odd_check;
    end  
   
    always@(posedge sysclk or negedge rst_n) begin
      if(rst_n==1'b0)
        check<=1'b0;
      else if(EVEN_ODD_CHECK==1'b1)  
        check<=even_check;
      else if(EVEN_ODD_CHECK==1'b0)
        check<=odd_check;
      else 
        check<=check;
    end        

    always@(posedge sysclk or negedge rst_n) begin
        if(rst_n==1'b0)
        error<=1'b0;
        else if((state==CHECK)&&(tx==check))
        error<=1'b0;
        else if(state==CHECK&&tx!=check)
        error<=1'b1;
        else
        error<=error;
    end

endmodule

uart的top模块

module uart_top(
     input           sysclk ,
     input           rst_n  ,
     input     [7:0]   tx_data,
     input           tx_en  ,     
     output    [7:0] rx_data,
     output          error  ,
     output          rx_done
);
    wire tx_done;
    wire tx     ;

    uart_tx uart_tx_u(
    .sysclk (sysclk ),
    .rst_n  (rst_n  ),
    .tx_data(tx_data),
    .tx_en  (tx_en  ),
    .tx     (tx     ),
    .tx_done(tx_done)
    );

    uart_rx uart_rx_u(
    .sysclk (sysclk ),
    .rst_n  (rst_n  ),
    .tx     (tx     ),
    .tx_done(tx_done),
    .rx_data(rx_data),
    .error  (error  ),
    .rx_done(rx_done)
    );

endmodule

uart的tb模块

module uart_tb();
    reg        sysclk ;
    reg        rst_n  ;
    reg  [7:0] tx_data;
    reg        tx_en  ;
    wire [7:0] rx_data;
    wire       error  ;
    wire       rx_done;
uart_top uart_top_u(
    .sysclk (sysclk ),
    .rst_n  (rst_n  ),
    .tx_data(tx_data),
    .tx_en  (tx_en  ),
    .rx_data(rx_data),
    .error  (error  ),
    .rx_done(rx_done)
);

initial begin
    sysclk <=1'b0 ;
    rst_n  <=1'b0 ;
    tx_data<=8'd0 ;
    tx_en  <=1'b0 ;
#10;
    rst_n  <=1'b1 ;
    tx_en  <=1'b1 ;
#15;
   tx_data<=8'd36;
end

always #10sysclk=!sysclk;

endmodule

uart仿真原理图

可以看出,uart_tx模块跟uart_rx模块仅使用两条线路进行通讯

uart仿真波形

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UART(通用异步收发器)是一种串行通信协议,常用于微控制器和外设之间的通信。以下是一个简单的 UARTVerilog 实现: ```verilog module uart ( input clk, // 系统时钟 input reset, // 复位信号 input tx_en, // 传输使能 input [7:0] tx_data, // 传输数据 output reg tx_busy, // 传输忙标志 output reg tx_done, // 传输完成标志 output reg [7:0] rx_data, // 接收数据 output reg rx_done // 接收完成标志 ); reg [3:0] state; // 状态机状态 reg [7:0] tx_reg; // 传输寄存器 reg [7:0] rx_reg; // 接收寄存器 reg rx_start; // 接收起始标志 reg rx_busy; // 接收忙标志 reg rx_parity; // 接收校验位 reg [2:0] rx_cnt; // 接收计数器 parameter IDLE = 0; parameter START = 1; parameter DATA = 2; parameter PARITY = 3; parameter STOP = 4; always @(posedge clk) begin if (reset) begin state <= IDLE; tx_busy <= 0; tx_done <= 0; rx_done <= 0; rx_cnt <= 0; end else begin case (state) IDLE: begin if (tx_en) begin tx_reg <= tx_data; tx_busy <= 1; state <= START; end else if (rx_start) begin rx_busy <= 1; rx_parity <= 0; rx_cnt <= 0; state <= DATA; end end START: begin tx_done <= 0; $display("TX: %c", tx_reg); state <= DATA; end DATA: begin if (tx_busy) begin $display("TX: %c", tx_reg); tx_reg <= tx_reg >> 1; if (!tx_reg[0]) begin tx_busy <= 0; tx_done <= 1; state <= STOP; end end else if (rx_busy) begin rx_reg <= {rx_reg[6:0], rx_start}; rx_cnt <= rx_cnt + 1; if (rx_cnt == 7) begin rx_busy <= 0; rx_done <= 1; state <= PARITY; end end end PARITY: begin rx_parity <= rx_reg[0] ^ rx_reg[1] ^ rx_reg[2] ^ rx_reg[3] ^ rx_reg[4] ^ rx_reg[5] ^ rx_reg[6]; state <= STOP; end STOP: begin rx_start <= 0; rx_data <= rx_reg[6:0]; rx_done <= 1; state <= IDLE; end endcase end end always @(posedge clk) begin if (reset) begin rx_start <= 0; end else begin rx_start <= !rx_busy && !rx_start && !rx_done && !tx_busy && !tx_en && !rx_parity && (rx_data == 8'hFF); end end endmodule ``` 这个例子实现了一个简单的 8 位 UART,支持 1 个停止位和无奇偶校验。当传输使能 tx_en 为 1 时,它会将 tx_data 传输到串行线路上,并且在传输完成时将 tx_done 置为 1。当接收到启动位时,它会开始接收数据并在接收完成时将 rx_done 置为 1,并将接收到的数据存储在 rx_data 中。注意到这个 UART 实现使用了一个简单的状态机来控制传输和接收流程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值