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
count_usec_e = 1;
if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감
next_state = S_IDLE;
count_usec_e = 0;
end
// if(count_usec < 22'd3)begin //어짜피 다음 하강엣지만 감지하면 되서 주석처리
// count_usec_e = 1;
// end
// else
else if(dht_nedge)begin
count_usec_e = 0;
next_state = S_LOW_80US;
end
end
S_LOW_80US:begin
count_usec_e = 1;
if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감
next_state = S_IDLE;
count_usec_e = 0;
end
else if(dht_pedge)begin
next_state = S_HIGH_80US;
count_usec_e = 0;
end
end
S_HIGH_80US:begin //for80us는 대략 80us 정확하진 않지만 엣지는 들어온다
count_usec_e = 1;
if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감
next_state = S_IDLE;
count_usec_e = 0;
end
else if(dht_nedge)begin
next_state = S_READ_DATA;
count_usec_e = 0;
end
end
S_READ_DATA:begin
count_usec_e = 1;
if(count_usec > 22'd100_000)begin //다음 else if의 하강엣지를 기다리는데 100_000us를 넘어가면 다시 S_IDLE로 돌아감
next_state = S_IDLE;
count_usec_e = 0;
data_count = 0; //S_IDLE로 돌아갈때는 오류난거기 때문에 data_count=0
read_state = S_WAIT_PEDGE;
end
else begin
case(read_state)
S_WAIT_PEDGE:begin
if(dht_pedge)read_state = S_WAIT_NEDGE;
end
S_WAIT_NEDGE:begin
if(dht_nedge)begin //count_usec은 us단위 카운트
if(count_usec < 95)begin //데이터를 받기 시작하는 순간부터 카운트를 시작하기때문에, 신호가 0일때는 50+(26~28)us, 신호가 1일떄는 50+70us이기 때문에 95us정도로 설정해서 신호0과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;
count_usec_e = 0;
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;
read_state = S_WAIT_PEDGE;
count_usec_e = 0;
if(temp_data[39:32] + temp_data[31:24] + temp_data[23:16] + temp_data[15:8] == temp_data[7:0])begin
humidity = temp_data[39:32]; //통신에러 확인, 8비트4개의 합과 check sum비트와 비교하여 if문 동작
temperature = temp_data[23:16];
end
end
end
end
endcase
end
end
endmodule


거리에따라 에코펄스의 길이가 달라지는데 멀 수록 펄스폭이 길어진다.
보드에서 10us길이의 트리거 펄스가 보내지면 초음파 센서의 Sonic Burst가 초음파를 발생시키고 다시 받아 거리에따른 시간을 측정한다, 측정값을 에코펄스로 보낸다. 최대 펄스 길이는 18ms이고 앞에 장애물이 없으면 36ms 펄스 생성, 에코 펄스의 하강엣지가 발생하고 다음 트리거까지는 10ms의 시간 필요
음속은 343m/s -> 343m/(us x 1,000,000) -> (343 x 100)cm/(us x 1,000,000) ->(초음파가 갔다가 온거라서 측정거리를 2로 나눔) 1/58
module hc_sr04_cntr(
input clk, reset_p,
input echo,
output reg [8:0] dist,
output reg trig,
output [15:0] led_debug);
parameter S_IDLE = 3'b001;
parameter S_TRIG = 3'b010;
parameter S_ECHO = 3'b100;
parameter S_ECHO_PEDGE = 2'b01;
parameter S_ECHO_NEDGE = 2'b10;
reg [2:0] state, next_state;
reg [1:0] read_state;
assign led_debug[5:0] = state;
reg [21:0] count_usec; //3초대기를 위한 레지스터 크기(펄스 크기 모두 처리 가능), 데이터시트의 최대 펄스길이는 36ms(장애물이없을시 에코펄스길이)
reg count_usec_e;
wire clk_usec;
clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec));
wire pecho, necho;
edge_detector_n ed_echo(.clk(clk), .reset_p(reset_p), .cp(echo), .p_edge(pecho), .n_edge(necho));
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;
else if(!count_usec_e) count_usec = 0;
end
always @(negedge clk or posedge reset_p)begin
if(reset_p) state = S_IDLE;
else state = next_state;
end
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
next_state = S_IDLE;
read_state = S_ECHO_PEDGE;
count_usec_e = 0;
end
else begin
case(state)
S_IDLE:begin
if(count_usec < 22'd3_000_000)begin
count_usec_e = 1;
end
else begin
count_usec_e = 0;
next_state = S_TRIG;
end
end
S_TRIG:begin
if(count_usec < 22'd10)begin
count_usec_e = 1;
trig = 1;
end
else begin
count_usec_e = 0;
trig = 0;
next_state = S_ECHO;
end
end
S_ECHO:begin
// if(pecho)begin
// if(count_usec < 22'd1_000_000)begin
// count_usec_e = 1;
// end
// end
// else if(necho)begin
// dist = count_usec / 58;
// next_state = S_IDLE;
// count_usec_e = 0;
// end
case(read_state)
S_ECHO_PEDGE:begin
if(pecho)begin
count_usec_e = 1;
read_state = S_ECHO_NEDGE;
end
end
S_ECHO_NEDGE:begin
if(necho)begin
dist = count_usec/58;
read_state = S_ECHO_PEDGE;
next_state = S_IDLE;
count_usec_e = 0;
end
end
endcase
end
endcase
end
end
endmodule
FSM형식의 코드를 작성
클럭생성시 주의, one cycle pulse를 사용하여야 한다, 그렇지 않으면 카운트가 의도하지 않은대로 동작한다.
module hc_sr04_test_top(
input clk, reset_p,
input echo,
output trig,
output [3:0] com,
output [7:0] seg_7, led_debug);
wire [8:0] dist;
wire [15:0] value;
assign value = {8'b0, dist};
hc_sr04_cntr(.clk(clk), .reset_p(reset_p), .echo(echo), .dist(dist), .trig(trig), .led_debug(led_debug));
fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));
endmodule

(basys3의 출력 3.3v, avr의 출력 5v, 센서 정격 5v필요, basys3에서 신호처리를 하는데 필요한 정격 전압을 위해 avr의 출력 사용)(양쪽 모두에 전원을 넣어줘야 하는것을 잊지말자...)