거북이처럼 천천히

개인 프로젝트 - 스마트 선풍기 만들기 (1) 본문

RTL Design/Verilog 프로젝트

개인 프로젝트 - 스마트 선풍기 만들기 (1)

유로 청년 2024. 8. 21. 19:33

1. 서론 

  • 스마트 선풍기 프로젝트는 다음과 같은 기능을 수행할 수 있는 선풍기를 제작하는 개인 프로젝트이다.
  • 스마트 선풍기 만들기 개인 프로젝트는 여러 단계로 나누어 게시글을 올릴 예정이다.

 

 

1.1. 스마트 선풍기가 수행할 수 있는 기능들

  • 스마트 선풍기는 다음과 같은 기능들을 지원한다.
  • Normal Mode : 일반 선풍기 모드로서 0 ~ 3단까지 팬 파워를 지원
  • Safe Mode : 절전 모드로서 전기세 절약을 목적으로 하는 모드이다. 해당 모드에서는 dht11로 부터 얻은 온도를 기준으로 팬 파워가 결정하며, 4단계에서는 usonic을 통해 전방 혹은 180도에 사물이 없다고 판단되면 자동으로 선풍기가 꺼지도록 설계
  • Timer Mode : 
    ▶ 0 단계 :
    Timer 기능 off
    ▶ 1 단계 : 5초 타이머 기능 on
    ▶ 2 단계 : 10초 타이머 기능 on
    ▶ 3 단계 : 15초 타이머 기능 on
  • LED Control :
    0 ~ 3단계 : 단순히 PWM의 Duty ratio에 의해 밝기가 결정
    4단계 : 조도 센서로 부터 얻은 외부 밝기에 따라 LED의 밝기가 결정
    5단계 : DHT11로 부터 얻은 온도 정보에 따라 Red (30도 이상), Yellow (27도 이상, 29도 이하),
       Green (24도 이상, 26도 이하), Blue (24도 미만) 색상으로 표현
    ▶ 6단계 : 조이스틱을 통해 LED의 색상 조절
  • Servo motor Control :
    ▶1단계 :
    일반 서보 모터 회전 모드
    ▶2단계 : 조이스틱을 통한 컨트롤 모드 
  • uSonic을 통한 절전 모드 돌입 :
    ▶1단계 :
    전방에 사물이 있는지 여부 확인하는 모드
    ▶2단계 :
    전방 180도에 사물이 있는지 여부 확인하는 모드

 

 

 

 

2. 본론 

2.1. 프로젝트에 필요한 기본 모듈 생성 

  • 스마트 선풍기 프로젝트에 필요한 기본 모듈을 생성한다.
    ▶Edge detector
       -
    입력 Pulse wave의 Edge를 감지하여 One Cycle Pulse를 발생시키는 모듈
    ▶Clock Divider 100, 1000
       -
    100분주, 1000분주화
    ▶Decoder of 7-segment
       -
    입력 4bit  데이터를 7-Segment로 출력시키기 위해 변환시켜주는 decoder 모듈
    ▶FND Control module
       -
    FND을 제어하는 통합 모듈
    ▶Convert from binary to BCD code
       -
    이진수 입력값을 BCD 코드로 변환 시켜주는 Converter 모듈
    ▶Button control module
    d
       -
    Button Chattering 문제를 소프트웨어로 해결하기 위한 모듈
    ▶Ring counter of com
       -
    Ring Counter를 통해 common anode를 컨트롤하는 모듈

 

< Source, Decoder of 7-segment >

// Decoder of 7-Segment.
module decoder_seg7 (
    input [3:0] hex_value,
    output reg [7:0] seg_7 );
    
    always @(hex_value) begin
        case(hex_value)
            0  : seg_7 = 8'b0000_0011; //common anode, 0이 켜지는거임 
            1  : seg_7 = 8'b1001_1111;  // 1
            2  : seg_7 = 8'b0010_0101;  // 2
            3  : seg_7 = 8'b0000_1101;  // 3
            4  : seg_7 = 8'b1001_1001;  // 4
            5  : seg_7 = 8'b0100_1001;  // 5
            6  : seg_7 = 8'b0100_0001;  // 6
            7  : seg_7 = 8'b0001_1111;  // 7
            8  : seg_7 = 8'b0000_0001;  // 8
            9  : seg_7 = 8'b0000_1001;  // 9
            10 : seg_7 = 8'b0001_0001;  // A
            11 : seg_7 = 8'b1100_0001;  // b
            12 : seg_7 = 8'b0110_0011;  // C
            13 : seg_7 = 8'b1000_0101;  // d
            14 : seg_7 = 8'b0110_0001;  // E
            15 : seg_7 = 8'b0111_0001;  // F
        endcase
    end
    
endmodule

 

< Source, Edge detector >

// Edge detector 
module edge_detector (
    input clk, reset_p,
    input cp,
    output p_edge, n_edge );
    
    reg flip_flop_current, flip_flop_old;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            flip_flop_current <= 0;
            flip_flop_old <= 0;
        end
        else begin
            flip_flop_current <= cp;
            flip_flop_old <= flip_flop_current;
        end
    end
    
    assign p_edge = ({flip_flop_current, flip_flop_old} == 2'b10) ? 1 : 0;
    assign n_edge = ({flip_flop_current, flip_flop_old} == 2'b01) ? 1 : 0;
    
endmodule

 

< Source, Clock divider 1000 >

// Clock divider 1000
module clk_div_1000 (
    input clk, reset_p,
    input clk_source,
    output clk_div_1000,
    output clk_div_1000_nedge, clk_div_1000_pedge );
    
    wire clk_source_nedge;
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p),
                        .cp(clk_source), .n_edge(clk_source_nedge));
    
    reg [9:0] counter;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else if(clk_source_nedge) begin
            if(counter >= 999) counter = 0;
            else counter = counter + 1;
        end
    end
    
    assign clk_div_1000 = (counter < 500)? 0 : 1;
    
    edge_detector edge_detector_1 (.clk(clk), .reset_p(reset_p),
                        .cp(clk_div_1000), .p_edge(clk_div_1000_pedge), .n_edge(clk_div_1000_nedge));
endmodule

 

< Source, Clock divider 100 >

// Clock divider 100
module clk_div_100 (
    input clk, reset_p,
    output clk_div_100,
    output clk_div_100_nedge, clk_div_100_pedge );
    
    reg [6:0] counter;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else begin
            if(counter >= 99) counter = 0;
            else counter = counter + 1;
        end
    end
    
    assign clk_div_100 = (counter < 50)? 0 : 1;
    
    edge_detector edge_detector_clk_div_100(.clk(clk), .reset_p(reset_p),
                        .cp(clk_div_100), .p_edge(clk_div_100_pedge), .n_edge(clk_div_100_nedge));
endmodule

 

< Source, Ring counter of common anode >

// Ring Counter for com
module ring_counter_com (
    input clk, reset_p,
    output reg [3:0] com );
    
    // Get 1msec one cycle pulse
    wire clk_1usec, clk_1msec;
    clk_div_100 clk_div_1usec(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec));
    clk_div_1000 clk_div_1msec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) com = 4'b1110;
        else if(clk_1msec) com = {com[2:0], com[3]};
    end
    
 endmodule

 

< Source, FND Control >

// FND Control
module fnd_cntr(
    input clk, reset_p,
    input [15:0] hex_value,
    output [3:0] com,
    output [7:0] seg_7);
    
    // Ring counter of com
    ring_counter_com ring_counter_0 (.clk(clk), .reset_p(reset_p), .com(com));
    
    // Select value
    reg [3:0] value;
    always @(*) begin
        case(com)
            4'b1110 : value = hex_value[3:0];
            4'b1101 : value = hex_value[7:4];
            4'b1011 : value = hex_value[11:8];
            4'b0111 : value = hex_value[15:12];
            default : value = value;
        endcase
    end
    
    // decoder of 7-segment
    decoder_seg7 decoder_seg7_0 (.hex_value(value), .seg_7(seg_7));
    
endmodule

 

< Source, Convert from binary to BCD code >

// Convert from binary to bcd
module bin_to_dec(
        input [11:0] bin,
        output reg [15:0] bcd
    );

    reg [3:0] i;

    always @(bin) begin
        bcd = 0;
        for (i=0;i<12;i=i+1)begin
            bcd = {bcd[14:0], bin[11-i]};
            if(i < 11 && bcd[3:0] > 4) bcd[3:0] = bcd[3:0] + 3;
            if(i < 11 && bcd[7:4] > 4) bcd[7:4] = bcd[7:4] + 3;
            if(i < 11 && bcd[11:8] > 4) bcd[11:8] = bcd[11:8] + 3;
            if(i < 11 && bcd[15:12] > 4) bcd[15:12] = bcd[15:12] + 3;
        end
    end
endmodule

 

< Source, Button Control >

// Button control
module btn_cntr (
    input clk, reset_p,
    input btn,
    output btn_pedge, btn_nedge );
    
    // Get 1msec one cycle pulse
    wire clk_1usec, clk_1msec;
    clk_div_100 clk_div_1usec(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec));
    clk_div_1000 clk_div_1msec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
    
    reg btn_reg;
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) btn_reg = 0;
        else if(clk_1msec) btn_reg = btn;
    end
    
    edge_detector edge_detector_0(.clk(clk), .reset_p(reset_p),
                        .cp(btn_reg), .p_edge(btn_pedge), .n_edge(btn_nedge));
    
endmodule

 

 

 

 

 

2.2. Top module of electric fan기본 틀 생성 

  • Top_module_of_electric_fan 모듈은 스마트 선풍기의 각각의 기능들을 통합하는 Top module이다.
  • 각각의 기능을 구현 및 각각의 모듈을 설계하기 전에 Top module을 설계함으로서 프로젝트에 필요한 변수 정하고, 모듈에 필요한 Input / Output을 선언하여 프로젝트의 방향성과 틀을 생성하고자 한다.
  • Top module of electric fan에 대한 설명은 코드와 함께 설명하도록 하겠다.

 

< Source, Top module of electric fan >

// Top module of Electric Fan
module Top_module_of_electric_fan(
    input clk, reset_p,
    input [3:0] btn,
    inout dht11_data,
    output pwm,
    output [3:0] com,
    output [7:0] seg_7);
    
    // Button Control
    wire btn_power, btn_timer, btn_safe;
    btn_cntr btn_cntr_power (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_power));
    btn_cntr btn_cntr_timer (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_timer));
    btn_cntr btn_cntr_safe (.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_safe));
    
    // Declare state 
    parameter NORMAL_MODE = 2'b01;
    parameter SAFE_MODE = 2'b10;
    
    // Declare the necessary variables.
    reg [1:0] state, next_state;
    wire [1:0] normal_mode_duty, safe_mode_duty; // PWM duty ratio of normal mode, safe mode
    wire [1:0] duty;                             // Final selected PWM duty
    wire time_out;                               // One cycle pulse of time out
    wire [3:0] lefted_time;                      // Storage of lefted time
    reg new_reset;                               // Declared new reset
    wire [7:0] temperature, humidity; 
    
    // 언제 다음 state 로 넘어가는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p || new_reset) state = NORMAL_MODE;
        else state = next_state;
    end
    
    // 각 state에 대한 동작 및 다음 상태 넘어가기 위한 조건 정의
    always @(negedge clk or posedge reset_p) begin
        if(reset_p || new_reset) next_state = NORMAL_MODE;
        else begin
            if(btn_power) next_state = NORMAL_MODE;
            else if(btn_safe) next_state = SAFE_MODE;
        end
    end
    
    // Instance of modules
    control_normal_mode control_normal_mode_0 (.clk(clk), .reset_p(reset_p), .btn_power(btn_power), .new_reset(new_reset), .duty(normal_mode_duty));
    control_safe_mode control_safe_mode_0 (.clk(clk), .reset_p(reset_p), .new_reset(new_reset), .temperature(temperature), .humidity(humidity), .duty(safe_mode_duty));
    
    // 현재 모드의 duty을 선택 한 후, 해당 duty을 전달하여 pwm을 얻기.
    assign duty = (state == NORMAL_MODE) ? normal_mode_duty : safe_mode_duty;
    
    // PWM Control module
    pwm_cntr #(.duty_step(4), .pwm_freq(100)) cntr_noraml_fan_power (.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
    
    // Timer Control module
    control_timer control_timer_0 (.clk(clk), .reset_p(reset_p), .btn_timer(btn_timer), .duty(duty), .time_out(time_out), .lefted_time(lefted_time), .timer_enable(timer_enable));
    
    // DHT11 Control module
    dht11_cntr dht11_control_module (.clk(clk), .reset_p(reset_p), .dht11_data(dht11_data), .temperature(temperature), .humidity(humidity));
    
    // assign new reset.
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) new_reset = 0;
        else new_reset = time_out;
    end
    
    // // Convert from binary to BCD Code
    wire [15:0] bcd_lefted_time, selected_fnd_data;
    wire [7:0] bcd_temperature, bcd_humidity;  
    bin_to_dec convert_from_bin_to_bcd_for_lefted_time (.bin(lefted_time), .bcd(bcd_lefted_time));
    bin_to_dec bin_to_dec_temperature (.bin(temperature), .bcd(bcd_temperature));
    bin_to_dec bin_to_dec_humidity (.bin(humidity), .bcd(bcd_humidity));
    
    assign selected_fnd_data = (state == NORMAL_MODE || timer_enable) ? {bcd_lefted_time[7:0], 6'b0, duty} : {bcd_temperature, 6'b0, duty};
    
    // FND Control module
    fnd_cntr fnd_control (.clk(clk), .reset_p(reset_p), .hex_value(selected_fnd_data), .com(com), .seg_7(seg_7));
endmodule

 

 

 

< Source, Top module의 Input / Output 선언 >

// Top module of Electric Fan
module Top_module_of_electric_fan(
    input clk, reset_p,
    input [3:0] btn,
    inout dht11_data,
    output pwm,
    output [3:0] com,
    output [7:0] seg_7);

endmodule
  • Top module에서 필요한 Input / Output 변수 선언한다.
  • Output pwm은 선풍기의 메인 모터에 인가되는 pwm을 의미한다.

 

 

< Source, Button의 One Cycle Pulse 얻기 >

// Button Control
wire btn_power, btn_timer, btn_safe;
btn_cntr btn_cntr_power (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_power));
btn_cntr btn_cntr_timer (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_timer));
btn_cntr btn_cntr_safe (.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_safe));
  • button의 chattering 문제를 소프트웨어적으로 해결하기 위해 D-Flip Flop과 1ms delay time을 갖고, 버튼 값의 입력을 받게 된다.
  • 버튼이 누르게 되면 신호 레벨 값이 0 → 1 로 변화하기 때문에 Positive edge가 발생하며, 버튼의 신호값에서 전압 레벨 변화가 발생했음을 One cycle pulse을 통해 알려주게 된다.

 

 

< Source, Main state parameter 선언 >

// Declare state 
parameter NORMAL_MODE = 2'b01;
parameter SAFE_MODE = 2'b10;

 

 

 

< Source, 필요한 변수 선언 >

// Declare the necessary variables.
reg [1:0] state, next_state;
wire [1:0] normal_mode_duty, safe_mode_duty; // PWM duty ratio of normal mode, safe mode
wire [1:0] duty;                             // Final selected PWM duty
wire time_out;                               // One cycle pulse of time out
wire [3:0] lefted_time;                      // Storage of lefted time
reg new_reset;                               // Declared new reset
wire [7:0] temperature, humidity;
  • 각각의 변수들은 다음과 같은 역활 및 의미를 갖는다.
    ▶ state, next_state : 현재 상태 단계와 다음 상태 단계 정보를 담고 있는 변수
    ▶ normal_mode_duty, safe_mode_duty : 각각의 모드에서의 선풍기 메인 모터에 전달할 PWM의 duty 값
    ▶ duty : 실제로 선풍기 메인 모터에 전달되는 PWM의 duty 값
                    일반 모드, 절전 모드의 duty 값을 모두 받지만, 실제로 전달되는 duty 값은 현재 모드가 어떤 모드
                    냐에  따라 결정하게 된다.
    ▶ time_out : Timer 모드가 동작한 상태일 때, 0분 0초가 되어 다른 모듈 및 회로에게 0분 0초가 되었음을
                          알려주는 Flag 변수이다.
    ▶ lefted_time : Timer 모드가 동작하는 상태일 때, 현재 Timer 남은 시간 정보를 담고 있는 변수
    ▶ new_reset : 새로운 Reset 변수로서 Timer 모드가 끝나거나 절전 모드에서 모터나 변수를 끄거나
                             초기화해야할 경우, 해당 변수를 이용하게 된다.
    ▶temperature, humidity : 온도, 습도 정보를 담고 있는 변수

 

 

 

< Source, 언제 다음 상태로 넘어가는가? >

// 언제 다음 state 로 넘어가는가?
always @(posedge clk or posedge reset_p) begin
    if(reset_p || new_reset) state = NORMAL_MODE;
    else state = next_state;
end
  • 시스템 시작 초기의 초기화 (reset_p) or  새로운 초기화 (new_reset)에 의해 현재 상태 변수(state)를 NORAML_MODE로 초기화되도록 설계하였다.

 

 

 

< Source, 어떤 조건이 만족했을 때, 어느 상태로 넘어가는가? >

// 각 state에 대한 동작 및 다음 상태 넘어가기 위한 조건 정의
always @(negedge clk or posedge reset_p) begin
    if(reset_p || new_reset) next_state = NORMAL_MODE;
    else begin
        if(btn_power) next_state = NORMAL_MODE;
        else if(btn_safe) next_state = SAFE_MODE;
    end
end
  • 각각의 버튼이 활성화되며, 해당 모드가 next_state로 설정되도록 설계된다.

 

 

 

< Source, Instance of module 선언 >

// Instance of modules
control_normal_mode control_normal_mode_0 (.clk(clk), .reset_p(reset_p), .btn_power(btn_power), .new_reset(new_reset), .duty(normal_mode_duty));
control_safe_mode control_safe_mode_0 (.clk(clk), .reset_p(reset_p), .new_reset(new_reset), .temperature(temperature), .humidity(humidity), .duty(safe_mode_duty));
  • 해당 모듈에 대한 자세한 설명은 아래에서 설명하도록 하겠다.

 

 

< Source, Duty 값을 선택한 후, PWM Control Module 전달한다. >

// 현재 모드의 duty을 선택 한 후, 해당 duty을 전달하여 pwm을 얻기.
assign duty = (state == NORMAL_MODE) ? normal_mode_duty : safe_mode_duty;
    
// PWM Control module
pwm_cntr #(.duty_step(4), .pwm_freq(100)) cntr_noraml_fan_power (.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
  • control_normal_mode와 control_safe_mode 모듈로부터 duty 값을 받지만, 실질적으로 pwm_cntr 모듈에게 전달되는 duty값은 "Top module의 현재 모드가 어떤 모드인가?" 에 따라 결정된다.
  • pwm_cntr 모듈은 100Hz 주파수를 갖으며, duty ratio의 step이 4단계인 PWM을 생성하는 모듈이다.

 

 

< Source, Timer control 모듈과 DHT11 control 모듈의 Instance 선언 >

 // Timer Control module
control_timer control_timer_0 (.clk(clk), .reset_p(reset_p), .btn_timer(btn_timer), .duty(duty), .time_out(time_out), .lefted_time(lefted_time), .timer_enable(timer_enable));
    
// DHT11 Control module
dht11_cntr dht11_control_module (.clk(clk), .reset_p(reset_p), .dht11_data(dht11_data), .temperature(temperature), .humidity(humidity));
  • 각각의 모듈에 대한 자세한 설명은 아래에서 설명하도록 하겠다.

 

 

< Source, 새로운 reset 변수 값의 할당 정의 >

// assign new reset.
always @(posedge clk or posedge reset_p) begin
   if(reset_p) new_reset = 0;
   else new_reset = time_out;
end
  • new_reset 변수는 특정 조건을 만족하여 선풍기의 메인 모터, 서브 모터, LED 등 기능을 초기화해야 하는 경우에 사용하는 Flag 변수이다.
  • 현재는 Timer 모드에서 0분 0초 일때, 선풍기의 메인모터를 종료해야 할 때, new_reset 변수를 활성화하게 된다.

 

 

< Source, Converting from binary to BCD code >

// Convert from binary to BCD Code
wire [15:0] bcd_lefted_time, selected_fnd_data;
wire [7:0] bcd_temperature, bcd_humidity;  
bin_to_dec convert_from_bin_to_bcd_for_lefted_time (.bin(lefted_time), .bcd(bcd_lefted_time));
bin_to_dec bin_to_dec_temperature (.bin(temperature), .bcd(bcd_temperature));
bin_to_dec bin_to_dec_humidity (.bin(humidity), .bcd(bcd_humidity));
  • 남은 시간, 온도, 습도 데이터를 FND로 출력하기 위해 BCD 코드로  변환한다.

 

 

< Source, Print data on FND >

assign selected_fnd_data = (state == NORMAL_MODE || timer_enable) ? {bcd_lefted_time[7:0], 6'b0, duty} : {bcd_temperature, 6'b0, duty};
    
// FND Control module
fnd_cntr fnd_control (.clk(clk), .reset_p(reset_p), .hex_value(selected_fnd_data), .com(com), .seg_7(seg_7));
  • 현재 상태 단계에 따라 FND에 출력할 데이터를 선택하여 FND Control 모듈을 통해 출력하게 된다.

 

 

 

 

 

 

 

2.3. Control Normal mode fan power module

  • control_normal_mode 모듈은 일반 모드에서 선풍기의 메인 모터의 duty 값을 결정하는 모듈이다.
  • power 버튼이 눌러질 때마다 btn_power 변수가 활성화되며, btn_power 변수를 감지하여 pwn의 duty값을 1씩 증가시켜 Top module로 증가된 duty 값을 전달하게 된다.
  • 선풍기 팬 파워는 4단계로 나누어 지게 되며, 최대 3단계까지의 파워를 갖게 된다.
// Control NORMAL_MODE
module control_normal_mode (
    input clk, reset_p,
    input btn_power,
    input new_reset,
    output [1:0] duty );
    
    // Declare variables.
    reg [1:0] fan_power; // Power of electric fan
    
    // Power 버튼이 누를 때마다 선풍기 파워 변경
    always @(posedge clk or posedge reset_p) begin
        if(reset_p || new_reset) fan_power = 0;
        else if(btn_power) begin
            if(fan_power >= 3) fan_power = 0;
            else fan_power = fan_power + 1;
        end
    end
    
    // 현재의 fan power의 단계(= duty ratio of fan)을 출력하기.
    assign duty = fan_power;
    
endmodule

 

 

 

 

 

 

2.4. Control Safe mode fan power module

  • control_safe_mode 모듈은 절전 모드에서 선풍기의 메인 모터의 duty 값을 결정하는 모듈이다.
  • Top module에서 선언한 dht11 모듈의 instance로 부터 얻은 온, 습도 데이터를 기준으로 절전모드에서의 선풍기의 메인 모터의 duty 값을 결정한다.
// Safe mode
module control_safe_mode (
    input clk, reset_p,
    input new_reset,
    input [7:0] temperature,humidity,
    output reg [1:0] duty );
                            
    // Control fan power by using temperature.
    always @(posedge clk or posedge reset_p) begin
        if(reset_p || new_reset) duty = 2'd0;
        else if(24 <= temperature && temperature <= 26) duty = 2'd1;
        else if(27 <= temperature && temperature <= 29) duty = 2'd2;
        else if(temperature >= 30) duty = 2'd3;
    end
    
endmodule

 

 

 

 

 

 

 

2.5. Control Timer mode module

  • control_timer 모듈은 Timer 모드를 컨트롤 하는 모듈로서 총 4단계로 나누어 컨트롤하게 된다.
  • timer 모드를 컨트롤하기 위해 FSM (Finite State Machine) 형식으로 구현하였으며, 아래와 같이 4개의 state parameter로 나누어 정의하였다.
    NO_SETTING : Timer 모드 off
    SETTING_5SEC : 타이머 5초 세팅
    SETTING_10SEC : 
    타이머 10초 세팅
    SETTING_15SEC : 
    타이머 15초 세팅
  • control_timer 모듈내에서 선언된 변수들의 역활 및 의미는 다음과 같다.
    ▶ setting_timer : 세팅된 Timer 값
    ▶ timer_set :
    현재 타이머 모드로 세팅되었는가를 나타내는 변수
         -
    timer_set == 0 : 타이머 모드 off
         -
    timer_set == 1 : 타이모 모드 on
    ▶ cur_timer :
    down counting을 위한 변수로서 setting_timer 변수로부터 세팅된 값을 받아 down counting
                            을 하게 된다.
    ▶ time_out :
    타이머 모드인 상태에서 down counting을 하여 0분 0초가 되었을 때, "0분 0초가 되었음"을 
                          알려주는 Flag 변수
         - time_out == 0 : 아직 0분 0초에 도달하지 않은 상태
       
         - time_out == 1 : 0분 0초에 도달한 상태

  • control_timer 모듈의 소스 코드에 대한 설명은 코드와 함께 설명하도록 하겠다.
// Control Timer 
module control_timer (
    input clk, reset_p,
    input btn_timer,
    input [1:0] duty,
    output timer_enable,
    output reg time_out,
    output [6:0] lefted_time );
    
    // Declare state.
    parameter NO_SETTING = 4'b0001;
    parameter SETTING_5SEC = 4'b0010;
    parameter SETTING_10SEC = 4'b0100;
    parameter SETTING_15SEC = 4'b1000;
    
    // state, next_state 변수 선언
    reg [3:0] state, next_state;
    
    // Declare variables.
    reg [3:0] setting_timer;
    reg timer_set; // timer_set = 1 : setting timer, timer_set = 0 : no-setting timer
    
    // 언제 다음 state로 넘어가는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p || time_out) state = NO_SETTING;
        else if(btn_timer) state = next_state;
    end
    
    // 각 state에 대한 동작 정의
    always @(negedge clk or posedge reset_p) begin
        if(reset_p || time_out) begin
            next_state = SETTING_5SEC;
            setting_timer = 0; 
            timer_set = 0;
        end
        else begin
            case(state) 
                // 1단계) Timer mode off
                NO_SETTING : begin
                    setting_timer = 7'd5;
                    timer_set = 0;
                    next_state = SETTING_5SEC;
                end
                
                // 2단계) 5초 Timer set
                SETTING_5SEC : begin
                    setting_timer = 7'd10;
                    timer_set = 1;
                    next_state = SETTING_10SEC;
                end
                
                // 3단계) 10초 Timer set
                SETTING_10SEC : begin
                    setting_timer = 7'd15;
                    timer_set = 1;
                    next_state = SETTING_15SEC;
                end
                
                // 4단계) 15초 Timer set
                SETTING_15SEC : begin
                    setting_timer = 7'd0;
                    timer_set = 1;
                    next_state = NO_SETTING;
                end
            endcase
        end
    end
    
    // Assign timer_enable
    assign timer_enable = timer_set;
    
    // Timer down counting
    wire clk_1usec, clk_1msec, clk_1sec;
    clk_div_100 clk_div_1usec (.clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec));
    clk_div_1000 clk_div_1msec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
    clk_div_1000 clk_div_1sec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1msec), .clk_div_1000_nedge(clk_1sec));
    
    reg [6:0] cur_timer;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p || time_out) begin cur_timer = 0; time_out = 0; end
        else if(btn_timer) cur_timer = setting_timer;
        else if(timer_set && clk_1sec && !time_out) begin
            if(cur_timer <= 0) time_out = 1; 
            else if(duty) cur_timer = cur_timer - 1;
        end
    end
    
    assign lefted_time = cur_timer;
endmodule
  • Q) 왜 각각의 상태에서 타이머를 세팅하는 과정에서 현재 상태 단계의 시간으로 세팅하는 것이 아닌 다음 상태 단계의 시간으로 세팅하는가?
    A) 
    setting_timer는 타이머 세팅 값을 의미하지만, 정확히는 "다음 단계의 타이머 세팅 값"을 의미한다. 따라서 setting_timer 값을 현재 타이머 단계가 아닌 다음 단계의 타이머 시간으로 세팅해야 btn_timer를 눌러 다음 단계로 넘어갔을 때, 다음 단계의 타이머로 down counting이 가능해진다.

 

< Source, Timer down-counting >

always @(posedge clk or posedge reset_p) begin
     if(reset_p || time_out) begin cur_timer = 0; time_out = 0; end
     else if(btn_timer) cur_timer = setting_timer;
     else if(timer_set && clk_1sec && !time_out) begin
         if(cur_timer <= 0) time_out = 1; 
         else if(duty) cur_timer = cur_timer - 1;
     end
 end
  • btn_timer을 통해 timer 버튼이 눌렀음을 감지하면 setting_timer 변수로부터 세팅된 타이머 값을 받아 cur_timer 변수에 저장하며, cur_timer 변수를 통해 down-counting하게 된다.
  • cur_timer == 0이라면 0분 0초가 되었기 때문에 time_out 값을 1로 설정해주고, cur_timer != 0이라면 계속해서 down counting을 하게 된다.
  • timer control module내에서 time_out 값이 1로 설정되게 되면 Top module에서 new_reset 변수 값이 1로 변경되어 new_reset 변수에 의해 변수 값 및 상태가 초기화 된다.

 

 

 

 

2.6. DHT11 Control module

 

Verilog RTL 설계(7월 23일 - 4, DHT11 구현 (3) )

1. 예외 처리의 필요성위 그림은 MCU와 DHT11 간에 통신 과정을 설명하고 있다.만약 MCU와 DHT11 간에 신호를 주고받는 과정에서 신호를 제대로 받지 못할 경우, 부정확한 신호 및 데이터를 받거나 잘

jbhdeve.tistory.com

Verilog RTL 설계(7월 23일 - 5, DHT11 구현 (4) ) (tistory.com)

 

Verilog RTL 설계(7월 23일 - 5, DHT11 구현 (4) )

1. DHT11과 Basys3 통신 과정에서 예외 상황이 발생했을 경우이전 게시글인 Verilog RTL 설계(7월 23일 - 4, DHT11 구현 (3))  에서는 dht11과 Basys3 간에 통신이 원활히 이루어져서 온도, 습도 데이터를 제대

jbhdeve.tistory.com

 

< Source, dht11_cntr module >

// DHT11 Control module
module dht11_cntr(
    input clk, reset_p,
    inout dht11_data,
    output [5:0] state_led,
    output reg [7:0] temperature, humidity);
    
    // Declare state 
    parameter S_IDLE = 6'b00_0001;
    parameter S_LOW_20MS = 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_DATA = 6'b10_0000;
    
    // Declare sub state.
    parameter S_WAIT_PEDGE = 2'b01;
    parameter S_WAIT_NEDGE = 2'b10;
    
    // Declare state, next_state
    reg [5:0] state, next_state;
    reg [1:0] sub_state;
    
    // Get 1usec one cycle pulse
    wire clk_1usec_dht11; 
    clk_div_100 clk_div_1usec(.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_1usec_dht11));
    
    // Declare necessary variables.
    reg [21:0] usec_counter;
    reg counter_enable;
    
    // Declare register of dht11_data wire.
    reg reg_of_dht11_data;
    assign dht11_data = reg_of_dht11_data;
    
    // Get one cycle pulse of negative edge of dht11_data.
    wire dht11_data_nedge, dht11_data_pedge;
    edge_detector edge_detector_dht11_data (.clk(clk), .reset_p(reset_p),
                        .cp(dht11_data), .p_edge(dht11_data_pedge), .n_edge(dht11_data_nedge));
    
    // Declare useconed counter.
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) usec_counter = 0;
        else if(clk_1usec_dht11 && counter_enable) usec_counter = usec_counter + 1;
        else if(!counter_enable) usec_counter = 0;
    end 
    
    // 언제 다음 state로 넘어가는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) state = S_IDLE;         
        else state = next_state;
    end 
    
    // dht11로 받은 데이터를 임시저장하는 레지스터 선언
    // check sum과 비교한 후, 데이터 손실이 없음을 확인하면 그 때, 출력
    reg [39:0] temp_data;
    reg [5:0] n_data;
    
    // 현재 어떤 상태에 있는지 확인하기 위해 LED 출력
    assign state_led = state;
    
    // 각 상태 단계에서의 동작과 다음 상태 단계로 넘어가기 위한 조건을 정의
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            next_state = S_IDLE;
            sub_state = S_WAIT_PEDGE;
            reg_of_dht11_data = 0;
            temp_data = 40'b0;
            n_data = 0;
            counter_enable = 0;
            temperature = 0;
            humidity = 0;
        end    
        else begin
            case(state)
                // 1단계) S_IDLE (3초 대기)
                S_IDLE : begin
                     if(usec_counter < 22'd3_000_000) begin
                        counter_enable = 1;
                        next_state = S_IDLE;
                        reg_of_dht11_data = 'bz;
                     end 
                     else begin
                        counter_enable = 0;
                        next_state = S_LOW_20MS;
                     end
                end
                
                // 2단계) S_LOW_20MS
                S_LOW_20MS : begin
                    if(usec_counter < 22'd20_000) begin
                        counter_enable = 1;
                        next_state = S_LOW_20MS;
                        reg_of_dht11_data = 0;
                    end
                    else begin
                        counter_enable = 0;
                        next_state = S_HIGH_20US;
                    end 
                end
                
                // 3단계) S_HIGH_20US
                S_HIGH_20US : begin
                    counter_enable = 1;
                    reg_of_dht11_data = 'bz;
                
                    if(usec_counter >= 22'd100_000) begin
                        next_state = S_IDLE;
                        counter_enable = 0;
                    end
                    else if(dht11_data_nedge) begin
                        counter_enable = 0;
                        next_state = S_LOW_80US;
                    end 
                end
                
                // 4단계) S_LOW_80US
                S_LOW_80US : begin
                    counter_enable  = 1;
                    if(usec_counter >= 22'd100_000) begin
                        next_state = S_IDLE;
                        counter_enable  = 0;
                    end
                    else if(dht11_data_pedge) begin
                        next_state = S_HIGH_80US;
                        counter_enable  = 0;
                    end
                end
                
                
                // 5단계) S_HIGH_80US
                S_HIGH_80US : begin
                    counter_enable  = 1;
                    if(usec_counter >= 22'd100_000) begin
                        next_state = S_IDLE;
                        counter_enable  = 0;
                    end
                    else if(dht11_data_nedge) begin
                        next_state = S_DATA;
                        counter_enable  = 0;
                    end
                end
                
                
                // 6단계) S_DATA
                S_DATA : begin
                    counter_enable = 1;
                    
                    if(usec_counter >= 22'd100_000) begin
                        counter_enable  = 0;
                        next_state = S_IDLE;
                        sub_state = S_WAIT_PEDGE;
                        n_data = 0;
                        temp_data = 0;
                    end
                    else begin
                        case(sub_state)
                            S_WAIT_PEDGE : 
                                if(dht11_data_pedge) sub_state = S_WAIT_NEDGE;
                        
                            S_WAIT_NEDGE : begin
                                if(dht11_data_nedge) begin
                                    if(usec_counter < 95) temp_data = {temp_data[38:0], 1'b0};
                                    else temp_data = {temp_data[38:0], 1'b1};
                                
                                    counter_enable = 0;
                                    sub_state = S_WAIT_PEDGE;
                                    n_data = n_data + 1;
                                end
                            end
                        endcase
                    end 
                
                   
                       
                    if(n_data >= 40) begin
                         if(temp_data[39:32] + temp_data[31:24] + temp_data[23:16] + temp_data[15:8] == temp_data[7:0]) begin
                             temperature = temp_data[23:16];
                             humidity = temp_data[39:32];
                         end
                        
                         n_data = 0;
                         counter_enable = 0;
                            
                         sub_state = S_WAIT_PEDGE;
                         next_state = S_IDLE;
                   end
                end
            endcase
        end
    end
endmodule

 

 

 

 

 

 

 

3. 구현 

< 일반 모드에서의 풍속 조절 >

< Timer 모드에서의 설정 및 동작 >

< 절전모드, DHT11을 통해 얻은 온도 값에 따른 풍속 조절 >