Verilog_RTL 설계/Basys_3

[FSM] Ultrasonic_sensor_(HC_SR04)

juniha 2025. 7. 16. 00:05

목적

  • 초음파 센서로부터 측정된 거리를 사용자에게 직관적으로 7세그먼트 디스플레이로 보여주는 임베디드 하드웨어 시스템을 Verilog로 구현하는 것

 

  • HC-SR04 제어
    • trig를 통해 센서에 초음파 발사 신호를 주고
    • echo 신호의 길이를 측정해 물체까지의 거리(cm)를 계산
    • 거리 = echo 시간(us) / 58 공식 사용.
  • 이진 거리 → BCD 변환
    • 계산된 거리 값은 이진(binary)로 저장
    • 7세그먼트에 표시하려면 BCD(Binary Coded Decimal)로 변환
  • 7세그먼트에 출력
    • 변환된 BCD 값을 4자리 7세그먼트 디스플레이에 반복적으로 출력하여

 

Datasheet

Distance 공식

사용 코드_ 상위 모듈

module hc_sr04_top(
    input clk,             // 시스템 클럭 입력
    input reset_p,         // 비동기 리셋 입력 (active high)
    input echo,            // 초음파 센서의 Echo 신호 입력
    output trig,           // 초음파 센서의 Trig 신호 출력
    output [7:0] seg_7,    // 7-세그먼트 디스플레이 제어 신호 (a~g, dp)
    output [3:0] com,      // 7-세그먼트 디스플레이 공통 단자 (자릿수 선택)
    output [15:0] debug_led // 디버깅용 LED 출력 (센서 내부 상태 등을 확인)
);

    // 초음파 센서 거리 측정 결과 (단위: cm 또는 us, 내부 정의에 따름)
    wire [15:0] distance;

    // 초음파 센서 제어 및 거리 계산 모듈 인스턴스화
    hc_sr04_cntr sr04 (
        .clk(clk), 
        .reset_p(reset_p), 
        .echo(echo),        // 센서의 Echo 입력
        .trig(trig),        // 센서에 Trig 출력
        .distance(distance),// 측정된 거리 값 출력
        .debug_led(debug_led) // 디버그용 내부 신호 출력
    );

    // 이진 거리 값을 BCD (Binary-Coded Decimal)로 변환
    wire [15:0] distance_bcd;
    bin_to_dec bcd_sec (
        .bin(distance), 
        .bcd(distance_bcd)  // 변환된 BCD 값 출력 (각 자리수 4비트씩)
    );

    // 4자리 7세그먼트 디스플레이 컨트롤러
    fnd_4digit_cntr fnd (
        .clk(clk), 
        .reset_p(reset_p), 
        .value({distance_bcd[15:8], distance_bcd[7:0]}), // 상위 2자리 + 하위 2자리
        .seg_7(seg_7),  // 각 자리의 세그먼트 출력
        .com(com)       // 공통 단자 제어 (4자리 중 어떤 자리인지 선택)
    );

endmodule

사용 코드_하위 모듈

module hc_sr04_cntr(
    input clk, reset_p,              // 시스템 클럭과 비동기 리셋 입력 (reset_p: active-high)
    input echo,                      // 초음파 센서로부터의 Echo 신호 입력
    output reg trig,                 // 초음파 센서로의 Trig 출력 (펄스 발생)
    output reg [15:0] distance,      // 계산된 거리 값 출력 (단위: cm)
    output [15:0] debug_led          // 디버깅용 LED 출력 (현재 상태 확인용)
);

    // 상태 정의 (1-hot 인코딩 방식)
    parameter S_IDLE        = 4'b0001;  // 거리 측정 대기 상태
    parameter S_TRIGGER     = 4'b0010;  // Trig 신호 10us 출력
    parameter S_ECHO_HIGH   = 4'b0100;  // Echo 상승 에지 대기 상태
    parameter S_ECHO_LOW    = 4'b1000;  // Echo 하강 에지 대기 및 거리 계산 상태

    // 1us 주기의 클럭 생성 (100분주기 사용)
    wire clk_usec;
    clock_div_100 us_clk(
        .clk(clk), 
        .reset_p(reset_p), 
        .nedge_div_100(clk_usec)   // 1us마다 falling edge 발생
    );
    
    // Echo 펄스 시간 측정용 카운터 (단위: us)
    reg [19:0] count_usec;    // 최대 측정 거리 3~4m 이상도 커버 가능
    reg count_usec_e;         // 카운터 활성화 신호

    // us 단위 카운터: clk_usec이 발생할 때마다 증가
    always @(negedge clk or posedge reset_p) begin
        if(reset_p)
            count_usec = 0;
        else if(clk_usec && count_usec_e)
            count_usec = count_usec + 1;
        else if(!count_usec_e)
            count_usec = 0;
    end

    // Echo 신호의 상승/하강 에지 검출
    wire echo_nedge, echo_pedge;
    edge_detector_p ed (
        .clk(clk), 
        .reset_p(reset_p), 
        .cp(echo), 
        .p_edge(echo_pedge),   // echo가 0→1일 때 HIGH
        .n_edge(echo_nedge)    // echo가 1→0일 때 HIGH
    );

    // 상태 레지스터 및 전이 상태
    reg [3:0] state, next_state;

    // 현재 상태 디버깅용 출력
    assign debug_led[3:0] = state;

    // 상태 전이: 네거티브 클럭 에지에서 상태 업데이트
    always @(negedge clk or posedge reset_p) begin
        if(reset_p)
            state = S_IDLE;
        else
            state = next_state;
    end

    // Echo 길이를 저장할 레지스터
    reg [19:0] echo_end;

    // 메인 FSM (모든 제어 로직 포함)
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            next_state = S_IDLE;
            count_usec_e = 0;
            distance = 0;
            echo_end = 0;
            trig = 0;
        end else begin
            case(state)
                // 1. IDLE 상태: Trig 전 대기 (약 500ms 간격으로 측정)
                S_IDLE: begin
                    if(count_usec < 500_000) begin  // 약 500ms 기다림
                        count_usec_e = 1;
                    end else begin
                        count_usec_e = 0;
                        next_state = S_TRIGGER;
                    end
                end

                // 2. Trig 출력 상태: 약 10us 동안 trig=1
                S_TRIGGER: begin
                    trig = 1;  // Trig 핀 HIGH
                    if(count_usec < 10) begin       // 약 10us 유지
                        count_usec_e = 1;
                    end else begin
                        count_usec_e = 0;
                        trig = 0;                   // Trig LOW로 복원
                        next_state = S_ECHO_HIGH;
                    end
                end

                // 3. Echo 신호 상승 에지 대기 상태 (시작 시간 기준점 설정)
                S_ECHO_HIGH: begin
                    if(echo_pedge) begin
                        count_usec_e = 1;           // 시간 측정 시작
                        next_state = S_ECHO_LOW;
                    end
                end

                // 4. Echo 신호 하강 에지 감지 후 시간 계산 및 거리 계산
                S_ECHO_LOW: begin
                    if(echo_nedge) begin
                        echo_end = count_usec;               // Echo 지속 시간 저장
                        count_usec_e = 0;
                        distance = echo_end / 58;            // 거리 계산 (단위: cm)
                        next_state = S_IDLE;
                    end
                end

                default: next_state = S_IDLE;
            endcase
        end
    end
endmodule

사용 코드_XDC

## This file is a general .xdc for the Basys3 rev B board
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock signal
set_property -dict { PACKAGE_PIN W5   IOSTANDARD LVCMOS33 } [get_ports clk]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]                                                                                   s clk]


## Switches
set_property -dict { PACKAGE_PIN V16   IOSTANDARD LVCMOS33 } [get_ports reset_p]

## LEDs
set_property -dict { PACKAGE_PIN U16   IOSTANDARD LVCMOS33 } [get_ports {debug_led[0]}]
set_property -dict { PACKAGE_PIN E19   IOSTANDARD LVCMOS33 } [get_ports {debug_led[1]}]
set_property -dict { PACKAGE_PIN U19   IOSTANDARD LVCMOS33 } [get_ports {debug_led[2]}]
set_property -dict { PACKAGE_PIN V19   IOSTANDARD LVCMOS33 } [get_ports {debug_led[3]}]
set_property -dict { PACKAGE_PIN W18   IOSTANDARD LVCMOS33 } [get_ports {debug_led[4]}]
set_property -dict { PACKAGE_PIN U15   IOSTANDARD LVCMOS33 } [get_ports {debug_led[5]}]
set_property -dict { PACKAGE_PIN U14   IOSTANDARD LVCMOS33 } [get_ports {debug_led[6]}]
set_property -dict { PACKAGE_PIN V14   IOSTANDARD LVCMOS33 } [get_ports {debug_led[7]}]
set_property -dict { PACKAGE_PIN V13   IOSTANDARD LVCMOS33 } [get_ports {debug_led[8]}]
set_property -dict { PACKAGE_PIN V3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[9]}]
set_property -dict { PACKAGE_PIN W3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[10]}]
set_property -dict { PACKAGE_PIN U3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[11]}]
set_property -dict { PACKAGE_PIN P3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[12]}]
set_property -dict { PACKAGE_PIN N3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[13]}]
set_property -dict { PACKAGE_PIN P1    IOSTANDARD LVCMOS33 } [get_ports {debug_led[14]}]
set_property -dict { PACKAGE_PIN L1    IOSTANDARD LVCMOS33 } [get_ports {debug_led[15]}]


##7 Segment Display
set_property -dict { PACKAGE_PIN W7   IOSTANDARD LVCMOS33 } [get_ports {seg_7[7]}]
set_property -dict { PACKAGE_PIN W6   IOSTANDARD LVCMOS33 } [get_ports {seg_7[6]}]
set_property -dict { PACKAGE_PIN U8   IOSTANDARD LVCMOS33 } [get_ports {seg_7[5]}]
set_property -dict { PACKAGE_PIN V8   IOSTANDARD LVCMOS33 } [get_ports {seg_7[4]}]
set_property -dict { PACKAGE_PIN U5   IOSTANDARD LVCMOS33 } [get_ports {seg_7[3]}]
set_property -dict { PACKAGE_PIN V5   IOSTANDARD LVCMOS33 } [get_ports {seg_7[2]}]
set_property -dict { PACKAGE_PIN U7   IOSTANDARD LVCMOS33 } [get_ports {seg_7[1]}]

set_property -dict { PACKAGE_PIN V7   IOSTANDARD LVCMOS33 } [get_ports seg_7[0]]

set_property -dict { PACKAGE_PIN U2   IOSTANDARD LVCMOS33 } [get_ports {com[0]}]
set_property -dict { PACKAGE_PIN U4   IOSTANDARD LVCMOS33 } [get_ports {com[1]}]
set_property -dict { PACKAGE_PIN V4   IOSTANDARD LVCMOS33 } [get_ports {com[2]}]
set_property -dict { PACKAGE_PIN W4   IOSTANDARD LVCMOS33 } [get_ports {com[3]}]

##Buttons
set_property -dict { PACKAGE_PIN U18   IOSTANDARD LVCMOS33 } [get_ports start_btn]

##Pmod Header JA
set_property -dict { PACKAGE_PIN J1   IOSTANDARD LVCMOS33 } [get_ports echo];#Sch name = JA1
set_property -dict { PACKAGE_PIN L2   IOSTANDARD LVCMOS33 } [get_ports trig];#Sch name = JA2

## Configuration options, can be used for all designs
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]

## SPI configuration mode options for QSPI boot, can be used for all designs
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 33 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]

 

보드 연결

동작 영상

결과

1. Trig 신호 출력 및 Echo 감지

  • FSM 기반 제어 모듈은 일정 시간 간격으로 10μs 폭의 Trig 펄스를 센서에 출력한다. 초음파를 발사시키기 위한 트리거 역할을 하며, 이후 센서는 반사파 수신 여부에 따라 Echo 핀을 High 상태로 유지한다.

 2. Echo High 시간 측정

  • Echo 핀이 High로 전이된 시점부터 Low로 떨어질 때까지의 시간을 1μs 해상도의 카운터로 정밀하게 측정한다. 이 시간은 초음파가 왕복하는 데 걸린 시간이며,
  • 계산 공식은 거리(cm) = Echo 시간(us) / 58 이다.

 3. 거리 값 측정

  •  측정된 거리 값은 16비트 이진 데이터로 처리되고, BCD (Binary Coded Decimal) 형식으로 변환되어 7_Segment에 출력된다. 측정된 거리를 cm 단위로 출력되는 것을 확인할 수 있다.

  4. 디버깅 

  • 시스템의 상태는 FSM의 현재 상태를 반영한 debug_led[3:0] 출력을 통해 외부에서 확인 할 수 있다.

  5. 동작 영상

  • 영상을 통해, 초음파 센서가 발사한 신호가 물체에 반사되어 돌아오는 시간을 측정하고, 이를 거리(cm)로 환산하여 7세그먼트 디스플레이(FND)에 출력하는 과정을 확인할 수 있다.
  • 현재 센서와 천장 사이의 거리는 약 177cm로 측정되며, 물체를 가까이하거나 멀리 이동시킬 때마다 측정 거리 값이 실시간으로 변화하는 모습을 통해 시스템이 정상적으로 동작함을 확인할 수 있다.

'Verilog_RTL 설계 > Basys_3' 카테고리의 다른 글

[FSM] I2C_LCD_Control  (3) 2025.07.22
[PWM]LED_128_Step  (2) 2025.07.17
[FSM] DTH11_Sensor  (0) 2025.07.14
[Sequential_Circuit] edge_detector  (0) 2025.07.13
[Shift_Register] PIPO_(Parallel-In Parallel-Out)  (0) 2025.07.12