라즈베리파이에서 초음파센서는 정리를 한번 했기때문에 간단하게 하겠습니다.
- 맨위의 초음파를 쏘라고 신호를 보내준다(Trigger신호) 이는
10us의 신호를 보내주고 두번째 줄에서 초음파가 발생한다 초음파가 발생하고 물체에 부딪혀 돌아온다. 이 초음파가 발생하고 돌아오는 시간만큼 3번째에서 펄스를 발생.
- 음파의 속도(343m/s)를 기준으로 초음파가 발생하고 되돌아오는 시간을 58로 나누어주면 거리가 나옴
Echopulse는 최대36ms까지 펄스를 출력할 수 있다. 이는 초음파가 발생하고 돌아오는시간이36ms가 최대라는 의미이다. 이를 넘어가면 측정이 불가능하다!
module Echo_Controller(
input clk, reset_p,
input echo,
output reg trigger,
output reg[15:0] distance,
output reg[15:0] led_state);
parameter S_IDLE = 4'b0001;
parameter S_TRIGGER = 4'b0010;
parameter S_ECHO = 4'b0100;
parameter S_CAL_DISTANCE = 4'b1000;
reg[3:0] state, next_state;
wire clk_microsec;
clock_div_100 microsec_clk(.clk(clk), .reset_p(reset_p),.clk_div_100_nedge(clk_microsec));
reg[21:0] count_microsec;
reg count_microsec_enable;
reg count_1us, count_dis;
always@(negedge clk or posedge reset_p)begin
if(reset_p) count_microsec = 0;
else if(clk_microsec && count_microsec_enable) count_microsec = count_microsec + 1;
else if(!count_microsec_enable)count_microsec = 0;
end
wire echo_posedge, echo_negedge;
edge_dectector_n ed_echo(.clk(clk), .reset_p(reset_p),.cp(echo), .p_edge(echo_posedge), .n_edge(echo_negedge));
always@(negedge clk or posedge reset_p)begin
if(reset_p)state = S_IDLE;
else state = next_state;
end
//assign dht11_data = dht11_buffer;
always@(posedge clk or posedge reset_p)begin
if(reset_p)begin
next_state = S_IDLE;
led_state = 0;
trigger = 0;
count_microsec_enable = 0;
end
else begin
case(state)
S_IDLE:begin
if(count_microsec < 22'd3_000_000)begin
count_microsec_enable = 1;
led_state[0] = 1;
count_1us = 0;
count_dis = 0;
end
else begin
count_microsec_enable = 0;
led_state[1] = 1;
next_state = S_TRIGGER;
end
end
S_TRIGGER:begin
if(count_microsec < 22'd10)begin
trigger = 1;
count_microsec_enable = 1;
led_state[2] = 1;
end
else begin
next_state = S_ECHO;
count_microsec_enable = 0;
trigger = 0;
led_state[3] = 1;
end
end
S_ECHO:begin
led_state[4] = 1;
if(echo_posedge)begin
led_state[5] = 1;
next_state = S_CAL_DISTANCE;
count_microsec_enable = 1;
end
end
S_CAL_DISTANCE:begin
led_state[6] = 1;
if(echo_negedge)begin
distance = count_microsec / 22'd58;
count_microsec_enable = 0;
led_state[8] = 1;
next_state = S_IDLE;
end
else begin
count_microsec_enable = 1;
led_state[7] = 1;
end
end
default:next_state =S_IDLE;
endcase
end
end
endmodule
FSM을 사용하여 4개의 상태를 반복하도록 하였습니다. S_IDLE상태에서는 3초 대기하도록하고 3초가 지나면 S_TRIGGER상태로 넘어가고 10us 동안 Trigger신호를 줍니다. Trigger신호가 발생하면 초음파가 발생해 물체에 부딫혀 되돌아오는데 S_ECHO상태에서 echo핀의 상승엣지가 발생하면 그때부터 시간을 카운트합니다. 그리고 하강엣지가 발생하면 S_CAL_DISTANCE상태로 넘어가 시간 카운트를 멈추고 측정한 시간에 58을 나누어 거리를 계산하여 distance에 저장합니다.오실로스코프 실습
Trigger 신호 ↖
Echo 신호 ↖※ 오실로스코프에서 엣지를 확인하는 방법. 기본세팅모드에서 트리거의 메뉴레벨을 조절한다. 나는 ch1을 사용하고 있기 때문에 ch1의 메뉴레벨(?.? V)을 조절하면 메뉴레벨에서 설정한 전압 이상의 전압 즉, 엣지가 발생하면 다음과 같이 트리거 신호를 측정할 수 있다.
위 2개의 사진은
DHT11의dht_data를 측정한 결과입니다. 첫번째 결과는 3초마다 한번씩 온도와 습도를 측정하여 데이터를 받아오는 신호의Scale을 키워서 확인한 결과입니다. 하지만Scale을 더 확대해서DHT11이 통신하는 신호와 데이터가 들어오는것을 확인할 수 있습니다. 제가 작성한DHT11코드의 결과를 보면 처음에20ms동안Basys-3가DHT11을 감지하고 이후10us(원래는 20us~40us)동안DHT11응답을 기다리고 이 후DHT11이80us동안High상태로 응답신호를 전송하고 또80us동안 데이터전송을 위해LOW상태를 유지하고 이 후DHT11이 데이터를 전송합니다.
input clk, reset_p,
input echo,
output reg trigger,
output reg[15:0] distance,
output reg[15:0] led_state);
parameter S_IDLE = 4'b0001;
parameter S_TRIGGER = 4'b0010;
parameter S_ECHO = 4'b0100;
parameter S_CAL_DISTANCE = 4'b1000;
reg[3:0] state, next_state;
//assign led_state[3:0] = state;
wire clk_microsec;
clock_div_100 microsec_clk(.clk(clk), .reset_p(reset_p),.clk_div_100_nedge(clk_microsec));
reg cnt_enable;
wire[11:0]cm;
SR04_div_58 div58(.clk(clk), .reset_p(reset_p),.clk_microsec(clk_microsec),.cnt_enable(cnt_enable), .cm(cm));
reg[21:0] count_microsec;
reg count_microsec_enable;
reg count_1us, count_dis;
always@(negedge clk or posedge reset_p)begin
if(reset_p) count_microsec = 0;
else if(clk_microsec && count_microsec_enable) count_microsec = count_microsec + 1;
else if(!count_microsec_enable)count_microsec = 0;
end
wire echo_posedge, echo_negedge;
edge_dectector_n ed_echo(.clk(clk), .reset_p(reset_p),.cp(echo), .p_edge(echo_posedge), .n_edge(echo_negedge));
//reg[21:0] echo_time;
always@(negedge clk or posedge reset_p)begin
if(reset_p)state = S_IDLE;
else state = next_state;
end
//assign dht11_data = dht11_buffer;
always@(posedge clk or posedge reset_p)begin
if(reset_p)begin
next_state = S_IDLE;
led_state = 0;
trigger = 0;
count_microsec_enable = 0;
cnt_enable = 0;
end
else begin
case(state)
S_IDLE:begin
if(count_microsec < 22'd3_000_000)begin
count_microsec_enable = 1;
led_state[0] = 1;
count_1us = 0;
count_dis = 0;
end
else begin
count_microsec_enable = 0;
led_state[1] = 1;
next_state = S_TRIGGER;
end
end
S_TRIGGER:begin
if(count_microsec < 22'd10)begin
trigger = 1;
count_microsec_enable = 1;
led_state[2] = 1;
end
else begin
next_state = S_ECHO;
count_microsec_enable = 0;
trigger = 0;
led_state[3] = 1;
end
end
S_ECHO:begin
led_state[4] = 1;
if(echo_posedge)begin
led_state[5] = 1;
next_state = S_CAL_DISTANCE;
//count_microsec_enable = 1;
cnt_enable = 1;
end
end
S_CAL_DISTANCE:begin
led_state[6] = 1;
if(echo_negedge)begin
distance = cm;
//echo_time = count_microsec;
cnt_enable = 0;
//count_microsec_enable = 0;
led_state[8] = 1;
next_state = S_IDLE;
end
else begin
cnt_enable = 1;
//count_microsec_enable = 1;
led_state[7] = 1;
end
end
default:next_state =S_IDLE;
endcase
end
end
// always@(posedge clk or posedge reset_p)begin
// if(reset_p) distance = 0;
// else begin
// 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 = 16;
// else if(echo_time < 1102)distance = 17;
// else if(echo_time < 1160)distance = 18;
// else if(echo_time < 1218)distance = 19;
// else if(echo_time < 1276)distance = 20;
// else if(echo_time < 1334)distance = 21;
// else if(echo_time < 1392)distance = 22;
// else if(echo_time < 1450)distance = 23;
// else if(echo_time < 1508)distance = 24;
// else if(echo_time < 1566)distance = 25;
// else if(echo_time < 1624)distance = 26;
// else if(echo_time < 1682)distance = 27;
// else if(echo_time < 1740)distance = 28;
// else if(echo_time < 1798)distance = 29;
// else if(echo_time < 1856)distance = 30;
// else if(echo_time < 1914)distance = 31;
// else if(echo_time < 1972)distance = 32;
// else if(echo_time < 2030)distance = 33;
// else if(echo_time < 2088)distance = 34;
// else if(echo_time < 2146)distance = 35;
// else if(echo_time < 2204)distance = 36;
// else distance = 36;
// end
// end
endmodule
echo_time의 경우의수를echo핀이 측정할 수 있는 최대길이까지를 경우의수를 모두 조건식으로 처리하여 작성한 코드 내용입니다.이렇게 always문으로 많은 조건식을 작성하게되면 조합논리회로가 만들어진다. 이러면
32bit LUT이 2개가 만들어질 것이다. 왜냐하면 조건문이 37개가 작성되어있기 때문에32bit LUT의 입력으로는 부족할 것이다. 그렇기에 2개의LUT이 사용되는데 이때 이 2개의LUT의 출력또한 하나의LUT으로 연결해야한다. 이렇게되면LUT의 출력이 나오기까지PDT가 중복되면서 PDT가 늘어나게된다. 이 다음에 정리를 하겠지만PDT가 늘어나면 Ta(arrival Time : 도달시간 = PDT + SetUpTime[이는 상황에따라 포함할 것인지 제외할 것인지 정할 수 있다.])가 늘어나게된다!Ta(arrival Time : 도달시간)?, Tr(requriement Time : 요구시간)?
- 주기가
10ns인Adder를 설계한다고 하자. 만약4bit Adder를 동작시킨다고 할 때 첫번째Adder의Carry가 발생하면 다음 Adder에 입력으로 들어가게되는데 이를Ripple Carry Adder라고 한다. 만약 이Ripple Carry Adder에서 계산을 진행할때 하나의Adder가 계산할 때 소요되는 시간이1ns라고 한다면4bit Adder가 연산하는 시간은 총4ns일 것이다. 이처럼 연산에 걸리는4ns의 시간은delay시간일 것이고 이를PDT라고 할 수 있다. 그렇다면 위에서 잠깐 언급했듯이PDT는 Ta(arrival Time : 도달시간 = PDT + SetUpTime) 이다 그렇다면4bit Adder의 주기10ns는 Tr(requriement Time : 요구시간 = CLK + HoldTime[이는 상황에따라 포함할 것인지 제외할 것인지 정할 수 있다.]에 해당한다. 여기서Tr과Ta를 빼면 이것을 Slack(마진)이라고한다. 이slack이(+)값으로 여유가 많다면 클럭주기를 빠르게 하여 성능을 높일 수 있다. 하지만 만약negative Slack이 나오게된다면?PDT시간이 크다고 생각할 수 있다.PDT시간이 높으면 도달시간(Ta)이 요구시간(Tr)보다 커져서negative Slack이 나오게된다.negative Slack이 발생하는 이유는 보통 Longest Path(두개의 F/F사이에 많은 CL이 존재하는 Path)에서 발생하지만 문제가 되지 않을 수도 있습니다. 이를 해결하기위해서PDT를 줄이는 방법이 가장 효율적입니다. Verilog코딩을 할때 always문(CL)의 조건을 많이 작성하게되면 Ta(도달시간)가 늘어나게되면서 negative Slack이 발생하게됩니다.SetUpTime은 클럭주기가
10ns라면 정확하게10ns에 주기가 변경되는것이 아니다 약간의 오차범위가 존재하고 이는10ns보다 빨리 주기가 변경될 수 있고,10ns보다 늦게 변경될 수 도 있다. 그렇기 때문에 데이터의 입력신호가F/F의 클럭. 즉 ,10ns전에 안정된 상태로 유지되어야하는 최소시간입니다, holdtime은clock입력 이후에도 일정시간동안 안정된 상태로 유지되어야하는 최소시간입니다. 이 시간동안 데이터를 유지시켜줘야하기에 도달시간에 setupTime, holdTime를 추가시켜줍니다.if조건문(CL : 조합논리회로)을 F/F으로 변경
- 다음과 같이 58분주기를 이용해서
58us를 카운트 할 때마다cm의 +1 해줍니다. 그리고distance에 할당하면cm가 나오게됩니다!(왜? echo핀이 상승엣지인 기간동안 측정한 시간(us)을 58로 나누어주어 거리를 계산하는것이 SR04(초음파센서)가 거리를 측정하는 방법이기 때문!)
- 이처럼
분주기(F/F)을 사용해서 설계를 하게된다면 이전처럼F/F사이의 조건이 많은CL이 만들어지면서**PDT가 증가합니다. 하지만 분주기를 사용하면CL을F/F으로 대체하면서PDT가 줄어드는 것을 볼 수 있습니다. 그 결과WNS(요구시간 - 도달시간)이 양수 값으로 나타나는 것을 볼 수 있으면 이는 도달시간이 줄어들었음**을 확인 할 수 있습니다.
안녕하세요..! 현재 대학교에서 징크 7020 보드로 프로젝트를 진행중인데 3.3v hcsr04 초음파 센서를 활용하려합니다. 현재 3주간 IP 생성하는 과정에서 계속 오류가 발생해서 혹시 생성하신 ip_repo 가 있으시다면 공유 부탁드려도 될까요..? ㅠㅠ