7/24 온습도 통신오류 확인, 초음파 센서

정유석·2024년 7월 24일

교육 - 베릴로그

목록 보기
15/28

온습도센서의 통신오류 확인

check sum, 응답신호 기다림

  • 컨트롤러 모듈 마지막에 check sum 비트 확인용 if문 추가
  • S_HIGH_20US 상태에서 응답신호를 기다리는데, 100_000us(0.1ms)를 넘어가면 다시 S_IDLE상태로 돌아감
  • 각각의 상태들에서도 다음 엣지의 신호가 오래걸리면 다시 S_IDLE로 돌아가도록 추가
  • S_READ_DATA의 state의 case문 수정
module dht11_cntr(
    input clk, reset_p,
    inout dht11_data,   //단일 데이터선이라 inout으로 선언
    output reg [7:0] humidity, temperature,
    output [15:0] led_debug);
    
    parameter S_IDLE = 6'b00_0001; //상태천이도에있는 6개의 상태 설정
    parameter S_LOW_18MS = 6'b00_0010;
    parameter S_HIGH_20US = 6'b00_0100;
    parameter S_LOW_80US = 6'b00_1000;
    parameter S_HIGH_80US = 6'b01_0000;
    parameter S_READ_DATA = 6'b10_0000;
    
    parameter S_WAIT_PEDGE = 2'b01; //read_data에서의 40번의 데이터 교환 따로 설정
    parameter S_WAIT_NEDGE = 2'b10;
    
    reg [5:0] state, next_state;
    reg [1:0] read_state;
    
    assign led_debug[5:0] = state;    
    
    wire clk_usec; //정확한 클락을 위해 clk_div가 아니라 분주 사용
    clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec)); //클락이 아니라 엣지 사용
    
    reg [21:0] count_usec; //3초 대기를 위함
    reg count_usec_e;
    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; //S_IDLE에서 count_usec_e를 1주면 카운트 시작
        else if(!count_usec_e)count_usec = 0; //count_usec_e가 0이되면 리셋
    end
            
    always @(negedge clk or posedge reset_p)begin //next_state와 state를 겹쳐서 동시에 바뀌면 한클락 뒤에 읽히게되서 
        if(reset_p)state = S_IDLE;                //하나는 상승엣지 다른하나는 하강엣지
        else state = next_state;
    end
    
    reg dht11_buffer; //input, inout에는 reg선언 안됨
    assign dht11_data = dht11_buffer;
    
    wire dht_pedge, dht_nedge;
    edge_detector_p ed(
        .clk(clk), .reset_p(reset_p), .cp(dht11_data),
        .p_edge(dht_pedge), .n_edge(dht_nedge));
    
    reg [39:0] temp_data; //데이터시트 5번에 최상위비트부터 보낸다고 되어있음, 쉬프트레지스터 사용
    reg [5:0] data_count;
    
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)begin
            next_state = S_IDLE;
            read_state = S_WAIT_PEDGE;
            temp_data = 0;
            data_count = 0;
//            count_usec_e = 0;
        end
        else begin
            case(state)
                S_IDLE:begin //최초 풀업에의해 high신호
                    if(count_usec < 22'd3_000_000)begin
                        count_usec_e = 1;
                        dht11_buffer = 'bz; //풀업저항 회로에의해 임피던스가 있으면 Vcc
                    end
                    else begin
                        count_usec_e = 0;
                        next_state = S_LOW_18MS;                
                    end
                end
                S_LOW_18MS:begin //low신호를 주고난후에 임피던스로 신호를 끊음 그러면 풀업에의해 다시 high
                    if(count_usec <22'd20_000)begin //18ms에서 여유시간 추가
                        dht11_buffer = 0;
                        count_usec_e = 1;
                    end
                    else begin
                        count_usec_e = 0;
                        next_state = S_HIGH_20US;
                        dht11_buffer = 'bz;
                    end
                end
                S_HIGH_20US:begin
                    count_usec_e = 1;
                    if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감 
                        next_state = S_IDLE;
                        count_usec_e = 0;
                    end
//                    if(count_usec < 22'd3)begin //어짜피 다음 하강엣지만 감지하면 되서 주석처리
//                        count_usec_e = 1;
//                    end
//                    else 
                    else if(dht_nedge)begin
                        count_usec_e = 0;
                        next_state = S_LOW_80US;                    
                    end
                end
                S_LOW_80US:begin
                count_usec_e = 1;
                    if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감 
                        next_state = S_IDLE;
                        count_usec_e = 0;
                    end
                    else if(dht_pedge)begin
                        next_state = S_HIGH_80US;
                        count_usec_e = 0;
                    end
                end
                S_HIGH_80US:begin //for80us는 대략 80us 정확하진 않지만 엣지는 들어온다
                count_usec_e = 1;
                    if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감 
                        next_state = S_IDLE;
                        count_usec_e = 0;
                    end
                else if(dht_nedge)begin
                        next_state = S_READ_DATA;
                        count_usec_e = 0;
                    end
                end
                S_READ_DATA:begin
                count_usec_e = 1;
                    if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감 
                        next_state = S_IDLE;
                        count_usec_e = 0;
                        data_count = 0; //S_IDLE로 돌아갈때는 오류난거기 때문에 data_count=0
                        read_state = S_WAIT_PEDGE;
                    end
                    else begin
                        case(read_state)
                            S_WAIT_PEDGE:begin
                                if(dht_pedge)read_state = S_WAIT_NEDGE;
                                
                            end
                            S_WAIT_NEDGE:begin
                                if(dht_nedge)begin             //count_usec은 us단위 카운트
                                    if(count_usec < 95)begin //데이터를 받기 시작하는 순간부터 카운트를 시작하기때문에, 신호가 0일때는 50+(26~28)us, 신호가 1일떄는 50+70us이기 때문에 95us정도로 설정해서 신호0과1을 구분 
                                        temp_data = {temp_data[38:0], 1'b0};
                                    end
                                    else begin
                                        temp_data = {temp_data[38:0], 1'b1};
                                    end
                                    data_count = data_count + 1;
                                    read_state = S_WAIT_PEDGE;
                                    count_usec_e = 0;
                                end
                                else begin
                                    count_usec_e = 1;
                                end
                            end
                        endcase
                        if(data_count >= 40)begin //10진수40 따로 설정하지않으면 기본 32비트, 저장공간은 6비트라 하위6비트까지만 저장
                            data_count = 0;
                            next_state = S_IDLE;
                            read_state = S_WAIT_PEDGE;
                            count_usec_e = 0;
                            if(temp_data[39:32] + temp_data[31:24] + temp_data[23:16] + temp_data[15:8] == temp_data[7:0])begin 
                                humidity = temp_data[39:32]; //통신에러 확인, 8비트4개의 합과 check sum비트와 비교하여 if문 동작
                                temperature = temp_data[23:16];
                            end
                        end
                    end
                end                    
            endcase
        end
    end
        
endmodule

초음파 센서

거리에따라 에코펄스의 길이가 달라지는데 멀 수록 펄스폭이 길어진다.
보드에서 10us길이의 트리거 펄스가 보내지면 초음파 센서의 Sonic Burst가 초음파를 발생시키고 다시 받아 거리에따른 시간을 측정한다, 측정값을 에코펄스로 보낸다. 최대 펄스 길이는 18ms이고 앞에 장애물이 없으면 36ms 펄스 생성, 에코 펄스의 하강엣지가 발생하고 다음 트리거까지는 10ms의 시간 필요


음속은 343m/s -> 343m/(us x 1,000,000) -> (343 x 100)cm/(us x 1,000,000) ->(초음파가 갔다가 온거라서 측정거리를 2로 나눔) 1/58

직접 만들어본 코드

초음파 센서 컨트롤 모듈 코드

module hc_sr04_cntr(
    input clk, reset_p,
    input echo,
    output reg [8:0] dist,
    output reg trig,
    output [15:0] led_debug);

    parameter S_IDLE = 3'b001;
    parameter S_TRIG = 3'b010;
    parameter S_ECHO = 3'b100;
    
    parameter S_ECHO_PEDGE = 2'b01;
    parameter S_ECHO_NEDGE = 2'b10;
    
    reg [2:0] state, next_state;
    reg [1:0] read_state;
    
    assign led_debug[5:0] = state;
    
    reg [21:0] count_usec; //3초대기를 위한 레지스터 크기(펄스 크기 모두 처리 가능), 데이터시트의 최대 펄스길이는  36ms(장애물이없을시 에코펄스길이) 
    reg count_usec_e;
    wire clk_usec;
    clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec));
    
    wire pecho, necho;
    edge_detector_n ed_echo(.clk(clk), .reset_p(reset_p), .cp(echo), .p_edge(pecho), .n_edge(necho));
        
    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
    
    always @(negedge clk or posedge reset_p)begin
        if(reset_p) state = S_IDLE;
        else state = next_state;
    end
    
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)begin
            next_state = S_IDLE;
            read_state = S_ECHO_PEDGE;
            count_usec_e = 0;
        end
        else begin
            case(state)
                S_IDLE:begin
                    if(count_usec < 22'd3_000_000)begin
                        count_usec_e = 1; 
                    end
                    else begin
                        count_usec_e = 0;
                        next_state = S_TRIG;
                    end
                end
                S_TRIG:begin
                    if(count_usec < 22'd10)begin
                        count_usec_e = 1;
                        trig = 1;
                    end
                    else begin
                        count_usec_e = 0;
                        trig = 0;
                        next_state = S_ECHO;
                    end
                end
                S_ECHO:begin
//                    if(pecho)begin
//                        if(count_usec < 22'd1_000_000)begin
//                        count_usec_e = 1;
//                        end
//                    end
//                    else if(necho)begin
//                        dist = count_usec / 58;
//                        next_state = S_IDLE;
//                        count_usec_e = 0;
//                    end
                    case(read_state)
                        S_ECHO_PEDGE:begin
                            if(pecho)begin
                                count_usec_e = 1;
                                read_state = S_ECHO_NEDGE;
                            end
                        end
                        S_ECHO_NEDGE:begin
                            if(necho)begin
                                dist = count_usec/58;
                                read_state = S_ECHO_PEDGE;
                                next_state = S_IDLE;
                                count_usec_e = 0;
                            end 
                        end            
                    endcase
                end
            endcase
        end
    end
endmodule

FSM형식의 코드를 작성

  • 신호 대기를 위한 S_IDLE 상태
  • 트리거 발생을 위한 S_TRIG 상태
  • 에코 수신과 데이터 처리를 위한 S_ECHO 상태

클럭생성시 주의, one cycle pulse를 사용하여야 한다, 그렇지 않으면 카운트가 의도하지 않은대로 동작한다.

동작 모듈 코드

module hc_sr04_test_top(
    input clk, reset_p,
    input echo,
    output trig,
    output [3:0] com,
    output [7:0] seg_7, led_debug);
    
    wire [8:0] dist;
    wire [15:0] value;
    assign value = {8'b0, dist};
    hc_sr04_cntr(.clk(clk), .reset_p(reset_p), .echo(echo), .dist(dist), .trig(trig), .led_debug(led_debug));
    
    fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));

endmodule


(basys3의 출력 3.3v, avr의 출력 5v, 센서 정격 5v필요, basys3에서 신호처리를 하는데 필요한 정격 전압을 위해 avr의 출력 사용)(양쪽 모두에 전원을 넣어줘야 하는것을 잊지말자...)

profile
개인 기록공간

0개의 댓글