FPGA - UART シリアルポート出力に基づく超音波測距


序文

環境:
1. Quartus18.0
2. vscode
3. ボードモデル: EP4CE10F17C8
4. 超音波モジュール: HC_SR04
要件:
EP4CE10F17C8 開発ボードを使用して超音波検出モジュール (HC_SR04) を駆動し、測定データをシリアル ポート アシスタントに表示します。


1.超音波モジュールの紹介

1. 製品の特長

HC-SR04超音波測距モジュールは、2cm~400cmの非接触距離感知機能を提供でき、測距精度は3mmに達することができ、モジュールには超音波送信機、受信機、制御回路が含まれています。
基本的な動作原理:

(1) IO ポート TRIG を使用してレンジングをトリガーし、少なくとも 10us のハイレベル信号を与えます。
(2) モジュールは 40khz の 8 つの方形波を自動的に送信し、信号の戻りがあるかどうかを自動的に検出します;
(3) 信号の戻りがある場合、IO ポート ECHO を通じて High レベルが出力され、High の期間はレベルは超音波を送信してから戻ってくるまでの時間です。テスト距離 = (高レベル時間 * 音速 (340M/S))/2;

ここに画像の説明を挿入

2. 超音波モジュールのタイミング図

ここに画像の説明を挿入

上のタイミング図は、10uS を超えるパルス トリガー信号を提供するだけで、モジュールが内部で 8 つの 40kHz 周期レベルを送信し、エコーを検出することを示しています。エコー信号が検出されると、エコー信号が出力されます。エコー信号のパルス幅は測定された距離に比例します。したがって、信号を送信してからエコー信号を受信するまでの時間間隔から距離を計算できます。

2. システム設計

1. システムモジュールのブロック図

ここに画像の説明を挿入

2.RTLビュー

ここに画像の説明を挿入

3. ソースコード

1. div_clk_us (1usの分周)

/**************
芯片晶振为50MHZ,HC_SR04需要一个10us的以上脉冲触发信号
所以这里我们需要对系统时钟进行分频,方便我们产生10us的持续电平
**************/
module div_clk_us (
    input sys_clk,
    input sys_rst_n,

    output wire  clk_us
);

//根据晶振换算,1us只需要计数50次即可

parameter [5:0] MAX_us = 6'd49;
reg [5:0] cnt;
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)begin
        cnt <= 6'd0;
    end
    else if(cnt == MAX_us)begin
        cnt <= 6'd0;
    end
    else begin
        cnt <= cnt + 6'd1;
    end
end
assign clk_us = cnt >= MAX_us;
endmodule

2. 超音波を駆動する信号を生成します。

/****************
根据分频的1us时钟,产生一个持续10us的电平用于驱动HC_SR04
最好是稍微大于10us,这样稳妥一些
****************/
module trig_driver(
    input       sys_us        ,//1us时钟
    input       sys_rst_n     ,

    output      trig          //驱动超声波的信号
);

parameter T = 19'd29_9999;//设置触发信号的周期,这里设置得越小,其触发越频繁,应该返回的距离更新更频繁

reg [18:0] cnt;

always @(posedge sys_us) begin// or negedge sys_rst_n
    if(!sys_rst_n)begin
        cnt <= 19'd0;
    end
    else if(cnt == T)begin
        cnt <= 19'd0;
    end
    else begin
        cnt <= cnt + 1'd1;
    end
end
//15us的高电平
assign trig = (cnt <15 ) ? 1'b1 : 1'b0;//正确的,只是时间太短,观察不到,目前应该是串口问题
endmodule

3. シリアル送信モジュール

module uart_send
#(
    parameter  CLK        =   26'd50000000    ,    // 时钟频率
    parameter  BAUD        =   17'd115200           // 波特率
)
(
    input   wire            clk         ,
    input   wire            rstn        ,   
    input   wire    [7 : 0] data_in     ,   // 需要发送的数据
    input   wire            flag_in     ,   // 数据接收标志位,既发送标志位

    output  wire            tx_done     , 
    output  reg             UART_tx         // 串口输出位         
);

    localparam Baud_Clk     =   CLK/BAUD       ;    // 传输每个 Baud 需要的时钟数

    reg             tx_en       ;   // 发送使能
    reg             flag_bit    ;   // 比特标志位,采用下降沿发送
    reg [8 : 0]     cnt_baud    ;   // 波特率计数器
    reg [3 : 0]     cnt_bit     ;   // 比特计数器

    assign tx_done = cnt_bit == 4'd9 && flag_bit == 1'b1;

    // 发送使能
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            tx_en <= 1'b0;
        end
        // 已经发送了十位 bit 并且到达下一个下降沿,输入只需要判断到数据位最后一位,输出则需要判断完整输出
        else if(cnt_bit == 4'd9 && flag_bit == 1'b1) begin
            tx_en <= 1'b0;
        end
        else if(flag_in == 1'b1) begin
            tx_en <= 1'b1;
        end
    end

    // 波特计数器
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_baud <= 9'd0;
        end
        // 传输完成所有波特或者使能失效,表示发送结束
        else if(cnt_baud == Baud_Clk - 1'b1 || tx_en == 1'b0) begin
            cnt_baud <= 9'd0;
        end
        else begin
            cnt_baud <= cnt_baud + 9'd1;
        end
    end

    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            flag_bit <= 1'b0;
        end
        // 只有刚开始发送的一瞬间会产生一个时钟周期上升沿和下降沿
        else if(cnt_baud == 9'd1) begin
            flag_bit <= 1'b1;
        end
        else begin
            flag_bit <= 1'b0;
        end
    end

    // 计数10分有效数据位
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_bit <= 4'd0;
        end
        // 已经发送了十位 bit 并且到达下一个下降沿
        else if(cnt_bit == 4'd9 && flag_bit == 1'b1) begin
            cnt_bit <= 4'd0;
        end
        // 使能有效,下降沿发送数据
        else if(flag_bit == 1'b1 && tx_en == 1'b1) begin
            cnt_bit <= cnt_bit + 4'd1;
        end
        else begin
            cnt_bit <= cnt_bit;
        end
    end

    // 满足 RS232 协议 起始位为 0,停止位为 1,并按位输出
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            UART_tx <= 1'd1;
        end
        // 下降沿发送数据
        else if(flag_bit == 1'b1) begin
            case (cnt_bit)
                0:       UART_tx <= 1'd0        ;
                1:       UART_tx <= data_in[0]  ;
                2:       UART_tx <= data_in[1]  ;
                3:       UART_tx <= data_in[2]  ;
                4:       UART_tx <= data_in[3]  ;
                5:       UART_tx <= data_in[4]  ;
                6:       UART_tx <= data_in[5]  ;
                7:       UART_tx <= data_in[6]  ;
                8:       UART_tx <= data_in[7]  ;
                9:       UART_tx <= 1'd1        ;
                default: UART_tx <= 1'd1        ;
            endcase
        end
    end
endmodule //UART_send

4. HC_SR04_uart (最上位ファイル)

module HC_SR04_uart(
    input               sys_clk       ,
    input               sys_rst_n     ,
    input               echo          ,
    input        		uart_rx		  , // 串口输入 

    output              trig          ,         
    output              uart_tx       //串口发送端口
);
wire             clk_us;
wire [18:0]      data_o_r;//待发送的数据
//时钟分频
div_clk_us div_clk_us_inst(
    /*input */      .sys_clk         (sys_clk  ),
    /*input */      .sys_rst_n       (sys_rst_n),

    /*output*/      .clk_us          (clk_us)
);
//产生驱动超声波信号
trig_driver trig_driver_inst(
    /*input */      .sys_us        (clk_us),//1us时钟
    /*input */      .sys_rst_n     (sys_rst_n),

    /*output*/      .trig          (trig)//驱动超声波的信号
);
//对返回来的echo信号进行计算得出距离
echo_driver echo_driver_inst(
    /*input        */   .sys_clk         (sys_clk),
    /*input        */   .sys_us          (clk_us),
    /*input        */   .sys_rst_n       (sys_rst_n),
    /*input        */   .echo            (echo),

    /*output [18:0]*/   .data_o          (data_o_r)//检测距离,保留三位小数,*1000实现
);
//初步想法是使用串口发送模块直接操作,不需要串口回环,否则需要发送到接收,接收模块再发送给发送模块,发送模块再发送给PC
uart_driver2 uart_driver2_inst(
	.clk         (sys_clk  ),
	.rstn        (sys_rst_n),
	.data_in	 (data_o_r	),
    .UART_rx     (uart_rx),

	.UART_tx     (uart_tx	)
);
endmodule

4.効果

FPGAシリアルポート出力範囲情報


V. まとめ

FPGAレンジングのデジタル管表示とSTM32のレンジングシリアルポートの出力について書きましたが、実はこの記事の内容は以前に完成していました。シリアル ポート ループバックの片側を以前に学習したので、それをもう一度入力して、FPGA のシリアル ポート出力を実現しました。実行しましたが、それでも 1 日苦労し、午後中ずっとシミュレーションと SignalTap II が信号をキャプチャしました。しかし、今回は前回よりも理解が深まり、得るものが多かったです。

6. 参考文献

1. FPGA に基づく超音波測距 - デジタル管ディスプレイ
2. ソースコード: https://github.com/no1jiangjiang/HC-SR04_uart_FPGA

おすすめ

転載: blog.csdn.net/qq_52215423/article/details/132034419