본문 바로가기

[STM32F746G-DISCO] TCP echo client 테스트

by rudals.kim 2024. 7. 20. 댓글 개
반응형
아래 테스트는 STM32CubeIDE 1.7.0/STM32Cube_FW_F7_V1.16.1를 사용하여 테스트 되었습니다.


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

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

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

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

LwIP_TCP_Echo_Client 예제를 참고하여 메인 함수는 아래와 같이 작성하였습니다.

/* USER CODE BEGIN 2 */
BSP_Config();
lwip_init();
Netif_Config();
User_notification(&gnetif);
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
  /* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
  ethernetif_input(&gnetif);

  /* Handle timeouts */
  sys_check_timeouts();

  DHCP_Periodic_Handle(&gnetif);

  if (BSP_PB_GetState(BUTTON_KEY) != RESET){
    tcp_echoclient_connect();
  }
}
/* USER CODE END WHILE */


자동 IP 할당인 DHCP만 고려하여 수동으로 IP 설정하는 부분은 삭제하였습니다.

void BSP_Config(void)
{
  BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);

  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 Echo Client Raw 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, &ethernet_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_Process(struct netif *netif)
{
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t gw;
  struct dhcp *dhcp;
  uint8_t iptxt[20];

  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_state = DHCP_WAIT_ADDRESS;
      dhcp_start(netif);

      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, &ipaddr, &netmask, &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;
  }
}

void DHCP_Periodic_Handle(struct netif *netif)
{
  /* Fine DHCP periodic process every 500ms */
  if (HAL_GetTick() - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
  {
    DHCPfineTimer =  HAL_GetTick();
    /* process DHCP state machine */
    DHCP_Process(netif);
  }
}


tcp echo client 관련 소스는 tcp_echoclient.c 파일을 그래로 가져다 사용하였습니다.
TCP 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 echo client 예제의 기본적인 동작은 아래 그림과 같이 동작됩니다.
PC에서 echotool을 사용하여 TCP Server 모드로 실행시킨 후 Client인 STM32F746G-DISCO에서 User 버튼을 누를 시 TCP 프로토콜을 사용하여 'sending tcp client message #'을 Server로 전송하고 Server는 전송된 메시지를 Client로 되돌려 보냅니다.

tcp_echoclient.c 소스를 살펴보면 tcp_echoclient_connect 함수로 메시지가 Server로 전송되며 tcp_echoclient_recv 함수에서 Server로부터 loopback 된 메시지를 STM32F746G-DISCO 보드에서 수신 후 message_count 변수를 증가시키고 다음번 메시지 전송 시 증가된 message_count 값이 적용되어 전송되는 구조입니다.

STM32F746G-DISCO 보드에 이더넷 케이블을 연결 후 적용된 소스를 빌드하여 보드에 다운로드를 합니다.
다운로드가 완료되면 자동으로 공유기에서 IP를 할당받아 옵니다. (아래 그림 참고)

PC에서 'echotool /p tcp /s' 명령어를 사용하여 tcp echo server를 실행합니다.

STM32F746G-DISCO 보드 뒷면의 User 키를 누르면 아래와 같이 서버에서 'sending tcp client message #'가 출력됩니다. 수신 메시지의 마지막에 message_count 값이 증가되면서 출력되고 있습니다.

정상적으로 loopback 동작이 됨을 확인할 수 있습니다.

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

반응형

댓글