FPGA:UART串口接收(高干扰情况)

发布于:2025-02-27 ⋅ 阅读:(10) ⋅ 点赞:(0)

高干扰下UART串口接收思路

在上一篇文章中介绍过UART串口接收模块,其中一个需要优化的方向就是在高干扰情况下,如何实现可靠的UART串口接收任务。解决的思路是:在每一位进行多次采样,去掉干扰较强的初始传输段和传输结束段,取中间几段进行概率统计,如果逻辑 1 1 1的次数比较多,则认为接收的该位为逻辑 1 1 1,否则为逻辑 0 0 0。这里对每一位再进行 16 16 16次采样,去掉前面 6 6 6次和后面 4 4 4次,取中间 6 6 6次进行采样。

UART改善串口接收模块及其思路

波特率选择模块

为了实现适应不同波特率,这里引入一个波特率选择模块,一共提供 6 6 6种不同的波特率,分别是 9600 、 19200 、 38400 、 57600 、 115200 、 1562500 9600、19200、38400、57600、115200、1562500 96001920038400576001152001562500,波特率设置信号定义为baud_set。bps_DR为波特率计数器最大值。默认情况下选择 9600 9600 9600波特率。逻辑如下:

  parameter CLK_FRQ = 50000000;
  localparam BAUD_9600_CNT    = CLK_FRQ/9600/16 - 2;
  localparam BAUD_19200_CNT   = CLK_FRQ/19200/16 - 2;
  localparam BAUD_38400_CNT   = CLK_FRQ/38400/16 - 2;
  localparam BAUD_57600_CNT   = CLK_FRQ/57600/16 - 2;
  localparam BAUD_115200_CNT  = CLK_FRQ/115200/16 - 2;
  localparam BAUD_1562500_CNT = CLK_FRQ/1562500/16 - 2;
  always@(posedge clk or posedge reset)
  if(reset)
    bps_DR <= 16'd324;
  else begin
    case(baud_set)
      0:bps_DR <= BAUD_9600_CNT;
      1:bps_DR <= BAUD_19200_CNT;
      2:bps_DR <= BAUD_38400_CNT;
      3:bps_DR <= BAUD_57600_CNT;
      4:bps_DR <= BAUD_115200_CNT;
      5:bps_DR <= BAUD_1562500_CNT;
      default:bps_DR <= BAUD_9600_CNT;
    endcase
  end

打拍延迟和边沿检测电路

因为输入信号是异步信号,需要对其进行打拍延迟同步,以及检测传输开始时需要进行下降沿检测。
其逻辑在上一节阐述过,这里不再赘述:

  reg uart_rx_sync1;   //同步寄存器
  reg uart_rx_sync2;   //同步寄存器
  reg uart_rx_reg1;    //数据寄存器
  reg uart_rx_reg2;    //数据寄存器
  wire uart_rx_nedge;
  //同步串行输入信号,消除亚稳态
  always@(posedge clk or posedge reset)
  if(reset)begin
    uart_rx_sync1 <= 1'b0;
    uart_rx_sync2 <= 1'b0;
  end
  else begin
    uart_rx_sync1 <= uart_rx;
    uart_rx_sync2 <= uart_rx_sync1;  
  end

  //数据寄存器
  always@(posedge clk or posedge reset)
  if(reset)begin
    uart_rx_reg1 <= 1'b0;
    uart_rx_reg2 <= 1'b0;
  end
  else begin
    uart_rx_reg1 <= uart_rx_sync2;
    uart_rx_reg2 <= uart_rx_reg1;
  end

  //下降沿检测
  assign uart_rx_nedge = !uart_rx_reg1 & uart_rx_reg2;

计数使能逻辑

这里不同点是在与我们对每一位进行 16 16 16次的上采样。当检测到下降沿的时候,意味着传输开始,这个时候需要将其置 1 1 1,另外分析两种异常情况:如果传输开始,我们检测到起始位为 1 1 1或者传输结束的时候检测到停止位为 0 0 0,那这个时候我们认为这一次传输数据有误,将使能信号置 0 0 0,对于起始位到底是 1 1 1还是 0 0 0,我们是使用一个小的统计计数,如下图所示,如果在起始位的中间 6 6 6个采样点得到的 1 1 1的数量超过 1 1 1次,即出现所谓的多次毛刺这个时候则认为起始位为 1 1 1,同理,如果停止位采样时 6 6 6次数据出现的逻辑 1 1 1次数少于3次,则认为停止位为 0 0 0。逻辑如下:

每一位进行16次上采样

  always@(posedge clk or posedge reset)
  if(reset)
    uart_state <= 1'b0;
  else if(uart_rx_nedge)
    uart_state <= 1'b1;
  else if(rx_done || (bps_cnt == 8'd12 && (START_BIT > 2)) || (bps_cnt == 8'd155 && (STOP_BIT < 3)))
    uart_state <= 1'b0;
  else
    uart_state <= uart_state;

这里的START_BIT和STOP_BIT均是一个 3 3 3位计数器,统计逻辑 1 1 1出现的次数。

波特率分频计数器

与之前不同,这里是对一位数据进行 16 16 16次的上采样,所以分频计数器的逻辑为:

 always@(posedge clk or posedge reset)
  if(reset)
    div_cnt <= 16'd0;
  else if(uart_state)begin
    if(div_cnt == bps_DR)
      div_cnt <= 16'd0;
    else
      div_cnt <= div_cnt + 1'b1;
  end
  else
    div_cnt <= 16'd0;

位计数器

对细分后的每一位进行计数,一共有 10 10 10位数据,每一位细分成 16 16 16次进行采样,所以细分后一次传输一共有 160 160 160个最小计数单位。这个计数器需要考虑下列异常情况:即起始位如果为 1 1 1,则传输认为是无效数据,不进行计数。或者当其计数到 159 159 159时进行清零,当分频计数器开始计时时让位计数自加。逻辑代码如下:

// bps_clk gen
  always@(posedge clk or posedge reset)
  if(reset)
    bps_clk <= 1'b0;
  else if(div_cnt == 16'd1)
    bps_clk <= 1'b1;
  else
    bps_clk <= 1'b0;

  //bps counter
  always@(posedge clk or posedge reset)
  if(reset)  
    bps_cnt <= 8'd0;
  else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
    bps_cnt <= 8'd0;
  else if(bps_clk)
    bps_cnt <= bps_cnt + 1'b1;
  else
    bps_cnt <= bps_cnt;

完成标志信号

在一次数据传输完成后,即停止位发送完毕时,认为发送完成,此时将完成标志信号拉高。逻辑如下:

always@(posedge clk or posedge reset)
  if(reset)
    rx_done <= 1'b0;
  else if(bps_cnt == 8'd159)
    rx_done <= 1'b1;
  else
    rx_done <= 1'b0;

计数判断逻辑

在前面说过,对每一位数据的进行 16 16 16次上采样,去掉前面 6 6 6次和后面 4 4 4次,只统计中间 6 6 6次出现 1 1 1的次数,最多就是 6 6 6个点全是 1 1 1,所以需要一个深度为 3 3 3的计数器进行统计, 1 ∼ 6 1\sim 6 16的三位二进制分别如下 001 、 010 、 011 、 100 、 101 、 110 001、010、011、100、101、110 001010011100101110,当 1 1 1出现的次数不低于 4 4 4次,则认为传输的是逻辑 1 1 1,如下表

十进制 二进制
1 001
2 010
3 011
4 100
5 101
6 110

我们可以取这个三位计数器的最高位作为该位的值。 和之前一样,先将统计数据存储在data_type_pre里面,其是一个二维数组,是一个 8 8 8 3 3 3 位寄存器组成的数组,每个寄存器可以独立存储一个 3 3 3 位的二进制值。

always@(posedge clk or posedge reset)
  if(reset)begin
    START_BIT        <= 3'd0;
    data_byte_pre[0] <= 3'd0;
    data_byte_pre[1] <= 3'd0;
    data_byte_pre[2] <= 3'd0;
    data_byte_pre[3] <= 3'd0;
    data_byte_pre[4] <= 3'd0;
    data_byte_pre[5] <= 3'd0;
    data_byte_pre[6] <= 3'd0;
    data_byte_pre[7] <= 3'd0;
    STOP_BIT         <= 3'd0;
  end
  else if(bps_clk)begin
    case(bps_cnt)
      0:begin
        START_BIT        <= 3'd0;
        data_byte_pre[0] <= 3'd0;
        data_byte_pre[1] <= 3'd0;
        data_byte_pre[2] <= 3'd0;
        data_byte_pre[3] <= 3'd0;
        data_byte_pre[4] <= 3'd0;
        data_byte_pre[5] <= 3'd0;
        data_byte_pre[6] <= 3'd0;
        data_byte_pre[7] <= 3'd0;
        STOP_BIT         <= 3'd0;
      end
      6 ,7 ,8 ,9 ,10,11:START_BIT <= START_BIT + uart_rx_sync2;
      22,23,24,25,26,27:data_byte_pre[0] <= data_byte_pre[0] + uart_rx_sync2;
      38,39,40,41,42,43:data_byte_pre[1] <= data_byte_pre[1] + uart_rx_sync2;
      54,55,56,57,58,59:data_byte_pre[2] <= data_byte_pre[2] + uart_rx_sync2;
      70,71,72,73,74,75:data_byte_pre[3] <= data_byte_pre[3] + uart_rx_sync2;
      86,87,88,89,90,91:data_byte_pre[4] <= data_byte_pre[4] + uart_rx_sync2;
      102,103,104,105,106,107:data_byte_pre[5] <= data_byte_pre[5] + uart_rx_sync2;
      118,119,120,121,122,123:data_byte_pre[6] <= data_byte_pre[6] + uart_rx_sync2;
      134,135,136,137,138,139:data_byte_pre[7] <= data_byte_pre[7] + uart_rx_sync2;
      150,151,152,153,154,155:STOP_BIT <= STOP_BIT + uart_rx_sync2;
      default:
      begin
        START_BIT        <= START_BIT;
        data_byte_pre[0] <= data_byte_pre[0];
        data_byte_pre[1] <= data_byte_pre[1];
        data_byte_pre[2] <= data_byte_pre[2];
        data_byte_pre[3] <= data_byte_pre[3];
        data_byte_pre[4] <= data_byte_pre[4];
        data_byte_pre[5] <= data_byte_pre[5];
        data_byte_pre[6] <= data_byte_pre[6];
        data_byte_pre[7] <= data_byte_pre[7];
        STOP_BIT         <= STOP_BIT;
      end
    endcase
  end
  always@(posedge clk or posedge reset)
  if(reset)
    data_byte <= 8'd0;
  else if(bps_cnt == 8'd159)begin
    data_byte[0] <= data_byte_pre[0][2];
    data_byte[1] <= data_byte_pre[1][2];
    data_byte[2] <= data_byte_pre[2][2];
    data_byte[3] <= data_byte_pre[3][2];
    data_byte[4] <= data_byte_pre[4][2];
    data_byte[5] <= data_byte_pre[5][2];
    data_byte[6] <= data_byte_pre[6][2];
    data_byte[7] <= data_byte_pre[7][2];
  end

整体代码

代码整合如下:

module uart_byte_rx(
  clk,
  reset,

  baud_set,
  uart_rx,

  data_byte,
  rx_done
);
  input clk;                 //时钟输入
  input reset;               //复位信号输入

  input [2:0] baud_set;      //波特率设置
  input uart_rx;             //串口输入信号

  output reg [7:0]data_byte; //串口接收的1byte数据
  output reg rx_done;        //1byte数据接收完成标志

  parameter CLK_FRQ = 50000000;
  localparam BAUD_9600_CNT    = CLK_FRQ/9600/16 - 2;
  localparam BAUD_19200_CNT   = CLK_FRQ/19200/16 - 2;
  localparam BAUD_38400_CNT   = CLK_FRQ/38400/16 - 2;
  localparam BAUD_57600_CNT   = CLK_FRQ/57600/16 - 2;
  localparam BAUD_115200_CNT  = CLK_FRQ/115200/16 - 2;
  localparam BAUD_1562500_CNT = CLK_FRQ/1562500/16 - 2;

  reg uart_rx_sync1;   //同步寄存器
  reg uart_rx_sync2;   //同步寄存器

  reg uart_rx_reg1;    //数据寄存器
  reg uart_rx_reg2;    //数据寄存器

  reg [15:0]bps_DR;    //分频计数最大值
  reg [15:0]div_cnt;   //分频计数器
  reg       bps_clk;   //波特率时钟
  reg [7:0] bps_cnt;   //波特率时钟计数器
  reg       uart_state;//接收数据状态

  wire      uart_rx_nedge;

  reg [2:0] START_BIT;
  reg [2:0] STOP_BIT;
  reg [2:0] data_byte_pre [7:0];

  //同步串行输入信号,消除亚稳态
  always@(posedge clk or posedge reset)
  if(reset)begin
    uart_rx_sync1 <= 1'b0;
    uart_rx_sync2 <= 1'b0;
  end
  else begin
    uart_rx_sync1 <= uart_rx;
    uart_rx_sync2 <= uart_rx_sync1;  
  end

  //数据寄存器
  always@(posedge clk or posedge reset)
  if(reset)begin
    uart_rx_reg1 <= 1'b0;
    uart_rx_reg2 <= 1'b0;
  end
  else begin
    uart_rx_reg1 <= uart_rx_sync2;
    uart_rx_reg2 <= uart_rx_reg1;
  end

  //下降沿检测
  assign uart_rx_nedge = !uart_rx_reg1 & uart_rx_reg2;

  always@(posedge clk or posedge reset)
  if(reset)
    bps_DR <= 16'd324;
  else begin
    case(baud_set)
      0:bps_DR <= BAUD_9600_CNT;
      1:bps_DR <= BAUD_19200_CNT;
      2:bps_DR <= BAUD_38400_CNT;
      3:bps_DR <= BAUD_57600_CNT;
      4:bps_DR <= BAUD_115200_CNT;
      5:bps_DR <= BAUD_1562500_CNT;
      default:bps_DR <= BAUD_9600_CNT;
    endcase
  end

  //counter
  always@(posedge clk or posedge reset)
  if(reset)
    div_cnt <= 16'd0;
  else if(uart_state)begin
    if(div_cnt == bps_DR)
      div_cnt <= 16'd0;
    else
      div_cnt <= div_cnt + 1'b1;
  end
  else
    div_cnt <= 16'd0;

  // bps_clk gen
  always@(posedge clk or posedge reset)
  if(reset)
    bps_clk <= 1'b0;
  else if(div_cnt == 16'd1)
    bps_clk <= 1'b1;
  else
    bps_clk <= 1'b0;

  //bps counter
  always@(posedge clk or posedge reset)
  if(reset)  
    bps_cnt <= 8'd0;
  else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
    bps_cnt <= 8'd0;
  else if(bps_clk)
    bps_cnt <= bps_cnt + 1'b1;
  else
    bps_cnt <= bps_cnt;

  always@(posedge clk or posedge reset)
  if(reset)
    rx_done <= 1'b0;
  else if(bps_cnt == 8'd159)
    rx_done <= 1'b1;
  else
    rx_done <= 1'b0;

  always@(posedge clk or posedge reset)
  if(reset)begin
    START_BIT        <= 3'd0;
    data_byte_pre[0] <= 3'd0;
    data_byte_pre[1] <= 3'd0;
    data_byte_pre[2] <= 3'd0;
    data_byte_pre[3] <= 3'd0;
    data_byte_pre[4] <= 3'd0;
    data_byte_pre[5] <= 3'd0;
    data_byte_pre[6] <= 3'd0;
    data_byte_pre[7] <= 3'd0;
    STOP_BIT         <= 3'd0;
  end
  else if(bps_clk)begin
    case(bps_cnt)
      0:begin
        START_BIT        <= 3'd0;
        data_byte_pre[0] <= 3'd0;
        data_byte_pre[1] <= 3'd0;
        data_byte_pre[2] <= 3'd0;
        data_byte_pre[3] <= 3'd0;
        data_byte_pre[4] <= 3'd0;
        data_byte_pre[5] <= 3'd0;
        data_byte_pre[6] <= 3'd0;
        data_byte_pre[7] <= 3'd0;
        STOP_BIT         <= 3'd0;
      end
      6 ,7 ,8 ,9 ,10,11:START_BIT <= START_BIT + uart_rx_sync2;
      22,23,24,25,26,27:data_byte_pre[0] <= data_byte_pre[0] + uart_rx_sync2;
      38,39,40,41,42,43:data_byte_pre[1] <= data_byte_pre[1] + uart_rx_sync2;
      54,55,56,57,58,59:data_byte_pre[2] <= data_byte_pre[2] + uart_rx_sync2;
      70,71,72,73,74,75:data_byte_pre[3] <= data_byte_pre[3] + uart_rx_sync2;
      86,87,88,89,90,91:data_byte_pre[4] <= data_byte_pre[4] + uart_rx_sync2;
      102,103,104,105,106,107:data_byte_pre[5] <= data_byte_pre[5] + uart_rx_sync2;
      118,119,120,121,122,123:data_byte_pre[6] <= data_byte_pre[6] + uart_rx_sync2;
      134,135,136,137,138,139:data_byte_pre[7] <= data_byte_pre[7] + uart_rx_sync2;
      150,151,152,153,154,155:STOP_BIT <= STOP_BIT + uart_rx_sync2;
      default:
      begin
        START_BIT        <= START_BIT;
        data_byte_pre[0] <= data_byte_pre[0];
        data_byte_pre[1] <= data_byte_pre[1];
        data_byte_pre[2] <= data_byte_pre[2];
        data_byte_pre[3] <= data_byte_pre[3];
        data_byte_pre[4] <= data_byte_pre[4];
        data_byte_pre[5] <= data_byte_pre[5];
        data_byte_pre[6] <= data_byte_pre[6];
        data_byte_pre[7] <= data_byte_pre[7];
        STOP_BIT         <= STOP_BIT;
      end
    endcase
  end

  always@(posedge clk or posedge reset)
  if(reset)
    data_byte <= 8'd0;
  else if(bps_cnt == 8'd159)begin
    data_byte[0] <= data_byte_pre[0][2];
    data_byte[1] <= data_byte_pre[1][2];
    data_byte[2] <= data_byte_pre[2][2];
    data_byte[3] <= data_byte_pre[3][2];
    data_byte[4] <= data_byte_pre[4][2];
    data_byte[5] <= data_byte_pre[5][2];
    data_byte[6] <= data_byte_pre[6][2];
    data_byte[7] <= data_byte_pre[7][2];
  end

  always@(posedge clk or posedge reset)
  if(reset)
    uart_state <= 1'b0;
  else if(uart_rx_nedge)
    uart_state <= 1'b1;
  else if(rx_done || (bps_cnt == 8'd12 && (START_BIT > 2)) || (bps_cnt == 8'd155 && (STOP_BIT < 3)))
    uart_state <= 1'b0;
  else
    uart_state <= uart_state;

endmodule

板级验证和测试

生成比特率,这里管脚分配为:

reg [2:0]Baud_set;     分配给三个拨码开关SW2,SW1,SW0
reg [7:0] data_type;   分配给8个LED灯
reset                  分配给拨码开关SW7
clk                    板载晶振
rx_done                蜂鸣器旁的LED灯

首先,选择默认波特率,9600,发送55,即8’b01010101
在这里插入图片描述

在这里插入图片描述
接下来,切换波特率,即将拨码开关切换至3’b100,即选择波特率为115200,传输AA,即8’b10101010,如下所示
在这里插入图片描述

在这里插入图片描述
该方法适用于工业干扰较强的环境下。
声明:该思路来源于小梅哥FPGA,点击可进入其视频页进行观看。