

보드에서 임피던스 상태로 있는것은 센서가 Vcc를 읽는것, 보드에서 출력0 상태로 있는것은 접지와 연결한것이고 센서가 데이터선의 전위0을 읽는 것
module dht11_cntr(
input clk, reset_p,
inout dht11_data, //단일 데이터선이라 inout으로 선언
output reg [7:0] humidity, temperature);
parameter S_IDLE = 6'b00_0001; //상태천이도에있는 6개의 상태 설정
parameter S_LOW_18MS = 6'b00_0010;
parameter S_HIGH_20US = 6'b00_0100;
parameter S_LOW_80US = 6'b00_1000;
parameter S_HIGH_80US = 6'b01_0000;
parameter S_READ_DATA = 6'b10_0000;
parameter S_WAIT_PEDGE = 2'b01; //read_data에서의 40번의 데이터 교환을위한 상태로써 따로 설정
parameter S_WAIT_NEDGE = 2'b10;
reg [5:0] state, next_state;
reg [1:0] read_state;
wire clk_usec; //정확한 클락을 위해 clk_div가 아니라 분주 사용
clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec)); //클락이 아니라 엣지 사용
reg [21:0] count_usec; //3초 대기를 위함
reg count_usec_e;
always @(negedge clk or posedge reset_p)begin
if(reset_p)count_usec = 0;
else if(clk_usec && count_usec_e)count_usec = count_usec + 1; //S_IDLE에서 count_usec_e를 1주면 카운트 시작
else if(!count_usec_e)count_usec = 0; //count_usec_e가 0이되면 리셋
end
always @(negedge clk or posedge reset_p)begin //next_state와 state를 겹쳐서 동시에 바뀌면 한클락 뒤에 읽히게되서
if(reset_p)state = S_IDLE; //하나는 상승엣지 다른하나는 하강엣지
else state = next_state;
end
reg dht11_buffer; //input, inout에는 reg선언 안됨, 새로운 변수 만들어서 대입
assign dht11_data = dht11_buffer;
wire dht_pedge, dht_nedge;
edge_detector_p ed(
.clk(clk), .reset_p(reset_p), .cp(dht11_data),
.p_edge(dht_pedge), .n_edge(dht_nedge));
reg [39:0] temp_data; //데이터시트 5번에 최상위비트부터 보낸다고 되어있음, 쉬프트레지스터 사용
reg [5:0] data_count;
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
next_state = S_IDLE;
read_state = S_WAIT_PEDGE;
temp_data = 0;
data_count = 0;
end
else begin
case(state)
S_IDLE:begin //최초 풀업에의해 high신호
if(count_usec < 22'd10_000)begin //22'd3_000_000
count_usec_e = 1;
dht11_buffer = 'bz; //풀업저항 회로에의해 임피던스가 있으면 Vcc
end
else begin
count_usec_e = 0;
next_state = S_LOW_18MS;
end
end
S_LOW_18MS:begin //low신호를 주고난후에 임피던스로 신호를 끊음 그러면 풀업에의해 다시 high
if(count_usec <22'd20_000)begin //18ms에서 여유시간 추가
dht11_buffer = 0;
count_usec_e = 1;
end
else begin
count_usec_e = 0;
next_state = S_HIGH_20US;
end
end
S_HIGH_20US:begin
if(count_usec < 22'd20)begin
count_usec_e = 1;
dht11_buffer = 'bz;
end
else if(dht_nedge)begin //센서가 응답할때 1->0으로 신호가 변하면서 하강엣지 발생
count_usec_e = 0;
next_state = S_LOW_80US;
end
end
S_LOW_80US:begin
if(dht_pedge)begin
next_state = S_HIGH_80US;
end
end
S_HIGH_80US:begin //for80us는 대략 80us 정확하진 않지만 엣지는 들어온다
if(dht_nedge)begin
next_state = S_READ_DATA;
end
end
S_READ_DATA:begin
case(read_state)
S_WAIT_PEDGE:begin
if(dht_pedge)read_state = S_WAIT_NEDGE;
count_usec_e = 0;
end
S_WAIT_NEDGE:begin
if(dht_nedge)begin //count_usec은 us단위 카운트
if(count_usec < 45)begin //45보다 짧으면 신호0 크면1//데이터시트에 high신호 26~28us이면0, 70us이면1
temp_data = {temp_data[38:0], 1'b0};
end
else begin
temp_data = {temp_data[38:0], 1'b1};
end
data_count = data_count + 1;
read_state = S_WAIT_PEDGE;
end
else begin
count_usec_e = 1;
end
end
endcase
if(data_count >= 40)begin //10진수40 따로 설정하지않으면 기본 32비트, 저장공간은 6비트라 하위6비트까지만 저장
data_count = 0;
next_state = S_IDLE;
humidity = temp_data[39:32];
temperature = temp_data[23:16];
end
end
endcase
end
end
endmodule
BCD변환 모듈까지 들어가있고 BCD변환 모듈은 맨 아래에 있음
module dht11_cntr(
input clk, reset_p,
inout dht11_data, //단일 데이터선이라 inout으로 선언
output reg [7:0] humidity, temperature,
output [15:0] led_debug); //보드 동작시 디버깅용
parameter S_IDLE = 6'b00_0001; //상태천이도에있는 6개의 상태 설정
parameter S_LOW_18MS = 6'b00_0010;
parameter S_HIGH_20US = 6'b00_0100;
parameter S_LOW_80US = 6'b00_1000;
parameter S_HIGH_80US = 6'b01_0000;
parameter S_READ_DATA = 6'b10_0000;
parameter S_WAIT_PEDGE = 2'b01; //read_data에서의 40번의 데이터 교환 따로 설정
parameter S_WAIT_NEDGE = 2'b10;
reg [5:0] state, next_state;
reg [1:0] read_state;
assign led_debug[5:0] = state;
wire clk_usec; //정확한 클락을 위해 clk_div가 아니라 분주 사용
clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec)); //클락이 아니라 엣지 사용
reg [21:0] count_usec; //3초 대기를 위함
reg count_usec_e;
always @(negedge clk or posedge reset_p)begin
if(reset_p)count_usec = 0;
else if(clk_usec && count_usec_e)count_usec = count_usec + 1; //S_IDLE에서 count_usec_e를 1주면 카운트 시작
else if(!count_usec_e)count_usec = 0; //count_usec_e가 0이되면 리셋
end
always @(negedge clk or posedge reset_p)begin //next_state와 state를 겹쳐서 동시에 바뀌면 한클락 뒤에 읽히게되서
if(reset_p)state = S_IDLE; //하나는 상승엣지 다른하나는 하강엣지
else state = next_state;
end
reg dht11_buffer; //input, inout에는 reg선언 안됨, 새로운 변수 만들어서 대입
assign dht11_data = dht11_buffer;
wire dht_pedge, dht_nedge;
edge_detector_p ed(
.clk(clk), .reset_p(reset_p), .cp(dht11_data),
.p_edge(dht_pedge), .n_edge(dht_nedge));
reg [39:0] temp_data; //데이터시트 5번에 최상위비트부터 보낸다고 되어있음, 쉬프트레지스터 사용
reg [5:0] data_count;
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
next_state = S_IDLE;
read_state = S_WAIT_PEDGE;
temp_data = 0;
data_count = 0;
// count_usec_e = 0;
end
else begin
case(state)
S_IDLE:begin //최초 풀업에의해 high신호
if(count_usec < 22'd3_000_000)begin //
count_usec_e = 1;
dht11_buffer = 'bz; //풀업저항 회로에의해 임피던스가 있으면 Vcc
end
else begin
count_usec_e = 0;
next_state = S_LOW_18MS;
end
end
S_LOW_18MS:begin //low신호를 주고난후에 임피던스로 신호를 끊음 그러면 풀업에의해 다시 high
if(count_usec <22'd20_000)begin //18ms에서 여유시간 추가
dht11_buffer = 0;
count_usec_e = 1;
end
else begin
count_usec_e = 0;
next_state = S_HIGH_20US;
dht11_buffer = 'bz;
end
end
S_HIGH_20US:begin //어짜피 다음 하강엣지만 감지하면 되서 주석처리
// if(count_usec < 22'd3)begin
// count_usec_e = 1;
// end
// else
if(dht_nedge)begin
count_usec_e = 0;
next_state = S_LOW_80US;
end
end
S_LOW_80US:begin
if(dht_pedge)begin
next_state = S_HIGH_80US;
end
end
S_HIGH_80US:begin //for80us는 대략 80us 정확하진 않지만 엣지는 들어온다
if(dht_nedge)begin
next_state = S_READ_DATA;
end
end
S_READ_DATA:begin
case(read_state)
S_WAIT_PEDGE:begin
if(dht_pedge)read_state = S_WAIT_NEDGE;
count_usec_e = 0;
end
S_WAIT_NEDGE:begin
if(dht_nedge)begin //count_usec은 us단위 카운트
if(count_usec < 45)begin //45보다 짧으면 신호0 크면1//데이터시트에 high신호 26~28us이면0, 70us이면1
temp_data = {temp_data[38:0], 1'b0};
end
else begin
temp_data = {temp_data[38:0], 1'b1};
end
data_count = data_count + 1;
read_state = S_WAIT_PEDGE;
end
else begin
count_usec_e = 1;
end
end
endcase
if(data_count >= 40)begin //10진수40 따로 설정하지않으면 기본 32비트, 저장공간은 6비트라 하위6비트까지만 저장
data_count = 0;
next_state = S_IDLE;
humidity = temp_data[39:32];
temperature = temp_data[23:16];
end
end
endcase
end
end
endmodule
module dht11_test_top(
input clk, reset_p,
inout dht11_data,
output [3:0] com,
output [7:0] seg_7, led_debug);
wire [7:0] humidity, temperature;
dht11_cntr dht11(.clk(clk), .reset_p(reset_p),
.dht11_data(dht11_data),
.humidity(humidity), .temperature(temperature), .led_debug(led_debug));
wire [15:0] humidity_bcd, temperature_bcd;
bin_to_dec bcd_humi(.bin({4'b0, humidity}), .bcd(humidity_bcd));
bin_to_dec bcd_tmpr(.bin({4'b0, temperature}), .bcd(temperature_bcd));
wire [15:0] value;
assign value = {humidity_bcd[7:0], temperature_bcd[7:0]};
fnd_cntr fnd (.clk(clk), .reset_p(reset_p),
.value(value), .com(com), .seg_7(seg_7));
endmodule
//dht11의 관점
module tb_dht11_cntr();
//보드에 보내줄 온습도 데이터 임의로 생성
parameter humi_data = 8'h27;
parameter tmpr_data = 8'h23;
parameter check_sum = humi_data + tmpr_data; //앞의 8비트 4개 모두 더한 값, 보낸쪽과 받은쪽에서 모두 값이 같아야 온전한 값
parameter [39:0] data = {humi_data, 8'b0, tmpr_data, 8'b0, check_sum};
//입력이였던건 reg, 출력이였던건 wire 선언
reg clk, reset_p;
wire [7:0] humidity, temperature;
tri1 dht11_data; //wire타입이고 풀업이 달린 와이어, 시뮬레이션에서만 사용가능한 변수, 회로만들때는 못만듬//tri0은 풀다운
reg data_buffer, wr_en;
assign dht11_data = wr_en ? data_buffer : 'bz; //출력안할때는 임피던스, 할때는 출력선 사용
dht11_cntr DUT(clk, reset_p, dht11_data, humidity, temperature);
initial begin
clk = 0;
reset_p = 1;
wr_en = 0;
end
always #5 clk = ~clk;
integer i; //변수선언은 always문이나 initial문 밖에서
initial begin
#10; //잠깐 리셋
reset_p = 0; #10
wait(!dht11_data); //괄호안이 참이 될때까지 기다림, 시작신호 18ms동안의 0신호
wait(dht11_data);
#20_000;
data_buffer = 0; wr_en = 1; #80_000; //응답신호 보내는중
wr_en = 0; #80_000;
for(i=0; i<40; i=i+1)begin //언더플로우가 되기때문에 i=39부터 시작해서 줄어들면 안됨
wr_en = 1; data_buffer = 0; #50_000;
data_buffer = 1;
if(data[39-i]) #70_000; //신호1을 의미하는 신호 길이
else #29_000; //신호0을 의미하는 신호 길이
end
wr_en = 1; data_buffer = 0; #10;
wr_en = 0; #10_000;
$finish;
end
endmodule
//dht11의 관점
module tb_dht11_cntr();
//보드에 보내줄 온습도 데이터 임의로 생성
parameter humi_data = 8'h27;
parameter tmpr_data = 8'h23;
parameter check_sum = humi_data + tmpr_data; //앞의 8비트 4개 모두 더한 값, 보낸쪽과 받은쪽에서 모두 값이 같아야 온전한 값
parameter [39:0] data = {humi_data, 8'b0, tmpr_data, 8'b0, check_sum};
reg clk, reset_p;
wire [7:0] humidity, temperature;
tri1 dht11_data; //wire타입이고 풀업이 달린 와이어, 시뮬레이션에서만 사용가능한 변수, 회로만들때는 못만듬//tri0은 풀다운
reg data_buffer, wr_en;
assign dht11_data = wr_en ? data_buffer : 'bz; //출력안할때는 임피던스, 할때는 출력선 사용
dht11_cntr DUT(clk, reset_p, dht11_data, humidity, temperature);
initial begin
clk = 0;
reset_p = 1;
wr_en = 0;
end
always #5 clk = ~clk;
integer i; //변수선언은 always문이나 initial문 밖에서
initial begin
#10; //잠깐 리셋
reset_p = 0; #10
wait(!dht11_data); //괄호안이 참이 될때까지 기다림, 시작신호 18ms동안의 0신호
wait(dht11_data);
#20_000;
data_buffer = 0; wr_en = 1; #80_000; //응답신호 보내는중
wr_en = 0; #80_000;
for(i=0; i<40; i=i+1)begin //언더플로우가 되기때문에 i=39부터 시작해서 줄어들면 안됨
wr_en = 1; data_buffer = 0; #50_000;
data_buffer = 1;
if(data[39-i]) #70_000;
else #29_000;
end
wr_en = 1; data_buffer = 0; #10;
wr_en = 0; #10_000;
$finish;
end
endmodule

온습도 결과가 처음 임의로 정해준 값으로 나옴
실제 보드에서의 동작확인과 디버깅을 위한 LED
# LEDs
set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports {led_debug[0]}]
set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports {led_debug[1]}]
set_property -dict { PACKAGE_PIN U19 IOSTANDARD LVCMOS33 } [get_ports {led_debug[2]}]
set_property -dict { PACKAGE_PIN V19 IOSTANDARD LVCMOS33 } [get_ports {led_debug[3]}]
set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports {led_debug[4]}]
set_property -dict { PACKAGE_PIN U15 IOSTANDARD LVCMOS33 } [get_ports {led_debug[5]}]
set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports {led_debug[6]}]
set_property -dict { PACKAGE_PIN V14 IOSTANDARD LVCMOS33 } [get_ports {led_debug[7]}]
set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports {led_debug[8]}]
set_property -dict { PACKAGE_PIN V3 IOSTANDARD LVCMOS33 } [get_ports {led_debug[9]}]
set_property -dict { PACKAGE_PIN W3 IOSTANDARD LVCMOS33 } [get_ports {led_debug[10]}]
set_property -dict { PACKAGE_PIN U3 IOSTANDARD LVCMOS33 } [get_ports {led_debug[11]}]
set_property -dict { PACKAGE_PIN P3 IOSTANDARD LVCMOS33 } [get_ports {led_debug[12]}]
set_property -dict { PACKAGE_PIN N3 IOSTANDARD LVCMOS33 } [get_ports {led_debug[13]}]
set_property -dict { PACKAGE_PIN P1 IOSTANDARD LVCMOS33 } [get_ports {led_debug[14]}]
set_property -dict { PACKAGE_PIN L1 IOSTANDARD LVCMOS33 } [get_ports {led_debug[15]}]
연결 포트
##Pmod Header JA
set_property -dict { PACKAGE_PIN J1 IOSTANDARD LVCMOS33 } [get_ports {dht11_data}];#Sch name = JA1

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
1의 자리에서 A에서 부터 변환을 해줘야 하는데 이때 6(0110)을 더해주면 된다, 이때 1의자리 상위 3비트만을 보고 3(011)을 더해주고 좌쉬프트 한번하고 하위 첫번째 비트를 그대로 내려준다. 이렇게 하면 보다 적은 비트수를 계산하게 된다.