일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- Recursion
- i2c 통신
- uart 통신
- D Flip Flop
- gpio
- LED
- structural modeling
- FND
- stop watch
- test bench
- atmega 128a
- Algorithm
- dataflow modeling
- soc 설계
- Edge Detector
- hc-sr04
- prescaling
- KEYPAD
- half adder
- Linked List
- ATMEGA128A
- behavioral modeling
- vivado
- java
- verilog
- DHT11
- pwm
- ring counter
- Pspice
- BASYS3
- Today
- Total
거북이처럼 천천히
개인 프로젝트 - 스마트 선풍기 만들기 (1) 본문
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 moduled
- 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
- DHT11 Control Module에 대한 자세한 설명은 아래 링크를 통해 참조하길 바란다.
Verilog RTL 설계(7월 23일 - 4, DHT11 구현 (3) ) (tistory.com)
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을 통해 얻은 온도 값에 따른 풍속 조절 >
'RTL Design > Verilog 프로젝트' 카테고리의 다른 글
Verilog 팀 프로젝트 - 에너지 절약 선풍기 (1) | 2024.10.05 |
---|---|
Verilog 팀 프로젝트 - 스마트 수경재배기 (0) | 2024.09.10 |
통합 시계 (0) | 2024.07.30 |