2015/01/11 - [IT / Development/WIZnet] - W5500-EVB를 이용한 Chat Client 구현해 보자 - 1

2015/01/12 - [IT / Development/WIZnet] - W5500-EVB를 이용한 Chat Client 구현해 보자 - 2

2015/01/26 - [IT / Development/WIZnet] - W5500-EVB를 이용한 Chat Client 구현해 보자 - 3

2015/01/26 - [IT / Development/WIZnet] - W5500-EVB를 이용한 Chat Client 구현해 보자 - 4

2015/03/19 - [IT / Development/WIZnet] - W5500-EVB를 이용한 Chat Client 구현해 보자 - 5


이번 포스팅으로 1월부터 진행해온 "W5500-EVB를 이용한 Chat Client 구현해 보자"를 마무리 하려고 한다.

W5500의 드라이버 소스를 이용하는 것에서는 큰문제가 없었지만, UART 데이터를 받아 처리하는 부분에서 많이 헤맸다. 그래서 구글링을 통해 링버퍼를 소스를 찾아 적용하니 UART 데이터 처리하는 분이 해결이 되었다.

아래 소스는 링버퍼 소스이다. ring_buffer.h와 ring_buffer.c에 링버퍼를 사용하기 위한 소스가 있다. 링버퍼는 간단한 함수 설명만 하겠다.

rb_put 함수 : 링버퍼에 데이터를 저장하는 함수이다.

rb_get 함수 : 링퍼버의 데이터를 읽어오는 함수이다.

finder 함수 : 링버퍼의 데이터에서 특정 값을 찾아내는 함수이다.

/******************************** ring_buffer.h ********************************/
#ifndef __RING_BUFFER_H__
#define __RING_BUFFER_H__
#include 
//UART 버퍼사이즈를 설정한다.
#define SERIAL_BUF_SIZE	2048

//링버퍼 구조체 선언
typedef struct {	// creation ring buffer
	int tail;		// read data pointer
	int head;		// write data pointer
	char data[SERIAL_BUF_SIZE]; // receive buffer
} t_rb;
uint16_t rb_put( t_rb *rb , uint8_t d );
uint8_t rb_get( t_rb *rb);
uint8_t finder ( t_rb *rb, uint8_t ch );
#endif
/******************************** ring_buffer.c ********************************/
#include "ring_buffer.h"
//rb_put함수는 링버퍼에 데이터를 저장하는 함수이다.
uint16_t rb_put( t_rb *rb , uint8_t d )
{
	uint16_t nhead = (rb->head+1) % (SERIAL_BUF_SIZE);	//
	if(rb->tail == nhead){
		return 0;
	}
	rb->data[rb->head] = d;
	rb->head = nhead;
	return 1;
}
//rb_get함수는 링퍼버의 데이터를 읽어오는 함수이다.
uint8_t rb_get( t_rb *rb)//, uint16_t *err)	// pop -> send Ethernet
{
	uint8_t d;
	uint16_t ntail = (rb->tail+1) % (SERIAL_BUF_SIZE);
	if(rb->head == rb->tail){
		return 0;		// buffer non-write -> empty
	}
	d = rb->data[rb->tail];
	rb->tail = ntail;
	return d;		// buffer read
}
//finder함수는 링버퍼의 데이터에서 특정 값을 찾아내는 함수이다.
uint8_t finder ( t_rb *rb, uint8_t ch )	// new line finder
{
	uint16_t i;
	uint16_t cnt;
	if(rb->head == rb->tail){
		return 0;
	}
	else if(rb->head > rb->tail){
		for(i=rb->tail; i < rb->head; i++){
			if(rb->data[i] == ch){
				return (i+1);
			}
		}
	}
	else{
		for(i=rb->tail; i  < SERIAL_BUF_SIZE; i++){
			if(rb->data[i] == ch){
				return (i+1);
			}
		}
		cnt = i;
		for(i=0; ihead; i++){
			if(rb->data[i] == ch){
				return (cnt+i+1);
			}
		}
	}
	return 0;
}
메인 소스는 W5500-EVB.c 이다. 구체적인 설명은 소스에 주석으로 대체하겠다.
/******************************** W5500-EVB.c ********************************/
#if defined (__USE_LPCOPEN)
#if defined(NO_BOARD_LIB)
#include "chip.h"
#else
#include "board.h"
#endif
#endif

#include "socket.h"
#include "spi_handler.h"
#include "w5500_init.h"
#include "ring_buffer.h"

// TODO: insert other include files here

// TODO: insert other definitions and declarations here
#define SOCK_TCPS0       0
#define DATA_BUF_SIZE   2048
uint8_t gDATABUF[DATA_BUF_SIZE];
uint8_t SERIAL_BUF[SERIAL_BUF_SIZE];
#define CR	0x0D
#define LF	0x0A

//링버퍼 초기화
t_rb ringbuffer = {0, 0, };		// tail = 0, head = 0
uint8_t AutoSend_flag=0;		// global variable
uint8_t chat_close=1;

// Ethernet =>> Serial
uint8_t EtherToSerial(uint8_t* buf,uint16_t size)
{
	uint8_t i;
	for(i=0 ; i < size ; i++){
		putchar(buf[i]);	//uart 출력
	}
	putchar('\r');
	putchar('\n');
	return 0 ;
}
// Serial => Ethernet
uint8_t SerialToEther(uint8_t sn)
{
	//finder함수를 이용해 UART로 들어온 데이터 중에 LF(linefeed) 찾아 LF가 저장된 인덱스를 len 변수에 저장한다. 
	//len 변수가 UART로 받은 데이터의 사이즈 이다.
	int len = finder(&ringbuffer, LF);
	//int err;
	int i;
	//len이 0보다 크다면
	if(len > 0){
		//링버퍼에 저장된 데이터를 len 만큼 읽어 SERIAL_BUF에 저장한다.
		for(i=0 ; i < size ; i++){
			SERIAL_BUF[i] = rb_get(&ringbuffer);
		}
		//send 함수를 이용해 SERIAL_BUF의 데이터를 TCP로 전송한다.
		send(sn, SERIAL_BUF, len);
		return len; 	// data send to len
	}
	//len이 0보다 크지 않다면
	else{
		if(AutoSend_flag == 1){
			for(i=0 ; i < SERIAL_BUF_SIZE ; i++){
				SERIAL_BUF[i] = rb_get(&ringbuffer);
			}
			send(sn, SERIAL_BUF, SERIAL_BUF_SIZE);
			AutoSend_flag = 0;
			return SERIAL_BUF_SIZE;		// data send to max size
		}
		else{
			//UART로 '@'입력되면 disconnect 를 진행한다.
			if(chat_close == 1){
				int len2 = finder(&ringbuffer, '@'); // find '@'
				if(len2 > 0){
					for(i=0 ; i < len2 ; i++){
						SERIAL_BUF[i] = rb_get(&ringbuffer);	// send to buffer to result
					}
					send(sn, SERIAL_BUF, len2);		// send data
					disconnect(sn);		// disconnect
					return len2;
				}
			}
		}
	}
	return 0 ;
}

//UART interrupt function
void UART_IRQHandler(void)
{
	
	if((Chip_UART_ReadLineStatus(LPC_USART) & UART_LSR_RDR) == 1){
		//UART로 받은 1byte를 rb_put함수로 전달한다.
		if(rb_put(&ringbuffer, Chip_UART_ReadByte(LPC_USART)) == 1){
			//printf("Return1 data output%s");
		}
		else{
			AutoSend_flag = 1;
			//printf("Return0 data no output%s");
		}
	}
}

int main(void) {
	uint8_t ip[4]={192,168,0,227};	//디바이스의 IP 설정
	uint16_t port=23;		//포트 설정
	uint8_t sn=SOCK_TCPS0;	//0번 소켓 설정
	uint16_t size=0;		//수신 버퍼 사이즈 초기화

#if defined (__USE_LPCOPEN)
#if !defined(NO_BOARD_LIB)
	// Read clock settings and update SystemCoreClock variable
	SystemCoreClockUpdate();
	// Set up and initialize all required blocks and
	// functions related to the board hardware
	Board_Init();
	// Set the LED to the state of "On"
	Board_LED_Set(0, true);
#endif
#endif
	SPI_Init();		//SPI 초기화
	W5500_Init();	//W5500 초기화
	Net_Conf();
	// Enable UART interrupt
    Chip_UART_IntEnable(LPC_USART, (UART_IER_RBRINT | UART_IER_RLSINT));
    // Start UART interrupt
    NVIC_EnableIRQ(UART0_IRQn);
	while(1) {
		//W5500의 Sn_SR 레지스터를 읽어서 W5500의 상태를 확인한다. W5500의 상태에 따라 동작 형태가 결정된다.
		switch(getSn_SR(sn)) {
			//W5500dl close 모드 라면 소켓을 생성한다.
			case SOCK_CLOSED:
		        /* Socket Closed State */
				//소켓을 생성한다. 소켓함수 전달 인자는 ("소켓 번호, 통시 모드, 포트 번호, 플래그)" 이다.
				if(socket(sn, Sn_MR_TCP, port, 0x00)==sn){
					printf("%d:Socket Opened\r\n",sn);
				}
				else{
					printf("%d:Socket Error\r\n",sn);
					while(1);
				}
		        break;
			//소켓생성이 완료 되면, W5500 init 모드가 되며, init 모드 일 때 W5500이 server 모드로 동작할지 client 모드로 동작할지 정하게 된다.
		    case SOCK_INIT:
		        /* TCP Socket Creatation */
		    	printf("%d:Connecting, ip[%d.%d.%d.%d] port [%d]\r\n", sn, ip[0], ip[1], ip[2], ip[3], port);
		    	//connect 함수를 사용해서 TCP서버로 접속을 시도 한다. connect 함수의 전달 인자는 "(서버의 소켓번호, 서버의 IP, 서버의 포트번호)" 이다.
				//connect 함수를 사용하게 되면 W5500은 client 모드가 되고, listen 함수를 사용하게 되면 W5500은 server 모드가 된다.
		    	if(connect(sn,ip,port)==SOCK_OK);
		    	else {
		    		printf("%d:Connect Error\r\n",sn);
		    		while(1);
		    	}
		        break;
			//listen 모드는 W5500이 server로 동작할때 이고, client의 접속을 기다리고 있을 상태이다.
		    case SOCK_LISTEN:
		        /* TCP Server Mode */

		        break;
			//TCP 연결이 완료 됬을 때, established 모드가 된다. established 일때 server와 client사이에 데이터 전송이 가능하다.
		    case SOCK_ESTABLISHED:
		        /* TCP ESTABLISHED */
		    	//W5500의 Sn_IR 레지스터를 읽어 connect 인터럽트를 확인한다.  established 모드로 변경됬을 때 한번 인터럽트가 발생한다.
		    	if(getSn_IR(sn) & Sn_IR_CON) {
		    		printf("%d:Connected\r\n",sn);
		    		setSn_IR(sn,Sn_IR_CON);		//Sn_IR 레지스터의 connect 인터럽트를 초기화 한다.
		    	    Board_LED_Set(0, false);
		    	    Board_LED_Set(1, true);
		    	}
		    	//Sn_RX_RSR 레지스터를 읽어 W5500의 RX 버퍼에 저장된 데이터 사이즈를 읽어 size 변수(수신할 사이즈)에 저장한다. 
		    	if((size = getSn_RX_RSR(sn)) > 0) {
		    		if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE;	//수신할 사이즈가 DATA_BUF_SIZE 보다 크다면, 수신할 사이즈를 DATA_BUF_SIZE로 맞쳐 준다. 
		    	    recv(sn,gDATABUF,size);		//recv 함수를 이용해 TCP 데이터를 수신한다.
		    	    EtherToSerial(gDATABUF, size);	//수신한 TCP 데이터를 UART로 전송한다.
		    	}
		    	SerialToEther(sn);	//수시한 UART 데이터를 TCP로 전송한다.
		        break;

		    case SOCK_CLOSE_WAIT:
		        /* Disconnect request */

		        break;
		}
    }
    return 0 ;
}

이제 위 소스로 동작하는 것을 보여 주겠다.

< TCP Server ==>> W5500-EVB ==>> PC Serial Terminal  >


< PC Serial Terminal ==>> W5500-EVB ==>> TCP Server >

이 경우에는 주위 할것이 있다. 'LF(Linefeed)'로 데이터의 마지막을 체크하기 때문에, 전송하는 데이터의 마지막에 LF를 삽입하도록 한다.

드디어 마무리를 했다. 소스는 한달전에 완성이 되었는데 좀 정리가 되지 않아 이제서야 블로그에 글을 남긴다. 이번 프로젝트를 하면서 W5500의 사용 법이 참 쉽다는 것을 다시 알게 되었고, 좀더 열심히 프로그램밍 공부를 해야겠다는 생각을 했다.

다음으로 진행할 것은 W5500의 드라이버 코드를 분석하는 것이다. 이것도 최대한 빨리 해야할텐데....

ring_buffer.c

ring_buffer.h

W5500-EVB.c


+ Recent posts