개인프로젝트

[FPGA] FSM 감응신호 "신호등" 만들기 (라즈베리파이 - Zynq 소켓통신)

섭섭입니다 2021. 8. 20. 17:08



우선, 감응신호란?


교차로로 들어오는 각 도로에서 차량이 진입하거나 보행자의 유무에 따라서 신호를 자동으로 부여하는 신호체계를 말합니다.


즉 차가 별로 다니지 않는 도로에 차가 대기한다면 신호를 바꿔주는 것입니다.

좌회전 차선, 횡단보도도 마찬가지 입니다.

 



Verilog 를 공부하던 도중 신호등 예제를 만들어 봤는데

FPGA에 동작시켜보면 재밌겠다! 싶어서 기획하여 구현하게 되었습니다.


위와 같이 어떠한 센서를 통해 들어온 값을 토대로 zynq 보드에서 신호등처럼 나타내었습니다.



저는 라즈베리파이와 zynq를 소켓통신을 통해서 연결한 뒤에

라즈베라파이에 달려 있는 초음파 센서를 활용해서 감응신호를 잡아내어 보낼 것 입니다.

차가 있는지 없는지 판단한 데이터를 zynq에서 차의 유무에 맞게 처리할 것입니다.



신호등의 RED, GREEN, YELLOW 불빛은 RGB LED를 활용해서 나타내었습니다.



 

라즈베리파이 4 4GB, Zybo-z7-20, Vivado 18.3 에서 진행하였습니다.



 

라즈베리파이 client code


< Client.c >

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <wiringPi.h>

void error_handling(char* message);

const int pinEcho = 22;
const int pinTrigger = 21;

int main(int argc, char* argv[])
{
	int clnt_sock;
	struct sockaddr_in serv_addr;
	char message[1024] = {0x00, };
	
	wiringPiSetup();

	pinMode(pinEcho, INPUT);
	pinMode(pinTrigger, OUTPUT);

	digitalWrite(pinTrigger, LOW);
	delay(30);
	
	clnt_sock = socket(PF_INET, SOCK_STREAM, 0);
	if(clnt_sock == -1)
		error_handling("socket error");

	memset(&serv_addr, 0, sizeof(serv_addr));
	
	serv_addr.sin_family = AF_INET;                
	serv_addr.sin_addr.s_addr = inet_addr(argv[1]);  
	serv_addr.sin_port = htons(atoi(argv[2]));

	if(connect(clnt_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
		error_handling("connect error");
		
	char msg[] = "Car_ON";
	char msg2[] = "Car_OFF";
	
	while(1)
	{
		digitalWrite(pinTrigger, HIGH);
		delayMicroseconds(20);
		digitalWrite(pinTrigger, LOW);

		while(digitalRead(pinEcho)==LOW);
		long startTime = micros();

		while(digitalRead(pinEcho)==HIGH);
		long endTime = micros()-startTime;

		int distance = endTime / 58;


		
		if(distance <10) {
			write(clnt_sock, msg, sizeof(msg));
			printf("Send message :%s\n", msg);
		}
		else {
	        write(clnt_sock, msg2, sizeof(msg));
	        printf("Send message :%s\n", msg2);
		}
		delay(1000);
	}
	


	if(read(clnt_sock, message, sizeof(message)-1) == -1)
		error_handling("read error");
	printf("Message from server :%s\n", message);
   
    
	close(clnt_sock);
	return 0;
}
void error_handling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}





FSM 신호등 verilog code

vivodo 내에서 한글로 주석처리를 하면 깨져 영문으로 주석처리 하였습니다.


< traffic_light_control.v >

module traffic_light (reset_n,X,clk,main_G, main_R, cntry_G, cntry_R);

input reset_n;
input X;
input clk;
output main_G, main_R;
output cntry_G, cntry_R;

reg main_G, main_R;
reg cntry_G, cntry_R;

reg [2:0] state;
reg [2:0] next_state;

reg [27:0] counter;

//parameter RED = 2'b00, YELLOW = 2'b01, GREEN = 2'b11;

parameter LED_ON = 1'b1 , LED_OFF = 1'b0;

parameter  S0 = 3'b000,            // main : Green,     cntry : RED
           S1 = 3'b001,            // main : Yellow,    cntry : RED
           S2 = 3'b010,            // main : RED,       cntry : RED
           S3 = 3'b011,            // main : RED,       cntry : Green
           S4 = 3'b100;            // main : RED,       cntry : Yellow

always @(posedge clk or negedge reset_n) begin
    
    if(!reset_n)  begin
        state <= S0;
        counter <= 0;
    end
    else if(state == S1 || state == S2 || state == S4) begin
        if(counter == 28'b1011111010111100001000000000) begin  // Axi clk Freq = 100MHz , 2sec delay  = 200,000,000
            state <= next_state;   
            counter <= 0;
        end
        else counter <= counter +1;
    end 
    else 
        state <= next_state;    
end

always @(state) begin

    case(state)
    
    S0 : begin
        // default setting      
        // main : Green, cntry : Red
        cntry_G <= LED_OFF;
        cntry_R <= LED_ON;
        main_G <= LED_ON;
        main_R <= LED_OFF;
    end
    S1 : begin
        // main : Yellow, cntry : Red
        main_G <= LED_ON;
        main_R <=  LED_ON;
        cntry_G <= LED_OFF;
        cntry_R <= LED_ON;
    end
    S2 : begin
         // main : RED, cntry : RED
        main_G <= LED_OFF;
        main_R <= LED_ON;
        cntry_G <= LED_OFF;
        cntry_R <= LED_ON;        
    end
    S3 : begin
        // main : RED, cntry : Green
        main_G <= LED_OFF;
        main_R <= LED_ON; 
        cntry_G <= LED_ON;
        cntry_R <= LED_OFF;
    end
    S4 : begin
        // main : RED, cntry : Yellow
        main_R <= LED_ON;
        main_G <= LED_OFF;
        cntry_G <= LED_ON;
        cntry_R <= LED_ON;
    end
    endcase
end    

always @(state or X) begin
    
    case (state)

    S0 : if(X)  // car_on -> next state
    next_state <= S1; 
    else 
    next_state <= S0;

    S1 : 
    next_state <= S2;

    S2 : 
    next_state <= S3;

    S3 : if(X)  // car_on -> current state
    next_state <= S3; 
    else 
    next_state <= S4;  

    S4 :
    next_state <= S0;
    
    default : next_state <= S0;

    endcase
end

endmodule



신호의 딜레이는 counter 를 활용하여 지연을 시켰고

AXI 클럭 주파수가 100 Mhz로 설정하였고
counter는 200M (2초)를 딜레이 하였습니다



Axi 인터페이스와 신호등 코드를 연결하는 구문을 추가 하였습니다.



main_out[0] = RED, main_out[1] = GREEN
cntry_out[0] = GREEN, cntry_out[1] = RED

YELLOW 색은 REDGREEN색을 섞어서, 즉 [0], [1] 을 둘다 1로 만들어서 LED 2개를 켜도록 하였습니다.




AXI 인터페이스를 이용하여 제가 만든 신호등 ip를 추가하여 디자인을 구성했습니다.



SDK 내에서 [ IwIP Echo Server ] 템플릿을 활용해서 진행하였습니다.

 




헤더를 추가하고

#include "xparameters.h"

#include "xbasic_types.h"

#include "xil_io.h"




기본적으로 만들어지는 echo.c 파일에 아래의 코드를 첨가해

제가 구성한 AXI 인터페이스에 맞게 들어 오는 센서값을 제어하도록 하였다.






보드 ip에 맞게 socket을 해주면 된다.




< 시연 영상 >


2개의 LED 중 오른쪽 LED가 메인 도로의 LED로서 기본적으로 초록불이 점등되어 있습니다.

허나 자동차가 감지 되면 신호가 바뀌게 됩니다.