LwIP(Lightweight IP)는 스웨덴 컴퓨터 과학 연구소의 Adam Dunkels가 처음 만든 오픈소스(Modified BSD license) TCP/IP 프로토콜 스택이다. 이름 그대로 경량 인터넷 프로토콜으로 임베디드 시스템에 자주 사용된다. LwIP는 수십 Kbyte의 RAM과 코드를 위한 40Kbyte이상의 ROM이 있는 곳에서 사용할 수 있다.
LwIP의 latest version은 2.1.3이며 🔗 LwIP 공식 홈페이지 에서 다운로드 받을 수 있다. LwIP의 최신 버전은 2.3.1 이지만 STM32F407에 포팅된 LwIP 버전은 1.4.1 을 사용하고 있다. 포팅이된 예제 및 프로젝트는 🔗 ST 공식 홈페이지 에서 다운받을 수 있다. (STM32F4x7에 포팅된 버전을 기준으로 설명한다.)
LwIP Architecture는 TCP/IP 4계층을 따른다.
LwIP는 다음과 같은 프로토콜들을 지원한다.
LwIP를 이용하는 API는 총 3가지가 존재한다.
Raw API :
가장 Low Level의 API로 이벤트와 콜백을 이용한 어플리케이션 제작이 가능하다. 가장 Low Level로써 최적의 코드사이즈 및 성능을 보여주지만, 사용하기에 다소 복잡하다.
Netconn API
RTOS의 서비스를 필요로하는 API로 Raw API 보다 High Level의 API이다. Netconn API는 멀티쓰레드 동작이 가능하다.
BSD Socket API
Netconn API를 이용해 만든 Socket API로 가장 High Level의 API다.
STSW-STM32070의 압축을 풀어 /Utilities/Third_Party/LwIP-1.4.1
를 살펴보면 아래와 같은 트리구조를 볼 수 있다.
port/STM32F4x7
: LwIP를 STM32F4x7 시리즈에 맞춰 구현한 파일들의 모음이다. arch
: 데이터 타입 등 STM32의 아키텍쳐 관련 파일FreeRTOS
: FreeRTOS를 이용하는 LwIP의 이더넷 인터페이스 및 시스템 아키텍쳐 소스 및 헤더파일Standalone
: Standalone mod의 LwIP의 이더넷 인터페이스 소스 및 헤더파일위에 언급했듯 LwIP는 총 3가지의 API가 존재한다. API는 낮은 Level 순서로 Raw ➝ Netconn ➝ Socket 이 존재하며 Level이 낮을수록 사이즈 및 성능이 높아지지만 복잡해 진다.
LwIP는 pbuf
라는 구조체로 버퍼를 관리한다. pbuf
는 구조체 멤버변수중 next
라는 변수가 있는데 이는 다음 pbuf
를 가르키는 포인터 변수다. pbuf
는 링크드리스트 형태로 버퍼를 관리하는것을 확인할 수 있다.
pbuf
구조체의 주소ref
멤버 변수가 0이어야 메모리에서 해제할 수 있다.pbuf
의 타입을 나타내는 변수LwIP는 세가지 형태의 타입이 있는데 각각 메모리를 어떻게 할당받느냐에 따라 정의가 된다.
PBUF_POOL
미리 확보해놓은 정적메모리에 pubf
를 할당한다. 할당해야하는 데이터의 크기에 따라 pbuf
의 갯수가 달라진다.
PBUF_RAM
요청시 필요한 만큼 메모리를 할당해주는 동적 메모리 (heap)에 pbuf
를 할당한다.
PBUF_ROM
메모리에 충분한 공간이 없을때 데이터를 ROM에 할당한다.
패킷 수신시 가장 적절한 pbuf
타입은 가장신속하게 메모리 할당이 가능한 PBUF_POOL 타입이다. PBUF_RAM은 메모리(heap)에 동적할당하므로, 딜레이와 메모리 단편화가 일어날 수 있다.
STM32F4x7 시리즈에 LwIP 포팅하기위한 스택은 /port/STM32F4x7
에 정의되어 있다. 디렉토리엔 다음과 같은 두 가지 구현체가 있는다.
두 디렉토리 모두 ethernet_if.c
라는 파일이 존재하며, 이 파일 에서 LwIP 스택과 STM32F4x7 Ethernet Interface의 연결을 담당한다. RTOS 디렉토리에는 sys_arch.c
라는 파일도 존재하는데, 이 파일은 RTOS에서 제공하는 서비스를 담당한다.
이더넷 케이블을 통한 패킷이 메모리로 전달되기 위한 인터페이스는 DMA descriptor라는 자료구조를 이용한DMA 전송이다.
위의 그림에서 보듯 DMA descriptor는 두 가지의 포맷을 가질 수 있는데 하나는 링크드 리스트 형태의 버퍼이며 다른 하나는 버퍼를 두 개 사용하는 형태이다. STM32F4X7 Ethernet Driver에서는 옵션1의 형태를 채용하고있다. 옵션 1의 형태를 자세히 살펴보면 원형 링크드 리스트임을 확인할 수 있다.
ethernet_if.c
파일을 확인하면 외부 전역변수를 사용하는 것을 볼 수 있다. 이 변수들을 이용해 메모리에 전달된 패킷 데이터에 접근이 가능하다. 이 전역 변수는 stm32f4x7_eth.c
에 정의되어 있다.
/* Ethernet Rx & Tx DMA Descriptors */
extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
/* Ethernet Driver Receive buffers */
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE];
/* Ethernet Driver Transmit buffers */
extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
/* Global pointers to track current transmit and receive descriptors */
extern ETH_DMADESCTypeDef *DMATxDescToSet;
extern ETH_DMADESCTypeDef *DMARxDescToGet;
배열 길이로 사용되는 ETH_RXBUFNB
, ETH_RXBUFNB
등 은 stm32f4x7_eth.h
에서 변경이 가능하다.