基于FPGA实现异步串口UART

概述

串口是一种支持串行通信协议的接口泛称,串行通信协议包括UART,CAN,SPI等,本文介绍的UART只是其中的异步串行通信协议,即数据通信时没有伴随时钟信号的传输,而是通过约定相同的波特率,配合起始结束位实现数据通信。也是一种点对点通信协议,支持全双工通信。

1、UART硬件

1.1、逻辑电平

UART支持的逻辑电平有TTL/RS232/RS485等,RS232由于噪声容限较大,抗干扰能力远优于TTL电平,但是速率不如TTL,RS458属于差分逻辑电平,具有较好的共模抑制能力,抗干扰性能优于RS232但是占用引脚数较多,在应用中常见的用RS232和TTL较多,一般TTL常用于芯片见互连,RS232常用于设备之间互连。

1.1、电平转换芯片

常见电平转换芯片,MAX3232实现TTL和RS232电平转换,CP2102和CH340等实现USB转TTL的功能,UART是一种协议,而逻辑电平是信号实际传输时的物理特性,注意区分看待。

2、UART协议

2.1、串口参数

在任意串口工具上可以看到,实现串行数据传输,首先需要配置协议的几个参数,分别是波特率、数据位、奇偶位和停止位,以下分别介绍这几位参数。

波特率:是收发双方约定好的bit传输速率,由于UART不使用时钟来同步接收发送端,而是通过起始位、停止位和波特率实现数据的正确发送和接收,这样收端和发端就可以通过各自内部的时钟对信号进行收发,约定好相同的波特率,依靠起始位和停止位开始或结束通信,而不依赖额外的随路时钟,这就是所谓的“异步”。
帧结构:约束好波特率之后,就可以开始数据包的传输,数据包格式并不是唯一的,具有一定的灵活性,其帧结构包括四个部分,起始位(必须)、数据位(5-9位可选)、校验位(可选)、停止位(必须)。

① 起始位:起始位默认空闲状态为高电平,当发送端发送数据是TX管脚从高拉低并保持一个bit周期,代表数据起始位。
②数据位:5-9位可选,常见传输一个字节,即8bit。
③校验位:通常使用奇偶校验,即检查数据位中1的个数,若为偶数,校验位为0,若为奇数,校验位为1。这样接收端可以通过对接收数据进行奇偶校验并和校验位进行比较,以此简单的判断数据传输的准确性。
④停止位:保持高电平1-2bit时间。

2.2、通信时序

UART异步通信时序如下所示,发端和收端接收端的信号皆如下所示,不同的是发端需要考虑,如何将一个数据按照以下时序发出,接收端需要考虑,如何在以下数据时序中提取出数据帧部分。

2.3、系统时钟

那么约定好波特率BAUD之后,没bit位宽时间就已经确定了1/BAUD,那么发端发送数据如何实现保持每位宽时间呢,这肯定是需要使用发端内部时钟进行计数计量的,比如系统时钟是SYS_CLK,每个时钟周期为1/SYS_CLK,每个位宽时间需要持续( 1/BAUD ) / (1/SYS_CLK)= SYS_CLK / BAUD;
按照这样计算得到计数量:

3、基于FPGA实现UART协议

3.1 UART Tx

基于FPGA实现UART发送:代码如下所示,核心是:1、根据波特率和系统时钟计算出每bit位宽,在有效发送数据到来时,将每bit数据保持一定时间宽度送出,2、TX线上空闲位均为高电平。

module testuarttx(  
   input        clk_50m,
   input        arst_n,
   //input valid tx data
   input [7:0]  uart_tx_data,
   input        uart_tx_data_valid,
   //output uart data
   output reg   uart_txd
 );
 
 
 localparam baud_cnt = 'd2604-'d1;
 localparam IDLE = 3'd0;
 localparam DATATRANS = 3'd1;
 
 
 reg [2:0]  state;
 reg [11:0] star_end_cnt;
 reg [3:0]  bit_cnt;

 always @ (posedge clk_50m or negedge arst_n)
 begin
  if(!arst_n)
    star_end_cnt <= 'd0;
  else if((state == DATATRANS) && (star_end_cnt < baud_cnt))
    star_end_cnt <= star_end_cnt +'d1;
  else 
    star_end_cnt <= 'd0;  
 end
 
always @ (posedge clk_50m or negedge arst_n)
 begin
  if(!arst_n)
    bit_cnt <= 'd0;
  else if(state == IDLE)  
    bit_cnt <= 'd0;
  else if((state == DATATRANS) && (star_end_cnt==baud_cnt)) 
    bit_cnt <= bit_cnt +'d1;
  else 
    bit_cnt <= bit_cnt;  
 end
 
 always @ (posedge clk_50m or negedge arst_n)
begin
 if(!arst_n)
  state <= IDLE;
 else 
  begin 
    case(state)
     IDLE:begin
           if(uart_tx_data_valid)
             state <= DATATRANS;
           else
             state <= IDLE;
          end
     DATATRANS:begin
          if((bit_cnt=='d9)&&(star_end_cnt ==baud_cnt))
             state <= IDLE;
          else
             state <= DATATRANS;         
          end
    endcase
  end 
end 
  
// reg [7:0] uart_tx_data_reg ;
 
  always @ (posedge clk_50m or negedge arst_n)
 begin
 if(!arst_n)
    uart_txd <= 'd1;
 else if((state == DATATRANS)&&(star_end_cnt==baud_cnt))
   begin
       case(bit_cnt)
        'd0: uart_txd <= 'd0;
        'd1: uart_txd <= uart_tx_data[0];
        'd2: uart_txd <= uart_tx_data[1];
        'd3: uart_txd <= uart_tx_data[2];
        'd4: uart_txd <= uart_tx_data[3];
        'd5: uart_txd <= uart_tx_data[4];
        'd6: uart_txd <= uart_tx_data[5];
        'd7: uart_txd <= uart_tx_data[6];
        'd8: uart_txd <= uart_tx_data[7];
        'd9: uart_txd <= 'd1;
        default:uart_txd <= uart_txd;
       endcase
   end
 else 
    uart_txd <= uart_txd;
end
 
endmodule

仿真结果:
在这里插入图片描述

3.2 UART Rx

基于FPGA实现UART接收:代码如下所示,核心是:1、找到第一个下降沿的位置,即表明起始位置;2、根据波特率和系统时钟计算出每bit数据宽度,并在每个数据中间部分接收数据,避免在边沿误判。

module uart_rx
(  
   input         clk_50m,
   input         arst_n,
   //input valid rx data
   output reg [7:0]  uart_rx_data,
   output reg      uart_rx_data_valid,
   //output uart data
   input         uart_rxd

);
 localparam baud_cnt = 'd2604-'d1;
 localparam baud_cnt2 = 'd1302;
 localparam IDLE = 3'd0;
 localparam DATATRANS = 3'd1;


 reg [2:0]  state;
 reg [11:0] star_end_cnt;
 reg [3:0]  bit_cnt;
 reg uart_rxd1;
 reg uart_rxd2;
 
 always@(posedge clk_50m)
begin
	uart_rxd1 <= uart_rxd;
	uart_rxd2 <= uart_rxd1;
end

wire nedge_flag;
assign nedge_flag = uart_rxd2 && ~uart_rxd1 ;

  always @ (posedge clk_50m or negedge arst_n)
 begin
  if(!arst_n)
    star_end_cnt <= 'd0;
  else if((state == DATATRANS) && (star_end_cnt < baud_cnt))
    star_end_cnt <= star_end_cnt +'d1;
  else 
    star_end_cnt <= 'd0;  
 end
 
always @ (posedge clk_50m or negedge arst_n)
 begin
  if(!arst_n)
    bit_cnt <= 'd0;
  else if(state == IDLE)  
    bit_cnt <= 'd0;
  else if((state == DATATRANS) && (star_end_cnt==baud_cnt)) 
    bit_cnt <= bit_cnt +'d1;
  else 
    bit_cnt <= bit_cnt;  
 end
 
always @ (posedge clk_50m or negedge arst_n)
begin
 if(!arst_n)
  state <= IDLE;
 else 
  begin 
    case(state)
     IDLE:begin
           if(nedge_flag)
             state <= DATATRANS;
           else
             state <= IDLE;
          end
     DATATRANS:begin
          if((bit_cnt=='d9)&&(star_end_cnt ==baud_cnt))
             state <= IDLE;
          else
             state <= DATATRANS;         
          end
    endcase
  end 
end 

always @ (posedge clk_50m or negedge arst_n)
 begin
 if(!arst_n)
    uart_rx_data <= 'd0;
 else if((state == DATATRANS)&&(star_end_cnt==baud_cnt2))
   begin
       case(bit_cnt)
        'd1: uart_rx_data[0] <= uart_rxd;
        'd2: uart_rx_data[1] <= uart_rxd;
        'd3: uart_rx_data[2] <= uart_rxd;
        'd4: uart_rx_data[3] <= uart_rxd;
        'd5: uart_rx_data[4] <= uart_rxd;
        'd6: uart_rx_data[5] <= uart_rxd;
        'd7: uart_rx_data[6] <= uart_rxd;
        'd8: uart_rx_data[7] <= uart_rxd;
        default:uart_rx_data <= uart_rx_data;
       endcase
   end
 else 
    uart_rx_data <= uart_rx_data;
end

always @ (posedge clk_50m or negedge arst_n)
 begin
    if(!arst_n)
        uart_rx_data_valid <= 'd0;
    else if((bit_cnt=='d9)&&(star_end_cnt ==baud_cnt))
        uart_rx_data_valid <= 'd1;
    else   
        uart_rx_data_valid <= 'd0;
 end 
 endmodule 

仿真结果:
在这里插入图片描述
参考资料来源:
https://www.analog.com/media/cn/analog-dialogue/volume-54/number-4/uart-a-hardware-communication-protocol_cn.pdf
http://www.openpcba.com/web/contents/get?id=3736&tid=15
https://www.circuitbasics.com/basics-uart-communication/

版权声明:本文为CSDN博主「青豆哒哒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43813325/article/details/121539492

生成海报
点赞 0

青豆哒哒

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

AXI DMA IP核操作流程

直接寄存器模式 访问DMACR,SA,DA,length寄存器初始化DMA传输,当传输完成,相关通道的DMASR.IOC_Irq有效(前提是使能该中断&#xf

按键消抖+点亮led灯

前言 本课程是按键消抖的一个扩展内容,主要是通过实验观察按键消抖和不消抖的一个区别。 一、按键消抖 按键抖动:按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点