RTL Design/Verilog 프로젝트

Verilog 팀 프로젝트 - 스마트 수경재배기

유로 청년 2024. 9. 10. 13:47

1. 서론 

  • 스마트 수경 재배기 라는 주제로 Verilog 팀 프로젝트를 진행하였습니다.
  • 스마트 수경 재배기는 수업을 통해 배운 Verilog 지식과 다양한 센서 모듈을 이용하여 기존의 수경 재배기의 불편함을 반 자동화하여 해소하고자 해당 주제를 팀 프로젝트의 주제로 선정하였습니다.

 

 

1.1. 기존 수경 재배기의 불편함

  • 아래 사진은 글쓴이의 집에서 실제로 재배하고 있는 수경 재배기 입니다.

글쓴이가 실제로 재배중인 수경 재배기 입니다.

  • 실제로 수경 재배기을 이용하먼서 다양한 농작물 및 채소를 재배할 수 있었지만, 재배하는 과정에서 다양한 불편함 및 개선사항을  찾을 수 있었습니다. 
  • 제가 경험한 기존 수경 재배기의 불편함은 다음과 같은 3가지 였습니다.
    1. 식물 작물에 필요한 급수 수동 공급
    2. 재배기의 LED 높이 수동 조절
    ▶ 3. 더운 날씨로 인한 작물 성장 부진
  • 위와 같은 기존 수경 재배기의 불편함을 Basys3와 다양한 센서를 이용하여 해결하기 위해 "스마트 수경 재배기" 라는 주제를 선택하여 프로젝트를 진행하였습니다.

 

 

1.2. 스마트 수경 재배기의 특징

1) 식물 작물에 필요한 급수 수동 공급 문제 해결

  • 기존 수경 재배기는 사용자가 직접 눈으로 급수가 충분한지 여부를 확인한 뒤, 부족하다고 판단되면 사용자가 수동적으로 수경 재배기에 급수를 보충해야 하는 불편함을 가지고 있었습니다.
  • 이는 특히 바쁜 현대인들에게 번거롭고 불편한 요소가 될 수 있습니다.
  • 이 문제를 해결하기 위해 수경 재배기의 벽면에 수위 센서를 부착하여 실시간으로 수위를 모니터링하고, 물이 부족할 경우 자동으로 워터 펌프를 작동시켜 충분한 수위에 도달할 때까지 물을 공급함으로써 불편함을 해소할 수 있습니다.

 

2) 재배기의 LED 높이 수동 조절 문제 해결

  • 수경 재배기의 LED는 밤과 같은 광합성이 부족한 환경에서도 충분한 빛을 제공하며, 식물의 성장에 따라 LED의 높이를 조정함으로써 작물에 필요한 광합성을 효과적으로 공급할 수 있습니다.
  • 다만, 사용자가 매일 식물의 높이를 확인하고 직접 LED의 높이를 조절해야 하는 번거로움이 있습니다.
  • 이러한 불편함을 해결하기 위해 LED에 초음파 센서를 부착하여 실시간으로 작물의 높이를 모니터링하고, LED와의 거리가 너무 가깝다고 판단하면 상단 모터를 작동시켜 LED를 적절한 거리로 자동 조정함으로써 식물에게 필요한 광합성을 효과적으로 공급할 수 있습니다.

 

3)  작물 성장에 어려운 환경으로부터 보호

  • 2024년 여름은 예년보다 유난히 더웠습니다. 이로 인해 작물의 성장이 어려워졌고, 씨앗이 발아하여 싹이 나야 할 시기가 지나도 싹이 트지 않는 문제가 발생하였습니다.
  • 이러한 문제점을 해결하고자 외부에 장착된 온습도 센서와 조도 센서를 활용하여, 외부 환경 변화에 맞춰 작물을 보호하고 최적의 성장 환경을 조성함으로써 작물의 성장을 극대화할 수 있습니다.

 

4) 휴대폰을 통한 재배기 외부 환경 확인 가능

  • 재배기 외부의 온도와 습도를 직접 확인하기 위해 현장에 갈 필요 없이, UART 통신을 활용한 블루투스 모듈을 통해 휴대폰에서 실시간으로 환경 정보를 확인할 수 있습니다.

 

 

 

 

1.3. 3D 구상도

  • 스마트 수경 재배기의 3D 하드웨어 구상도는 다음과 같습니다.

 

 

 

 

 

 

2. 본론

2.1. 스마트 수경 재배기의 블록 다이어그램

    • 스마트 수경 재배기의 블록 다이어그램은 다음과 같습니다.
    • 스마트 수경 재배기의 모듈은 크게 1) 센서 모듈 과 2) 컨트롤 모듈로 나누어 설계하였습니다.
      ▶ 센서 모듈
          - dht11_control [온습도 센서 컨트롤 모듈]
          - cds_and_water_level_control [조도 센서와 수위 센서 컨트롤 모듈]
          - hc_sr04_control [초음파 센서 컨트롤 모듈]

      ▶ 컨트롤 모듈
          - led_control [수경 재배기의 LED 밝기 조절 컨트롤 모듈]
          - led_height_control [LED 높이 컨트롤 모듈]
          - electric_fan_control [선풍기 컨트롤 모듈]
          - window_control [수경 재배기 창문 컨트롤 모듈]
          - water_pump_control [워터 펌프 컨트롤 모듈]
          - lcd_display_control [LCD Display 컨트롤 모듈]
          - uart_app_control [UART 통신 및 앱 데이터 전달 컨트롤 모듈]


Block diagram of smart hydroponic grower

 

 

2.2. 스마트 수경 재배기의 동작 

  • 스마트 수경 재배기는 다음과 같은 기능 및 동작을 지원합니다.

 

 

 

 

 

2.3. 스마트 수경 재배기의 컨트롤 버튼 및 스위치

  • 수경 재배기의 기능을 제어하기 위해 다음과 같이 버튼과 스위치를 구성하였습니다.

위와 같이 스마트 수경 재배기를 컨트롤하기 위해 버튼 및 스위치를 구성하였습니다.

 

각각의 버튼 및 스위치의 변수명은 위와 같이 선언하였습니다.

 

 

 

 

 

 

2.4. 스마트 수경 재배기의 모듈 구현

  • 스마트 수경 재배기의 모듈에 대한 설명은 소스코드와 함께 주석 및 추가적인 설명을 통해 설명하도록 하겠습니다.

 

< Source, Top module of smart farm >

// Top moudle of Smarr Farm
module top_module_of_smart_farm (
    input clk, reset_p,
    input [3:0] btn,
    input [15:0] sw,
    inout dht11_data,
    input vauxp6, vauxn6,
    input vauxp15, vauxn15,
    input hc_sr04_echo,
    input rx,
    output tx,
    output hc_sr04_trig,
    output left_window_pwm, right_window_pwm,
    output fan_pwm,
    output fan_dir_pwm,
    output led_pwm,
    output warning_water_level_led,
    output pump_on_off,
    output [3:0] half_step_mode_sequence,
    output scl, sda,
    output [3:0] com,
    output [7:0] seg_7);
    
    // Button Control module
    // Button Chattering 문제를 소프트웨어로 해결하기 위해 1ms delay time을 갖고 버튼 값을 받는다.
    wire btn_led_light, btn_window_mode, btn_electric_fan_power;
    button_cntr btn0(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_led_light));
    button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_window_mode));
    button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_electric_fan_power));
    
    // Declare Switch
    // 수경 재배기를 제어하기 위한 스위치 선언
    wire sw_led_up, sw_led_down, sw_window_open, sw_window_close, sw_cntr_electirc_fan_dir, sw_electric_fan_mode;
    wire sw_led_mode, sw_led_height_mode;
    assign sw_led_height_mode = sw[0];
    assign sw_led_up = sw[1];
    assign sw_led_down = sw[2];
    assign sw_window_open = sw[6];
    assign sw_window_close = sw[7];
    assign sw_led_mode = sw[10];
    assign sw_cntr_electirc_fan_dir = sw[11];
    assign sw_electric_fan_mode = sw[15];
    
    // Declare sensor variables.
    // 센서 모듈에서 제어 모듈로 센서값을 전달에 필요한 Wire 선언
    wire [15:0] dht11_value;          // 온습도 데이터 
    wire [7:0] sunlight_value;        // 조도 값 (8bit 양자화)
    wire water_flag;                  // 재배기에 물이 부족함을 알려주는 플래그
    wire led_up_down;                 // LED의 높이를 올릴지 내릴지를 알려주는 플래그
    wire [21:0] distance_cm;          // 현재 LED와 작물과의 거리
    
    // Instance of sensor module
    // 센서 모듈 인스턴스 선언
    dht11_control dht11_control_instance (.clk(clk), .reset_p(reset_p), .dht11_data(dht11_data), .dht11_value(dht11_value));
    cds_and_water_level_control cds_and_water_level_control_instance (.clk(clk), .reset_p(reset_p), .vauxp6(vauxp6), .vauxn6(vauxn6), .vauxp15(vauxp15), .vauxn15(vauxn15), .water_flag(water_flag), .sunlight_value(sunlight_value) );
    HC_SR04_cntr HC_SR04_cntr_0(.clk(clk), .reset_p(reset_p), .hc_sr04_echo(hc_sr04_echo), .hc_sr04_trig(hc_sr04_trig), .distance(distance_cm),  .led_debug(led_debug)); 
    
    // Instance of Control module
    // 컨트롤 모듈 인스턴스 선언
    window_control window_control_instance (.clk(clk), .reset_p(reset_p), .dht11_value(dht11_value), .sw_window_open(sw_window_open), .sw_window_close(sw_window_close), .btn_window_control(btn_window_mode), .left_window_pwm(left_window_pwm), .right_window_pwm(right_window_pwm));
    electric_fan_control electric_fan_control_instance (.clk(clk), .reset_p(reset_p), .sw_cntr_electirc_fan_dir(sw_cntr_electirc_fan_dir), .sw_electric_fan_mode(sw_electric_fan_mode), .dht11_value(dht11_value), .btn_electric_fan_power(btn_electric_fan_power), .fan_pwm(fan_pwm), .fan_dir_pwm(fan_dir_pwm));
    led_control led_control_instance (.clk(clk), .reset_p(reset_p), .sw_led_mode(sw_led_mode), .btn_led_light(btn_led_light), .water_flag(water_flag), .sunlight_value(sunlight_value), .led_pwm(led_pwm), .warning_water_level_led(warning_water_level_led));
    water_pump water_pump_instance (.clk(clk), .reset_p(reset_p), .water_flag(water_flag), .pump_on_off(pump_on_off));  
    led_height_control led_height_control_instance ( .clk(clk), .reset_p(reset_p), .sw_led_height_mode(sw_led_height_mode), .distance_cm(distance_cm),
                        .sw_led_up(sw_led_up), .sw_led_down(sw_led_down), .half_step_mode_sequence(half_step_mode_sequence));
    
    // Instance of a module that displays temperature and humidity information      
    // 현재의 외부 환경 온도, 습도를 LCD 화면과 휴대폰으로 실시간 출력
    lcd_display_control lcd_display_control_instance (.clk(clk), .reset_p(reset_p), .dht11_value(dht11_value), .sunlight_value(sunlight_value), .water_flag(water_flag), .scl(scl), .sda(sda));
    uart_app_control uart_app_control_instance (.clk(clk), .reset_p(reset_p), .rx(rx), .dht11_value(dht11_value), .tx(tx));
    
    // Show temperature, humidity to FND
    // Basys3 의 FND을 통해 외부 환경 온도, 습도를 실시간 출력
    show_the_fnd show_the_fnd_instance(.clk(clk), .reset_p(reset_p), .hex_value(dht11_value), .sunlight_value(sunlight_value), .com(com), .seg_7(seg_7), .distance_cm(distance_cm));
    
    
endmodule
  • Top module of smart-farm은 센서 모듈들로부터 다음과 같은 데이터들 받게 됩니다.
    ▶ dht11_value : DHT11 센서로부터 얻은 온도, 습도 데이터
    ▶ sunlight_value : 조도 센서 값을 8bit 양자화하여 디지털 값으로 변환한 데이터
    ▶ water_flag : 현재 재배기의 물이 부족한지 여부를 알려주는 플래그 [물이 부족할 경우 == 1]
    ▶ led_up_down : 초음파 센서로 부터 LED 높이를 올릴지 내릴지에 관한 정보를 갖는 데이터
    ▶ distance_cm : 초음파 센서를 통해 측정한 현재 LED와 작물간에 거리 데이터 
  • Top module of smart-farm에서는 이렇게 전달 받은 센서 값을 각각의 제어 모듈로 전달되게 되며, 이를 통해 제어 모듈은 각각의 기능들을 컨트롤하게 됩니다.

 

 

 

< Source, The current temperature and humidity data are displayed on the FND.  >

module show_the_fnd (
    input clk, reset_p,
    input [15:0] hex_value,
    input [7:0] sunlight_value,
    input [21:0] distance_cm,
    output [3:0] com,
    output [7:0] seg_7);
    
    // Convert from binary to BCD Code
    // 이진수로 표현된 데이터를 BCD 코드로 변환한다.
    wire [11:0] temperature_bcd, humidity_bcd;
    bin_to_dec bcd_temp(.bin({4'b0, hex_value[15:8]}),  .bcd(temperature_bcd));
    bin_to_dec bcd_humi(.bin({4'b0, hex_value[7:0]}),  .bcd(humidity_bcd));
    
    // FND Control module
    // Basys3의 FND로 현재의 온도, 습도 출력한다.
    fnd_cntr fnd_cntr_instance (.clk(clk), .reset_p(reset_p), .value({temperature_bcd[7:0], humidity_bcd[7:0]}), .com(com), .seg_7(seg_7));
    
endmodule
  • 실시간으로 외부 환경의 온도, 습도 데이터를 전달 받은 뒤, BCD 코드로 변환한 후, Basys3의 FND로 출력합니다.

 

 

 

 

2.4.1. Sensor Module 

< Source, DHT11 Control Module >

// DHT11 Control module
module dht11_control(
    input clk, reset_p, 
    inout dht11_data,
    output [15:0] dht11_value);

    wire [7:0] humidity, temperature; 
    dht11_cntrl dth11( .clk(clk), .reset_p(reset_p), .dht11_data(dht11_data), .humidity(humidity), .temperature(temperature), .led_debug(led_debug));

    assign dht11_value = {temperature[7:0], humidity[7:0]};

endmodule
  • DHT11 온습도 센서를 통해 현재 외부 환경의 온습도 데이터를 얻은 뒤, 16bit wire 변수에 결합하여 Top module로 전달합니다.
  • DHT11 control module에 대해서 궁금하다면 아래 게시글을 참고해주세요.
    https://jbhdeve.tistory.com/311
 

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

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

jbhdeve.tistory.com

 

 

 

 

 

< Source, Cds and Water Level Control Module >

// cds_and_water_level_control module
module cds_and_water_level_control (
    input clk, reset_p,
    input vauxp6, vauxn6,
    input vauxp15, vauxn15,
    output reg water_flag,
    output reg [7:0] sunlight_value );
    
     wire [4:0] channel_out;
     wire [16:0] do_out;
     wire eoc_out;

     // ADC Module instance
       xadc_wiz_10 xadc_cds (
        .daddr_in({2'b0, channel_out}),
        .dclk_in(clk),
        .den_in(eoc_out),
        .reset_in(reset_p),
        .vauxp6(vauxp6),
        .vauxn6(vauxn6),
        .vauxp15(vauxp15),
        .vauxn15(vauxn15),
        .channel_out(channel_out),
        .do_out(do_out),
        .eoc_out(eoc_out)
    );
    
    // Get positive edge of eoc_out.
    wire eoc_out_pedge;
    edge_detector_p ed_eoc_out_cds(.clk(clk), .reset_p(reset_p), .cp(eoc_out), .p_edge(eoc_out_pedge));
    
    // Channel_out 변수 값에 따라 변환된 Channel이 무엇인지를 확인 후, 해당 값을 출력
    // do_out 값이 100미만 이면 물이 부족한 상태 ----> 1
    // do_out 값이 100이상 이면 물이 충분한 상태 ----> 0  
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            sunlight_value = 0;
            water_flag = 0;
        end
        else if(eoc_out_pedge) begin
            case(channel_out[3:0])
                6 : begin sunlight_value = do_out[15:8]; end
                15 : begin water_flag  = (do_out[15:9]<100) ? 1 : 0; end
            endcase
        end
    end
     
endmodule
  • 수위 센서를 통해 실시간으로 수경 재배기의 수위 상태를 모니터링 합니다.
  • 따라서 물이 부족하다고 판단할 경우, Top module 에게 water_flag 플래그를  통해 "물이 부족한 상태이니, 워터 펌프를 가동해주세요." 라고 알리도록 설계하였습니다.
  • 실험을 통해 수위 센서의 아날로그 값을 8bit로 양자화했을 경우, 100이하 일 때, 물이 부족함을 알리도록 설계하였습니다.

    ★★★★★★★★  이번 프로젝트를 통해 알게 된 점  ★★★★★★★★
  • 이전 프로젝트까지는 하나의 ADC 컨버터만 사용했지만, 이번 프로젝트에서는 두 개의 아날로그 값을 디지털로 변환하는 작업을 수행하였습니다.
  • 이 과정에서 서로 다른 두 개의 ADC 변환을 하나의 모듈 내에서 동시에 처리해야 한다는 점을 배우게 되었습니다.

 

 

 

 

< Source, HC-SR04 Control Module >

// hc_sr04_control 
module hc_sr04_control (
    input clk, reset_p, 
    input hc_sr04_echo,
    output hc_sr04_trig,
    output [7:0] distance_between_plant_and_led );
    
    // Instance of HC_SR04 Control module
    HC_SR04_cntr HC_SR04_cntr_1 (.clk(clk), .reset_p(reset_p), .hc_sr04_echo(hc_sr04_echo), .hc_sr04_trig(hc_sr04_trig), .distance(distance_between_plant_and_led)); 
endmodule
  • HC-SR04 초음파 센서를 통해 실시간으로 LED와 식물간에 거리 값을 Top module로 전달하며, 이를 바탕으로 식물에게 효과적으로 광합성을 제공할 수 있는 높이로 LED를 조정하게 됩니다.
  • HC_SR04 Control Module에 대해서 궁금하시다면 아래 게시글을 참고 해주세요.
    https://jbhdeve.tistory.com/312
 

Verilog RTL 설계(7월 24일 - 1, HC-SR04 구현 )

1. HC-SR04 초음파 센서굉장히 늦었지만, 이번 게시글에서는 초음파 센서인 HC-SR04에 대해서 다루어보도록 하겠다.HC-SR04 초음파 센서는 40kHz 주파수를 갖는 초음파를 통해 거리를 측정하는 초음파

jbhdeve.tistory.com

 

 

 

 

 

 

2.4.2. Control Module 

< Source, led_control Module >

// LED Control module
module led_control (
    input clk, reset_p,
    input sw_led_mode,
    input btn_led_light,
    input water_flag,
    input [7:0] sunlight_value,
    output led_pwm, warning_water_level_led );
    
    // Warning Water Level LED
    assign warning_water_level_led = water_flag;
    
    // Main LED Control
    // Declare state machine
    parameter S_MANUAL_MODE = 2'b01;
    parameter S_AUTO_MODE = 2'B10;
    
    // Declare state variable
    reg [1:0] state;
    
    // Declare duty variabels
    reg [2:0] manual_duty;         // 수동 모드의 LED Duty 값
    reg [7:0] auto_duty;           // 자동 모드의 LED Duty 값
    wire manual_pwm, auto_pwm;     // 수동 모드의 LED PWM, 자동 모드의 LED PWM
    
    // 언제 다음 상태로 전이되는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) state = S_MANUAL_MODE;
        else if(sw_led_mode) state = S_AUTO_MODE;
        else if(!sw_led_mode) state = S_MANUAL_MODE;
    end
    
    // 각 상태에 대한 행동 및 다음 상태로 전이되기 위한 조건 정의
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            manual_duty = 0;
            auto_duty = 0;
        end
        else begin
            case(state) 
            	// 수동 모드, LED 밝기 단계는 총 5단계로 나뉜다.
                S_MANUAL_MODE : begin
                    if(btn_led_light) begin
                        if(manual_duty >= 5) manual_duty = 0;
                        else manual_duty = manual_duty + 1;
                    end
                end
                
                // 자동모드, 조도 값에 의해 LED 밝기가 결정된다.
                S_AUTO_MODE : begin
                   auto_duty = 256 - sunlight_value;
                   
                   // auto duty 값이 50이하이면 LED를 끈다.
                   if(auto_duty <= 50) auto_duty = 0;
                end
            endcase
        end
    end
    
    // Instance of pwm control
    pwm_cntr #(.pwm_freq(10_000), .duty_step(6)) control_manual_led_pwm (.clk(clk), .reset_p(reset_p), .duty(manual_duty), .pwm(manual_pwm));
    pwm_cntr #(.pwm_freq(10_000), .duty_step(256)) control_auto_led_pwm (.clk(clk), .reset_p(reset_p), .duty(auto_duty), .pwm(auto_pwm));
    
    // Select led pwm
    // 현재 모드에 따라 최종 LED PWM을 선택하여 LED를 밝히게 된다.
    assign led_pwm = (state == S_MANUAL_MODE) ? manual_pwm : auto_pwm;
    
endmodule

Flow chart of LED Control Module

  • LED Control Module은 FSM (Finite State Machine)으로 설계하였으며, 다음과 같은 상태 단계를 갖습니다.
    ▶ S_AUTO_MODE : LED 밝기 자동 모드, 조도 센서에 의해 LED 밝기를 256단계로 나누어 컨트롤
    ▶ S_MANUAL_MODE : LED 밝기 수동 모드, 버튼에 의해 LED 밝기를  5단계로 나누어 컨트롤
  • 각 모드의 LED Duty 값에 의해 LED PWM 값을 얻게 되지만, 현재 모드 (state) 에 따라 최종적으로 외부 (Top module)로 전달되는 LED의 PWM이 결정되게 됩니다. 
  • LED 모드는 sw_led_mode 에 의해 결정되며, 수동 모드에서는 btn_led_light 버튼에 의해 LED 밝기를 조절할 수 있습니다.

 

 

 

 

< Source, led_height_control Module >

// LED Height Control module
module led_height_control(
    input clk, reset_p,
    input sw_led_height_mode,
    input [21:0] distance_cm,
    input sw_led_up, sw_led_down,
    output [3:0] half_step_mode_sequence);
    
    // Declare state machine 
    parameter S_MANUAL_MODE = 2'b01;
    parameter S_AUTO_MODE = 2'b10;
    
    // Declare state variable
    reg [1:0] state;
    
    // 언제 다음 state로 전이되는가?
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) state = S_MANUAL_MODE;
        else if(sw_led_height_mode) state = S_AUTO_MODE;
        else if(!sw_led_height_mode) state = S_MANUAL_MODE;
    end
    
    // Declare necessary variables
    // up_down : LED 을 올릴 것인지, 내릴 것인지에 대한 정보를 갖고 있는 레지스터
    // motor_enable : Step motor를 동작 시킬 것인지에 대한 정보를 갖고 있는 레지스터
    reg up_down, motor_enable;
    
    // 각 상태 단계에서의 동작 및 다음 상태 전이 조건
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            up_down = 1;
            motor_enable = 0;
        end
        else begin
            case(state) 
                // 수동 모드 : sw_led_up, sw_led_down에 의해 LED 높이 조절
                S_MANUAL_MODE :begin
                    if(sw_led_up) begin
                        up_down = 1;
                        if(sw_led_down) motor_enable = 0;
                        else motor_enable = 1;
                    end
                    else if(sw_led_down) begin
                        up_down = 0;
                        if(sw_led_up) motor_enable = 0;
                        else motor_enable = 1;
                    end
                    else motor_enable = 0;
                end
                
                // 자동 모드 : 초음파 센서에 의해 자동으로 LED 높이 조절
                // 작물과 센서간에 거리가 7cm 미만이면 LED 을 올린다.
                // 작물과 센서간에 거리가 15cm 초과이면 LED 를 내린다.
                // 이를 통해 LED와 작물간에 거리를 7cm ~ 15cm 사이를 유지하게 된다.
                S_AUTO_MODE : begin
                    if(distance_cm < 22'd7) begin
                        up_down = 1;
                        motor_enable = 1; 
                    end
                    else if(distance_cm > 22'd15) begin
                        up_down = 0;
                        motor_enable = 1;
                    end
                    else motor_enable = 0;                    
                end
            endcase
        end
    end
    
    // Instance of Step motor control module
    step_motor_control step_motor_control_instance (.clk(clk), .reset_p(reset_p), .up_down(up_down), .motor_enable(motor_enable), .half_step_mode_sequence(half_step_mode_sequence));
endmodule

 

Flow chart of LED Height Control Module

  • LED Height Control Module은 FSM (Finite State Machine)으로 설계하였으며, 다음과 같은 상태 단계를 갖습니다.
    ▶ S_AUTO_MODE : LED 높이 자동 조절 모드, 초음파 센서에 의해 LED 높이를 자동적으로 조정합니다.
    ▶ S_MANUAL_MODE : LED 높이 수동 조절 모드, 스위치에 의해 LED 높이를 수동적으로 조정합니다.
  • 각 모드에서 조건에 따라 up_down, motor_enable 값을 결정하여 Step motor control module에게 전달하게 됩니다.
  • LED 높이 조절 모드는 sw_led_height_mode 에 의해 결정되며, 수동 모드에서는 sw_led_up, sw_led_down 스위치에 의해 LED 높이를 수동으로 조절할 수 있습니다.
  • 특히, sw_led_up, sw_led_down 스위치 모두가 ON인 상태인 경우에는 LED를 올려야 할 지, 내려야 할 지 불분명하기 때문에 해당 경우가 발생하면 motor_enable 값을 0으로 설정하여 Step-motor를 동작시키지 않도록 설계하였습니다.

 

 

 

 

 

< Source, electric fan control module >

// Electric Fan Control
module electric_fan_control (
    input clk, reset_p,
    input sw_cntr_electirc_fan_dir,
    input sw_electric_fan_mode,
    input btn_electric_fan_power,
    input [15:0] dht11_value,
    output fan_pwm, fan_dir_pwm );
    
    // Declare state machine
    parameter S_MANUAL_MODE = 2'b01;
    parameter S_AUTO_MODE = 2'b10;
    
    // Declare state, next state
    reg [1:0] power_state;
    
    // 언제 다음 상태로 넘어가는가?
    // sw_electric_fan_mode 스위치에 의해 선풍기 파워 모드 변경 가능
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) power_state = S_MANUAL_MODE; 
        else if(sw_electric_fan_mode) power_state = S_AUTO_MODE;
        else if(!sw_electric_fan_mode) power_state = S_MANUAL_MODE;
    end
    
    // Register duty of electirc fan.
    reg [1:0] fan_manual_duty, fan_auto_duty;   
    
    
    // Declare temperature, humidity 
    wire [7:0] temperature, humidity;
    assign temperature = dht11_value [15:8];
    assign humidity = dht11_value [7:0];
    
    // 각 상태의 동작 및 다음 상태 단계로 전이되기 위한 조건 정의
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            fan_manual_duty = 0;
            fan_auto_duty = 0;
        end
        else begin
            case(power_state)
                // 수동 모드, btn_electric_fan_power 버튼에 의해 선풍기 파워 수동 조절 가능
                S_MANUAL_MODE : begin
                    if(btn_electric_fan_power) begin
                        if(fan_manual_duty >= 3) fan_manual_duty = 0;
                        else fan_manual_duty = fan_manual_duty + 1;
                    end
                end
                
                // 자동 모드, 온도 데이터에 의해 선풍기 파워 자동 조절 
                S_AUTO_MODE : begin
                    if(temperature < 27) fan_auto_duty = 0;
                    else if(27 <= temperature && temperature < 29) fan_auto_duty = 1;
                    else if(29 <= temperature && temperature <= 31) fan_auto_duty = 2;
                    else if(temperature > 31) fan_auto_duty = 3;
                end
           endcase        
        end
    end
    
    // Select duty of electric fan
    // 현재 모드에 따라 fan_manual_duty와 fan_auto_duty 중 하나 선택된다.
    wire [1:0] duty;
    assign duty = (power_state == S_MANUAL_MODE) ? fan_manual_duty : fan_auto_duty;
    
    // Get pwm of electric fan
    pwm_cntr #(.pwm_freq(100), .duty_step(4)) control_power_pwm (.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(fan_pwm));
  
    
    
    // ********** 해당 기능은 하드웨어상으로 구현하지 못했지만, 소프트웨어로 구현하였습니다. **********
    
    
    // Declare State Machine
    parameter S_SERVO_STOP = 2'b01;
    parameter S_SERVO_START = 2'b10;
    
    // Declcare state variable
    reg [1:0] dir_state;
    
    // clock divider 1sec
    wire clk_1usec, clk_1msec, clk_1sec;
    clock_div_100   usec_clk( .clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec)); 
    clock_div_1000 msec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
    clock_div_1000 sec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_1msec), .clk_div_1000_nedge(clk_1sec));
    
    // Declare necessary variables
    reg [4:0] dir_duty;
    reg [4:0] dir_min_duty;
    reg [4:0] dir_max_duty;
    reg direction_of_fan; // direction_of_fan == 0 이면 감소, direction_of_fan == 1 이면 증가
    
    // 언제 다음 state로 전이되는가?
    // sw_cntr_electric_fan_dir 스위치에 의해 선풍기 방향 조절 가능
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) dir_state = S_SERVO_STOP;
        else if(sw_cntr_electirc_fan_dir) dir_state = S_SERVO_START;
        else if(!sw_cntr_electirc_fan_dir) dir_state = S_SERVO_STOP;
    end
  
  	// 각 상태 단계의 동작 및 다음 상태 전이 조건 정의
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            dir_min_duty = 5'd8;
            dir_max_duty = 5'd22;
            dir_duty = 5'd15;
            direction_of_fan = 0;
        end
        else if(clk_1sec) begin
            case(dir_state)
            	// 선풍기 회전 기능 중지된 경우
                S_SERVO_STOP : begin
                    dir_duty = dir_duty;
                end
            
                // 선풍기 회전 기능 동작시킨 경우
                S_SERVO_START : begin
                    if(direction_of_fan) begin
                        if(dir_duty == dir_max_duty) begin
                            dir_duty = dir_duty - 1;
                            direction_of_fan = ~ direction_of_fan;
                        end
                        else dir_duty = dir_duty + 1;
                    end
                    else begin
                        if(dir_duty == dir_min_duty) begin
                            dir_duty = dir_duty + 1;
                            direction_of_fan = ~ direction_of_fan;
                        end
                        else dir_duty = dir_duty - 1;
                    end
                end
            endcase
        end
    end
  
    // Get pwm of servo-motor
    pwm_cntr #(.pwm_freq(50), .duty_step(200)) control_servo_pwm (.clk(clk), .reset_p(reset_p), .duty(dir_duty), .pwm(fan_dir_pwm));

endmodule

Flow chart of eletric fan power module

  • 위 모듈은 선풍기의 파워와 선풍기 회전에 관한 컨트롤 모듈입니다.
  • 선풍기 회전 기능은 하드웨어 구조상 회전 기능이 포함되어 있지 않습니다.
  • Electric Fan Power Module은 FSM (Finite State Machine)으로 설계하였으며, 다음과 같은 상태 단계를 갖습니다.
    ▶ S_AUTO_MODE : 선풍기의 자동 파워 조절 모드는 외부 온도가 설정된 기준에 따라 자동으로 파워를 조정합니다.
    ▶ S_MANUAL_MODE : 선풍기 파워 수동 조절 모드, 버튼을 통해 수동으로 선풍기 파워를 조정할 수 있습니다.
  • 현재 상태에 따라 수동 모드의 Fan Duty 또는 자동 모드의 Fan Duty 중 하나가 선택되며, 선택된 값은 pwm_cntr 모듈을 통해 처리된 후 선풍기의 PWM 신호로 변환되어 외부 모듈로 출력됩니다.
  • 선풍기 파워 모드는 sw_electric_fan_mode 스위치에 의해 결정되며, 수동 모드에서는 btn_electric_fan_power 버튼을 통해 선풍기 파워를 조절할 수 있습니다.
  • 선풍기 PWM은 4단계 duty step를 가지며, fan_duty 값을 전달하면 100Hz 주파수를 갖는 PWM가 만들어지게 됩니다.

 

 

 

 

< Source, Window Control Module >

// Window control module
module window_control (
    input clk, reset_p,
    input [15:0] dht11_value,
    input sw_window_open,
    input sw_window_close,
    input btn_window_control,
    output left_window_pwm, right_window_pwm);
    
    // Declare temperature, humidity 
    wire [7:0] temperature, humidity;
    assign temperature = dht11_value [15:8];
    assign humidity = dht11_value [7:0];
    
    // Declare state machine.
    parameter S_MANUAL_MODE = 2'b01;
    parameter S_AUTO_MODE = 2'b10;
    
    // Declare state, next state variables
    reg [1:0] state, next_state;
    
    // 언제 다음 상태 단계로 전이되는가?
    // btn_window_control 버튼에 의해 모드 변경이 가능하다.
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) state = S_MANUAL_MODE;
        else if(btn_window_control) state = next_state;
    end
    
    // Declare duty of window
    // right_min_duty == Window Open, right_max_duty == Window Close
    // left_min_duty == Window Close, left_max_duty == Window Open
    reg [5:0] left_duty, right_duty;
    reg [5:0] right_min_duty, right_max_duty;
    reg [5:0] left_min_duty, left_max_duty;

    // 1msec Clock Pulse
    wire clk_usec, clk_msec;
    clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec));
    clock_div_1000 msec_clk(.clk(clk), .reset_p(reset_p), 
        .clk_source(clk_usec), .clk_div_1000(clk_msec));
    
    // 각 상태 단계의 동작 및 다음 상태 전이 조건 정의
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
           next_state =  S_MANUAL_MODE;
           right_duty = right_max_duty;
           left_duty = left_min_duty;
              
           right_max_duty = 6'd18;
           right_min_duty = 6'd5;
           
           left_max_duty = 6'd25;
           left_min_duty = 6'd11;
        end
        else if(clk_msec) begin
            case (state) 
                // 1단계) 수동 조작 단계
                S_MANUAL_MODE: begin
                    if(sw_window_open) begin
                        if(right_duty > right_min_duty) right_duty = right_duty - 1;
                        if(left_duty < left_max_duty) left_duty = left_duty + 1;
                    end
                    else if(sw_window_close) begin
                        if(right_duty < right_max_duty) right_duty = right_duty + 1;
                        if(left_duty > left_min_duty) left_duty = left_duty - 1;
                    end 
                    
                    next_state = S_AUTO_MODE;        
                end
                
                // 2단계) 자동 조작 단계
                // 27도 이상일 경우, 창문을 닫힌다.
                 S_AUTO_MODE : begin
                    if(temperature < 8'd27) begin
                        right_duty = right_max_duty;
                        left_duty = left_min_duty;
                    end
                    else begin 
                        right_duty = right_min_duty;
                        left_duty = left_max_duty;
                    end
                    
                    next_state = S_MANUAL_MODE;
                 end   
            endcase 
        end
    end
    
    // Instance of pwm_control module
    pwm_Nstep_freq #(
    .duty_step(200),
    .pwm_freq(50)) 
    pwm_right_servo_motor (.clk(clk), .reset_p(reset_p), .duty(right_duty), .pwm(right_window_pwm));
    
    pwm_Nstep_freq #(
    .duty_step(200),
    .pwm_freq(50)) 
    pwm_left_servo_motor (.clk(clk), .reset_p(reset_p), .duty(left_duty), .pwm(left_window_pwm));
    
endmodule

 

Flow chart of Window Control Module

 

  • 위 모듈은 재배기 창문을 컨트롤 하기 위해 설치된 Servo-motor를 제어하는 모듈입니다.
  • Window Control Module은 FSM (Finite State Machine)으로 설계하였으며, 다음과 같은 상태 단계를 갖습니다.
    ▶ S_AUTO_MODE : 재배기 창문 자동 제어 모드, 외부 온도를 통해 자동적으로 창문을 제어하게 됩니다.
    ▶ S_MANUAL_MODE : 재배기 창문 수동 제어 모드, 두 개의 스위치에 의해 창문을 수동으로 제어할 수 있습니다.
  • 현재 모드에 따라 왼쪽, 오른쪽 서보 모터의 duty 값을 결정한 뒤, pwm_Nstep_freq 모듈을 통해 해당 해당 duty 값을 갖는 50Hz PWM을 생성하게 됩니다.
    (pwm_Nstep_freq  모듈은 duty step = 200단계로 나뉘어 있으며, 주파수가 50Hz인 PWM을 생성하는 모듈입니다.)
  • 창문 컨트롤 모드는 btn_window_control 버튼에 의해 결정되며, 수동 모드에서는 sw_window_open, sw_window_close  스위치에 컨트롤할 수 있습니다.
  • 자동 모드에서 외부 온도가 27도 초과이면 창문을 닫히며, 27도 이하이면 창문히 닫히도록 설게했습니다.

 

 

 

 

< Source, Water Pump Control Modue >

// Water Pump Control module
module water_pump (
    input clk, reset_p,
    input water_flag,
    output pump_on_off );
    
    assign pump_on_off = water_flag;
    
endmodule
  • 수위 센서로부터 얻은 water_flag 플래그 변수 값에 따라 워터 펌프 동작 여부가 결정됩니다.
  • 수위 센서 컨트롤 모듈에서 "재배기 수위가 낮으니, 워터 펌프 동작시켜줘!" 라고 플래그를 활성화시키면 워터 펌프 컨트롤 모듈에서 해당 플래그가 활성화되는 동안 워터 펌프를 동작하게 됩니다. 

 

 

 

 

< Source, LCD Display Control Module >

module lcd_display_control (
    input clk, reset_p,
    input [15:0] dht11_value,
    input [7:0] sunlight_value,
    input water_flag,
    output scl, sda);
    
    // Get temperature / humidity from DHT11 Control Module
    wire [7:0] temperature, humidity;
    wire [15:0] temperature_bcd, humidity_bcd;
    
    assign temperature = dht11_value[15:8];
    assign humidity = dht11_value[7:0];
    
    // Convert from binary to BCD Code
    bin_to_dec bcd_temp(.bin({4'b0, temperature}),  .bcd(temperature_bcd));
    bin_to_dec bcd_humi(.bin({4'b0, humidity}),  .bcd(humidity_bcd));
    
    // Declare state machine
    parameter IDLE = 9'b0_0000_0001;                         // LCD 디스플레이 대기 상태 단계
    parameter INIT = 9'b0_0000_0010;                         // LCD 초기화 과정
    parameter SEND_STRING_TEMPERATURE = 9'b0_0000_0100;      // "Temperature" 문자열 출력
    parameter SEND_TEMPERATURE_DATA = 9'b0_0000_1000;        // 온도 데이터 출력
    parameter SEND_COMMAND_NEXT_LINE = 9'b0_0001_0000;       // 다음 줄로 전환 명령어 전송
    parameter SEND_STRING_HUMIDITY = 9'b0_0010_0000;         // "Humidity" 문자열 출력 
    parameter SEND_HUMIDITY_DATA = 9'b0_0100_0000;           // 습도 데이터 출력
    parameter WAIT_1SEC = 9'b0_1000_0000;                    // 모든 데이터를 출력한 후, 1초대기
    parameter SEND_COMMAND = 9'b1_0000_0000;                 // 화면 클리어 명령어 전송

      // Get usecond clock
      wire clk_usec;
      clock_div_100 usec_clk (.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec));
        
      // 마이크로세컨드 단위로 카운트 
      // enable 이 1이면 카운트 동작 , 1이 아니면 0으로 카운트 초기화 
      reg [21:0] count_usec;
      reg count_usec_en;
      always @(negedge clk or posedge reset_p)begin
             if(reset_p) count_usec = 0;
             else if(clk_usec && count_usec_en) count_usec = count_usec + 1;
             else if(!count_usec_en) count_usec = 0;
        end
        
        // Declare register of text
        reg [7:0] send_buffer;
        
        // Declare varables
        reg rs, send;
        wire busy;
        
        //  instance of i2c lcd send byte 
        i2c_lcd_send_byte i2c_lcd_send_byte_0 (.clk(clk), .reset_p(reset_p),
                                        .addr(7'h27), .send_buffer(send_buffer), .rs(rs), .send(send),             
                                        .scl(scl), .sda(sda), .busy(busy));                                   
                                        
        // Declare state, next state
        reg [10:0] state, next_state;
        
        // 언제 다음 상태로 넘어가는가?
        always @(negedge clk or posedge reset_p) begin
                if(reset_p) state = IDLE;
                else state = next_state;
        end
    
        // 초기화 했는지 여부를 나타내는 FLAG Register
        reg init_flag, flag_reset;
        
        // Counting for data
        reg [9:0] cnt_data ;
        
        // 문자열 Register
        reg [14*8-1:0] str_temperature;
        reg [11*8-1:0] str_humidity;
        reg [9:0] cnt_string;
        
        // 각 상태에 대한 동작 정의
        always @(posedge clk or posedge reset_p) begin
                if(reset_p) begin
                    next_state = IDLE;
                    init_flag = 0;
                    count_usec_en = 0;
                    cnt_data = 0;
                    rs = 0;
                    str_temperature = "Temperature : "; // C언어처럼 마지막에 NULL은 없다.
                    str_humidity = "Humidity : ";
                    cnt_string = 0;
                    flag_reset = 0;
                end
                else begin
                    case (state)
                            // 1단계) IDLE
                            IDLE : begin
                                    if(init_flag) begin
                                        if(count_usec < 22'd1000) begin
                                                count_usec_en = 1;
                                        end
                                        else begin
                                            count_usec_en = 0;
                                            next_state = SEND_STRING_TEMPERATURE;          
                                        end
                                    end
                                    else  begin
                                            if(count_usec <= 22'd80_000) begin
                                                count_usec_en = 1;
                                            end
                                            else begin
                                                count_usec_en = 0;
                                                next_state = INIT;
                                             end
                                    end
                            end
                            
                            // 2단계) INIT
                            INIT : begin
                                if(busy) begin
                                     send = 0;
                                     
                                     if(cnt_data >=6) begin                   
                                        init_flag = 1;
                                        cnt_data = 0;
                                        next_state = IDLE;       
                                     end
                                end
                                else if(!send) begin // send == 0 && busy == 0일 때만 동작
                                                     // busy는 다음 posedge 일 때 0-> 1로 변화하기 때문에
                                                     // 8'h33을 보내고, 바로 8'h82로 보내서 위와 같은 조건을 추가
                                        case(cnt_data)
                                                0: send_buffer = 8'h33;
                                                1: send_buffer = 8'h32;
                                                2: send_buffer = 8'h2a; // 2줄, 5X8 Dots 사용
                                                3: send_buffer = 8'h0c; // Display : 1, Cursor : on + 깜빡임 
                                                4: send_buffer = 8'h01;
                                                5: send_buffer = 8'h06; 
                                        endcase 
                                        
                                        rs = 0;
                                        cnt_data = cnt_data + 1;
                                        send = 1;
                                end
                            end
                            
                            // 3단계) SEND_STRING_TEMPERATURE
                            SEND_STRING_TEMPERATURE : begin
                                if(busy) begin
                                     send = 0;
                                     
                                     if(cnt_string >= 14) begin                   
                                        cnt_string = 0;
                                        next_state = SEND_TEMPERATURE_DATA;       
                                     end
                                end
                                else if(!send) begin // send == 0 && busy == 0일 때만 동작
                                                     // busy는 다음 posedge 일 때 0-> 1로 변화하기 때문에
                                                     // 8'h33을 보내고, 바로 8'h82로 보내서 위와 같은 조건을 추가
                                        case(cnt_string)
                                                0: send_buffer = str_temperature[111 : 104];
                                                1: send_buffer = str_temperature[103 : 96];
                                                2: send_buffer = str_temperature[95 : 88];
                                                3: send_buffer = str_temperature[87 : 80];
                                                4: send_buffer = str_temperature[79 : 72];
                                                5: send_buffer = str_temperature[71 : 64];
                                                6: send_buffer = str_temperature[63 : 56];
                                                7: send_buffer = str_temperature[55 : 48];
                                                8: send_buffer = str_temperature[47 : 40];
                                                9: send_buffer = str_temperature[39 : 32];
                                                10: send_buffer = str_temperature[31 : 24];
                                                11: send_buffer = str_temperature[23 : 16];
                                                12: send_buffer = str_temperature[15 : 8];
                                                13: send_buffer = str_temperature[7 : 0];
                                        endcase 
                                        
                                        rs = 1;
                                        cnt_string = cnt_string + 1;
                                        send = 1;
                                end
                          end
                            
                            
                            // 4단계) SEND_TEMPERATURE_DATA
                            SEND_TEMPERATURE_DATA : begin
                                    if(busy) begin
                                            send = 0;
        
                                            if(cnt_data >= 2) begin 
                                                    cnt_data = 0;
                                                    next_state = SEND_COMMAND_NEXT_LINE;
                                            end
                                    end
                                    else if(!send) begin
                                        case(cnt_data) 
                                            0 : send_buffer = "0" + temperature_bcd[7:4];
                                            1 : send_buffer = "0" + temperature_bcd[3:0];
                                        endcase
                                        
                                        cnt_data = cnt_data + 1;
                                        rs = 1;
                                        send = 1;
                                    end
                            end
                            
                            // 5단계) SEND_COMMAND_NEXT_LINE
                            SEND_COMMAND_NEXT_LINE : begin
                                    if(busy) begin
                                            if(flag_reset) begin
                                                flag_reset = 0;
                                                next_state = SEND_STRING_HUMIDITY;
                                            end
                                            send = 0;
                                  end
                                  else begin
                                            send_buffer = 8'hc0;
                                            rs = 0;
                                            send = 1;
                                            flag_reset = 1;
                                 end
                            end
                            
                            
                            // 6단계) SEND_STRING_HUMIDITY
                            SEND_STRING_HUMIDITY : begin
                                if(busy) begin
                                     send = 0;
                                     
                                     if(cnt_string >= 11) begin                   
                                        cnt_string = 0;
                                        next_state = SEND_HUMIDITY_DATA;       
                                     end
                                end
                                else if(!send) begin // send == 0 && busy == 0일 때만 동작
                                                     // busy는 다음 posedge 일 때 0-> 1로 변화하기 때문에
                                                     // 8'h33을 보내고, 바로 8'h82로 보내서 위와 같은 조건을 추가
                                        case(cnt_string)
                                                0: send_buffer = str_humidity[87 : 80];
                                                1: send_buffer = str_humidity[79 : 72];
                                                2: send_buffer = str_humidity[71 : 64];
                                                3: send_buffer = str_humidity[63 : 56];
                                                4: send_buffer = str_humidity[55 : 48];
                                                5: send_buffer = str_humidity[47 : 40];
                                                6: send_buffer = str_humidity[39 : 32];
                                                7: send_buffer = str_humidity[31 : 24];
                                                8: send_buffer = str_humidity[23 : 16];
                                                9: send_buffer = str_humidity[15 : 8];
                                                10: send_buffer = str_humidity[7 : 0];
                                        endcase 
                                        
                                        rs = 1;
                                        cnt_string = cnt_string + 1;
                                        send = 1;
                                end
                          end
                          
                  
                          // 7단계) SEND_HUMIDITY_DATA
                          SEND_HUMIDITY_DATA : begin
                                if(busy) begin
                                            send = 0;
                                            
                                            if(cnt_data >= 2) begin
                                                cnt_data = 0;       
                                                next_state = WAIT_1SEC;
                                             end
                                    end
                                    else if(!send) begin
                                            case(cnt_data) 
                                                0 : send_buffer = "0" + humidity_bcd[7:4];
                                                1 : send_buffer = "0" + humidity_bcd[3:0];
                                            endcase
                                            
                                            cnt_data = cnt_data + 1;
                                            rs = 1;
                                            send = 1;
                                    end
                          end
                          
                          // 8단계) WAIT_1SEC
                          WAIT_1SEC : begin
                                if(count_usec < 22'd1_000_000) begin
                                     count_usec_en = 1;
                                end
                                else begin
                                       count_usec_en = 0;
                                       next_state = SEND_COMMAND;
                                end
                          end
                          
                          // 9단계) SEND_COMMAND
                          SEND_COMMAND : begin
                                 if(busy) begin
                                       send = 0;
                                  end 
                                  else if(!send) begin
                                            if(flag_reset) begin
                                                flag_reset = 0;
                                                next_state = IDLE;
                                            end
                                            else begin
                                                send_buffer = 8'h01;
                                                rs = 0;
                                                send = 1;
                                                flag_reset = 1;
                                            end     
                                 end
                          end
                    endcase
                end
                
        end
        
endmodule

  • 이번 하드웨어 구현에서 HD44780U LCD 디스프레이 모듈을 사용했습니다. 
  • LCD 디스플레이에서 실시간은 DHT11로부터 온도, 습도 데이터를 전달받아 1초마다 출력하도록 설계하였습니다.
  • LCD 디스플레이 컨트롤 모듈은 FSM (Finite State Machine) 방식으로 설계하였으며, 각각의 상태 단계는 다음과 같은 작업들을 수행하게 됩니다.
    ▶ S_IDLE 단계 : DHT11로부터 데이터를 받아 출력하기 위해 잠시 대기 상태
    ▶ S_INIT 단계 : LCD 디스플레이 모듈을 사용하기 전, 디스플레이 모듈 초기화
    ▶ S_SEND_STRING_TEMPERATURE 단계 : LCD 모듈에 "Temperature" 문자열을 전송하여 출력하는 단계
    ▶ S_TEMPERATURE_DATA 단계 : LCD 모듈에 온도 데이터를 전송하여 출력하는 단계
    ▶ S_COMMAND_NEXT_LINE 단계 : 다음 줄로 넘어가기 위해 next line 명령어를 전송하는 단계
    ▶ S_SNED_HUMIDITY 단계 : LCD 모듈에 "Humidity" 문자열을 전송하여 출력하는 단계
    ▶ S_HUMIDITY_DATA 단계 : LCD 모듈에 습도 데이터를 전송하여 출력하는 단계
    ▶ S_WAIT_1SEC 단계 : 모든 데이터를 전송한 뒤, LCD 화면에 해당 데이터를 그대로 출력하기 위해 1초 대기
    ▶ S_COMMAND_CLEAR 단계 : 새로운 데이터를 다시 쓰기 위해 LCD 디스플레이를 클리어하는 단계
  • LCD 디스플레이 컨트롤 모듈에 대한 자세한 설명은 아래 게시글을 참고해주세요.
    https://jbhdeve.tistory.com/319
 

Verilog RTL 설계(8월 22일 - 3, I2C 통신을 통한 LCD 컨트롤 - (3))

1. 버튼을 누를 때마다 'A' 문자를 LCD 디스플레이 출력하기이번 게시글에서는 이전 게시글에서 설계한 i2c_master 모듈과 i2c_lcd_send_byte 모듈을 이용하여 I2C 통신을 통해 LCD 모듈에 'A' 문자 데이터를

jbhdeve.tistory.com

 

 

 

 

 

< Source, UART-App Control Module >

  • 해당 모듈은 소스 코드가 상당히 길기 때문에 본 게시글에 첨부하지 못했습니다.
  • 아래 Github QR 코드에 들어가셔 해당 모듈의 소스 코드를 확인해주세요.

 

 

 

 

3. 시연 영상

 

 

 

 

 

4. 해당 프로젝트의 발표 자료

혁스퀘어_스마트팜_최종.pdf
3.59MB

 

 

 

 

5. Github QR 코드

  • 해당 프로젝트의 소스 코드는 아래 QR 코드를 통해 확인하실 수 있습니다.