2024.7.3 수업날
오늘은 어제 습도센서를 Basys3 보드와 연결했을 때, 결과가 왜 나오지 않는지를 확인하고 이를 방지하는 방법에 대해 알아본다.
Basys3 보드에 결과가 출력되지 않으면, 두가지 문제점을 고려해야한다.
하나는 코드의 문제이고, 다른 하나는 보드의 문제가 있다는 것이다.
먼저 코드에 문제가 있는지 테스트벤치(testbench)를 추가로 작성해보자.
테스트벤치란 Verilog 코드의 동작을 검증하는 시뮬레이션을 위해 필요한 파일을 만드는 것이다.
즉, 작성한 코드가 의도하는 대로 동작하는지 보는 과정이다.
테스트벤치 파일을 만드는 방법은 아래와 같다.
먼저 Add Sources에서 simulation source를 추가한다.
다음으로 Create File를 클릭하여 아래와 같이 파일 이름을 지정해준다.
그러면 아래와 같이 Simulation Sources 밑에 파일이 생성되는 것을 확인할 수 있다.
이제 테스트벤치 소스코드를 작성해보자.
테스트벤치를 작성하는 순서는 아래와 같다.
1. 테스트벤치는 input, output을 선언하지 않는다. 대신에 input을 reg형으로, output은 wire형으로 선언한다. inout은 tri1,tri0으로 선언한다.
2. DUT(디자인 언더 테스트)를 만든다.
3. dout, wr를 선언한다. dout는 dht11 센서가 보낼 데이터 비트를 시뮬레이션하기 위해 사용된다. wr는 dht11_data 신호가 데이터 송신 모드인지 수신 모드인지를 결정한다.
4. parameter를 선언한다.
5. initial을 작성한다. 그리고 clk, reet_p와 같은 레지스터 변수들을 초기화한다.
6. always로 clk를 생성해준다.
7. 다시 initial을 작성하여 원하는 시간 간격으로 입력값들을 넣어준다. 마지막에는 시스템 명령어인 $stop를 적는다.
8. endmodule를 쓴다.
tb_dht11_cntr 소스코드
/////////////////////////////////////////2024.7.3
//////////// 습도센서 테스트벤치 만들기
module tb_dht11_cntr(); // 테스트벤치는 입출력이 없음
reg clk, reset_p;
tri1 dht11_data; //tri1: 풀업달려있는 와이어, tri0: 풀다운 달려있는 와이어--> 회로설계할때는 사용하지 않음, 테스트벤치때만 사용
wire [7:0] humidity, temperature;
dht11_cntr DUT( //디자인 언더 테스트(DUT)
clk, reset_p,
dht11_data,
humidity, temperature);
reg dout, wr;
assign dht11_data = wr ? dout : 1'bz;
parameter [7:0] humi_value = 8'd80; // 센서 대신에 시뮬레이션 하기 위해 데이터를 dht11_data에 보내기 위해서 상수 선언
parameter [7:0] tmpr_value = 8'd25;
parameter [7:0] check_sum = humi_value + tmpr_value;
parameter [39:0] data = {humi_value, 8'd0, tmpr_value, 8'd0, check_sum};
initial begin //이것도 테스트벤치에만 있는 , 초기화 선언
clk = 0;
reset_p = 1;
wr = 0;
end
// 10ns짜리 한 주기 클록 만들기
always #5 clk = ~clk; // #은 딜레이를 의미, 5는 단위가 ns, 코딩할때의 시간 단위는 ns, 시뮬레이션의 실행 시간 단위는 ps
integer i;
initial begin //시뮬레이션에서 입력값을 주듯이 코드로 입력값을 주는 것임
#10;
reset_p = 0;
wait(!dht11_data); //dht11_data가 0이 될때까지 대기
wait(dht11_data);
#20000;
dout = 0; wr = 1; #80000;
wr = 0; #80000; // 임피던스 선언 후 80ns 딜레이
wr = 1;
for(i=0; i<40; i=i+1)begin // 40번 반복
dout = 0; #50000; // 0주고 50ns
dout = 1;
if(data[39-i]) #70000;
else #27000;
end
dout = 0; wr = 1; #10; // 0으로 떨어뜨려야 하강에지를 잡아서 길이를 계산할 수 있다.
wr = 0; #10000;
$stop; // 여기에서 시뮬레이션 멈춤 선언, 시스템 명령어($)
end
endmodule
시뮬레이션을 실행하기 전에 dht11_cntr에서 22'd30이라고 수정해야한다.
원래는 3_000_000이였는데, 이는 3초를 의미한다. 하지만 시뮬레이션 결과를 확인하기까지 시간이 오래 걸리기 때문에 시뮬레이션을 확인할때만 22'd30으로 쓰고 테스트벤치가 끝나면 다시 22'd3_000_000 으로 수정한다.
dht11 테스트벤치 시뮬레이션 결과는 아래와 같다.
여태 시뮬레이션을 실행할때는 재생버튼 옆에 (T)가 붙은 기호를 클릭했다면,
이번에는 바로 왼쪽에있는 아무것도 붙어있지 않은 재생버튼 기호를 클릭하면 시뮬레이션이 테스트벤치 소스코드에서 적은대로 stop할때까지 실행한다.
시뮬레이션에서 humidity[7:0], temperature[7:0]의 마지막에 숫자가 나타나는 것을 확인할 수 있다.
테스트벤치 소스코드에는 8'd80, 8'd25라고 적었는데 시뮬레이션에는 16진수로 표현되었다.
소스코드에서 적은대로 10진수 80, 25가 나오게 하려면 humidity[7:0], temperature[7:0]에서 오른쪽 클릭을 하여 signed Decimal를 선택해야한다.
그러면 코드에 설정했던대로
parameter [7:0] humi_value = 8'd80;
parameter [7:0] tmpr_value = 8'd25;
즉, 10진수 80, 25로 나오는 것을 확인할 수 있다.
시뮬레이션에서 임의로 지정한 입력값이 잘 출력되므로 코드에는 이상이 없다는 것을 알 수 있다.
그렇다는 고려해야하는 두가지 문제점 중 다른 문제점인 보드에 문제가 있는 것인지 확인해야한다.
먼저 보드에 문제가 있는지 확인하기 위해 dht11_cntr에 led가 들어오는지 확인한다.
아래와같이 dht11_cntr에 led와 관련하여 추가한다.
그리고 아래와같이 dht11_test_top에 led도 추가한다.
보드가 정상적으로 작동되는 것을 확인하려면 led가 led0에서 켜지다가 3초 후에 led1,led2,led3,led4,led5에 순서대로 켜져야한다.
/////////////////////////////////////// 습도센서
module dht11_test_top(
input clk, reset_p,
inout dht11_data,
output [3:0] com,
output [7:0] seg_7,
output [15:0] led); //보드에 이상이 있는지 확인하기 위함, 2024.7.3
wire [7:0] humidity, temperature;
dht11_cntr dht11(clk, reset_p, dht11_data, humidity, temperature, led);
wire[15:0] value;
assign value = {bcd_humi[7:0], bcd_tmpr[7:0]};
fnd_4digit_cntr fnd(clk, reset_p, value, seg_7, com);
endmodule
.xdc 파일에는 led를 확인하기위해 led관련하여 주석을 풀고 아래와 같이 수정한다.
그러면 led가 0번에서 켜져있다가 3초후 1,2,3,4,5 순서대로 이동하는 것을 확인할 수 있다.
이러면 보드에도 이상 없이 잘 진행된다는 의미이다.
그렇다면 오실로스코프에서 시그널 파형이 어떻게 나타나는지 확인해야한다.
오실로스코프에 연결하기 위해 한쪽 노드에는 습도센서의 시그널(JB1)에 연결하고 다른 쪽 노드에는 Basys3 보드의 gnd에 연결한다. gnd는 어느 포트의 gnd에 연결하던지 상관없다.
그리고 1,2번중 1번에만 연결했으므로 자동설정(Autoset)을 눌러 파형이 1번만 뜨게한다.
그리고 Basys3에 dht11_test_top이 연결된 상태에서 reset버튼을 누르고 오실로스코프의 Single버튼을 누르면 파형이 나오자마자 알아서 파형이 고정되어 화면을 정지시킨다.
Run/Stop을 눌러서 수동으로 파형이 화면에 나타나게 하여 화면을 멈출 수 있지만, 워낙 빠른 속도로 파형이 나타나기 때문에 Single 버튼을 누르는 것이 좋다.
그런데 오실로스코프에 나오는 값과 데이터시트에 나오는 딜레이 값에는 차이가 있다.
데이터시트에는 S_HIGH_20US에서 20~40us정도로 wait해야한다고 나와있지만 20~40us가 너무 짧아서 1비트를 놓치는 바람에 결과가 제대로 출력되지 않는 것이다.
즉, 데이터시트에 오류가 있기 때문에 데이터시트를 보고 코드를 작성해도 Basys3보드에 출력되지 않는 것이다.
이렇듯 소스코드를 모두 작성했다고 과정이 끝났다는 것이 아니라 테스트벤치, 오실로스코프를 사용하여 오류가 없는지 꼭 확인해야한다. 에러가 생기면 에러가 생긴 곳에서 멈추기 때문에 소스코드를 다 작성한 후 오류를 대비하기위한 코드를 추가로 작성해야한다.
이럴때 해결은 어떻게 해야하는 것일까?
어느 시간 동안 값이 출력되지 않는다면 다시 초기값으로 돌아가서 과정을 반복하는 소스코드를 작성하면 된다.
dht11_cntr 오류 방지 소스코드
//////////////////////////////////////////////// 습도센서 컨트롤러
module dht11_cntr(
input clk, reset_p,
inout dht11_data, // 데이터 선, input는 레지스터를 지정해 줄 수 없다.
output reg [7:0] humidity, temperature,
output [15:0] led); //보드에 이상이 있는지 확인하기 위함, 2024.7.3
parameter S_IDLE = 6'b00_0001;
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;
parameter S_WAIT_NEDGE = 2'b10;
reg [21:0] count_usec;
wire clk_usec;
reg count_usec_e;
clock_div_100 us_clk(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec));
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 == 0)count_usec = 0;
end
wire dht_pedge, dht_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(dht11_data),
.p_edge(dht_pedge), .n_edge(dht_nedge));
reg [5:0] state, next_state;
reg [1:0] read_state;
always @(negedge clk or posedge reset_p) begin
if(reset_p)state = S_IDLE;
else state = next_state;
end
/////////////////추가한 부분, 2024.7.3
assign led[5:0] = state; //0번부터 5번까지 led가 켜질 것임
reg [39:0] temp_data;
reg [5:0] data_count;
reg dht11_buffer;
assign dht11_data = dht11_buffer; // 임피던스는 끊는 것, 읽을 때는 dht11_buffer에서 임피던스를 출력을 하고 읽어야함
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
count_usec_e = 0;
next_state = S_IDLE;
read_state = S_WAIT_PEDGE;
data_count = 0;
dht11_buffer = 'bz;
end
else begin
case(state)
S_IDLE: begin
if(count_usec < 22'd3_000_000)begin //3초 기다림-->3,000,000us, 시뮬레이션에서 보기 위해 30으로 설정
count_usec_e = 1;
dht11_buffer = 'bz;
end
else begin
next_state = S_LOW_18MS;
count_usec_e = 0;
end
end
//
S_LOW_18MS: begin
if(count_usec < 22'd18_000)begin // 18ms
dht11_buffer = 0;
count_usec_e = 1;
end
else begin
next_state = S_HIGH_20US;
count_usec_e = 0;
dht11_buffer = 'bz;
end
end
//
S_HIGH_20US: begin
count_usec_e = 1;
if(count_usec > 22'd100_000)begin //100us동안 응답이 없으면 다시 IDLE로 돌아간다.
next_state = S_IDLE;
count_usec_e = 0; //값을 놓치거나 할 때 초기화 해서 다시 측정하도록 하기 위함
end
if(dht_nedge)begin
next_state = S_LOW_80US;
count_usec_e = 0;
end
end
//
S_LOW_80US: begin
count_usec_e = 1;
if(count_usec > 22'd100_000)begin //100us동안 응답이 없으면 다시 IDLE로 돌아간다.
next_state = S_IDLE;
count_usec_e = 0; //값을 놓치거나 할 때 초기화 해서 다시 측정하도록 하기 위함
end
if(dht_pedge) begin
next_state = S_HIGH_80US;
end
end
//
S_HIGH_80US: begin
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
if(count_usec < 45)begin
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 count_usec_e = 1;
if(count_usec > 22'd700_000)begin //700us동안 응답이 없으면 다시 IDLE로 돌아간다.
next_state = S_IDLE;
count_usec_e = 0;
data_count = 0;
read_state = S_WAIT_PEDGE;
end
end
endcase
if(data_count >= 40)begin
data_count = 0;
next_state = S_IDLE;
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]; //31~24까지는 소수부, 의미 없어서 뺌
temperature = temp_data[23:16];
end
end
end
default: next_state = S_IDLE;
endcase
end
end
endmodule
위와 같이 수정한 후 다시 dht11_test_top을 Basys3보드와 연결하면 상위 2비트에는 습도, 하위 2비트에는 온도가 16진수로 나타나는 것을 확인할 수 있다.
하지만 온습도계가 16진수로 표현되면 읽기가 상당히 번거롭다.
때문에 16진수를 10진수로 바꾸는 소스코드를 추가로 작성한다.
16진수->10진수로 바꾸는 bcd 코드
///////////////////////////////////////////////// 2024.7.3
/////////////////////// 16진수를 10진수로 바꾸는 bcd코드
module bin_to_dec(
input [11:0] bin,
output reg [15:0] bcd);
reg [3:0] i;
always @(bin) begin // always안에 for 는 동작정의
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; //최하위 4자리가 5이상이면 3(0011)을 더함
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
2진수 값을 더한 결과값에 좌시프트를 하거나,
좌시프트를 한 2진수 값을 서로 더해서 2배로 취하면
16진수를 10진수로 나타낼 수 있다.
그리고 16진수->10진수 bcd 소스코드를 dht11_test_top에 추가한다.
/////////////////////////////////////// 습도센서
module dht11_test_top(
input clk, reset_p,
inout dht11_data,
output [3:0] com,
output [7:0] seg_7,
output [15:0] led); //보드에 이상이 있는지 확인하기 위함, 2024.7.3
wire [7:0] humidity, temperature;
dht11_cntr dht11(clk, reset_p, dht11_data, humidity, temperature, led);
////////
wire [15:0] bcd_humi, bcd_tmpr;
bin_to_dec b2d_humi(.bin({4'b0000, humidity}), .bcd(bcd_humi));
bin_to_dec b2d_tmpr(.bin({4'b0000, temperature}), .bcd(bcd_tmpr));
wire[15:0] value;
assign value = {bcd_humi[7:0], bcd_tmpr[7:0]};
fnd_4digit_cntr fnd(clk, reset_p, value, seg_7, com);
endmodule
그러면 상위 2비트에 습도, 하위 2비트에 온도가 10진수로 나타나는 것을 확인할 수 있다.
아래의 사진에서는 습도가 61%, 온도가 26도인 것을 확인할 수 있다.
마지막으로 오실로스코프에서 잘 출력되는지 확인해보자.
이번에는 2번에만 연결하였기 때문에 2번 파형만 출력되게 하였다.
총 42개의 파형이 출력되었다. 맨 앞은 스타트비트이고, 2번째는 dht11에서 보낸 응답비트이다. 그 이후로는 40개의 파형이 나타나는 것을 확인할 수 있다.
3번째 파형부터 8개씩 끊어서 습도 정수부(8개), 습도 소수부(8개), 온도 정수부(8개), 온도 소수부(8개), check_sum(8개, 습도 정수부+습도 소수부+온도 정수부+온도 소수부)를 의미한다.
이번에 사용한 온습도계 센서는 소수부까지 출력되지 않기 때문에 소수부는 생략한다.
넓은 파형은 1, 좁은 파형은 0을 의미한다.
위의 사진은 습도 63%, 26도일 때의 파형을 나타낸 것이다.
3번째 파형부터 따져서 첫번째 8개 파형은 0011_1111, 즉 10진수로 63을 의미한다.
두번째 8개 파형은 0000_0000으로 출력되었다. 소수점을 의미해서 생략한다.
세번째 8개 파형은 0001_1010, 즉 10진수로 26을 의미한다.
네번째 8개 파형은 0000_0010으로 출력되었다. 역시 소수점을 의미해서 생략한다.
5번째 8개 파형은 0101_1011, 즉 10진수로 91을 의미한다. 앞의 네개의 파형값을 모두 더한 값을 의미한다.
네번째 소수점에 1이 출력된 부분이 있어서 check_sum이 63+26=89보다 높게 나왔지만, 그래도 비슷한 값이 나왔음을 확인할 수 있다.
습도센서 테스트벤치, 에러 대비, 16진수를 10진수로 변환하기 끝!
'Harman 세미콘 아카데미 > Verilog' 카테고리의 다른 글
2024.7.5 [Verilog]14 - 다기능 시계 만들기 개인프로젝트 (0) | 2024.07.05 |
---|---|
2024.7.4 [Verilog]13 - 초음파센서 (0) | 2024.07.04 |
2024.7.2 [Verilog]11 - 키패드, 습도센서 (0) | 2024.07.02 |
2024.7.1 [Verilog]10 - 주방 타이머 만들기 (0) | 2024.07.01 |
2024.6.28[Verilog]⑨ 스톱워치 만들기 기능추가+응용 (0) | 2024.06.28 |