2024.7.4 수업날
오늘은 습도센서와 방식이 유사한 초음파센서를 베릴로그로 구현한다.
초음파센서는 Atmega 128a에서도 사용한적이 있다.
Atmega 128a에서는 Uart 통신을 사용하여 초음파 센서를 사용하였는데,
이번에는 Basys3 보드와 연결하여 사용한다.
먼저 소스코드를 작성하기 전에 상태도를 그려서 초음파센서가 어떻게 구동되는지 전체적인 흐름을 파악해야한다.
크게 4가지 단계로 구분할 수 있다.
1. S_ILDE: 1초 딜레이 시간이 있으며 Trigger가 0인 상태이다.
2. Trigger: Trigger가 1이고, 10us를 카운트한다. 그 후에는 Trigger가 0이된다.
3. Echo: 상승에지를 먼저 기다린 후에 초음파 왕복펄스를 구하기 위해서 usec_count_e를 1로 하여 카운터를 허용한다. 그리고 하강에지를 기다린다.
4. distance: 초음파 왕복 거리를 측정하기 위해 측정한 값을 58로 나누면 거리값을 알 수 있다.
이러한 흐름을 가지고 소스코드를 작성하면 된다.
초음파센서 소스코드
////////////////////////////////////////////2024.7.4
////////////////////////////////// 초음파센서
module ultrasonic(
input clk, reset_p,
input echo,
output reg trigger,
output reg [11:0] distance,
output [3:0] led);
parameter S_IDLE = 3'b001;
parameter TRI_10US = 3'b010;
parameter ECHO_STATE= 3'b100;
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_usec usec_clk(clk, reset_p, clk_usec);
always @(negedge clk or posedge reset_p)begin
if(reset_p) count_usec = 0;
else begin
if(clk_usec && count_usec_e) count_usec = count_usec + 1;
else if(!count_usec_e) count_usec = 0;
end
end
wire echo_pedge, echo_nedge;
edge_detector_n ed(.clk(clk), .reset_p(reset_p), .cp(echo), .p_edge(echo_pedge), .n_edge(echo_nedge));
reg [3:0] state, next_state;
reg [1:0] read_state;
assign led[3:0] = state;
always @(negedge clk or posedge reset_p)begin
if(reset_p) state = S_IDLE;
else state = next_state;
end
// reg [11:0] echo_time;
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
count_usec_e = 0;
next_state = S_IDLE;
trigger = 0;
read_state = S_WAIT_PEDGE;
end
else begin
case(state)
S_IDLE:begin
if(count_usec < 22'd100_000)begin //100ms, 빠르면 빠를수록 갱신 속도가 빨라짐
count_usec_e = 1;
end
else begin
next_state = TRI_10US;
count_usec_e = 0;
end
end
TRI_10US:begin
if(count_usec <= 22'd10)begin
count_usec_e = 1;
trigger = 1;
end
else begin
count_usec_e = 0;
trigger = 0;
next_state = ECHO_STATE;
end
end
ECHO_STATE:begin
case(read_state)
S_WAIT_PEDGE:begin
count_usec_e = 0;
if(echo_pedge)begin
read_state = S_WAIT_NEDGE;
cnt_e = 1;
end
end
S_WAIT_NEDGE:begin
if(echo_nedge)begin
read_state = S_WAIT_PEDGE;
count_usec_e = 0;
distance = cm;
cnt_e = 0;
next_state = S_IDLE;
end
else begin
count_usec_e = 1;
end
end
endcase
end
default:next_state = S_IDLE;
endcase
end
end
always @(posedge clk or posedge reset_p)begin
if(reset_p)distance = 0;
else begin
//distance = echo_time / 58;
if(echo_time < 58) distance = 0;
else if(echo_time < 116) distance = 1;
else if(echo_time < 174) distance = 2;
else if(echo_time < 232) distance = 3;
else if(echo_time < 290) distance = 4;
else if(echo_time < 348) distance = 5;
else if(echo_time < 406) distance = 6;
else if(echo_time < 464) distance = 7;
else if(echo_time < 522) distance = 8;
else if(echo_time < 580) distance = 9;
else if(echo_time < 638) distance = 10;
else if(echo_time < 696) distance = 11;
else if(echo_time < 754) distance = 12;
else if(echo_time < 812) distance = 13;
else if(echo_time < 870) distance = 14;
else if(echo_time < 928) distance = 15;
else if(echo_time < 986) distance = 16;
else if(echo_time < 1044) distance = 17;
else if(echo_time < 1102) distance = 18;
else if(echo_time < 1160) distance = 19;
else if(echo_time < 1218) distance = 20;
else if(echo_time < 1276) distance = 21;
else if(echo_time < 1334) distance = 22;
else if(echo_time < 1392) distance = 23;
else if(echo_time < 1450) distance = 24;
else if(echo_time < 1508) distance = 25;
else if(echo_time < 1566) distance = 26;
else if(echo_time < 1624) distance = 27;
else if(echo_time < 1682) distance = 28;
else if(echo_time < 1740) distance = 29;
else if(echo_time < 1798) distance = 30;
end
end
endmodule
하지만 위와같이 소스코드를 작성하고 Bitstream을 실행하면, Design Runs에서 빨간색으로 숫자가 표시되는 것을 볼 수 있다. 베릴로그로 작성된 설계가 실제 FPGA 구현 과정에서 타이밍 제약을 만족하지 못한 결과인 것이다.
이를 네거티브 슬랙이라고 하는데, 네거티브 슬랙은 발생하면 안되는 것이다.
WNS (Worst Negative Slack): 이 값은 타이밍 분석에서 최악의 경로에 대해 요구된 타이밍을 충족하지 못한 정도를 나타내는 것이다. WNS가 음수이면 설계가 타이밍 요구사항을 충족하지 못했다는 뜻이다. 여기서 -11.255는 최악의 경로에서 11.255 ns가 부족하다는 것을 의미한다.
TNS (Total Negative Slack): 이 값은 모든 타이밍 위반 경로에 대해 타이밍 부족을 합산한 값을 나타내는 것이다. TNS가 음수이면 타이밍 위반이 여러 경로에 걸쳐 발생했음을 의미한다. 여기서 -149.813이라는 값은 모든 경로에서의 총 타이밍 부족을 나타낸다.
즉, fpga에는 나누기를 쓰면 안된다. 속도가 빠른 회로에서 나누기를 쓰면 워낙 복잡하기 때문에 pdt가 길게 나오는 것이다. 그리고 c언어의 나누기와 베릴로그에서의 나누기는 의미가 다르다.
해결하기 위해서는 나누기를 쓰지 말고 직접 지정해줘야한다. 그러면 음수로 나타나지 않는다.
하지만 일일히 적는것은 너무 코드가 길어지니까, 58로 나누었으니 58분주기를 만들면 간편하게 해결할 수 있다.
58분주기 소스코드
///////////////////////////////////////////////////////// 2024.7.4
////////////////////////// 1초음파센서 전용 cm를 출력하는 58분주기, 카운트 값을 내보냄, 나누기 안써도 됨
module sr_04_div_58(
input clk, reset_p,
input clk_usec, cnt_e,
output reg [11:0] cm );
integer cnt;
always @(negedge clk or posedge reset_p) begin
if(reset_p)begin
cnt = 0;
cm = 0;
end
else if(cnt_e)begin
if(clk_usec) begin
//1000원짜리 100개를 10개씩 카운트해서 마지막에 10묶음이 몇개인지 확인하는 것처럼 분주기도 마찬가지
if(cnt >= 58)begin cnt = 0; cm = cm +1; end //58us마다 cm가 1올라감
else cnt = cnt + 1;
end
end
else begin
cnt = 0;
cm = 0;
end
end
endmodule
58분주기를 추가한 초음파센서 소스코드
module ultrasonic(
input clk, reset_p,
input echo,
output reg trigger,
output reg [11:0] distance,
output [3:0] led
);
parameter S_IDLE = 3'b001; // 3개의 단계로 나누어서 3비트를 사용
parameter TRI_10US = 3'b010;
parameter ECHO_STATE= 3'b100;
parameter S_WAIT_PEDGE = 2'b01; // 2개의 단계(상승에지, 하강에지)로 사용하여 2비트를 사용
parameter S_WAIT_NEDGE = 2'b10;
reg [21:0] count_usec; //[19:0]
wire clk_usec;
reg count_usec_e;
clock_div_100 us_clk(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec)); // 100분주기, 10ns인 기본 클록을 100분주기 하면 1us가 되므로
always @(negedge clk or posedge reset_p)begin
if(reset_p) count_usec = 0;
else begin
if(clk_usec && count_usec_e) count_usec = count_usec + 1; // 1us 클록과 카운터를 활성화하겠다는 것이 둘다 참일 때, 카운트의 값을 1씩 증가하겠다는 의미
else if(!count_usec_e) count_usec = 0;
end
end
wire echo_pedge, echo_nedge;
edge_detector_n ed(.clk(clk), .reset_p(reset_p), .cp(echo), .p_edge(echo_pedge), .n_edge(echo_nedge));
reg [3:0] state, next_state; // 3단계로 나누어서
reg [1:0] read_state;
reg cnt_e;
wire [11:0] cm;
sr_04_div_58 div58(clk, reset_p, clk_usec, cnt_e, cm);
assign led[3:0] = state;
always @(negedge clk or posedge reset_p)begin
if(reset_p) state = S_IDLE;
else state = next_state;
end
// reg [11:0] echo_time;
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
count_usec_e = 0;
next_state = S_IDLE;
trigger = 0;
read_state = S_WAIT_PEDGE;
end
else begin
case(state)
S_IDLE:begin
if(count_usec < 22'd100_000)begin // 100ms, 빠르면 빠를수록 갱신 속도가 빨라짐
count_usec_e = 1;
end
else begin
next_state = TRI_10US;
count_usec_e = 0;
end
end
TRI_10US:begin
if(count_usec <= 22'd10)begin // 10us
count_usec_e = 1;
trigger = 1;
end
else begin
count_usec_e = 0;
trigger = 0;
next_state = ECHO_STATE;
end
end
ECHO_STATE:begin
case(read_state)
S_WAIT_PEDGE:begin
count_usec_e = 0;
if(echo_pedge)begin
read_state = S_WAIT_NEDGE;
cnt_e = 1;
end
end
S_WAIT_NEDGE:begin
if(echo_nedge)begin
read_state = S_WAIT_PEDGE;
count_usec_e = 0;
distance = cm;
cnt_e = 0;
next_state = S_IDLE;
end
else begin
count_usec_e = 1;
end
end
endcase
end
default:next_state = S_IDLE;
endcase
end
end
endmodule
초음파센서 test_top 소스코드
//////////////////////////////////////////////////2024.7.4
module ultrasonic_test_top(
input clk, reset_p, // 입력 clk, reset_p 정의
input echo, // 입력 echo 정의
output trigger,
output [3:0] com, // 출력 [3:0] com 정의
output [7:0] seg_7, // 출력 [7:0] seg_7 정의
output [15:0] led); // 출력 [15:0] led 정의
wire [15:0] distance;
wire [15:0] bcd_distance;
ultrasonic(clk, reset_p, echo, trigger, distance, led);
bin_to_dec btd_distance(.bin(distance), .bcd(bcd_distance));
wire [15:0] value; // [15:0] value 와이어 선언
assign value = bcd_distance;
fnd_4digit_cntr fnd(clk, reset_p, value, seg_7, com); // 4자리 FND 디스플레이 모듈을 인스턴스화하여 현재 value 값을 seg_7와 com으로 출력
endmodule
58분주기를 추가한 후 wns, tns가 음수로 나오지 않는 것을 확인할 수 있다.
회로도는 아래와 같이 연결하면 된다.
초음파 센서는 5v로 작동하는 것이기 때문에 초음파 센서의 Vcc를 Atmega128a에 연결한다.
그리고 Trigger, Echo는 Basys3 보드의 지정된 포트에 연결하고,
초음파 센서의 Gnd는 Basys3 보드와 Atmega128a에 모두 연결한다.
그러면 LED에 불이 켜지면서 초음파센서로부터의 거리가 Basys3 보드의 fnd에 출력되는 것을 확인할 수 있다.
네거티브 슬랙에 대해서 다른 코드에도 적용해서 추가로 살펴보자.
2024.7.2 때 했던 습도센서와 관련된 dht11_cntr에서 edge_detector_n으로 하면 네거티브 슬랙이 출력된다.
하지만 edge_detector_p로 수정하면 네거티브 슬랙이 출력되지 않는 것을 확인할 수 있다.
초음파센서 끝!
'Harman 세미콘 아카데미 > Verilog' 카테고리의 다른 글
2024.7.8 [Verilog]15 - PWM으로 led 제어하기 (0) | 2024.07.08 |
---|---|
2024.7.5 [Verilog]14 - 다기능 시계 만들기 개인프로젝트 (0) | 2024.07.05 |
2024.7.3 [Verilog]12 - 습도센서 테스트벤치, 에러 대비, 16진수를 10진수로 변환하기 (0) | 2024.07.03 |
2024.7.2 [Verilog]11 - 키패드, 습도센서 (0) | 2024.07.02 |
2024.7.1 [Verilog]10 - 주방 타이머 만들기 (0) | 2024.07.01 |