Verilog_ HC-SR04

공이지·2024년 7월 25일

라즈베리파이에서 초음파센서는 정리를 한번 했기때문에 간단하게 하겠습니다.

  • 맨위의 초음파를 쏘라고 신호를 보내준다(Trigger신호) 이는 10us의 신호를 보내주고 두번째 줄에서 초음파가 발생한다 초음파가 발생하고 물체에 부딪혀 돌아온다. 이 초음파가 발생하고 돌아오는 시간만큼 3번째에서 펄스를 발생.
  • 음파의 속도(343m/s)를 기준으로 초음파가 발생하고 되돌아오는 시간을 58로 나누어주면 거리가 나옴
  • Echopulse는 최대 36ms 까지 펄스를 출력할 수 있다. 이는 초음파가 발생하고 돌아오는시간이 36ms가 최대라는 의미이다. 이를 넘어가면 측정이 불가능하다!
module Echo_Controller(
    input clk, reset_p,
    input echo,
    output reg trigger,
    output reg[15:0] distance,
    output reg[15:0] led_state);  
    
    parameter S_IDLE = 4'b0001;
    parameter S_TRIGGER = 4'b0010;
    parameter S_ECHO = 4'b0100;
    parameter S_CAL_DISTANCE = 4'b1000;	
    reg[3:0] state, next_state;
    
    wire clk_microsec;
    clock_div_100 microsec_clk(.clk(clk), .reset_p(reset_p),.clk_div_100_nedge(clk_microsec));
    
    reg[21:0] count_microsec;
    reg count_microsec_enable;
    reg count_1us, count_dis;
    always@(negedge clk or posedge reset_p)begin
        if(reset_p) count_microsec = 0;
        else if(clk_microsec && count_microsec_enable) count_microsec = count_microsec + 1;
        else if(!count_microsec_enable)count_microsec = 0;
    end
    
    wire echo_posedge, echo_negedge;
    edge_dectector_n ed_echo(.clk(clk), .reset_p(reset_p),.cp(echo), .p_edge(echo_posedge), .n_edge(echo_negedge));
    
    always@(negedge clk or posedge reset_p)begin
        if(reset_p)state = S_IDLE;
        else state = next_state; 
    end
    //assign dht11_data = dht11_buffer;
    always@(posedge clk or posedge reset_p)begin
        if(reset_p)begin
            next_state = S_IDLE;
            led_state = 0;
            trigger = 0;
            count_microsec_enable = 0;
        end
        else begin
            case(state)
                S_IDLE:begin
                    if(count_microsec < 22'd3_000_000)begin
                        count_microsec_enable = 1;
                        led_state[0] = 1;
                        count_1us = 0;
                        count_dis = 0;
                    end  
                    else begin
                        count_microsec_enable = 0;
                        led_state[1] = 1;
                        next_state = S_TRIGGER;
                    end
                end
                S_TRIGGER:begin
                    if(count_microsec < 22'd10)begin
                        trigger = 1;
                        count_microsec_enable = 1;
                        led_state[2] = 1;
                    end
                    else begin
                        next_state = S_ECHO;
                        count_microsec_enable = 0;
                        trigger = 0;
                        led_state[3] = 1;
                    end
                end
                S_ECHO:begin
                    led_state[4] = 1;
                    if(echo_posedge)begin
                        led_state[5] = 1;
                        next_state = S_CAL_DISTANCE;
                        count_microsec_enable = 1;
                    end
                end
                S_CAL_DISTANCE:begin
                    led_state[6] = 1;
                    if(echo_negedge)begin
                        distance = count_microsec / 22'd58;
                        count_microsec_enable = 0;
                        led_state[8] = 1;
                        next_state = S_IDLE;
                    end
                    else begin
                        count_microsec_enable = 1;
                        led_state[7] = 1;
                    end      
                end
                default:next_state =S_IDLE; 
            endcase
        end
    end 
endmodule
  • 초기작성한 초음파센서 컨트롤러입니다. FSM을 사용하여 4개의 상태를 반복하도록 하였습니다. S_IDLE상태에서는 3초 대기하도록하고 3초가 지나면 S_TRIGGER상태로 넘어가고 10us 동안 Trigger신호를 줍니다. Trigger신호가 발생하면 초음파가 발생해 물체에 부딫혀 되돌아오는데 S_ECHO상태에서 echo핀의 상승엣지가 발생하면 그때부터 시간을 카운트합니다. 그리고 하강엣지가 발생하면 S_CAL_DISTANCE상태로 넘어가 시간 카운트를 멈추고 측정한 시간에 58을 나누어 거리를 계산하여 distance에 저장합니다.

    오실로스코프 실습

    Trigger 신호 ↖

    Echo 신호 ↖

    오실로스코프에서 엣지를 확인하는 방법. 기본세팅모드에서 트리거의 메뉴레벨을 조절한다. 나는 ch1을 사용하고 있기 때문에 ch1메뉴레벨(?.? V)을 조절하면 메뉴레벨에서 설정한 전압 이상의 전압 즉, 엣지가 발생하면 다음과 같이 트리거 신호를 측정할 수 있다.
    위 2개의 사진은 DHT11dht_data를 측정한 결과입니다. 첫번째 결과는 3초마다 한번씩 온도와 습도를 측정하여 데이터를 받아오는 신호의 Scale을 키워서 확인한 결과입니다. 하지만 Scale을 더 확대해서 DHT11이 통신하는 신호와 데이터가 들어오는것을 확인할 수 있습니다. 제가 작성한 DHT11 코드의 결과를 보면 처음에 20ms동안 Basys-3DHT11을 감지하고 이후 10us(원래는 20us~40us)동안 DHT11 응답을 기다리고 이 후 DHT1180us동안 High상태로 응답신호를 전송하고 또 80us동안 데이터전송을 위해 LOW상태를 유지하고 이 후 DHT11이 데이터를 전송합니다.

Slack을 고려한 설계

    input clk, reset_p,
    input echo,
    output reg trigger,
    output reg[15:0] distance,
    output reg[15:0] led_state);
    
    parameter S_IDLE = 4'b0001;
    parameter S_TRIGGER = 4'b0010;
    parameter S_ECHO = 4'b0100;
    parameter S_CAL_DISTANCE = 4'b1000;
    
    reg[3:0] state, next_state;
    //assign led_state[3:0] = state;
    
    wire clk_microsec;
    clock_div_100 microsec_clk(.clk(clk), .reset_p(reset_p),.clk_div_100_nedge(clk_microsec));
    
    reg cnt_enable;
    wire[11:0]cm;
    SR04_div_58 div58(.clk(clk), .reset_p(reset_p),.clk_microsec(clk_microsec),.cnt_enable(cnt_enable), .cm(cm));
    
    reg[21:0] count_microsec;
    reg count_microsec_enable;
    reg count_1us, count_dis;
    always@(negedge clk or posedge reset_p)begin
        if(reset_p) count_microsec = 0;
        else if(clk_microsec && count_microsec_enable) count_microsec = count_microsec + 1;
        else if(!count_microsec_enable)count_microsec = 0;
    end
    
    wire echo_posedge, echo_negedge;
    edge_dectector_n ed_echo(.clk(clk), .reset_p(reset_p),.cp(echo), .p_edge(echo_posedge), .n_edge(echo_negedge));
    
    
    //reg[21:0] echo_time;
    always@(negedge clk or posedge reset_p)begin
        if(reset_p)state = S_IDLE;
        else state = next_state; 
    end
    //assign dht11_data = dht11_buffer;
    always@(posedge clk or posedge reset_p)begin
        if(reset_p)begin
            next_state = S_IDLE;
            led_state = 0;
            trigger = 0;
            count_microsec_enable = 0;
            cnt_enable = 0;
        end
        else begin
            case(state)
                S_IDLE:begin
                    if(count_microsec < 22'd3_000_000)begin
                        count_microsec_enable = 1;
                        led_state[0] = 1;
                        count_1us = 0;
                        count_dis = 0;
                    end  
                    else begin
                        count_microsec_enable = 0;
                        led_state[1] = 1;
                        next_state = S_TRIGGER;
                    end
                end
                S_TRIGGER:begin
                    if(count_microsec < 22'd10)begin
                        trigger = 1;
                        count_microsec_enable = 1;
                        led_state[2] = 1;
                    end
                    else begin
                        next_state = S_ECHO;
                        count_microsec_enable = 0;
                        trigger = 0;
                        led_state[3] = 1;
                    end
                end
                S_ECHO:begin
                    led_state[4] = 1;
                    if(echo_posedge)begin
                        led_state[5] = 1;
                        next_state = S_CAL_DISTANCE;
                        //count_microsec_enable = 1;
                        cnt_enable = 1;
                    end
                end
                S_CAL_DISTANCE:begin
                    led_state[6] = 1;
                    if(echo_negedge)begin
                        distance = cm;
                        //echo_time = count_microsec;
                        cnt_enable = 0;
                        //count_microsec_enable = 0;
                        led_state[8] = 1;
                        next_state = S_IDLE;
                    end
                    else begin
                        cnt_enable = 1;
                        //count_microsec_enable = 1;
                        led_state[7] = 1;
                    end      
                end
                default:next_state =S_IDLE; 
            endcase
        end
    end 
    
    
//    always@(posedge clk or posedge reset_p)begin
//        if(reset_p) distance = 0;
//        else begin
//        if(echo_time < 174)distance = 2;
//        else if(echo_time < 232)distance = 3;
//        else if(echo_time < 290)distance = 4;
//        else if(echo_time < 348)distance = 5;
//        else if(echo_time < 406)distance = 6;
//        else if(echo_time < 464)distance = 7;
//        else if(echo_time < 522)distance = 8;
//        else if(echo_time < 580)distance = 9;
//        else if(echo_time < 638)distance = 10;
//        else if(echo_time < 696)distance = 11;
//        else if(echo_time < 754)distance = 12;
//        else if(echo_time < 812)distance = 13;
//        else if(echo_time < 870)distance = 14;
//        else if(echo_time < 928)distance = 15;
//        else if(echo_time < 986)distance = 16;
//        else if(echo_time < 1044)distance = 16;
//        else if(echo_time < 1102)distance = 17;
//        else if(echo_time < 1160)distance = 18;
//        else if(echo_time < 1218)distance = 19;
//        else if(echo_time < 1276)distance = 20;
//        else if(echo_time < 1334)distance = 21;
//        else if(echo_time < 1392)distance = 22;
//        else if(echo_time < 1450)distance = 23;
//        else if(echo_time < 1508)distance = 24;
//        else if(echo_time < 1566)distance = 25;
//        else if(echo_time < 1624)distance = 26;
//        else if(echo_time < 1682)distance = 27;
//        else if(echo_time < 1740)distance = 28;
//        else if(echo_time < 1798)distance = 29;
//        else if(echo_time < 1856)distance = 30;
//        else if(echo_time < 1914)distance = 31;
//        else if(echo_time < 1972)distance = 32;
//        else if(echo_time < 2030)distance = 33;
//        else if(echo_time < 2088)distance = 34;
//        else if(echo_time < 2146)distance = 35;
//        else if(echo_time < 2204)distance = 36;
//        else distance = 36;
//        end
//    end
    
endmodule

  • echo_time의 경우의수를 echo핀이 측정할 수 있는 최대길이까지를 경우의수를 모두 조건식으로 처리하여 작성한 코드 내용입니다.

    이렇게 always문으로 많은 조건식을 작성하게되면 조합논리회로가 만들어진다. 이러면 32bit LUT이 2개가 만들어질 것이다. 왜냐하면 조건문이 37개가 작성되어있기 때문에 32bit LUT의 입력으로는 부족할 것이다. 그렇기에 2개의 LUT이 사용되는데 이때 이 2개의 LUT의 출력또한 하나의 LUT으로 연결해야한다. 이렇게되면 LUT의 출력이 나오기까지 PDT가 중복되면서 PDT가 늘어나게된다. 이 다음에 정리를 하겠지만 PDT가 늘어나면 Ta(arrival Time : 도달시간 = PDT + SetUpTime[이는 상황에따라 포함할 것인지 제외할 것인지 정할 수 있다.])가 늘어나게된다!

Ta(arrival Time : 도달시간)?, Tr(requriement Time : 요구시간)?

  • 주기가 10nsAdder를 설계한다고 하자. 만약 4bit Adder를 동작시킨다고 할 때 첫번째 AdderCarry가 발생하면 다음 Adder에 입력으로 들어가게되는데 이를 Ripple Carry Adder라고 한다. 만약 이 Ripple Carry Adder에서 계산을 진행할때 하나의 Adder가 계산할 때 소요되는 시간이 1ns라고 한다면 4bit Adder가 연산하는 시간은 총 4ns일 것이다. 이처럼 연산에 걸리는4ns의 시간은 delay시간일 것이고 이를 PDT라고 할 수 있다. 그렇다면 위에서 잠깐 언급했듯이 PDTTa(arrival Time : 도달시간 = PDT + SetUpTime) 이다 그렇다면 4bit Adder의 주기 10nsTr(requriement Time : 요구시간 = CLK + HoldTime[이는 상황에따라 포함할 것인지 제외할 것인지 정할 수 있다.]에 해당한다. 여기서 TrTa를 빼면 이것을 Slack(마진)이라고한다. 이 slack(+)값으로 여유가 많다면 클럭주기를 빠르게 하여 성능을 높일 수 있다. 하지만 만약 negative Slack이 나오게된다면? PDT시간이 크다고 생각할 수 있다. PDT시간이 높으면 도달시간(Ta)요구시간(Tr)보다 커져서 negative Slack이 나오게된다. negative Slack이 발생하는 이유는 보통 Longest Path(두개의 F/F사이에 많은 CL이 존재하는 Path)에서 발생하지만 문제가 되지 않을 수도 있습니다. 이를 해결하기위해서 PDT를 줄이는 방법이 가장 효율적입니다. Verilog코딩을 할때 always문(CL)의 조건을 많이 작성하게되면 Ta(도달시간)가 늘어나게되면서 negative Slack이 발생하게됩니다.

    SetUpTime은 클럭주기가 10ns라면 정확하게 10ns에 주기가 변경되는것이 아니다 약간의 오차범위가 존재하고 이는 10ns보다 빨리 주기가 변경될 수 있고, 10ns보다 늦게 변경될 수 도 있다. 그렇기 때문에 데이터의 입력신호가 F/F의 클럭. 즉 , 10ns 전에 안정된 상태로 유지되어야하는 최소시간입니다, holdtimeclock입력 이후에도 일정시간동안 안정된 상태로 유지되어야하는 최소시간입니다. 이 시간동안 데이터를 유지시켜줘야하기에 도달시간에 setupTime, holdTime를 추가시켜줍니다.

if조건문(CL : 조합논리회로)을 F/F으로 변경

  • 다음과 같이 58분주기를 이용해서 58us를 카운트 할 때마다 cm의 +1 해줍니다. 그리고 distance에 할당하면 cm가 나오게됩니다!(왜? echo핀이 상승엣지인 기간동안 측정한 시간(us)을 58로 나누어주어 거리를 계산하는것이 SR04(초음파센서)가 거리를 측정하는 방법이기 때문!)
  • 이처럼 분주기(F/F)을 사용해서 설계를 하게된다면 이전처럼 F/F사이의 조건이 많은 CL이 만들어지면서 **PDT가 증가합니다. 하지만 분주기를 사용하면 CLF/F으로 대체하면서 PDT가 줄어드는 것을 볼 수 있습니다. 그 결과 WNS(요구시간 - 도달시간)이 양수 값으로 나타나는 것을 볼 수 있으면 이는 도달시간이 줄어들었음**을 확인 할 수 있습니다.
profile
화이팅..!

1개의 댓글

comment-user-thumbnail
2025년 6월 11일

안녕하세요..! 현재 대학교에서 징크 7020 보드로 프로젝트를 진행중인데 3.3v hcsr04 초음파 센서를 활용하려합니다. 현재 3주간 IP 생성하는 과정에서 계속 오류가 발생해서 혹시 생성하신 ip_repo 가 있으시다면 공유 부탁드려도 될까요..? ㅠㅠ

답글 달기