위즈네트 아카데미

TUTORIAL

튜토리얼

Home  > 튜토리얼
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

 

 

 

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

[sourcecode language="plain"]
int main (void)
{
Init_Device();

printf("Welcome to W5500 ioLibrary Porting Guide for 8051 Usersrn");

while (1) {} // Spin forever

// NOTREACHED

return 0;
}
[/sourcecode]

 

- C8051F380의 SPI 사용하기

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

ex_spi_copy

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 함수를 작성하겠습니다.

[sourcecode language="plain"]
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;
}
[/sourcecode]

 

- ioLibrary Todo List

본격적으로 W5500을 제어하기 위해 ioLibrary를 어떻게 사용해야 하는지 알아보겠습니다. 앞서 다운로드 받은 ioLibrary 의 Doxygen 문서 SOCKET_APIs.chm에서 Todo List 항목을 살펴보겠습니다.

socket_api

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 함수가 필요합니다.

[sourcecode language="plain"]
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;
}
[/sourcecode]

 

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

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

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

[sourcecode language="plain"]
SBIT(WIZCHIP_RESET_PIN, SFR_P1, 0);
[/sourcecode]

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

[sourcecode language="plain"]
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

}
[/sourcecode]

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

reg_wizchip_xxx_cbfucn() 함수를 이용하여 앞서 구현된 Call-back fucntion을 등록합니다. ioLibrary_W5500_main.c의 main()함수에 다음과 같이 구현합니다.

[sourcecode language="plain"]
int main (void)
{
Init_Device();

printf("Welcome to W5500 ioLibrary Porting Guide for 8051 Usersrn");

WIZChip_HW_Reset();

printf("W5500 : Registeration Callback funcrn");
//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;
}
[/sourcecode]

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

[sourcecode language="plain"]
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;
}
[/sourcecode]

 

- 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() 이용

[sourcecode language="plain"]
int main (void)
{
Init_Device();

printf("Welcome to W5500 ioLibrary Porting Guide for 8051 Usersrn");

WIZChip_HW_Reset();

printf("WIZCHP TEST : Registeration Callback funcrn");
//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=%02bxrn", getVERSIONR());
while(1);
}

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

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

while (1) {} // Spin forever

// NOTREACHED

return 0;
}
[/sourcecode]

- Flash Programming & Debugging & Testing
자 이제 컴파일된 프로그램을 실제 보드로 다운로드하고 구현한대로 동작하는지 확인하겠습니다.
우선 보드를 다음과 연결합니다.

Board_Powered

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

Hercules

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

이제 4를 눌러 Hercules 창에 문자열이 출력되는지 확인합니다.

 

Debug_download

기대와 전혀 다르게 아무런 반응이 없습니다.  당황하지 말고 Debugging Tool을 적극 활용합니다.

Running 중인 MCU를 Break 합니다. main() 어딘가에서 Break가 걸릴 거라 기대했는데, 전혀 엉뚱한 SILABS_STARTUP.A51 이란 startup code의 XDATA 영역 초기화 루틴을 맴돌고 있습니다.  (또 다시 맨붕...)

Startup code가 수행되고 있다는 것은 Reset이 걸렸다는 얘기. Watch-dog Reset이 의심되어 데이타시트를 살펴봅니다.

watchdog_doc

Reset 타임에 Watchdog Timer가 Enable은 된다고 명시 되어 있습니다. 이를 사용하지 않기 위해선 반드시 Explicitly disabled 해야됩니다. Watchdog Timer는 PCA module4를 이용하여 동작합니다.

Configuration Wizard2 의 [ Peripheral >> PCA ]를 살펴보겠습니다.

watchdog_code

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    0D9HNAME    ?C_STARTUP
?C_C51STARTUP   SEGMENT   CODE
?STACK                SEGMENT   IDATARSEG    ?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 합니다.

serial_code

아까랑 다르게 Startup code에서 돌지 않습니다. Watchdog Reset 문제는 해결되었는데 왜 Serial 출력이 되지 않는지 살펴보겠습니다. Break된 Code를 보면, 0x3FFF 주소에서 SCON.1 값이 1이 될때까지 무한 루프를 돌고 있습니다. 이것은 Serial 출력이 가능한 상태가 될때까지 것으로, SCON.1 값이 Reset 시 1이 아닌것으로  판단 강제로 다음과 같이 1로 만들어줍니다.

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

[sourcecode language="plain"]
void UART_Init()
{
SCON0 = 0x10;
SCON0_TI = 1;
}
[/sourcecode]

이제 아래와 같이 출력되는 것을 확인하실 수 있습니다.

serial_ok


========================================================================================

W5500 ioLibrary_BSD 를 Silab 8051에 포팅하기 관련 글
1.W5500 ioLibrary_BSD를 SiLab 8051 에 포팅하기 – 환경설정
2.W5500 ioLibrary_BSD를 SiLab 8051 에 포팅하기 – 테스트보드 만들기
3. W5500 ioLibrary_BSD를 SiLab 8051 에 포팅하기 – ioLibrary Download
4. W5500 ioLibrary_BSD를 SiLab 8051 에 포팅하기 – Project Build
5. W5500 ioLibrary_BSD를 SiLab 8051 에 포팅하기 – C8051F380 설정하기
6. W5500 ioLibrary_BSD를 SiLab 8051 에 포팅하기-W5500 I/O control function 구현하기
7. W5500 ioLibrary_BSD를 Silab8051에 포팅하기-W5500 초기화
8. W5500 ioLibrary_BSD를 SiLab 8051 에 포팅하기 – Loopback (Echo Server) 구현하기

☞ 출처 : 밤소의 IoT 잠못드는 밤