<목차>

1. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(1)

2. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(2)

3. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(3)

4. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(4)

5. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(5)

6. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(6)

7. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(7)

8. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(8)

 

 

5. 포팅하기 - W5500   I/O control function 구현하기

 

 

W5500에 초기화에 앞서 Debugging및 Logging을 위한 printf() 함수를 먼저 등록해본다.

 

일반적으로 printf() & scanf() 함수를 사용하기 위해서는 getchar() & putchar() 함수를 Target System에 맞게 구현한 후 overriding 해주어야 한다. 하지만, Simplicitity Studio IDE 는 단순히 stdio.h 파일만 include해주면 된다.(아마 getchar()& putchar()가 이미 8051 에 맞게 수정되어 있을 것이다)

 

ioLibaray_W5500_main.c 에서

#include 부에 아래와 같이 #include <stdio.h>를 추가한다.

 

#include <compiler_defs.h>
#include <SI_C8051F380_Register_Enums.h>                // SFR declarations
#include <stdio.h> 

 

main() 함수에 printf() 문을 다음과 같이 추가한다.

 
int main (void)
{
	Init_Device();
	
	printf("Welcome to W5500 ioLibrary Porting Guide for 8051 Users\r\n");

	while (1) {}                             // Spin forever

	// NOTREACHED

	return 0;
}

 

 

- C8051F380의 SPI 사용하기

W5500 을 제어하기 위해서 C8051F380의 SPI read/write 함수가 필요하다. Datasheet를 보면서 직접 구현해보는 것도 좋지만, Simplicity Studio에서 SPI 사용에 대한 Example을 제공한다.

 

 

Copy된 Project를 보면 SPI를 어떻게 사용하는지 알수 있다.

일반적으로 UART나 I2C, SPI 같은 Serial 통신 Peripheral은 Data 레지스터와 Status 레지스터들의 단순 제어로 쉽게 통신할 수 있다.

C8051F380의 SPI0은 Data 레지스터 SPI0DAT, Status 레지스터 SPI0CN_SPIF 두개로 제어된다. 또한 /SCS 제어를 위해 SPI0CN_NSSMD0 레지스터를 제공한다.

다음 코드는 SPI 인터페이를 이용한 EEPROM read/write example Code이다. 이를 참조하여 W5500 제어용 SPI read/write 함수를 작성할 것이다.

 

U8 EEPROM_Read (U16 address)
{
   // Reading a byte from the EEPROM is a three-step operation.

   // Step1: Send the READ command
   SPI0CN_NSSMD0   = 0;                 // Activate Slave Select
   SPI0DAT  = EEPROM_CMD_READ;
   while (!SPI0CN_SPIF);
   SPI0CN_SPIF     = 0;

   // Step2: Send the EEPROM source address (MSB first)
   SPI0DAT = (U8)((address >> 8) & 0x00FF);
   while (!SPI0CN_SPIF);
   SPI0CN_SPIF     = 0;
   SPI0DAT = (U8)(address & 0x00FF);
   while (!SPI0CN_SPIF);
   SPI0CN_SPIF     = 0;

   // Step3: Read the value returned
   SPI0DAT  = 0;                       // Dummy write to output serial clock
   while (!SPI0CN_SPIF);               // Wait for the value to be read
   SPI0CN_SPIF     = 0;
   SPI0CN_NSSMD0   = 1;                // Deactivate Slave Select
   Delay_us (1);

   return SPI0DAT;
}

 

- ioLibrary Todo List

자 본격적으로 W5500을 제어하기 위해 ioLibrary를 어떻게 사용해야 하는지 알아보자.

앞서 다운로드 받은 ioLibrary 의 Doxygen 문서 SOCKET_APIs.chm에서 Todo List 항목을 살펴보자.

 

 

Todo List는 ioLibrary를 WIZnet가 공급하는 Chip 들중은 원하는 칩 즉 W5500으로 설정하기 위한 각종 정의 및 정보들을 설정하고  칩 제어에 필요한 io call-back funtion들을 등록 사용하는 방법을설명하고 있다. 아래와 같이 wizchip_conf.h 파일에서 하나씩 W5500으로 변경해보자.

 


 1. WIZnet Chip 선택

    _WIZCHIP_ 을 W5500 으로 선택한다.

    #define _WIZCHIP_ 5500 

 

2. WIZnet Chip의 IO BASE 선택

    IO BASE는 Parallel Bus를 사용할 경우에만 정의한다. SPI mode를 사용하므로 무시.

 

3. WIZnet Chip IO mode 선택

    W5500은 SPI mode 두가지 Variable Data Mode(VDM)와 Fixed Data Mode(FDM)을 지원한다. 여기서는 SCS

   를 사용하는 VDM을 선택한다.

     #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_VDM_

 

4. WIZnet Chip IO control call-back function 등록

    사용자 구현 call-back 함수를 등록하지 않을 경우 ioLibrary가 정의한 Default 함수가 동작한다.

 

    4.1.   reg_wizchip_bus_cbfunc(uint8_t(*bus_rb)(uint32_t addr),

                             void(*bus_wb)(uint32_t addr, uint8_t wb))

           _WIZCHIP_IO_MODE_가 Bus Mode (_WIZCHIP_IO_MODE_BUS_ ,

         _WIZCHIP_IO_MODE_BUS_DIR_ _WIZCHIP_IO_MODE_BUS_INDIR_)로 정의 되었을 때

          사용자가 구현한 Read/Write 함수 등록. Bus mode를 사용하지 않으므로 무시

    4.2.  reg_wizchip_cris_cbfunc(void(*cris_en)(void), void(*cris_ex)(void))

          이 함수는 WIZnet Chip  I/O 제어 시 interrupt 등으로 부터 간섭을 피하기 위한 Critical session 시작/끝을

          알려 주는 함수를 등록한다. 여기서는 Interrupt를 사용하지 않기 때문에 무시한다.

          필요하다면 Intterupt disable(EA=0) enable(EA=0) 등으로 구현한다.

    4.3 .  reg_wizchip_cs_cbfunc(void(*cs_sel)(void), void(*cs_desel)(void))

          WIZnet Chip Select control call-back function 등록함수이다.

          W5500은 SPI를 사용하므로 SCS 제어 함수를 등록한다.

    4.4. reg_wizchip_spi_cbfunc(uint8_t(*spi_rb)(void), void(*spi_wb)(uint8_t wb))

           WIZnet Chip SPI 제어 Call-back 함수를 등록한다. W5500은 SPI를 사용하므로 반드시 사용자 구현 SPI

           read/write call-back 함수를 등록해야 한다.

 

- W5500 제어를 위한 Call-back 함수 구현

wizchip_conf.h 에서 W5500을 위한 define을 마무리한 후,

iolibrary_W5500_main.c에 W5500 제어를 위한 I/O call-back function 을 구현하자. Todo List에 설명한 것처럼 SCS 신호 제어와 SPI byte read/write 함수가 필요하다.

 

void wizchip_select(void)
{
	SPI0CN_NSSMD0   = 0;
}

void wizchip_deselect(void)
{
	SPI0CN_NSSMD0   = 1;
}

void SPI0_WriteByte(uint8_t dat)
{
	SPI0DAT  = dat;
	while (!SPI0CN_SPIF);
	SPI0CN_SPIF     = 0;
}

uint8_t SPI0_ReadByte(void)
{
	SPI0DAT  = 0;
	while (!SPI0CN_SPIF);
	SPI0CN_SPIF     = 0;
	return SPI0DAT;
}

 

- C8051F380 GPIO를 이용한 W5500 Reset 제어 함수 구현

W5500 Reset Signal은 C8051F380 GPIO P1_0과 연결되어 있다. 따라서 P1_0를 WIZNET_RESET_PIN으로 재정의하고 이를 제어하여 W5500 Hardware reset 함수를 구현하자.

 

ioLibaray_W5500_main.c 정의 부분에 다음과 같이 PIN 정의를 추가한다.

 

SBIT(WIZCHIP_RESET_PIN, SFR_P1, 0);

 

ioLibarary_W5500_main.c 에서 다음과 같이 void WIZChip_HW_Reset(void)를 구현한다

 

void WIZChip_HW_Reset(void)
{
	volatile uint16_t i;
	WIZCHIP_RESET_PIN = 0;
	for(i = 0; i < 0xFFFF; i++); // 20ms@48MHz
	WIZCHIP_RESET_PIN = 1;
	for(i = 0; i < 0xFFFF; i++); // 20ms@48MHz
	for(i = 0; i < 0xFFFF; i++); // 20ms@48MHz

}

 

- W5500 Reset 및 Call-back 함수 등록하기

reg_wizchip_xxx_cbfucn() 함수를 이용하여 앞서 구현된 Call-back fucntion을 등록하자.

ioLibrary_W5500_main.cmain()함수에 다음과 같이 구현한다.

 

int main (void)
{
	Init_Device();

	printf("Welcome to W5500 ioLibrary Porting Guide for 8051 Users\r\n");

	WIZChip_HW_Reset();

	printf("W5500 : Registeration Callback func\r\n");
	//reg_wizchip_cris_cbfucn(0,0); // No-use in example
	reg_wizchip_cs_cbfunc(wizchip_select,wizchip_deselect);
	reg_wizchip_spi_cbfunc(SPI0_ReadByte,SPI0_WriteByte);

	while (1) {}                             // Spin forever

	// NOTREACHED

	return 0;
}

여기서 Critical Session call-back funtion은 구현할 필요가 없기 때문에 따로 등록하지 않는다. 필요하다면 Critical Session call-back 함수를 구현하여 다음 처럼 등록하여 사용할 수 있다.

 

void W5500_Critical_Session_Enter(void) {  EA = 0; }

void W5500_Critical_Session_Exit(void)  { EA = 1; }

int main (void)
{
                // ...
	reg_wizchip_cris_cbfucn(W5500_Critical_Session_Enter,W5500_Critical_Session_Exit); 
	reg_wizchip_cs_cbfunc(wizchip_select,wizchip_deselect);
                // ...
	return 0;
}

 

- W5500 Access 검증하기

상기와 같이 ioLibrary  포팅이 완료되었다. 실제로 제대로 동작하는지 확인 필요하다.

Call-back으로 등록된 함수를 이용하여 W5500의 기본 레지스터등을 Access해보고 제대로 동작하는지 확인해 보자.

 

1. Read Test (Version 레지스터 읽기)

    VERSIONR은 Read Only 레지스터로 항상 0x04 값을 가진다.

    getVERSIONR() 이용

2. Write Test (RCR & RTR 레지스터 쓰고 읽기)

    RCR은 초기 0x08, RTR 0x07D0 값을 가진다. 먼저 읽어서 초기값이 맞는지 한번더 확인하고, 원하는

   값을  설정하여 설정값이 제대로 읽히는지 확인한다.

    setRCR() & setRTR() / getRCR() & getRTR() 이용

 

int main (void)
{
	Init_Device();

	printf("Welcome to W5500 ioLibrary Porting Guide for 8051 Users\r\n");

	WIZChip_HW_Reset();

	printf("WIZCHP TEST : Registeration Callback func\r\n");
	//reg_wizchip_cris_cbfucn(0,0); // No-use in example
	reg_wizchip_cs_cbfunc(wizchip_select,wizchip_deselect);
	reg_wizchip_spi_cbfunc(SPI0_ReadByte,SPI0_WriteByte);


	/* W5500 Access Test */
	// This code should be commented after testing W5500 access //
	if(getVERSIONR() != 0x04)
	{
		printf("ACCESS ERR: VERSIONR != 0x04, Read value=%02bx\r\n", getVERSIONR());
		while(1);
	}

	if(getRCR() != 0x08)
	{
		printf("ACCESS ERR: RCR != 0x08, Read value=%02bx\r\n",getRCR());
		while(1);
	}
	if(getRTR() != 0x07D0)
	{
		printf("ACCESS ERR: RTR != 0x07D0, Read value=%04x\r\n", getRTR());
		while(1);
	}

	setRCR(0xA5);
	setRTR(0x5A5A);
	if(getRCR() != 0xA5)
	{
		printf("ACCESS ERR: RCR != 0xA5, Read value=%02bx\r\n",getRCR());
		while(1);
	}
	if(getRTR() != 0x5A5A)
	{
		printf("ACCESS ERR: RTR != 0x5A5A, Read value=%04x\r\n", getRTR());
		while(1);
	}
	/////////////////////////////////////////////////////////////////////

	while (1) {}                             // Spin forever

	// NOTREACHED

	return 0;
}

 

Flash Programming & Debugging & Testing

자 이제 컴파일된 프로그램을 실제 보드로 다운로드하고 구현한대로 동작하는지 확인하자.

우선 보드를 다음과 연결하자.

 

 

연결을 마친 후, Hercules (Serial과 TCP/UDP 통신을 지원하는 프리웨어)를 실행시킨 후 다음 순서처럼 Serial Port를 Open한다.

 

 

Flash Image를 다운로드하고 Test 하기 위해서 다음 그림의 3까지 수행한다. 그럼 main() 함수내에 Init_Device() 전에 Break 되어 있다.

이제 4를 눌러 Hercules 창에 문자열이 출력되는지 확인하자. 자~~아  4를 꾸~~~욱 누른다.

 

 

기대와 전혀 다르게 아무런 반응이 없다. ㅠㅠ; 무슨일이 벌어지고 있는걸까?

나와야되는 시리얼 출력은 나오지 않고 당황스럽게도 MCU가 뭘하고 있는지 아무런 반응이 없다. (멘붕)

뭘 잘못한 건지 알수 없다.

 

자 당황하지 말고 Debugging Tool을 적극 활용해보자. Running 중인 MCU를 Break 한다. 어라 main() 어딘가에서 Break가 걸릴 거라 기대했는데, 전혀 엉뚱한 SILABS_STARTUP.A51 이란 startup code의 XDATA 영역 초기화 루틴을 맴돌고 있다. (또 다시 맨붕...)

 

자 잘 생각해보자. Startup code가 수행되고 있다는 것은 Reset이 걸렸다는 얘기. Watch-dog Reset이 의심스럽다. 조심히 데이타시트를 살펴보자.

 

 

역시 내 생각이 맞았다. Reset 타임에 Watchdog Timer가 Enable은 된다고 명시 되어있다. 이를 사용하지 않기 위해선 반드시 Explicitly disabled 해야된다. Watchdog Timer는 PCA module4를 이용하여 동작한다.

Configuration Wizard2 의 [ Peripheral >> PCA ]를 살펴보자.

 

 

나의 경험상 Init_Device() 함수에서 Watchdog disable Code를 넣는 것보단 Start-up code에 넣어두는 것이 원천적 으로 안전하다. SILABS_STARTUP.A51 파일에서 main 함수 Jump 전에 다음과 같이 코드를 삽입한다.

 

; Standard SFR Symbols
ACC     DATA    0E0H
B       DATA    0F0H
SP      DATA    81H
DPL     DATA    82H
DPH     DATA    83H
PCA0MD  DATA    0D9H

                NAME    ?C_STARTUP


?C_C51STARTUP   SEGMENT   CODE
?STACK                SEGMENT   IDATA

                           RSEG    ?STACK
                           DS      1

                           EXTRN CODE (?C_START)
                           PUBLIC  ?C_STARTUP

              CSEG    AT      0
?C_STARTUP:       LJMP    STARTUP1

              RSEG    ?C_C51STARTUP

STARTUP1:
    MOV     PCA0MD, #00H

$IF (SILABS_STARTUP = 1)
EXTRN CODE (SiLabs_Startup)
                LCALL  SiLabs_Startup
$ENDIF 

 

자 이제 기대를 품고 컴파일한 후 테스트 해보자. 헐~~ 역시 출력이 되지 않는다. 하지만 당황하지 말자. 우리에게는 강력한 Debugging Tool이 있다. 다시 한번 MCU를 Break를 해보자.

 

 

아까랑 다르게 Startup code에서 맴돌지 않는다. Watchdog Reset 문제는 해결되었다. 자 그럼 왜 Serial 출력이 되지 않는 걸까? Break된 Code를 살펴보자. 0x3FFF 주소에서 SCON.1 값이 1이 될때까지 무한 루프를 돌고 있다. 이것은 Serial 출력이 가능한 상태가 될때까지 기다리것이다. SCON.1 값이 Reset 시 1이 아닌것으로  판단 강제로 다음과 같이 1로 만들어준다. (일반적으로 reset시 TX  Flag  TI 는 1이지만 뭐... 쩝) 

ioLibrary_W5500_main.c에서 UART_Init()SCON0_T1 = 1 를 추가한다.

void UART_Init()
{
    SCON0     = 0x10;
    SCON0_TI = 1;
}

 

자 다시 고고싱 ~~~

드뎌 다음과 같이 시리얼이 출력된다.고생한 만큼 기쁨도 두배.

 

 

그러나 기쁨도 잠시. W5500 VERSIONR 레지스터 값을 제대로 읽어 오지 못한다. SPI Interface에 문제가 있나 보다.  다음시간에 계속 Debugging 해보자. 

 

 

< PREV                                                                                                           NEXT >

블로그 이미지

MidnightCow

위즈네트 칩(W5300, W5200, W7100, W7500) 개발자

,