разработка и программирование электронных устройств

Введение в Lightweight IP (LwIP) стек протоколов TCP/IP

LwIP — это стек протоколов TCP/IP с открытым исходным кодом. Первоначально LwIP был
разработан Адамом Дункельсом в Шведском институте компьютерных наук, сейчас разработка ведется силами сообщества.
LwIP получил очень широкое распространение во встраиваемых системах на базе микроконтроллеров благодаря
низкому потреблению оперативной памяти. Именно этот TCP/IP стек используется в фреймворке ARM mbed и генераторе кода инициализации STM32CubeMX.
LwIP распространяется под BSD лицензией.

В состав стека LwIP входят такие протоколы :

  • IPv4 и IPv6 (Интернет протокол 4-ой и 6-ой версий)
  • ICMP (протокол межсетевых управляющих сообщений)
  • IGMP (протокол управления группами Интернета)
  • UDP(протокол пользовательских датаграмм)
  • TCP(протокол управления передачей)
  • DNS(система доменных имен)
  • SNMP(простой протокол сетевого управления)
  • DHCP(протокол динамической настройки узла)
  • PPP(двухточечный протокол канального уровня)
  • ARP(протокол определения адреса)

Архитектура стека LwIP построена на модели, включающей четыре уровня абстракции. Список уровней от самого низкого до самого высокого :

  • Уровень связи (link layer) содержит технологии коммуникации для одного сегмента локальной сети.
  • Интернет уровень (IP) соединяет независимые сети, устанавливая межсетевое взаимодействие.
  • Транспортный уровень обрабатывает соединения между хостами.
  • Прикладной уровень содержит все протоколы для передачи данных между процессами.

В стеке LwIP доступно три интерфейса прикладного программирования (API) :

  • RAW API – родной интерфейс LwIP. Он предусматривает использование обратных вызовов функций (callbacks) внутри стека.
    Это означает, что вам перед началом работы со стеком необходимо присвоить указатели на функции-обработчики событий,
    которые в процессе работы будут вызываться внутри LwIP. RAW API имеет наибольшую производительность и наименьший результирующий размер кода.
  • Netconn API – высокоуровневый последовательный интерфейс, построенный поверх RAW API. Этот интерфейс требует наличия RTOS и поддерживает мнопоточные операции.
  • BSD Socket API – высокоуровневый интерфейс сокетов, разработанный поверх Netconn API. Этот интерфейс обеспечивает высокую переносимость ваших приложений, поскольку является стандартизированным API.

В состав пакета программ STM32CubeMX от компании STMicroelectronics входят примеры работы со стеком LwIP для отладочных плат STM32.
Именно этими примерами мы воспользуемся для первоначального ознакомления со стеком.
Я буду использовать отладочную плату NUCLEO_F429ZI на микроконтроллере STM32F429ZI, к которому через интерфейс RMII подключен физический уровень Ethernet на микросхеме LAN8742A.

После установки STM32CubeMX примеры программ можно найти в каталоге STM32Cube\Repository\STM32Cube_FW_F4_V1.21.0\Projects\STM32F429ZI-Nucleo\Applications.
Для демонстрации работы NUCLEO_F429ZI со стеком LwIP доступен только один пример, — HTTP сервер, использующий интерфейс Netconn API и работающий под управлением FreeRTOS (с интерфейсом CMSIS_RTOS первой версии).

Именно с этого примера мы и начнем наше знакомство со стеком Lightweight IP. Открываем проект в предпочитаемой среде разработки (IAR EWB, MDK-ARM, SW4STM32). Я буду это делать в бесплатной SW4STM32, построенной на базе Eclipse CDT.

Собираем и запускаем проект, а если ничего не получается, то читаем readme.txt 🙂 . В этом файле указано название руководства пользователя UM1713 “Интерфейс STM32Cube с LwIP и приложениями” для более детального изучения примера, а также предоставлено краткое описание основных файлов проекта.

Пример собирается в SW4STM32 без проблем, для загрузки прошивки в микроконтроллер достаточно подключить плату NUCLEO_F429ZI к компьютеру и скопировать бинарный файл на диск NODE_F429ZI.

По-умолчанию в примере используется динамическое назначение IP адреса с помощью DHCP . Узнать назначенный адрес можно с помощью консольной команды

Я установил для своей платы статический IP адрес 192.168.1.10 . Для этого необходимо закоментировать строку «#define USE_DHCP» и отредактировать IP адрес платы и шлюза в файле main.h.
После прошивки платы подключаем ее к компьютерной сети и набираем в строке браузера адрес web-сервера (в моем случае http://192.168.1.10).

Теперь настало время разобраться в работе web -сервера, изучая исходный код примера.
В главной функции main создается и запускается одна-единственная задача StartThread, которая выполняет начальную инициализацию lwip и запуск остальных задач.
Если вы ранее работали c FreeRTOS, но синтаксис функций вам кажется незнакомым (или не работали с RTOS вообще), ознакомьтесь с документацией CMSIS_RTOS API.

Первой вызывается функция tcpip_init , в которой производится начальная инициализация lwip и запуск задачи (программного потока), отвечающей за работу TCP/IP стека. Исходный код tcpip_init находится в файле tcpip.c .Второй вызываемой функцией является статическая функция Netif_Config , объявленная в файле main.c.

В этой функции назначается IP адрес платы (динамически или статически в зависимости от макроса USE_DHCP), маска сети и адрес шлюза .
Функция netif_add производит инициализацию структуры netif назначенными параметрами. Также в ней присваивается указатель на функцию инициализации Ethernet интерфейса ethernetif_init и функцию обратного вызова для обработки принятого пакета tcpip_input.

В функции netif_default_init регистрируется ранее инициализированная структура netif для использования в качестве сетевого интерфейса по-умолчанию.
Завершаем инициализацию проверкой флага NETIF_FLAG_LINK_UP и запуском интерфейса с помощью функции netif_set_up или отключением интерфейса с помощью функции netif_set_down в зависимости от значения флага.

Глубокого понимание инициализирующей последовательности на данном этапе от нас не требуется.

Дальше по коду производится инициализация web -сервера с помощью функции http_server_netconn_init, внутри которой создается новая задача (программный поток) для обработки запроса к web -серверу.
Web -сервер в этом примере очень простой, он обрабатывает только один запрос HTTP протокола. GET – запрос (команда GET) предназначен для получения указанного файла от web – сервера. Если это, например, html — файл , то он будет отображен в вашем браузере, архивный файл будет загружен на ваш компьютер и сохранен на жестком диске. Тип передаваемого контента указывается в параметрах GET – запроса.
Для получения подробной информации необходимо обратиться к спецификации протокола HTTP .
Исходный код обработки запроса к серверу можно просмотреть в функции http_server_netconn_thread из файла httpserver-netconn.c .

Вдаваться в подробности реализации web – сервера от компании STMicroelectronics мы не станем, потому что наша цель — научиться использовать стек LwIP.
Для нас на данном этапе наиболее важным моментом является понимание каким образом можно подключить
Ethernet драйвер к LwIP. Второе, чему мы должны научится , — это настраивать стек LwIP.


Интерфейс LwIP с драйвером Ethernet.

Для связывания LwIP стека с Ethernet интерфейсом микроконтроллеров stm32f4xx используется файл ethernetif.c.
Порт стека LwIP , предназначенный для работы с микроконтроллерами stm32f4xx, находится в каталоге lwip/system .

Вот перечень основных функций Ethernet драйвера :

  • low_level_init – вызывает функции драйвера для инициализации Ethernet периферии stm32f4xx .
  • low_level_output – вызывает функции драйвера для отправки Ethernet пакета.
  • low_level_input — вызывает функции драйвера для приема Ethernet пакета.
  • ethernetif_init – инициализирует структуру сетевого интерфейса (netif) и вызывает low_level_init.
  • ethernetif_input – вызывает low_level_input, чтобы принять пакет и потом передать его в стек LwIP.

Итак , в нашем примере реализация Ethernet драйвера для использования RMII интерфейса на микросхеме LAN8742A находиться в файле ethernetif.c.
Драйвер с одной стороны обеспечивает интерфейс со стеком LwIP, а с другой стороны использует функции библиотеки HAL для работы с Ethernet интерфейсом микроконтроллера.
Связывание Ethernet драйвера со стеком LwIP происходит при вызове функции netif_add, в параметрах которой передается указатель на функцию ethernetif_init.

Внутри ethernetif_init производится инициализация указателя netif->linkoutput функцией обратного вызова low_level_output, предназначенной для отправки пакетов.
Для приема пакетов внутри low_level_init запускается еще одна задача (поток выполнения) ethernetif_input.

Принятые данные копируются в связанный список pbuf и передаются в стек lwip через netif->input . При вызове netif_add в качестве параметра input мы передали адрес функции tcpip_input, именно эта функция и будет вызвана для обработки принятого пакета , точнее для отправки принятого пакета в очередь сообщений, из которой пакет может быть прочитан для дальнейшей обработки функциями Netconn API.

Настройка LwIP.

Для настройки стека LwIP используется заголовочный файл lwipopts.h. В этом файле можно настроить стек LwIP и все его модули. Вам не обязательно устанавливать все возможные параметры, если параметр не установлен, то используется его значение по-умолчанию из файла opt.h. Таким образом в файле lwipopts.h можно переопределить большую часть поведения стека.
В файле lwipopts.h можно выбрать только те модули, которые вы собираетесь использовать в своей программе и таким образом уменьшить размер кода прошивки.

Стек LwIP предоставляет гибкий способ управления размерами и организацией пула памяти. Он резервирует область статической памяти фиксированного размера в сегменте данных .
Существуют различные пулы, которые стек использует для различных структур данных. Например , есть пул для структур struct tcp_pcb и struct udp_pcb. Каждый пул может быть сконфигурирован для хранения фиксированного количества структур данных. Это количество можно изменить в файле lwipopts.h . Например, MEMP_NUM_TCP_PCB и MEMP_NUM_UDP_PCB определяют максимальное количество структур
struct tcp_pcb и struct udp_pcb, которые могут быть активны в системе одновременно .

Далее перечислены основные опции памяти :

Макрос Описание
MEM_SIZE размер кучи LwIP , которая используется при динамическом выделении памяти
MEMP_NUM_PBUF общее количество MEM_REF и MEM_ROM pbuf
MEMP_NUM_UDP_PCB общее количество структур UDP PCB
MEMP_NUM_TCP_PCB общее количество структур TCP PCB
MEMP_NUM_TCP_PCB _LISTEN общее количество прослушиваемых TCP PCB
MEMP_NUM_TCP_SEG максимальное количество одновременных сегментов TCP
PBUF_POOL_SIZE общее количество pbuf с типом PBUF_POOL
PBUF_POOL_BUFSIZE размер pbuf с типом PBUF_POOL
TCP_MSS максимальный размер сегмента TCP
TCP_SND_BUF пространство буфера отправки TCP для подключения
TCP_SND_QUEUELEN максимальное количество pbuf в очереди отправки TCP
TCP_WND регламентирует размер окна приема TCP

Обратите также внимание на конфигурационный файл stm32f4xx_hal_conf.h библиотеки HAL , в нем помимо прочего присутствуют настройки Ethernet интерфейса.

Отладка LwIP.

Для вывода отладочной информации в файле lwipopt.h нужно дописать соответсвующие макросы.
Для общего разрешения вывода отладочной информации используется :

Кроме того можно отдельно разрешать/запрещать вывод сообщений от различных модулей (уровней стека).
Например чтобы выводить отладочную информацию от сетевого интерфейса необходимо добавить в lwipopt.h макрос

Для вывода сообщений от Socket API необходимо добавить макрос

В конечном счете отладочная информация будет выведена через стандартную функцию printf().
Перенаправить вывод можно в UART или на символьный индикатор, или куда пожелаете.

Я буду выводить отладочную информацию в UART. Для среды разработки SW4STM32 необходимо реализовать функцию вывода символа в UART __io_putchar.

Перед использованием необходимо проинициализировать задействованный в выводе отладочной информации модуль UART .

Пускай читатели простят меня за сумбурность изложения и возможные неточности в описании, поскольку стек LwIP является достаточно сложным программным пакетом и с одного наскока разобраться в принципах его работы непросто.

В следующей статье я планирую разобрать пример работы со стеком LWIP через BSD Socket API.

Viewed 23308 times by 2893 viewers

Leave a Reply

You must be logged in to post a comment.