Verilog 팀 프로젝트 - 에너지 절약 선풍기
1. 서론
- 에너지 절약 선풍기 라는 주제로 팀 프로젝트를 진행하였습니다.
- 선택의 배경에는 Verilog 수업에서 습득한 지식을 실제 응용하고, 다양한 센서 모듈을 활용할 수 있는 기회가 있었기 때문입니다. 우리의 목표는 기존 선풍기의 기능을 넘어서, 에너지 효율성을 향상시키는 기능들을 추가하는 것입니다. 이 프로젝트를 통해 우리는 이론적 지식을 실제 문제 해결에 적용하며, 동시에 환경 친화적이고 실용적인 가전제품을 개발하는 과정을 경험할 수 있었습니다.
2. 기존 선풍기의 기능 & 추가적인 에너지 절약 기능
- 기존 선풍기에서 지원하는 기능들은 다음과 같습니다.
▶ 버튼을 통한 선풍기 풍속 조절
▶ Timer 모드를 통한 선풍기 동작 타이머 설정 가능
▶ 선풍기 헤드 방향 설정 (좌우)
▶ 선풍기 LED 밝기 조절 - 기존 선풍기는 위와 같은 기능을 지원했으나, 이번 프로젝트에서는 에너지 절약에 중점을 두어 새로운 기능을 추가했습니다.
- 에너지 절약을 위해 크게 2가지 기능을 추가하였습니다.
1) 온도에 따른 선풍기 파워 세기 조절
2) 초음파 센서를 통해 선풍기 주변에 사람이 없을 경우, 자동으로 선풍기 종료 - 이를 위해 크게 2가지 모드로 분리하여 설계하였습니다.
1) 일반 모드 : 일반적인 선풍기 모드
2) 에코 모드 : 에너지 절약 모드, 해당 모드에서 온도, 초음파 센서로 부터 얻은 거리 값에 의해
선풍기 동작 여부 결정
3. 에너지 절약 선풍기의 특징
- 1) 온도에 따른 선풍기 파워 자동 조절
DHT11 센서로 외부 온도를 실시간으로 감지해, 적절한 선풍기 파워로 자동 설정합니다. 이를 통해 비효율적인 사용을 방지하고 에너지를 절약할 수 있도록 설계했습니다. - 2) 초음파 센서를 통한 자동 선풍기 끄기
기존 선풍기는 사람이 없는데도 계속 작동해 에너지를 낭비할 수 있었습니다. 하지만 에너지 절약 선풍기는 전방에 부착된 초음파 센서를 통해 주변에 사람이 있는지 확인하고, 사람이 없을 경우 자동으로 꺼지도록 설계했습니다. 이를 통해 불필요한 에너지 낭비를 줄일 수 있을 것으로 기대합니다.
4. 에너지 절약 선풍기의 기능 및 동작
5. 에너지 절약 선풍기의 컨트롤 버튼 및 스위치
6. 에너지 절약 선풍기의 모듈 구현
- 본인이 맡은 역활은 다음과 같습니다.
▶ 일반 모드에서 버튼을 통한 선풍기 파워 조절
▶ 일반 모드에서 버튼을 통한 타이머 세팅 및 타이머를 통한 선풍기 파워 컨트롤 - 에너지 절약 선풍기의 모듈에 대한 설명은 소스 코드와 함께 설명하도록 하겠습니다.
< Source, Top Modue의 Input / Output 변수 선언 >
// Top module of Electric Fan
module top_module_of_electric_fan (
input clk, reset_p,
input [3:0] btn,
input sw_direction_cntr,
input echo,
inout dht11,
output trig,
output [8:0] led_debug,
output led, pwm,
output [3:0] com,
output [7:0] seg_7,
output rotation);
''' ( Body of Top module ) '''
endmodule
- 각각의 Input / Output 변수들은 다음과 같은 역활을 수행한다.
- btn [0] : 선풍기 파워 조절 (Power OFF, 1단, 2단, 3단)
- btn [1] : 타이머 모드 조절 (Timer OFF, 5초, 10초, 15초)
- btn [2] : LED 밝기 조절 (LED OFF, 1단, 2단, 3단)
- btn [3] : Echo 모드 전환 ( 선풍기 파워만 조정 )
- sw_direction_cntr : 선풍기 헤드 전환 컨트롤 스위치
- pwm : 선풍기 DC 모터의 PWM
- rotation : 선풍기 헤드를 조절하는 서보 모터의 PWN
< Source, Button Chattering를 해결하기 위해 1ms Delay Time을 갖고 버튼 값을 읽기 >
// Button 0 (btn_power) : 선풍기 파워 조절 (off, 1단, 2단, 3단)
// Button 1 (btn_timer) : 타이머 모드 (off, 5초, 10초 15초)
// Button 2 (btn_led) : LED 밝기 조절 (off, 1단, 2단, 3단)
// Button 3 (btn_echo) : Echo 모드 ( 선풍기 파워만 조정 )
// Switch (sw_direction) : 방향 설정
// Get One Cycle Pulse of buttons.
wire btn_power_pedge, btn_timer_pedge, btn_led_pedge, btn_echo_pedge;
button_cntr btn_power_cntr (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_power_pedge));
button_cntr btn_timer_cntr (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_timer_pedge));
button_cntr btn_led_cntr (.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_led_pedge));
button_cntr btn_echo_cntr (.clk(clk), .reset_p(reset_p), .btn(btn[3]), .btn_pedge(btn_echo_pedge));
< Source, FSM 형식으로 설계하기 위해 State 선언 >
// Declare Parameter
parameter POWER_CONTROL = 4'b0001;
parameter TIMER_CONTROL = 4'b0010;
parameter LED_CONTROL = 4'b0100;
parameter ECHO_CONTROL = 4'b1000;
parameter ROTATION_CONTROL = 4'b0001;
// Declare current state
reg [4:0] current_state;
< Source, 각각의 버튼에 의해 현재 모드 선택 및 전환 >
// 눌러진 버튼에 따른 선풍기 변화
always @(posedge clk or posedge reset_p) begin
if(reset_p) current_state = POWER_CONTROL;
else if(btn_power_pedge) current_state = POWER_CONTROL;
else if(btn_echo_pedge) begin
if(current_state == ECHO_CONTROL) current_state = POWER_CONTROL;
else current_state = ECHO_CONTROL;
end
end
- btn_power_pedge 버튼을 통해 현재 상태 모드를 일반 모드, POWER_CONTROL 상태로 전환 가능합니다.
- btn_echo_pedge 버튼을 통해 일반 모드와 에코 모드를 손쉽게 전환할 수 있습니다.
< Source, 각각 모듈과 현재 상태 모드를 통해 선풍기 파워, 서보 모터, 타이머의 동작 여부를 결정 >
// Declare Instance of module
wire [1:0] power_duty, echo_duty; // duty ratio of motor
wire [3:0] left_time; // if current state is timer mode, this variable has data of lefted time.
wire rotation_enable;
power_cntr power_cntr_0 (.clk(clk), .reset_p(reset_p), .btn_power_enable(btn_power_pedge), .btn_timer_enable(btn_timer_pedge), .duty(power_duty), .left_time(left_time), .rotation_enable(rotation_enable));
// current state 값에 따라 모터에 적용한 duty 값을 선택
wire [1:0] duty;
wire [15:0]temp_data;
wire led_debug_echo;
wire [11:0] distance, temperature;
assign duty = (current_state == ECHO_CONTROL) ? echo_duty : power_duty;
dht11_usonic_duty (.clk(clk),.reset_p(reset_p), .dht11_data(dht11), .echo(echo), .trig(trig), .echo_btn_enable(btn_echo_pedge),.duty(echo_duty),.t_data_out(temp_data),
.led_debug(led_debug_echo), .echo_buffer_out(distance), .temperature_bcd_out(temperature));
// 변화된 모터의 duty값을 모터에 적용
pwm_cntr #(.pwm_freq(100), .duty_step(4)) control_pwm (.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
- Echo 모드의 Motor duty 값(echo_duty) 과 Normal 모드의 Motor duty 값(power_duty)를 모두 구한 뒤, 삼항 조건 연산자를 통해 현재 모드 상태의 duty 값으로 선택하게 됩니다,
< Source, fan_led 모듈을 통해 선풍기 LED 제어 >
//led_mode
wire led_blue;
wire [7:0]led_led_debug;
fan_led blue_led(.clk(clk), .reset_p(reset_p), .btn_led(btn_led_pedge),
.led_blue(led_blue), .led_debug(led_led_debug[7:4]));
< Source, 타이머, 온도, 습도 데이터들을 Basys3의 FND로 출력 >
// FND로 현재의 모터 파워 출력
wire [15:0] bcd_left_time;
bin_to_dec convert_bin_to_dec_for_left_time (.bin(left_time), .bcd(bcd_left_time));
wire [15:0] fnd_led;
assign fnd_led = (current_state == ECHO_CONTROL) ? {temperature[7:0], 6'b0, duty} : {bcd_left_time[7:0], 6'b0, duty};
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(fnd_led), .com(com), .seg_7(seg_7));
- bin_to_dec 모듈을 통해 각각의 데이터들을 BCD Code로 변환해줍니다.
- 타이머, 온도, 습도 데이터를 현재 상태 모드에 따라 출력할 데이터를 선택하여 결합 연산자를 통해 결합하여 fnd_led 변수에 저장합니다,
- fnd_cntr 모듈을 통해 Basys3의 FND에 해당 데이터 값들을 순차적으로 출력합니다.
< Source, 현재 어떤 기능이 활성화 상태인지를 확인하기 위해 Basys3 LED로 해당 정보를 출력 >
// LED로 표시
assign led_debug[3:0] = (duty == 2'd0)? 4'b0001 : (duty == 2'd1)? 4'b0010 :
(duty == 2'd2)? 4'b0100 : 4'b1000;
assign led = led_blue;
assign led_debug[7:4] = led_led_debug[7:4];
assign led_debug[8] = led_debug_echo;
< Source, 선풍기 헤드를 제어하는 서보 모터의 모듈 선언 및 사용 >
// rotation instance
rotation_cntr( .clk(clk), .reset_p(reset_p), .sw_direction(sw_direction_cntr),.rotation(rotation), .rotation_enable(rotation_enable));
< Source, 선풍기의 파워, 타이머, 서보 모터의 동작 여부를 결정하는 모듈 >
// Power Control Module
module power_cntr (
input clk, reset_p,
input btn_power_enable,
input btn_timer_enable,
output [3:0] left_time,
output reg [1:0] duty,
output reg rotation_enable );
// Declare Parameter
parameter TURN_OFF = 4'b0001;
parameter FIRST_SPEED = 4'b0010;
parameter SECOND_SPEED = 4'b0100;
parameter THIRD_SPEED = 4'b1000;
// Declare state, next_state value
reg duty_enable;
wire reset_time_out;
reg [3:0] speed_state, speed_next_state;
// 언제 다음 state로 넘어가는가?
always @(posedge clk or posedge reset_p) begin
if(reset_p | reset_time_out) speed_state = TURN_OFF;
else if(btn_power_enable) speed_state = speed_next_state;
end
// 각 state의 동작 및 다음 state로 동작하도록 설계
always @(negedge clk or posedge reset_p) begin
if(reset_p | reset_time_out) begin
speed_next_state = FIRST_SPEED;
duty = 2'd0;
rotation_enable =0;
end
else begin
case (speed_state)
// 0단계 : 선풍기 끄기
TURN_OFF : begin
duty = 2'd0;
speed_next_state = FIRST_SPEED;
rotation_enable =0;
end
// 1단계 : 선풍기 약풍
FIRST_SPEED : begin
duty = 2'd1;
speed_next_state = SECOND_SPEED;
rotation_enable =1;
end
// 2단계 : 선풍기 중풍
SECOND_SPEED : begin
duty = 2'd2;
speed_next_state = THIRD_SPEED;
rotation_enable =1;
end
// 3단계 : 선풍기 강풍
THIRD_SPEED : begin
duty = 2'd3;
speed_next_state = TURN_OFF;
rotation_enable =1;
end
// Default case
default : begin
duty = duty;
speed_next_state = speed_next_state;
rotation_enable =0;
end
endcase
end
end
//// Timer Setting
// Prescaling operation to create a counter in seconds
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(clk_1msec));
clock_div_1000 sec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_1msec), .clk_div_1000_nedge(clk_1sec));
// Motor Run Enable variable Setting
reg timer_enable;
// Declare Parameter
parameter SETTING_0SEC = 4'd0;
parameter SETTING_5SEC = 4'd5;
parameter SETTING_10SEC = 4'd10;
parameter SETTING_15SEC = 4'd15;
// Declare state, next_state value
reg [3:0] timer_state, timer_next_state;
// 언제 다음 state로 넘어가는가?
always @(posedge clk or posedge reset_p) begin
if(reset_p | reset_time_out) timer_state = SETTING_0SEC;
else if(btn_timer_enable) timer_state = timer_next_state;
end
// 각 state의 동작 및 다음 state로 동작하도록 설계
always @(negedge clk or posedge reset_p) begin
if(reset_p | reset_time_out) begin
timer_next_state = SETTING_5SEC;
timer_enable = 0;
end
else begin
case (timer_state)
// 0단계 : Turn off electric fan
SETTING_0SEC : begin
timer_next_state = SETTING_5SEC;
timer_enable = 0;
end
// 1단계 : Setting Timer 5sec
SETTING_5SEC : begin
timer_next_state = SETTING_10SEC;
timer_enable = 1;
end
// 2단계 : Setting Timer 10sec
SETTING_10SEC : begin
timer_next_state = SETTING_15SEC;
timer_enable = 1;
end
// 3단계 : Setting Timer 15sec
SETTING_15SEC : begin
timer_next_state = SETTING_0SEC;
timer_enable = 1;
end
// Default case
default : begin
timer_next_state = timer_next_state;
timer_enable = timer_enable;
end
endcase
end
end
// Down Counting of Timer
reg [3:0] timer;
always @(posedge clk or posedge reset_p) begin
if(reset_p | reset_time_out) begin timer = 0; duty_enable = 1; end
else if(btn_timer_enable) timer = timer_next_state;
else if(clk_1sec && timer_enable && timer >= 0) begin
if(timer <= 0 && timer_enable) duty_enable = 0;
else if(duty_enable) timer = timer - 1;
end
end
// Print lefted time to FND.
assign left_time = timer;
// When the duty_enable variable is 0 and the btn_power_enable variable is activated, the reset for Time out is enabled.
assign reset_time_out = timer_enable && (timer == 0) && duty_enable;
endmodule
< Source, 전달 받은 Duty step 값을 활용하여 선풍기 파워를 제어하는 100Hz PWM 생성 >
// PWM Control Module
module pwm_cntr #(
parameter sys_clk = 100_000_000,
parameter pwm_freq = 100,
parameter duty_step = 4,
parameter temp = sys_clk / pwm_freq / duty_step,
parameter temp_half = temp / 2 )
(
input clk, reset_p,
input [31:0] duty,
output pwm );
// pwm_freq 주파수를 갖는 PWM을 생성하기 위한 temp 분주화 작업
integer cnt_temp;
always @(posedge clk or posedge reset_p) begin
if(reset_p) cnt_temp = 0;
else begin
if(cnt_temp >= temp - 1) cnt_temp = 0;
else cnt_temp = cnt_temp + 1;
end
end
// sys_clk / temp 주파수를 갖는 PWM를 생성
wire temp_pwm;
assign temp_pwm = (cnt_temp < temp_half) ? 0 : 1;
// Get One Cycle Pulse of negative edge of temp_pwm.
wire temp_pwm_nedge;
edge_detector_p ed(.clk(clk), .reset_p(reset_p), .cp(temp_pwm), .n_edge(temp_pwm_nedge));
// PWM의 Duty ratio를 duty_step 단계로 구분하여 컨트롤 하기 위해
// temp_pwm을 duty_step 분주화 작업
integer cnt_duty;
always @(posedge clk or posedge reset_p) begin
if(reset_p) cnt_duty = 0;
else if(temp_pwm_nedge) begin
if(cnt_duty >= duty_step-1) cnt_duty = 0;
else cnt_duty = cnt_duty + 1;
end
end
// Get sys_clk / temp / duty_step = pwm_freq(Hz) 주파수를 갖는 PWM 생성
assign pwm = (cnt_duty < duty) ? 1 : 0;
endmodule
< Source, 선풍기 헤드를 제어하는 서보 모터를 컨트롤하는 모듈 >
module rotation_cntr(
input clk, reset_p,
input sw_direction, // rotation switch
input rotation_enable,
output rotation // surbo moter output
);
integer clk_div;
always @(posedge clk) clk_div =clk_div +1; //clock divider
wire clk_div_24_nedge;
edge_detector_n ed( .clk(clk), .reset_p(reset_p), . cp(clk_div[24]), .n_edge(clk_div_24_nedge));
reg [6:0] duty, duty_max, duty_min;
reg down_up;
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
duty =45;
duty_max = 75; // 12.5% of pwm
duty_min = 15; // 2.5% of pwm
end
else if(sw_direction&&rotation_enable) begin // rotation switch 'on' -> start
if (clk_div_24_nedge) begin
if(down_up)begin // duty goes down until reach duty_min
if(duty>= duty_min) duty = duty-1;
else down_up =0;
end
else if(!down_up) begin // duty goes up until reach duty_max
if(duty<=duty_max)duty = duty+1;
else down_up =1;
end
end
end
end
pwm_Nstep_freq #(.duty_step(600), .pwm_freq(50))
pwm_rotation (.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(rotation)); //pwm pulse = rotation
endmodule
< Source, 선풍기의 LED를 4단계로 나누어 컨트롤 하는 모듈 >
module fan_led(
input clk,reset_p,
input btn_led,
output led_blue,
output [7:4] led_debug,
output [3:0] com,
output [7:0] seg_7);
//duty_code
reg [6:0] duty;
always @(posedge clk or posedge reset_p) begin
if(reset_p) duty = 0;
else if(btn_led) begin
if(duty>=99) duty =0;
else duty = duty + 33;
end
end
//PWM_inst
pwm_100step led_b(.clk(clk), .reset_p(reset_p) , .duty(duty), .pwm(led_blue));
//led
assign led_debug[4] = (duty == 0);
assign led_debug[5] = (duty == 33);
assign led_debug[6] = (duty == 66);
assign led_debug[7] = (duty == 99);
//convert_bcd
wire [6:0] duty_bcd;
bin_to_dec duty_bcds(.bin(duty), .bcd(duty_bcd));
//FND
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(duty_bcd),
.com(com), .seg_7(seg_7));
endmodule
< Source, DHT11, 초음파 센서를 통해 에코 모드에서의 선풍기 동작을 결정하는 모듈 >
//dht11 module
module dht11_usonic_duty(
input clk, reset_p,
input echo,
inout dht11_data,
input echo_btn_enable,
output trig,
output [8:11]led_debug,
output t_data_out,
output [1:0]duty,
output [11:0] temperature_bcd_out,
output [11:0] echo_buffer_out);
parameter ECHO_ON = 2'b01;
parameter ECHO_OFF = 2'b10;
wire [7:0] humidity, temperature;
dht11_cntrl dth11(.clk(clk), .reset_p(reset_p), .dht11_data(dht11_data), .humidity(humidity), .temperature(temperature));
reg[2:0] ehco_state, ehco_next_state;
wire [21:0] distance_cm;
HC_SR04_cntr HC_SR04_cntr_0(.clk(clk), .reset_p(reset_p), .hc_sr04_echo(echo), .hc_sr04_trig(trig), .distance(distance_cm));
always@(posedge clk or posedge reset_p)begin
if(reset_p)ehco_state = ECHO_OFF;
else if(echo_btn_enable) ehco_state = ehco_next_state;
end
assign usonic_enable = (distance_cm >= 22'd10) ? 0 : 1;
reg [1:0] temp_duty;
always@(negedge clk or posedge reset_p)begin
if(reset_p)begin
ehco_next_state = ECHO_ON;
temp_duty = 2'd0;
end
else begin
case(ehco_state)
ECHO_ON: begin
if(8'd24 <= temperature && temperature <= 8'd26) temp_duty = 2'd1;
else if(8'd27 <= temperature && temperature <= 8'd29) temp_duty = 2'd2;
else if(temperature > 8'd29) begin temp_duty = 2'd3;
ehco_next_state = ECHO_OFF;
end
end
ECHO_OFF: begin
temp_duty = 2'd0;
ehco_next_state = ECHO_ON;
end
endcase
end
end
assign duty =( temp_duty && usonic_enable) ? temp_duty : 0;
wire [11:0] distance_cm_bcd;
bin_to_dec bcd_humi_distance(.bin(distance_cm[11:0]), .bcd(distance_cm_bcd));
bin_to_dec bcd_humi_temperature(.bin(temperature), .bcd(temperature_bcd_out));
assign echo_buffer_out = distance_cm_bcd;
assign led_debug[8] = ehco_state;
endmodule
7. 시연 영상