【RS422】基于未来科技FT4232HL芯片的多波特率串口通信收发实现

功能简介

  串行通信接口常常用于在计算机和低速外部设备之间传输数据。串口通信存在多种标准,以RS422为例,它将数据分成多个位,采用异步通信方式进行传输。
  本文基于Xilinx VCU128 FPGA开发板,对RS422串口通信进行学习。
  根据用户手册ug1302,128中采用了一款来自未来科技(Future Technology Devices International Ltd.)的USB转UART的芯片FT4232HL(芯片手册)。
   FT4232HL芯片能够将USB接口转化为4个串口通道,并支持配置4个串口通道为不同类型的串口协议,根据FT4232HL芯片手册(P10)可以看到在配置为RS422模式下串口通道各引脚功能如下:
在这里插入图片描述
  在实际使用中,Xilinx配置芯片的通道A为JTAG模式用于JTAG调试链,通道B与通道C用于UART串口通信,通道D用于SYSCTLR。其中通道B、C仅引出了TXD、RXD、RTS_n、CTS_n四根引脚。其中通道C的TXD、RXD的引脚位置可通过如下约束获取

set_property BOARD_PART_PIN USB_UART1_TX [get_ports channel_tx]
set_property BOARD_PART_PIN USB_UART1_RX [get_ports channel_rx]

在这里插入图片描述

SystemVerilog实现(ft4232hl_uart.sv)

   根据422协议规定,编写串口接收代码如下,主要功能包括:

  • 采用偶校验、1停止位、8数据位。
  • 采样采用mmcm产生的400MHz时钟(800MHz时ila存在时序违例),采样串口接收到的数据时采取多次采样方式,即总样本里超过75%为1则为1,少于25%为1则为0。
  • vio用于将采样次数适配到串口波特率,由于采样时钟为400MHz,当需要波特率为115200bps时,需要vio设置采样次数为3472。
  • ila用于抓取串口接收到的字节数据、以及是否存在错误(无停止位错误、校验位错误、采样结果不确定错误)。
`timescale 1ns / 1ps
//
// Company: 
// Engineer: wjh776a68
// 
// Create Date: 03/15/2024 07:45:09 PM
// Design Name: 
// Module Name: ft4232hl_uart
// Project Name: 
// Target Devices: XCVU37P
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

// ===================================================================
// = 
// = https://ftdichip.com/wp-content/uploads/2020/08/DS_FT4232H.pdf
// = P15 signals difinition
// = 
// ===================================================================

module ft4232hl_uart(
    input   logic default_clk_p ,
    input   logic default_clk_n ,

    input   logic reset         ,
    
    input   logic channel_rx    ,
    output  logic channel_tx    
//    input   logic channel_rts_n ,
//    output  logic channel_cts_n 
);

//    assign channel_cts_n = 1;
    
    logic clk_100MHz;
    
    IBUFDS #(
      .DIFF_TERM("FALSE"),       // Differential Termination
      .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE" 
      .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
   ) IBUFDS_inst (
      .O(clk_100MHz),  // Buffer output
      .I(default_clk_p),  // Diff_p buffer input (connect directly to top-level port)
      .IB(default_clk_n) // Diff_n buffer input (connect directly to top-level port)
   );

    logic mmcm_fbclk_s;
    logic mmcm_locked_s;
    logic clk_800MHz;

    MMCME4_BASE #(
        .BANDWIDTH("OPTIMIZED"),    // Jitter programming
        .CLKFBOUT_MULT_F(8.0),      // Multiply value for all CLKOUT
        .CLKFBOUT_PHASE(0.0),       // Phase offset in degrees of CLKFB
        .CLKIN1_PERIOD(10.0),        // Input clock period in ns to ps resolution (i.e., 33.333 is 30 MHz).
        .CLKOUT0_DIVIDE_F(2.0),     // Divide amount for CLKOUT0
        .CLKOUT0_DUTY_CYCLE(0.5),   // Duty cycle for CLKOUT0
        .CLKOUT0_PHASE(0.0),        // Phase offset for CLKOUT0
        .CLKOUT1_DIVIDE(1),         // Divide amount for CLKOUT (1-128)
        .CLKOUT1_DUTY_CYCLE(0.5),   // Duty cycle for CLKOUT outputs (0.001-0.999).
        .CLKOUT1_PHASE(0.0),        // Phase offset for CLKOUT outputs (-360.000-360.000).
        .CLKOUT2_DIVIDE(1),         // Divide amount for CLKOUT (1-128)
        .CLKOUT2_DUTY_CYCLE(0.5),   // Duty cycle for CLKOUT outputs (0.001-0.999).
        .CLKOUT2_PHASE(0.0),        // Phase offset for CLKOUT outputs (-360.000-360.000).
        .CLKOUT3_DIVIDE(1),         // Divide amount for CLKOUT (1-128)
        .CLKOUT3_DUTY_CYCLE(0.5),   // Duty cycle for CLKOUT outputs (0.001-0.999).
        .CLKOUT3_PHASE(0.0),        // Phase offset for CLKOUT outputs (-360.000-360.000).
        .CLKOUT4_CASCADE("FALSE"),  // Divide amount for CLKOUT (1-128)
        .CLKOUT4_DIVIDE(1),         // Divide amount for CLKOUT (1-128)
        .CLKOUT4_DUTY_CYCLE(0.5),   // Duty cycle for CLKOUT outputs (0.001-0.999).
        .CLKOUT4_PHASE(0.0),        // Phase offset for CLKOUT outputs (-360.000-360.000).
        .CLKOUT5_DIVIDE(1),         // Divide amount for CLKOUT (1-128)
        .CLKOUT5_DUTY_CYCLE(0.5),   // Duty cycle for CLKOUT outputs (0.001-0.999).
        .CLKOUT5_PHASE(0.0),        // Phase offset for CLKOUT outputs (-360.000-360.000).
        .CLKOUT6_DIVIDE(1),         // Divide amount for CLKOUT (1-128)
        .CLKOUT6_DUTY_CYCLE(0.5),   // Duty cycle for CLKOUT outputs (0.001-0.999).
        .CLKOUT6_PHASE(0.0),        // Phase offset for CLKOUT outputs (-360.000-360.000).
        .DIVCLK_DIVIDE(1),          // Master division value
        .IS_CLKFBIN_INVERTED(1'b0), // Optional inversion for CLKFBIN
        .IS_CLKIN1_INVERTED(1'b0),  // Optional inversion for CLKIN1
        .IS_PWRDWN_INVERTED(1'b0),  // Optional inversion for PWRDWN
        .IS_RST_INVERTED(1'b0),     // Optional inversion for RST
        .REF_JITTER1(0.0),          // Reference input jitter in UI (0.000-0.999).
        .STARTUP_WAIT("FALSE")      // Delays DONE until MMCM is locked
    )
    MMCME4_BASE_inst (
        .CLKFBOUT(mmcm_fbclk_s),   // 1-bit output: Feedback clock pin to the MMCM
        .CLKFBOUTB(), // 1-bit output: Inverted CLKFBOUT
        .CLKOUT0(clk_800MHz),     // 1-bit output: CLKOUT0
        .CLKOUT0B(),   // 1-bit output: Inverted CLKOUT0
        .CLKOUT1(),     // 1-bit output: CLKOUT1
        .CLKOUT1B(),   // 1-bit output: Inverted CLKOUT1
        .CLKOUT2(),     // 1-bit output: CLKOUT2
        .CLKOUT2B(),   // 1-bit output: Inverted CLKOUT2
        .CLKOUT3(),     // 1-bit output: CLKOUT3
        .CLKOUT3B(),   // 1-bit output: Inverted CLKOUT3
        .CLKOUT4(),     // 1-bit output: CLKOUT4
        .CLKOUT5(),     // 1-bit output: CLKOUT5
        .CLKOUT6(),     // 1-bit output: CLKOUT6
        .LOCKED(mmcm_locked_s),       // 1-bit output: LOCK
        .CLKFBIN(mmcm_fbclk_s),     // 1-bit input: Feedback clock pin to the MMCM
        .CLKIN1(clk_100MHz),       // 1-bit input: Primary clock
        .PWRDWN(1'b0),       // 1-bit input: Power-down
        .RST(reset)              // 1-bit input: Reset
    );

    // clk_800MHz
    logic channel_rx_d1_r = 0, channel_rx_d2_r = 0, channel_rx_d3_r = 0;
    always_ff @(posedge clk_800MHz) begin
        channel_rx_d3_r <= channel_rx_d2_r;
        channel_rx_d2_r <= channel_rx_d1_r;
        channel_rx_d1_r <= channel_rx;
    end

    logic [31:0] cfg_datarate_i; 
    logic        cfg_datafresh_i; 
    logic [31:0] cfg_datarate_r = 0; 
    logic [31:0] cfg_datarate_sub1_r = 0; 
    logic [31:0] cfg_datarate_sub2_r = 0; 
    logic [31:0] cfg_datarate_m3d4_r = 0; 
    logic [31:0] cfg_datarate_m1d4_r = 0; 
    logic        cfg_datafresh_r = 0; 

    vio_0 vio_0_inst (
      .clk(clk_800MHz),                // input wire clk
      .probe_out0(cfg_datafresh_i),  // output wire [0 : 0] probe_out0
      .probe_out1(cfg_datarate_i)  // output wire [31 : 0] probe_out1
    );

    logic startbit_detected_s;

    assign startbit_detected_s = channel_rx_d3_r & ~channel_rx_d2_r;


    ila_0 ila_uartio_inst (
        .clk(clk_800MHz), // input wire clk

        .probe0(channel_rx_d3_r), // input wire [0:0]  probe0  
        .probe1(state_r), // input wire [7:0]  probe1 
        .probe2(channel_tx) // input wire [0:0]  probe2
    );

    enum logic[5:0] {
        RESET        ,
        IDLE         ,
        GET_STARTBIT ,
        GET_DATA     ,
        GET_PARITY   ,
        GET_STOPBIT  
    } state_r, state_s;

    logic [2:0] rx_getdata_cnt_r;
    logic [7:0] rx_data_r;
    logic       rx_valid_r;
    logic       rx_error_flag_s;
    logic       parity_error_flag_r;
    logic       undetect_error_flag_r;
    logic       nostop_error_flag_r;

    assign rx_error_flag_s = parity_error_flag_r | undetect_error_flag_r | nostop_error_flag_r;
    
    always_ff @(posedge clk_800MHz) begin
        if (reset)
            state_r <= RESET;
        else
            state_r <= state_s;
    end

    logic next_state_flag_r;
    logic capture_value_r;

    always_comb begin
        case (state_r)
        RESET: state_s = IDLE;
        IDLE: begin
            if (startbit_detected_s)
                state_s = GET_STARTBIT;
            else
                state_s = IDLE;
        end
        GET_STARTBIT: begin
            if (next_state_flag_r) begin
                if (~capture_value_r)
                    state_s = GET_DATA;
                else
                    state_s = IDLE;
            end else begin
                state_s = GET_STARTBIT;
            end
        end
        GET_DATA: begin
            if (next_state_flag_r && rx_getdata_cnt_r == 0) 
                state_s = GET_PARITY;
            else
                state_s = GET_DATA;
        end
        GET_PARITY: begin
            if (next_state_flag_r)
                state_s = GET_STOPBIT;
            else
                state_s = GET_PARITY;
        end
        GET_STOPBIT: begin
            if (next_state_flag_r)
                if (startbit_detected_s)
                    state_s = GET_STARTBIT;
                else
                    state_s = IDLE;
            else
                state_s = GET_STOPBIT;
        end
        default: state_s = IDLE;
        endcase
    end

    logic [31:0] capture_asserted_cnt_r = 'd0;
    logic [31:0] capture_total_cnt_r = 'd0;

    logic cnt_fresh_s;

    assign cnt_fresh_s = (capture_total_cnt_r == cfg_datarate_sub1_r) ? 1'b1 : 1'b0;

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        IDLE: begin
            capture_asserted_cnt_r <= 'd0;
        end
        default: begin
            if (cnt_fresh_s)
                capture_asserted_cnt_r <= 'd0;
            else if (channel_rx_d3_r)
                capture_asserted_cnt_r <= capture_asserted_cnt_r + 'd1;
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        IDLE: begin
            capture_total_cnt_r <= 'd0;
        end
        default: begin
            if (cnt_fresh_s)
                capture_total_cnt_r <= 'd0;
            else 
                capture_total_cnt_r <= capture_total_cnt_r + 'd1;
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        RESET, IDLE: begin
            rx_valid_r <= 1'b0;
        end 
        GET_STOPBIT: begin
            if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
                rx_valid_r <= 1'b1;
            end
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        RESET, IDLE: begin
            nostop_error_flag_r <= 1'b0;
        end 
        GET_STOPBIT: begin
            if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
                if (~capture_value_r) begin
                    nostop_error_flag_r <= 1'b1;
                end
            end
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        RESET, IDLE: begin
            undetect_error_flag_r <= 1'b0;
        end 
        GET_STARTBIT, GET_DATA, GET_PARITY, GET_STOPBIT: begin
            if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
                if (capture_asserted_cnt_r > cfg_datarate_m3d4_r) begin
                    // undetect_error_flag_r <= 1'b0;
                end else if (capture_asserted_cnt_r < cfg_datarate_m1d4_r) begin
                    // undetect_error_flag_r <= 1'b0;
                end else begin
                    undetect_error_flag_r <= 1'b1;
                end
            end
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        RESET: begin
            parity_error_flag_r <= 1'b0;
        end
        GET_PARITY: begin
            if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
                if (capture_value_r == ^rx_data_r[7:0]) begin
                    parity_error_flag_r <= 1'b0;
                end else begin
                    parity_error_flag_r <= 1'b1;
                end
            end
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        IDLE: begin
            next_state_flag_r <= 1'b0;
        end 
        default: begin
            if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
                next_state_flag_r <= 1'b1;
            end else if (capture_total_cnt_r == 0) begin
                next_state_flag_r <= 1'b0;
            end
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        IDLE: begin
            capture_value_r <= 1'b0;
        end 
        default: begin
            if (capture_total_cnt_r == cfg_datarate_sub2_r) begin
                if (capture_asserted_cnt_r > cfg_datarate_m3d4_r) begin
                    capture_value_r <= 1'b1;
                end else if (capture_asserted_cnt_r < cfg_datarate_m1d4_r) begin
                    capture_value_r <= 1'b0;
                end
            end
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (state_s)
        GET_DATA: begin
            if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
                rx_getdata_cnt_r <= rx_getdata_cnt_r + 'd1;
                rx_data_r[rx_getdata_cnt_r] <= capture_value_r;
            end
        end 
        default: begin
            rx_getdata_cnt_r <= 3'd0;
        end
        endcase
    end

    ila_0 ila_0_inst (
        .clk(clk_800MHz), // input wire clk

        .probe0(rx_valid_r), // input wire [0:0]  probe0  
        .probe1(rx_data_r), // input wire [7:0]  probe1 
        .probe2(rx_error_flag_s) // input wire [0:0]  probe2
    );

   串口发送模块的代码如下,它将收到的未检测出错误的数据转发给主机。

enum logic [5:0] {
        TX_RESET           ,
        TX_IDLE            ,
        TX_SEND_STARTBIT   ,
        TX_SEND_DATABIT    ,
        TX_SEND_PARITYBIT  ,
        TX_SEND_STOPBIT
    } send_state_r, send_state_s;

    always_ff @(posedge clk_800MHz) begin
        if (reset) begin
            send_state_r <= TX_RESET;
        end else begin
            send_state_r <= send_state_s;
        end
    end

    logic send_nextstate_r;
    logic [2:0]  tx_senddata_cnt_r;

    logic [7:0] tx_data_r;
    logic       tx_valid_r;

    always_comb begin
        case (send_state_r)
        TX_RESET: send_state_s = TX_IDLE;
        TX_IDLE: begin
            if (tx_valid_r) 
                send_state_s = TX_SEND_STARTBIT;
            else
                send_state_s = TX_IDLE;
        end
        TX_SEND_STARTBIT: begin
            if (send_nextstate_r) begin
                send_state_s = TX_SEND_DATABIT;
            end else begin
                send_state_s = TX_SEND_STARTBIT;
            end
        end
        TX_SEND_DATABIT: begin
            if (send_nextstate_r && tx_senddata_cnt_r == 3'd0) begin
                send_state_s = TX_SEND_PARITYBIT;
            end else begin
                send_state_s = TX_SEND_DATABIT;
            end
        end
        TX_SEND_PARITYBIT: begin
            if (send_nextstate_r) begin
                send_state_s = TX_SEND_STOPBIT;
            end else begin
                send_state_s = TX_SEND_PARITYBIT;
            end
        end
        TX_SEND_STOPBIT: begin
            if (send_nextstate_r) begin
                if (tx_valid_r) begin
                    send_state_s = TX_SEND_STARTBIT;
                end else begin
                    send_state_s = TX_IDLE;
                end
            end else begin
                send_state_s = TX_SEND_STOPBIT;
            end
        end
        default: send_state_s = TX_RESET;
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (send_state_s)
        TX_IDLE, TX_SEND_STOPBIT: begin
            if (rx_valid_r & ~rx_error_flag_s) begin
                tx_valid_r <= rx_valid_r;
                tx_data_r <= rx_data_r;
            end else if (~rx_valid_r & tx_valid_r) begin
                tx_valid_r <= 1'b0;
            end
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (send_state_s)
        TX_IDLE, TX_SEND_STOPBIT: begin
            if (~rx_valid_r) begin
                cfg_datafresh_r <= cfg_datafresh_i;
                if (cfg_datafresh_i) begin
                    cfg_datarate_r <= cfg_datarate_i;
                    cfg_datarate_sub1_r <= cfg_datarate_i - 1;
                    cfg_datarate_sub2_r <= cfg_datarate_i - 2;
                    cfg_datarate_m3d4_r <= (cfg_datarate_i >> 1) + (cfg_datarate_i >> 2);
                    cfg_datarate_m1d4_r <= (cfg_datarate_i >> 2);
                end
            end
        end
        endcase
    end

    logic [31:0] sent_total_cnt_r;

    always_ff @(posedge clk_800MHz) begin
        case (send_state_s)
        default: begin
            if (sent_total_cnt_r == cfg_datarate_sub1_r) begin
                send_nextstate_r <= 1'b1;
            end else begin
                send_nextstate_r <= 1'b0;
            end
        end
        TX_IDLE: begin
        end
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (send_state_s)
        default: begin
            if (sent_total_cnt_r == cfg_datarate_sub1_r) begin
                sent_total_cnt_r <= 'd0;
            end else begin
                sent_total_cnt_r <= sent_total_cnt_r + 1;
            end
        end
        TX_IDLE: sent_total_cnt_r <= 'd0;
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (send_state_s)
        TX_RESET, TX_IDLE, TX_SEND_STOPBIT: channel_tx <= 1'b1;
        TX_SEND_STARTBIT:  channel_tx <= 1'b0;
        TX_SEND_DATABIT:   channel_tx <= tx_data_r[tx_senddata_cnt_r];
        TX_SEND_PARITYBIT: channel_tx <= ^tx_data_r[7:0];
        endcase
    end

    always_ff @(posedge clk_800MHz) begin
        case (send_state_s)
        TX_SEND_STARTBIT: begin
            tx_senddata_cnt_r <= 3'd0;
        end
        TX_SEND_DATABIT: begin
            if (sent_total_cnt_r == cfg_datarate_sub1_r) begin
                tx_senddata_cnt_r  <= tx_senddata_cnt_r + 1;
            end
        end
        endcase
    end
endmodule

约束文件实现(ft4232hl_uart.xdc)

   对应约束文件如下:

set_property BOARD_PART_PIN default_100mhz_clk_p [get_ports default_clk_p]
set_property BOARD_PART_PIN default_100mhz_clk_n [get_ports default_clk_n]
set_property BOARD_PART_PIN CPU_RESET [get_ports reset]
set_property BOARD_PART_PIN USB_UART1_TX [get_ports channel_tx]
set_property BOARD_PART_PIN USB_UART1_RX [get_ports channel_rx]
set_property BOARD_PART_PIN USB_UART1_CTS [get_ports channel_cts]
set_property BOARD_PART_PIN USB_UART1_RTS [get_ports channel_rts]

# auto generate
set_property IOSTANDARD DIFF_SSTL12 [get_ports default_clk_p]
set_property IOSTANDARD DIFF_SSTL12 [get_ports default_clk_n]
set_property PACKAGE_PIN BH51 [get_ports default_clk_p]
set_property PACKAGE_PIN BJ51 [get_ports default_clk_n]
set_property IOSTANDARD LVCMOS12 [get_ports reset]
set_property PACKAGE_PIN BM29 [get_ports reset]
set_property IOSTANDARD LVCMOS18 [get_ports channel_tx]
set_property PACKAGE_PIN BN26 [get_ports channel_tx]
set_property IOSTANDARD LVCMOS18 [get_ports channel_rx]
set_property PACKAGE_PIN BP26 [get_ports channel_rx]

# STA constraint
create_clock -period 10.000 -waveform {0.000 5.000} [get_ports default_clk_p]
create_generated_clock -source [get_ports default_clk_p] -divide_by 1 [get_pins IBUFDS_inst/O]
# create_clock -period 2.500 -waveform {0.000 1.250} [get_pins MMCME4_BASE_inst/CLKOUT0]

set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]
set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]
set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]
connect_debug_port dbg_hub/clk [get_nets clk_800MHz_BUFG]

仿真文件实现(ft4232hl_uart_tb.sv)

`timescale 1ns / 1ps
//
// Company: 
// Engineer: wjh776a68
// 
// Create Date: 03/15/2024 10:35:44 PM
// Design Name: 
// Module Name: ft4232hl_uart_tb
// Project Name: 
// Target Devices: XCVU37P
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

module ft4232hl_uart_tb();

    bit   clk_100MHz    ;
    logic reset         ;
    bit channel_rx= 1'b0;
    logic channel_tx    ;

    always #5 clk_100MHz = ~clk_100MHz;

    ft4232hl_uart ft4232hl_uart_inst(
        .default_clk_p    (clk_100MHz),
        .default_clk_n    (~clk_100MHz),
        .reset         (reset     ),
                                  
        .channel_rx    (channel_rx),
        .channel_tx    (channel_tx)
    );

    initial begin
        ft4232hl_uart_inst.cfg_datafresh_i <= 1'b0;
        ft4232hl_uart_inst.cfg_datarate_i <= 0;

        @(posedge ft4232hl_uart_inst.mmcm_locked_s);
         
        ft4232hl_uart_inst.cfg_datafresh_i <= 1'b1;
        ft4232hl_uart_inst.cfg_datarate_i <= 217;

        @(posedge clk_100MHz);

        ft4232hl_uart_inst.cfg_datafresh_i <= 1'b0;
        ft4232hl_uart_inst.cfg_datarate_i <= 0;


    end
  
    bit clk_1_8432MHz ;
    bit [2:0] cnt;
    always #(500 / 1.8432) clk_1_8432MHz = ~clk_1_8432MHz;
    initial begin
        reset = 1'b1;
        @(posedge clk_1_8432MHz);
        reset <= 1'b0;
    end

    enum logic [3:0] {
        IDLE       = 4'd0 ,
        START_BIT  = 4'd1 ,
        DATA_BIT   = 4'd2 ,
        PARITY_BIT = 4'd3 ,
        STOP_BIT   = 4'd4 
    } state_r, state_s;

    always_ff @(posedge clk_1_8432MHz) begin
        if (reset) begin
            state_r <= IDLE;
        end else begin
            state_r <= state_s;
        end
    end

    logic [4:0] idle_cnt;
    always_comb begin
        case (state_r)
        IDLE: begin
            if (idle_cnt == 20) begin
                state_s = START_BIT;
            end else begin
                state_s = IDLE;
            end
        end
        START_BIT: state_s = DATA_BIT;
        DATA_BIT: begin
            if (cnt == 0)
                state_s = PARITY_BIT;
            else
                state_s = DATA_BIT;
        end
        PARITY_BIT: state_s = STOP_BIT;
        STOP_BIT: begin
            state_s = START_BIT;
            // state_s = IDLE;
        end
        endcase
    end

    logic [7:0] data_tosend = 8'h35;

    always_ff @(posedge clk_1_8432MHz) begin
        case (state_s)
        IDLE: channel_rx <= 1'b1;
        START_BIT: begin
            cnt <= 'd0;
            channel_rx <= 1'b0;
        end
        DATA_BIT: begin
            cnt <= cnt + 1;
            channel_rx <= data_tosend[cnt];
        end
        PARITY_BIT: begin
            channel_rx <= ^data_tosend[7:0];
        end
        STOP_BIT: begin
            channel_rx <= 1'b1;
        end
        endcase
    end

    always_ff @(posedge clk_1_8432MHz) begin
        case (state_s)
        IDLE: idle_cnt <= idle_cnt + 1;
        default: idle_cnt <= 0;
        endcase
    end

endmodule

实机测试

   由于是未来科技制造的芯片,需要使用来自未来科技编写的VCP驱动程序将一个USB设备拓展为4个串口设备,方能进行串口通信。
   官方提供了多平台的驱动程序,然而其中仅Windows驱动存在近期更新,故本文串口通信测试在Windows虚拟机上进行。

在这里插入图片描述

参考链接:

  1. 串口通讯UART/RS232/RS485/RS-422笔记
  2. 俺也学不会FPGA的博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/462651.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入探讨医保购药APP的技术架构与设计思路

随着移动互联网的发展&#xff0c;医疗保健行业也迎来了数字化转型的浪潮。医保购药APP作为医保体系数字化的一部分&#xff0c;其技术架构和设计思路至关重要。接下来&#xff0c;小编将为您讲解医保购药APP的技术架构与设计思路&#xff0c;为相关从业者提供参考和启发。 一、…

【IC设计】Verilog线性序列机点灯案例(二)(小梅哥课程)

文章目录 该系列目录&#xff1a;设计目标设计思路RTL 及 Testbench仿真结果存在的问题&#xff1f;改善后的代码RTL代码testbench代码 仿真结果 案例和代码来自小梅哥课程&#xff0c;本人仅对知识点做做笔记&#xff0c;如有学习需要请支持官方正版。 该系列目录&#xff1a;…

Java手写简易数据库--持续更新中

MYDB 0. 项目结构0.1 引用计数缓存框架为什么不使用LRU引用计数缓存缓存框架实现 0.2 共享内存数组 1. 事务管理器--TM1.1 XID 文件XID 规则XID 文件结构读取方式事务状态 1.2 代码实现 2. 数据管理器--DM2.1 页面缓存页面结构页面缓存数据页管理第一页普通页 2.2 日志文件 3. …

基于Spring Boot+Vue的校园二手交易平台

目录 一、 绪论1.1 开发背景1.2 系统开发平台1.3 系统开发环境 二、需求分析2.1 问题分析2.2 系统可行性分析2.2.1 技术可行性2.2.2 操作可行性 2.3 系统需求分析2.3.1 学生功能需求2.3.2 管理员功能需求2.3.3游客功能需求 三、系统设计3.1 功能结构图3.2 E-R模型3.3 数据库设计…

CSS3病毒病原体图形特效

CSS3病毒病原体图形特效&#xff0c;源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 下载地址 CSS3病毒病原体图形特效代码

Django 解决新建表删除后无法重新创建等问题

Django 解决新建表删除后无法重新创建等问题 问题发生描述处理办法首先删除了app对应目录migrations下除 __init__.py以外的所有文件:然后&#xff0c;删除migrations中关于你的app的同步数据数据库记录最后&#xff0c;重新执行迁移插入 问题发生描述 Django创建的表&#xf…

JS引用类型

在JavaScript中&#xff0c;除了基本类型&#xff08;如字符串、数字、布尔值、null和undefined&#xff09;之外&#xff0c;还有引用类型。引用类型包括对象、数组和函数。它们在内存中的存储方式与基本类型不同&#xff0c;因为它们在内存中存储的是对实际数据的引用&#x…

力扣热题100_矩阵_240_搜索二维矩阵 II

文章目录 题目链接解题思路解题代码 题目链接 240. 搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xf…

C#对ListBox控件中的数据进行的操作

目录 1.添加数据&#xff1a; 2.删除数据&#xff1a; 3.清空数据&#xff1a; 4.选择项&#xff1a; 5.排序&#xff1a; 6.获取选中的项&#xff1a; 7.获取ListBox中的所有项&#xff1a; 8.综合示例 C#中对ListBox控件中的数据进行的操作主要包括添加、删除、清空、…

RISC-V Optimization Guide(笔记)

官网发表的文章地址&#xff1a;RISC-V Optimization Guide B站有人做过解读视频&#xff0c;这篇文章也是看视频时做的笔记&#xff1a;视频地址 一、标量整数优化 1.1 常量的具体化 使用lui/addiw将立即数加载至寄存器&#xff0c;当立即数低12位的最高位为1时&#xff0c…

uniapp 利用uni-list 和 uni-load-more 组件上拉加载列表

列表的加载动作&#xff0c;在移动端开发中随处可见&#xff0c;笔者也是经常用到。今天正好有空&#xff0c;做一个总结&#xff0c;方便以后使用。uniapp 利用uni-list 和 uni-load-more 组件上拉加载列表操作步骤如下&#xff1a;1、资料准备 1&#xff09;、uni-load-more…

【安全类书籍-3】XSS跨站脚剖析与防御

目录 内容简介 作用 下载地址 内容简介 这本书涵盖以下几点: XSS攻击原理:解释XSS是如何利用Web应用未能有效过滤用户输入的缺陷,将恶意脚本注入到网页中,当其他用户访问时被执行,实现攻击者的目的,例如窃取用户会话凭证、实施钓鱼攻击等。 XSS分类:分为存储型XSS(…

MongoDB——linux中yum命令安装及配置

一、创建mongodb-org-3.4.repo文件 vi /etc/yum.repos.d/mongodb-org-3.4.repo 将下面内容添加到创建的文件中 [mongodb-org-3.4] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.4/x86_64/ gpgcheck1 enabled1 gpgkeyhttps://www…

力扣每日一题 矩阵中移动的最大次数 DP

Problem: 2684. 矩阵中移动的最大次数 复杂度 ⏰ 时间复杂度: O ( n m ) O(nm) O(nm) &#x1f30e; 空间复杂度: O ( n m ) O(nm) O(nm) Code class Solution { public int maxMoves(int[][] grid){int n grid.length;int m grid[0].length;int[][] f new int[n][m]…

sqllab第二十五A关通关笔记

知识点&#xff1a; 数值型注入双写绕过 oorranand这里不能用错误注入&#xff08;固定错误回显信息&#xff09;联合注入 测试发现跟25关好像一样&#xff0c;就是过滤了and or # 等东西 构造payload:id1/0 发现成功运算了&#xff0c;这是一个数值型的注入 构造payload:id…

MySQL基础架构

文章目录 MySQL基础架构一、连接器 - 建立连接&#xff0c;权限认证二、查缓存 - 提高效率三、分析器 - 做什么四、优化器 - 怎么做五、执行器 - 执行语句六、存储引擎1、存储引擎的概述2、存储引擎的对比3、存储引擎的命令4、存储引擎的选择 MySQL基础架构 大体来说&#xff…

【数据结构】二叉树OJ题(C语言实现)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

Ubuntu 14.04:安装 PaddleOCR 2.3

目录 一、说明 1.1 如何选择版本 1.2 查看 github 中的 PaddleOCR 版本 二、安装 2.1 安装前环境准备 2.2 下载包 2.3 解压 2.4 安装依赖库 异常处理&#xff1a;Read timed out. 2.5 下载推理模型&#xff1a;inference 2.5.1 模型存放位置 2.5.2 模型下载链接 2.5.…

云原生部署手册02:将本地应用部署至k8s集群

&#xff08;一&#xff09;部署集群镜像仓库 1. 集群配置 首先看一下集群配置&#xff1a; (base) ➜ ~ multipass ls Name State IPv4 Image master Running 192.168.64.5 Ubuntu 22.04 LTS1…

MySQL--深入理解MVCC机制原理

什么是MVCC&#xff1f; MVCC全称 Multi-Version Concurrency Control&#xff0c;即多版本并发控制&#xff0c;维持一个数据的多个版本&#xff0c;主要是为了提升数据库的并发访问性能&#xff0c;用更高性能的方式去处理数据库读写冲突问题&#xff0c;实现无锁并发。 什…