Нежданно-негаданно, благодаря теме на форуме, произошел возврат к одной из предыдущих тем, а именно к связке STM32 и ENC28J60 и Ethernet. И в этой, пятой, части обработаем принятый UDP-пакет и отправим на него ответ. Причем в отличие от предыдущих частей я не буду делать обширную врезку с теорией касаемо UDP, может быть потом, в отдельной статье, но вряд ли ) Просто и кратко дополним созданный ранее проект необходимым функционалом и посмотрим на результат.
Но для начала освежим в памяти предыдущие части:
- Технология Ethernet. Обзор, описание, формат кадра.
- STM32 и Ethernet. Часть 1. Подключение и настройка ENC28J60.
- STM32 и Ethernet. Часть 2. ENC28J60. Прием и передача кадров.
- STM32 и Ethernet. Часть 3. Канальный уровень. Протокол ARP.
- STM32 и Ethernet. Часть 4. Сетевой уровень. Протоколы IP и ICMP.
Итак, протокол UDP относится к транспортному уровню, а значит мы снова движемся вверх по модели OSI:

Отличительной особенностью UDP является то, что для обмена сообщениями-датаграммами устройствам не требуется никакой предварительной подготовки. То есть никаких установок соединений, рукопожатий, открытия каналов связи и тому подобного. Данные могут спокойно потеряться по пути, порядок следования датаграмм может быть нарушен, все это не контролируется. Возникает резонный вопрос - зачем тогда это нужно? А отчет достаточно прост - например, для очень чувствительных ко времени систем, систем реального времени. В таком случае в жертву приносятся дополнительные проверки и контроль, из-за чего и получаем выигрыш во времени. Если потери пакетов, ошибки, дублирования критичны, то тут уже вступает в дело, к примеру, TCP, но сегодня не об этом.
Для обмена данными по UDP необходимо знать ip-адрес и порт участников обмена. Порт представляет из себя целое число от 0 до 65535 (16-ти битное значение). Собственно, в практическом примере увидим этот механизм наглядно.
Каждая датаграмма включает в себя заголовок и непосредственно данные:

Пробежимся по полям заголовка:
- Source port - порт отправителя
- Destination port - порт получателя
- Length - длина датаграммы, причем включает в себя и заголовок и данные(!)
- Checksum - контрольная сумма
При этом для IPv4 контрольная сумма и порт отправителя являются необязательными полями. Все, что нам потребуется на практике, выяснили, добавляем в проект два новых файла:
- udp.c
- udp.h

Первым делом, определим структуру для UDP датаграмм в udp.h, выглядит она следующим образом:
typedef struct UDP_Frame
{
uint16_t srcPort;
uint16_t destPort;
uint16_t len;
uint16_t checkSum;
uint8_t data[];
} UDP_Frame;
Прекрасно, здесь же зададим номер порта, который используем для тестирования:
#define UDP_DEMO_PORT 33333
Для примера реализуем простой механизм: инкрементируем все байты в принятой датаграмме и отправим обратно уже измененными. Мате