카테고리 보관물: Tutorials

STM32 MCU 보드에서 W5500 사용하기 (4부 DHCP)

STM32 MCU에서 W5500을 이용한 DHCP 사용하기

이전에 포스팅한 “STM32 MCU 보드에서 W5500 사용하기”를 참조하여, 새로운 프로젝트를 생성한 후 W5500 Driver를 포팅 한다. 개발 보드 또한 이전 포스팅에서 사용한 보드를 그대로 사용하여 진행하였다.
STM32 MCU 보드에서 W5500 사용하기 (1부)
STM32 MCU 보드에서 W5500 사용하기 (2부)

아래 사이트에서 DHCP 소스 코드를 다운로드 받는다.
http://wizwiki.net/wiki/doku.php?id=products:w5500:driver
dhcp_0

소스 코드는 zip으로 압축된 형태로 배포된다. 압축을 풀면 Internet이라는 디렉토리가 생기며, 이 디렉토리 내의 DHCP 디렉토리에 DHCP 프로토콜 구현 파일들이 존재한다. Internet/DHCP 디렉토리를 새로 생성한 프로젝트 디렉토리 내의 src 디렉토리로 복사한다.

$ unzip iolibrary_bsd_internet_v111.zip
$ rm -rf Internet/DNS
$ mv Internet ~/workspace/w5500_dhcp/src

Eclipse Project Explorer에서 생성한 프로젝트를 선택한 후 “F5″를 눌러 refresh를 하면, 복사한 Internet 디렉토리를 Project Explorer에서 확인할 수있다.
dhcp_1

src/Internet 디렉토리를 Include Path에 등록한다. (STM32 MCU 보드에서 W5500 사용하기 2부 참조)

DHCP 프로토콜을 사용하기 위한 프로시져는 다음과 같다.

  1. DHCP 초기화 함수 호출 (DHCP_init)
  2. Call back 함수 등록
  3. DHCP Timer 함수를 1초마다 호출되도록 타이머에 등록 (DHCP_time_handler)
  4. 메인 루프문에서 DHCP 핸들러 호출 (DHCP_run)

DHCP 초기화 함수 원형은 다음과 같다.

void DHCP_init(uint8_t s, uint8_t * buf)    //s:socket number, buf:buffer

DHCP 프로토콜을 사용하기 위해서는 다음의 Call back 함수를 등록해야 한다.

  • void (*dhcp_ip_assign)(void) : DHCP 서버로부터 처음 IP를 할당 받을 때 호출되는 핸들러
  • void (*dhcp_ip_update)(void) : DHCP 서버로부터 IP가 업데이트 될 때 호출되는 핸들러
  • void (*dhcp_ip_update)(void) : DHCP 서버로부터 IP 충돌이 될 때 호출되는 핸들러

위의 Call back 함수를 등록해 주는 함수는 다음과 같다.

  • void reg_dhcp_cbfunc(void(ip_assign)(void), void(ip_update)(void), void(*ip_conflict)(void));

실제 코드를 수정을 진행한다. 중요 내용들만 설명하며, 전체 프로젝트 파일은 별로도 첨부하였으니 참고하기 바란다.
Timer 기능을 사용하기 위해 mcu_handler.c 파일내의 RCC_Configuration(void) 함수를 다음과 같이 수정한다.

void RCC_Configuration(void)
{
    /* TIM2 Enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    /* SPI2 Clock Enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

    /* GPIOA, GPIOB, UART1 Clock Enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_USART1, ENABLE);
}

mcu_handler.c 파일에 Timer 초기화 함수 및 인터럽트 핸들러를 구현한다. DHCP_time_handler() 함수는 1초에 한번씩 호출해야만 한다.

static uint32_t mill_sec = 0;

void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

        mill_sec++;

        if((mill_sec % 1000) == 0) {
            mill_sec = 0;
            DHCP_time_handler();
        }
    }
}

void Timer_Configuration(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* Enable the TIM2 global Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

    /* Time base configuration */
    TIM_TimeBaseStructure.TIM_Period = 1000;
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    /* Prescaler configuration */
    TIM_PrescalerConfig(TIM2, 71, TIM_PSCReloadMode_Immediate);

    /* TIM enable counter */
    TIM_Cmd(TIM2, ENABLE);

    /* TIM IT enable */
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}

기존에 구현된 Net_Conf 함수를 다음과 같이 수정한다. (w5500_init.c)

void Net_Conf(wiz_NetInfo *wiznet_info)
{
    ctlnetwork(CN_SET_NETINFO, (void*) wiznet_info);

    display_Net_Info();
}

main.c 파일내에 DHCP 관련 헤더 정보와 변수들을 선언한다.

#define SOCK_DHCP           0
#define MY_MAX_DHCP_RETRY   3

#define DATA_BUF_SIZE   2048
uint8_t gDATABUF[DATA_BUF_SIZE];
wiz_NetInfo gWIZNETINFO = { .mac = {0x11, 0x22, 0x33,0x44, 0x55, 0x66},
                            .ip = {192, 168, 11, 50},
                            .sn = {255,255,255,0},
                            .gw = {192, 168, 11, 1},
                            .dns = {0,0,0,0},
                            .dhcp = NETINFO_DHCP };

main.c 파일내에 DHCP Callback 함수를 구현한다.

void my_ip_assign(void)
{
   getIPfromDHCP(gWIZNETINFO.ip);
   getGWfromDHCP(gWIZNETINFO.gw);
   getSNfromDHCP(gWIZNETINFO.sn);
   getDNSfromDHCP(gWIZNETINFO.dns);
   gWIZNETINFO.dhcp = NETINFO_DHCP;

   /* Network initialization */
   Net_Conf(&gWIZNETINFO);
   myprintf("DHCP LEASED TIME : %ld Sec.\r\n", getDHCPLeasetime());
}

void my_ip_conflict(void)
{
    myprintf("CONFLICT IP from DHCP\r\n");

   //halt or reset or any...
   while(1); // this example is halt.
}

main 함수를 다음과 같이 수정한다.

int main(int argc, char* argv[])
{
    uint8_t my_dhcp_retry = 0;

    RCC_Configuration();
    USART1_Configuration();
    W5500_SPI_Init();
    W5500_Init();
    Timer_Configuration();

    DHCP_init(SOCK_DHCP, gDATABUF);
    reg_dhcp_cbfunc(my_ip_assign, my_ip_assign, my_ip_conflict);

    while(1)
    {
        switch(DHCP_run())
        {
            case DHCP_IP_ASSIGN:
            case DHCP_IP_CHANGED:
                /* If this block empty, act with default_ip_assign & default_ip_update */
                //
                // This example calls my_ip_assign in the two case.
                //
                // Add to ...
                //
                break;
            case DHCP_IP_LEASED:
                //
                // TO DO YOUR NETWORK APPs.
                //
                break;
            case DHCP_FAILED:
                /* ===== Example pseudo code =====  */
                // The below code can be replaced your code or omitted.
                // if omitted, retry to process DHCP
                my_dhcp_retry++;
                if(my_dhcp_retry > MY_MAX_DHCP_RETRY)
                {
                    my_dhcp_retry = 0;
                    DHCP_stop();      // if restart, recall DHCP_init()
                    Net_Conf(&gWIZNETINFO);
                }
                break;
            default:
                break;
        }

    }
}

프로젝트를 새롭게 빌드하여 Firmware 바이너리 이미지를 생성한 후, 개발보드에 Fusing 한다. 시리얼 터미널을 연결한 후, 정상적으로 DHCP 서버에서 IP 할당을 받으면 다음의 메시지를 볼 수 있다. dhcp_2

프로젝트 파일 : 다운로드