2-基于FPGA开发板DE23-Lite的串口通信设计 (FT2232H)

串口基础知识介绍请参考:1-串行通信基础知识

1 开发板串口简介

DE23-Lite开发板提供了一个UART通信接口(物理接口是下图的Type C接口),用户能够通过主机与Agilex 3 FPGA进行串口通信。

该接口使用DE23-Lite板载的USB Blaster III电路中的FT2232H芯片作为UART转USB的桥梁。将USB线连接到DE23-Lite板的Type-C接口和主机之间,即可启用USB Blaster III和FPGA UART功能,此时无需串行驱动程序,但用户在使用UART功能前需要确保已安装USB Blaster III驱动程序。

连接USB线后,通常在PC设备管理器中会显示USB Blaster III和一个COM端口号。

2 实验任务

设计一个串口回环实验,实现上位机发送数据给开发板串口,串口接收数据后又通过串口发送给上位机。

串口时序图如下:

要求:数据位为8位, 停止位1位,无校验位,波特率115200bps。

3 模块设计

DE23-Lite的串口回环设计主要是2个模块:串口发送模块(发送数据时将并行的数据转换成串行数据进行传输)和串口接收模块(在接收数据时将接收到的串行数据转换成并行数据)。

系统时钟是50MHz,波特率是115200bps,那么串口发送和接收时,数据的每个位将占用50000000/115200 ≈ 434个时钟周期。

在串口接收模块设置一个4状态的状态机:

空闲状态:在空闲状态下,检测起始位(低电平)。一旦检测到起始位,进入START状态,并设置计数器在半位时间后采样,这样可以确保在位的中心点采样,提高抗噪能力。

起始位检测状态:等待半个位周期后,再次检查线路状态。如果仍然是低电平,确认是有效的起始位,进入DATA状态;否则认为是噪声干扰,返回IDLE状态。

数据位接收状态:在每个位周期的中心点采样数据位,并存入移位寄存器。接收完8位数据后,进入STOP状态。

停止位处理状态:等待一个完整的位周期(停止位),然后将接收到的数据输出,并产生一个时钟周期的接收完成信号。

接收模块工程代码:

复制代码
module uart_rx(
  input clk,
  input rst_n,
  input uart_rx,
  output reg [7:0] rx_data,
  output reg rx_done
);

  parameter CLK_FREQ = 50000000;
  parameter BAUD_RATE = 115200;
    
    // 波特率计数器
  localparam BAUD_CNT_MAX = CLK_FREQ / BAUD_RATE;
  localparam HALF_BAUD_CNT = BAUD_CNT_MAX / 2;
  reg [15:0] baud_cnt;
    
    // 状态定义
  localparam IDLE = 2'd0;
  localparam START = 2'd1;
  localparam DATA = 2'd2;
  localparam STOP = 2'd3;
    
  reg [1:0] state;
  reg [2:0] bit_cnt;
  reg [7:0] rx_reg;
  reg uart_rx_sync1, uart_rx_sync2;
    
    // 同步输入信号
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    uart_rx_sync1 <= 1'b1;
    uart_rx_sync2 <= 1'b1;
  end else begin
    uart_rx_sync1 <= uart_rx;
    uart_rx_sync2 <= uart_rx_sync1;
  end
end
    
    // 状态机
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    state <= IDLE;
    rx_data <= 8'd0;
    rx_done <= 1'b0;
    baud_cnt <= 0;
    bit_cnt <= 0;
    rx_reg <= 0;
  end
  else begin
    rx_done <= 1'b0;     
    case (state)
      IDLE: begin
        if(uart_rx_sync2 == 1'b0) begin  // 检测起始位
          state <= START;
          baud_cnt <= HALF_BAUD_CNT - 1;  // 半位时间后采样
        end
      end
                
      START: begin
        if(baud_cnt == 0) begin
          if(uart_rx_sync2 == 1'b0) begin  // 确认起始位
            state <= DATA;
            baud_cnt <= BAUD_CNT_MAX - 1;
            bit_cnt <= 0;
          end
          else begin
            state <= IDLE;  // 假起始位
          end
        end else begin
          baud_cnt <= baud_cnt - 1;
        end
      end
                
      DATA: begin
        if(baud_cnt == 0) begin
          rx_reg[bit_cnt] <= uart_rx_sync2;
          if (bit_cnt == 3'd7) begin
            state <= STOP;
          end
          else begin
            bit_cnt <= bit_cnt + 1;
          end
          baud_cnt <= BAUD_CNT_MAX - 1;
        end 
        else begin
          baud_cnt <= baud_cnt - 1;
        end
      end
                
      STOP: begin
        if(baud_cnt == 0) begin
          rx_data <= rx_reg;
          rx_done <= 1'b1;
          state <= IDLE;
        end
        else begin
          baud_cnt <= baud_cnt - 1;
        end
      end
    endcase
  end
end

endmodule

串口发送模块同样设置了一个4状态的状态机:

空闲状态:空闲状态保持高电平。

起始位发送状态:发送起始位,低电平。

数据发送状态:数据位从最低位(LSB)开始发送,这是UART的标准格式。

停止位发送状态:发送停止位,高电平。

串口发送模块的工程代码:

复制代码
module uart_tx(
  input clk,
  input rst_n,
  input tx_start,
  input [7:0] tx_data,
  output reg uart_tx,
  output tx_busy
);

  parameter CLK_FREQ = 50000000;
  parameter BAUD_RATE = 115200;
    
    // 波特率计数器
  localparam BAUD_CNT_MAX = CLK_FREQ / BAUD_RATE;
  reg [15:0] baud_cnt;
  wire baud_tick = (baud_cnt == 0);
    
    // 状态定义
  localparam IDLE = 2'd0;
  localparam START = 2'd1;
  localparam DATA = 2'd2;
  localparam STOP = 2'd3;
    
  reg [1:0] state;
  reg [2:0] bit_cnt;
  reg [7:0] tx_reg;
    
    // 波特率计数器
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    baud_cnt <= 0;
  end
  elseif(state != IDLE) begin
    if(baud_cnt == 0) begin
      baud_cnt <= BAUD_CNT_MAX - 1;
    end
    else begin
      baud_cnt <= baud_cnt - 1;
    end
  end
  else begin
    baud_cnt <= 0;
  end
end
    
    // 状态机
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    state <= IDLE;
    uart_tx <= 1'b1;
    bit_cnt <= 0;
    tx_reg <= 0;
  end
  else begin
    case(state)
      IDLE: begin
        uart_tx <= 1'b1;
        if (tx_start) begin
          state <= START;
          tx_reg <= tx_data;
        end
      end
                
      START: begin
        if(baud_tick) begin
          uart_tx <= 1'b0;
          state <= DATA;
          bit_cnt <= 0;
        end
      end
                
      DATA: begin
        if(baud_tick) begin
          uart_tx <= tx_reg[bit_cnt];
          if (bit_cnt == 3'd7) begin
            state <= STOP;
          end
          else begin
            bit_cnt <= bit_cnt + 1;
          end
        end
      end
                
      STOP: begin
        if(baud_tick) begin
          uart_tx <= 1'b1;
          state <= IDLE;
        end
      end
    endcase
  end
end
    
    assign tx_busy = (state != IDLE);

endmodule
复制代码

top文件代码要完成的任务是:

  • 只有在检测到接收完成信号的上升沿时才启动发送

  • 只有在发送器不忙时才启动新的发送

  • 实现了接收数据到发送数据的无缝衔接

    module DE23_Lite_uart(
    input clk,
    input rst_n,
    input uart_rx,
    output uart_tx
    );

    复制代码
    /* synthesis keep */wire [7:0] rx_data;
    wire rx_done;
    wire rx_done_rise,tx_start;
    wire tx_busy;
      
      // UART接收模块
    uart_rx #(
      .CLK_FREQ(50000000),
      .BAUD_RATE(115200)
      ) uart_rx_inst (
      .clk(clk),
      .rst_n(rst_n),
      .uart_rx(uart_rx),
      .rx_data(rx_data),
      .rx_done(rx_done)
    );
      
      // UART发送模块
    uart_tx #(
      .CLK_FREQ(50000000),
      .BAUD_RATE(115200)
    ) uart_tx_inst (
      .clk(clk),
      .rst_n(rst_n),
      .tx_start(tx_start),
      .tx_data(rx_data),
      .uart_tx(uart_tx),
      .tx_busy(tx_busy)
    );
      
      // 回环控制逻辑
    reg rx_done_reg;
    always @(posedge clk or negedge rst_n) begin
      if(!rst_n) begin
        rx_done_reg <= 1'b0;
      end else begin
        rx_done_reg <= rx_done;
      end
    end
      
      // 检测接收完成的上升沿
    assign rx_done_rise = rx_done && !rx_done_reg;
      
      // 发送启动信号
    assign tx_start = rx_done_rise && !tx_busy;

    endmodule

仿真代码:

复制代码
`timescale 1ns/1ps

module DE23_Lite_uart_tb;

    // 输入
  reg clk;
  reg rst_n;
  reg uart_rx;
    
    // 输出
  wire uart_tx;
    
    // 测试参数
  parameter CLK_PERIOD = 20;  // 50MHz时钟周期
  parameter BIT_PERIOD = 8680; // 115200波特率的位周期(1/115200 ≈ 8.68μs)
    
    // 实例化顶层模块
DE23_Lite_uart uut(
  .clk(clk),
  .rst_n(rst_n),
  .uart_rx(uart_rx),
  .uart_tx(uart_tx)
  );
    
    // 时钟生成
  always begin
    clk = 0;
    #(CLK_PERIOD/2);
    clk = 1;
    #(CLK_PERIOD/2);
  end
    
    // 测试任务:发送一个字节
  task send_byte;
    input [7:0] data;
    integer i;
    begin
            // 发送起始位
      uart_rx = 0;
      #(BIT_PERIOD);        
            // 发送8个数据位
      for (i = 0; i < 8; i = i + 1) begin
        uart_rx = data[i];
        #(BIT_PERIOD);
      end
            
            // 发送停止位
      uart_rx = 1;
      #(BIT_PERIOD);
    end
  endtask
    
    // 主测试程序
  initial begin
        // 初始化
    rst_n = 0;
    uart_rx = 1;
        
        // 复位
    #100;
    rst_n = 1;
    #100;
        
        // 测试1:发送字节 8'h55
    send_byte(8'h55);
        
        // 测试2:发送字节 8'hAA
    send_byte(8'hAA);
        
        // 测试3:发送字节 8'hF0
    send_byte(8'hF0);
        
        // 测试4:发送字节 8'h0F
    send_byte(8'h0F);
        
        // 结束仿真
    #10000;
    $stop;
  end

endmodule

modelsim仿真波形:

可以看到:

第一个波特位时间内,rx先发送低电平起始位,然后发送8bit数据01010101(低位在前,8'h55),最后发送高电平停止位;tx则一直是高电平。

第二个波特位时间内,rx先发送低电平起始位,然后rx发送第二个测试数据10101010(低位在前,8'hAA),最后发送高电平停止位;tx则接收到8bit数据01010101。

第三个波特位时间内,rx先发送低电平起始位,然后rx接收第二个测试数据11110000(低位在前,8'hF0),最后发送高电平停止位;tx则接收到8bit数据10101010。

第四个波特位时间内,rx先发送低电平起始位,然后rx接收第二个测试数据00001111(低位在前,8'h0F),最后发送高电平停止位;tx则接收到8bit数据11110000。

第五个波特位时间内,rx保持高电平;tx则接收到8bit数据00001111。

Quartus版本选择:25.1,具体操作参考文章 最新版Quartus Prime Pro 25.1 的安装和使用演示(含Questa仿真)

引脚分配:

4 下板测试

打开串口工具比如Putty或者是下面截图所示的XCOM,然后按照如下操作去测试:

  • 选择正确的COM口

  • 波特率设置为115200

  • 停止位设置为1位

  • 无校验位

  • 点击打开串口

  • 在发送窗口随便发送数据,可以看到上面接收窗口得到同样的数据显示,表示测试成功。

附DE23-Lite的串口设计案例下载:

通过网盘分享的文件:DE23_Lite_uart.zip

链接: https://pan.baidu.com/s/1P6XlNc7KkZZuJH1wj73sgw 提取码: tera

最新版Quartus Prime Pro 25.1 的安装和使用演示(含Questa仿真)

1-串行通信基础知识

相关推荐
1560820721921 小时前
在vivado中,国产CH347芯片实现USB转JTAG的操作
fpga开发
数字芯片实验室1 天前
IP验证最终回归到时序级建模
网络·网络协议·tcp/ip·fpga开发
雨洛lhw1 天前
三模冗余资源量对比
fpga开发·三模冗余技术
XINVRY-FPGA1 天前
XC7VX690T-2FFG1761I Xilinx AMD FPGA Virtex-7
arm开发·嵌入式硬件·fpga开发·硬件工程·fpga
FPGA_无线通信1 天前
FPGA 组合逻辑和时序逻辑
fpga开发
Js_cold1 天前
Xilinx FPGA温度等级及选型建议
fpga开发·fpga·vivado·xilinx
从此不归路1 天前
FPGA 结构与 CAD 设计(第5章)上
fpga开发
洋洋Young1 天前
【Xilinx FPGA】7 Series Clocking 设计
fpga开发·xilinx fpga
156082072191 天前
FPGA下AD采集时钟相位的调整
fpga开发
从此不归路1 天前
FPGA 结构与 CAD 设计(第5章)下
fpga开发