임베디드 시스템에서 네트워크 기능은 필수적인 요소가 되었다. 특히 IoT 기기나 산업 자동화 분야에서 원격 제어, 데이터 모니터링, 펌웨어 업데이트 등의 기능을 구현하기 위해 TCP/IP 통신 스택의 사용이 요구된다. 본 포스트에서는 STM32 마이크로컨트롤러에 경량 TCP/IP 스택인 LWIP(LightWeight IP)를 구현하여 네트워크 기능을 통합하는 과정을 상세히 다루었다.
1. LWIP 스택 소개 및 STM32 통합의 필요성
LWIP는 임베디드 시스템과 같이 제한된 자원을 가진 환경에서 동작하도록 설계된 TCP/IP 스택이다. 작은 메모리 footprint와 높은 효율성을 특징으로 하며, STM32와 같은 ARM Cortex-M 기반 MCU에서 널리 사용된다. STM32CubeMX 및 STM32CubeIDE를 활용하면 LWIP 스택을 쉽게 프로젝트에 통합할 수 있다.
STM32에 LWIP를 통합하는 것은 다음과 같은 이점을 제공한다.
- 네트워크 통신 가능: 이더넷을 통해 다른 장치, 서버와 통신할 수 있다.
- 원격 관리: 웹 서버, Telnet, SSH 등을 구현하여 원격에서 장치를 관리할 수 있다.
- 데이터 전송: 센서 데이터나 제어 명령을 효율적으로 전송할 수 있다.
2. STM32CubeMX를 이용한 프로젝트 설정
LWIP 스택을 STM32 프로젝트에 통합하기 위해 STM32CubeMX를 사용했다. 다음은 주요 설정 단계이다.
2.1. 프로젝트 생성 및 MCU 선택
STM32CubeMX를 실행하고 New Project를 선택한 후, 사용할 STM32 마이크로컨트롤러(예: STM32F407ZG)를 선택한다.
2.2. 클록 구성
System Core > RCC에서 HSE(High Speed External)를 Crystal/Ceramic Resonator로 설정했다. 이후 Clock Configuration 탭에서 HCLK를 최대 주파수(예: 168MHz)로 설정하여 시스템 성능을 최적화했다.
2.3. 이더넷 MAC (Ethernet MAC) 설정
Connectivity > ETH를 선택하여 이더넷 인터페이스를 활성화한다.
- Mode:
RMII(Reduced Media Independent Interface) 또는MII를 선택한다. 일반적으로 RMII가 많이 사용된다. - Parameter Settings:
PHY Interface: 외부 PHY 칩에 맞춰 설정한다.NVIC Settings:Ethernet global interrupt를 활성화했다.
2.4. LWIP 스택 설정
Middleware > LWIP를 선택하여 LWIP 스택을 활성화한다.
- General Settings:
DHCP:Enabled또는Disabled를 선택한다. DHCP를 사용하면 자동으로 IP 주소를 할당받는다. 정적 IP를 사용할 경우Disabled로 설정하고 직접 IP 주소를 입력한다.Static IP Address,Netmask,Gateway Address: DHCP를 사용하지 않을 경우 이 값들을 설정한다.Maximum Number of Connections (TCP): 동시 TCP 연결 수를 설정한다.Receive (RX) PBUF Buffers,Transmission (TX) PBUF Buffers: PBUF(Packet Buffer)의 개수를 설정한다.
- Platform Settings:
FreeRTOS를 사용할 경우FreeRTOS를 선택하고, 그렇지 않으면Standalone을 선택한다. LWIP는 내부적으로 태스크 관리를 위해 RTOS를 권장한다.
2.5. FreeRTOS (선택 사항) 설정
Middleware > FreeRTOS를 선택하여 RTOS를 활성화했다. LWIP가 FreeRTOS 태스크로 동작하므로 안정적인 동작을 위해 필요하다.
Kernel V2를 선택하고, 필요한 경우Task and Timer탭에서 태스크 스택 크기나 우선순위를 조절한다.
2.6. GPIO 설정
이더넷 PHY 칩의 리셋 핀이나 전원 제어 핀이 있다면, 해당 GPIO를 GPIO_Output으로 설정하고 초기 상태를 지정한다.
설정 완료 후 GENERATE CODE 버튼을 클릭하여 프로젝트를 생성했다.
3. LWIP 코드 구현 및 테스트
STM32CubeIDE에서 생성된 프로젝트를 열고 LWIP 기능을 활용하는 코드를 구현한다.
3.1. LWIP 초기화 및 네트워크 인터페이스 설정
main.c 파일에서 MX_LWIP_Init() 함수가 호출되어 LWIP 스택이 초기화된다. 이 함수 내부에서 DHCP 클라이언트 시작 또는 정적 IP 설정이 이루어진다.
void MX_LWIP_Init(void)
{
/* IP addresses initialization */
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gw;
/* Initilialize the LwIP stack without RTOS if you want if you have chosen standalone mode. */
/* If you want to use RTOS, the initilization for the LwIP stack is done in the default task. */
#if LWIP_DHCP == 0
/* IP addresses setting */
IP4_ADDR(&ipaddr, 192, 168, 1, 10); // 정적 IP 주소
IP4_ADDR(&netmask, 255, 255, 255, 0); // 서브넷 마스크
IP4_ADDR(&gw, 192, 168, 1, 1); // 게이트웨이 주소
#endif
/* Initilise the LwIP stack */
lwip_init();
/* Add the network interface (Ethernet) */
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &netif_input);
/* Set as default network interface */
netif_set_default(&gnetif);
if (netif_is_link_up(&gnetif))
{
/* Set the link up */
netif_set_up(&gnetif);
}
else
{
/* Set the link down */
netif_set_down(&gnetif);
}
#if LWIP_DHCP
/* Start DHCP client if activated */
dhcp_start(&gnetif);
#endif
}
### 3.2. 간단한 TCP Echo 서버 구현 (FreeRTOS 환경)
FreeRTOS를 사용하는 경우, TCP 서버를 위한 태스크를 생성하여 이더넷 이벤트와 애플리케이션 로직을 분리하는 것이 좋다.
// main.c 또는 별도의 tcp_server.c 파일
#include "lwip/opt.h"
#include "lwip/arch.h"
#include "lwip/api.h"
#include "lwip/inet.h"
#define TCP_SERVER_PORT 7000
static void tcp_server_thread(void *arg)
{
struct netconn *conn, *newconn;
err_t err, accept_err;
LWIP_UNUSED_ARG(arg);
/* Create a new connection identifier. */
conn = netconn_new(NETCONN_TCP);
if (conn != NULL)
{
/* Bind connection to well known port number. */
err = netconn_bind(conn, IP_ADDR_ANY, TCP_SERVER_PORT);
if (err == ERR_OK)
{
/* Tell connection to go into listening mode. */
netconn_listen(conn);
while (1)
{
/* Grab new connection. */
accept_err = netconn_accept(conn, &newconn);
/* Process the new connection. */
if (accept_err == ERR_OK)
{
struct netbuf *buf;
void *data;
u16_t len;
while ((err = netconn_recv(newconn, &buf)) == ERR_OK)
{
do
{
netbuf_data(buf, &data, &len);
netconn_write(newconn, data, len, NETCONN_COPY); // Echo back
} while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
}
/* Close connection and discard connection identifier. */
netconn_close(newconn);
netconn_delete(newconn);
}
}
}
else
{
netconn_delete(conn);
// 오류 처리
}
}
// 오류 처리
}
/* FreeRTOS Default Task에서 호출 */
void StartDefaultTask(void *argument)
{
/* init code for LWIP */
MX_LWIP_Init();
/* Create the TCP server thread */
sys_thread_new("tcp_server_thread", tcp_server_thread, NULL, 512, osPriorityNormal);
/* Infinite loop */
for (;;)
{
osDelay(1);
}
}
StartDefaultTask 함수는 FreeRTOS에서 자동으로 생성되는 태스크이다. 이 안에서 MX_LWIP_Init()를 호출하여 LWIP를 초기화하고, tcp_server_thread를 별도의 태스크로 생성하여 TCP 서버를 실행했다.
3.3. 컴파일 및 펌웨어 다운로드
코드를 컴파일하고 STM32 개발 보드에 펌웨어를 다운로드한다.
3.4. 네트워크 테스트
펌웨어 다운로드 후, 개발 보드를 이더넷 케이블로 PC 또는 네트워크 스위치에 연결한다.
- Ping 테스트: PC에서 STM32 보드의 IP 주소로
ping명령어를 실행하여 네트워크 연결을 확인한다.ping 192.168.1.10 - TCP Echo 테스트: PC에서
netcat또는PuTTY와 같은 터미널 프로그램을 사용하여 STM32 보드의 TCP Echo 서버(포트 7000)에 연결하고 메시지를 전송하여 에코 기능을 확인한다.nc 192.168.1.10 7000 메시지를 입력하면 STM32 보드에서 동일한 메시지를 다시 전송하는 것을 확인할 수 있다. - Wireshark 분석: 필요하다면 Wireshark를 사용하여 네트워크 트래픽을 캡처하고 분석하여 패킷 교환이 올바르게 이루어지는지 확인할 수 있다.
4. 결론
본 포스트에서는 STM32 마이크로컨트롤러에 LWIP TCP/IP 스택을 구현하여 임베디드 시스템에 네트워크 기능을 통합하는 과정을 다루었다. STM32CubeMX를 이용한 설정부터 FreeRTOS 기반의 간단한 TCP Echo 서버 구현 및 테스트까지 진행했다. LWIP는 제한된 자원 환경에서 효율적인 네트워크 통신을 가능하게 하며, 이를 통해 임베디드 시스템의 활용 범위를 크게 확장할 수 있었다. 이 기본적인 구현을 바탕으로 HTTP 서버, MQTT 클라이언트 등 다양한 네트워크 애플리케이션을 개발할 수 있을 것이다.
DevBJ | No Bio, Just Log
기술 삽질로그