Hardware/STM32

[STM32F746G-DISCO] TCP & UDP echo server

rudals.kim 2024. 7. 22. 10:45
반응형
아래 테스트는 STM32CubeIDE 1.7.0/STM32Cube_FW_F7_V1.16.1를 사용하여 테스트 되었습니다.


STM32F746G-DISCO 보드용으로 제공되는 lwIP 예제가 적어서 STM32756G_EVAL에서 제공되는 예제를 살펴보니 다양한 lwIP 예제가 제공되고 있었고 이를 참고하여 TCP & UDP echo server 테스트를 해 보았습니다.
(참고 예제 : STM32Cube_FW_F7_V1.16.1/Projects/STM32756G_EVAL/Applications/LwIP/LwIP_UDPTCP_Echo_Server_Netconn_RTOS)

새 프로젝트를 생성 후 DMA2D/ETH/FMC/FREERTOS/GPIO/LTDC/LWIP/NVIC/RCC/SYS/USART1을 설정하였습니다.

ETH 설정은 아래와 같이 기본값을 적용하였습니다.

lwIP의 대부분의 값은 기본값을 사용하였으며 테스트에 불필요한 HTTPD/SNMP/SNTP/SMTP/MDNS/TFTP 기능은 disable로 설정하였습니다.

FREERTOS는 CMSIS_V1 인터페이스의 기본 설정을 사용하였습니다.

FREERTOS의 기본 thread의 StartDefaultTask는 아래와 같이 적용하였습니다.

void StartDefaultTask(void const * argument)
{
  /* init code for LWIP */
  //MX_LWIP_Init();
  /* USER CODE BEGIN 5 */
  BSP_Config();

  /* Create tcp_ip stack thread */
  tcpip_init(NULL, NULL);

  /* Initialize the LwIP stack */
  Netif_Config();

  /* Initialize tcp echo server */
  tcpecho_init();

  /* Initialize udp echo server */
  udpecho_init();

  /* Notify user about the network interface config */
  User_notification(&gnetif);

  osThreadDef(DHCP, DHCP_thread, osPriorityBelowNormal, 0, configMINIMAL_STACK_SIZE * 2);
  osThreadCreate (osThread(DHCP), &gnetif);

  /* Infinite loop */
  for(;;) {
  	osDelay(1);
  }
  /* USER CODE END 5 */
}


default thread에서 DHCP thread를 생성하는데 관련 부분은 이전과 마찬가지로 수동 IP 할당하는 부분은 삭제하였고, 동적 IP 할당인 DHCP 관련만 적용하였습니다.

void BSP_Config(void)
{
  BSP_LCD_Init();

  /* Initialize the LCD Layers */
  BSP_LCD_LayerDefaultInit(1, LCD_FB_START_ADDRESS);

  /* Set LCD Foreground Layer  */
  BSP_LCD_SelectLayer(1);

  BSP_LCD_SetFont(&LCD_DEFAULT_FONT);

  /* Initialize LCD Log module */
  LCD_LOG_Init();

  /* Show Header and Footer texts */
  LCD_LOG_SetHeader((uint8_t *)"TCP UDP Server Netconn API");
  LCD_LOG_SetFooter((uint8_t *)"STM32F746G-DISCO board");

  LCD_UsrLog ("  State: Ethernet Initialization ...\n");
}

void Netif_Config(void)
{
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t gw;

  ip_addr_set_zero_ip4(&ipaddr);
  ip_addr_set_zero_ip4(&netmask);
  ip_addr_set_zero_ip4(&gw);

  netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);

  /*  Registers the default network interface. */
  netif_set_default(&gnetif);

  if (netif_is_link_up(&gnetif))
  {
    /* When the netif is fully configured this function must be called.*/
    netif_set_up(&gnetif);
  }
  else
  {
    /* When the netif link is down this function must be called */
    netif_set_down(&gnetif);
  }
}

void User_notification(struct netif *netif)
{
  if (netif_is_up(netif))
  {
    /* Update DHCP state machine */
    DHCP_state = DHCP_START;
  }
  else
  {
    /* Update DHCP state machine */
    DHCP_state = DHCP_LINK_DOWN;
    LCD_UsrLog ("The network cable is not connected \n");
  }
}

void DHCP_thread(void const * argument)
{
  struct netif *netif = (struct netif *) argument;
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t gw;
  struct dhcp *dhcp;
  uint8_t iptxt[20];

  for (;;)
  {
    switch (DHCP_state)
    {
    case DHCP_START:
      {
        ip_addr_set_zero_ip4(&netif->ip_addr);
        ip_addr_set_zero_ip4(&netif->netmask);
        ip_addr_set_zero_ip4(&netif->gw);
        dhcp_start(netif);
        DHCP_state = DHCP_WAIT_ADDRESS;
        LCD_UsrLog ("  State: Looking for DHCP server ...\n");
      }
      break;

    case DHCP_WAIT_ADDRESS:
      {
        if (dhcp_supplied_address(netif))
        {
          DHCP_state = DHCP_ADDRESS_ASSIGNED;

          sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&netif->ip_addr));
          LCD_UsrLog ("IP address assigned by a DHCP server: %s\n", iptxt);
        }
        else
        {
          dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);

          /* DHCP timeout */
          if (dhcp->tries > MAX_DHCP_TRIES)
          {
            DHCP_state = DHCP_TIMEOUT;

            /* Stop DHCP */
            dhcp_stop(netif);

            /* Static address used */
            IP_ADDR4(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 );
            IP_ADDR4(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
            IP_ADDR4(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
            netif_set_addr(netif, ip_2_ip4(&ipaddr), ip_2_ip4(&netmask), ip_2_ip4(&gw));

            sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&netif->ip_addr));
            LCD_UsrLog ("DHCP Timeout !! \n");
            LCD_UsrLog ("Static IP address: %s\n", iptxt);
          }
        }
      }
      break;
  case DHCP_LINK_DOWN:
    {
      /* Stop DHCP */
      dhcp_stop(netif);
      DHCP_state = DHCP_OFF;
    }
    break;
    default: break;
    }

    /* wait 250 ms */
    osDelay(250);
  }
}


TCP/UDP echo server 관련 소스는 tcpecho.c 와 udpecho.c 파일을 그대로 가져다 사용하였습니다.

TCP/UDP echo 테스트를 하기 위해서는 echotool이라는 프로그램이 필요한데 아래 github에서 다운로드를 받습니다.

 

GitHub - PavelBansky/EchoTool: Command line echo server and client for Windows, designed according to RFC 862 specification for

Command line echo server and client for Windows, designed according to RFC 862 specification for Echo protocol. - PavelBansky/EchoTool

github.com

TCP/UDP echo server 예제의 기본적인 동작은 아래 그림과 같이 동작됩니다.

STM32F746G-DISCO에서 TCP/UDP echo server를 실행하여 메시지 수신 대기를 한 후 PC에서 echotool을 사용하여 TCP/UDP Client 모드에서 'Testing LwIP TCP/UDP echo server' 메시지를 Server인 STM32F746G-DISCO로 송신하면 Server단에서는 이 수신된 메세지는 다시 Client로 재전송을 하는 구조로 동작됩니다.

PC Client에서 아래 명령어를 사용하여 TCP/UDP 메시지를 Server로 전송합니다

TCP 명령어 : echotool IP_address /p tcp /r 7 /n 3 /t 2 /d Testing LwIP TCP echo server
UDP 명령어 : echotool IP_address /p udp /r 7 /l 7 /n 3 /t 2 /d Testing LwIP UDP echo server

Statistics를 보면 정상적으로 TCP/UDP 모두 총 송신 3회에 수신 3회가 된것을 확인 할 수 있습니다.

Client에서 TCP/UDP 명령어를 각 각 3회씩 전송시 Server에서는 아래와 같이 TCP 3회 및 UDP 3회의 수신 메시지를 확인 할 수 있습니다.

메시지는 아래 제공되는 netbuf API중 netbuf_data 함수를 참고하여 버퍼를 dump 받은 후 확인하였습니다.

TCP/UDP 프로토콜을 사용하여 메시지 echo 테스트가 정상적으로 동작됨을 확인해 보았습니다.

반응형