Урок 40
Часть 8
LAN. ENC28J60
В предыдущей части нашего урока мы написали ещё две нужные функции и код для отображения некоторых частей заголовка ARP-запроса.
В функции arp_send занесём в ARP-заголовок код ответа ARP
void arp_send(enc28j60_frame_ptr *frame)
{
arp_msg_ptr *msg = (void*)(frame->data);
msg->op = ARP_REPLY;
Занесём также в заголовок физические адреса
msg->op = ARP_REPLY;
memcpy(msg->macaddr_dst, msg->macaddr_src, 6);
memcpy(msg->macaddr_src, macaddr, 6);
Затем занесём IP-адреса
memcpy(msg->macaddr_src, macaddr, 6);
msg->ipaddr_dst = msg->ipaddr_src;
msg->ipaddr_src = IP_ADDR;
И в завершении функции отправим пакет
msg->ipaddr_src = IP_ADDR;
eth_send(frame, sizeof(arp_msg_ptr));
}
В функции eth_read ответим на ARP-запрос
if(arp_read(frame, len — sizeof(enc28j60_frame_ptr)))
{
arp_send(frame);
}
Соберём код, прошьём контроллер. Для того, чтобы увидеть наш ответ, желательно установить бесплатную программу Wireshark. Это анализатор сетевых пакетов.
Установим в данной программе фильтр только на пакеты arp
Пропинугем наш IP-адрес таким же образом, как и прежде. Ответа на пинг мы не получим, но на запрос ARP в утилите мы увидим ответ
Можно также открыть и посмотреть структуру пакета ARP
Также в таблице соответствия адресов ARP компьютера мы видим теперь наши данные
ARP-ответ мы отправили, хотелось бы ещё отвечать на пинги, но это не так просто. Нужно научиться работать с протоколом ICMP (Internet Control Message Protocol — протоеол межсетевых управляющих сообщений), находящимся между сетевым уровнем и транспортным. А чтобы работать с протоколом ICMP, необходимо разобраться ещё с протоколом IP. Вот с него мы пока и начнём.
Протокол IP является более тяжёлым, нежели протокол ARP, так как перед ним стоит много задач. Одна из них — найти маршрут, по которому нужно отправить пакет, также пакет может состоять из нескольких фрагментов, находящихся в различных фреймах. Но ничего, потихоньку разберёмся.
В функции eth_read добавим в условие распознавания протокола IP фигурные скобки и условие if исправим на else if
else if (frame->type==ETH_IP)
{
USART_TX((uint8_t*)«; ip»,4);
}
Над этой функцией добавим ещё функцию чтения пакета IP
//———————————————————
uint8_t ip_read(enc28j60_frame_ptr *frame, uint16_t len)
{
}
//———————————————————
Вот формат заголовка пакета IP
Первое поле — номер версии, в нашем случае 4, так как версия протокола у нас IPv4, в которой IP-адрес состоит из 4 байтов.
Дальше идет длина заголовка, измеряемая в 4-байтных величинах или в двойных словах (DWORD), поэтому заголовок IP должен быть кратным 4 байтам. В нашем случае без параметров и доп. опций здесь будет цифра 5.
Тип обслуживания — в данный момент практически не используется.
Общая длина — это суммарная длина пакета вместе с данными. Нам она важна, так как мы из-за выравнивания пакетов её не знаем. Измеряется в байтах. Максимальное значение — 65535 байт. Но на практике не превышает 1500 из-за максимальной длины кадра Ethernet.
Идентификатор пакета, флаги и смещение фрагмента используются для фрагментации, мы её пока не рассматриваем.
Время жизни — это количество хопов (прыжков). После прохождения каждого маршрутизатора уменьшается на единицу, чтобы всякие левые пакеты не гуляли вечно по сети.
Тип протокола — пример — TCP — 6, UDP — 17, ICMP — 1.
Контрольная сумма заголовка — это хитро рассчитанная величина, которая служит для проверки дохождения пакета до получателя. Расчитывается и на приёмнике и на источнике и сравнивается. Также контрольная сумма меняется после прохождения через маршрутизатор, так как там время жизни уменьшается на 1 и поле меняется.
А дальше понятно — IP-адреса отправителя и получателя.
Добавим структуру для пакте IP в файл net.h
//——————————————
typedef struct ip_pkt {
uint8_t verlen; //версия протокола и длина заголовка
uint8_t ts; //тип сервиса
uint16_t len; //длина
uint16_t id; //идентификатор пакета
uint16_t fl_frg_of; //флаги и смещение фрагмента
uint8_t ttl; //время жизни
uint8_t prt; //тип протокола
uint16_t cs; //контрольная сумма заголовка
uint32_t ipaddr_src; //IP-адрес отправителя
uint32_t ipaddr_dst; //IP-адрес получателя
uint8_t data[]; //данные
} ip_pkt_ptr;
//——————————————
Ещё напишем макросы для типов протоколов
//——————————————
#define IP_ICMP 1
#define IP_TCP 6
#define IP_UDP 17
//——————————————
В следующей части нашего урока мы напишем функцию расчета контрольной суммы, а также напишем функцию чтения IP-пакетов.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Приобрести плату Atmega 328p Pro Mini можно здесь.
Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий