Урок 40
Часть 10
LAN. ENC28J60
В предыдущей части нашего урока мы написали функцию расчета контрольной суммы, а также написали функцию чтения IP-пакетов.
Теперь разберёмся с пакетами ICMP.
В функции ip_read мы всё же добавим переменную для результата и вернём его, так сказать, на будущее
uint8_t icmp_read(enc28j60_frame_ptr *frame, uint16_t len)
{
uint8_t res = 0;
return res;
}
Поставим в этой функции пока указатель на пакет IP
uint8_t res = 0;
ip_pkt_ptr *ip_pkt = (void*)frame->data;
Вот формат заголовка ICMP
Существует несколько типов сообщений ICMP:
0 — эхо-ответ,
3 — узел назначения недостижим,
5 — перенаправление маршрута,
8 — эхо-запрос,
9 — сообщение о маршрутизаторе,
10 — запрос сообщения о маршрутизаторе,
11 — истечение жизни пакета,
12 — проблемы с параметрами,
13 — запрос отметки времени,
14 — ответ отметки времени.
Из всего данного разнообразия сообщений нам пока будут нужны 2 — это эхо-ответ (0) и эхо-запрос (8), которые используются при работе утилиты ping.
Код пакета в случаях эхо-запросов и ответов — 0. В других случаях будут другие коды, но мы их в рамках нашего занятия пока не рассматривает.
Контрольная сумма пакета рассчитывается так же как и в случае IP, но не совсем так. В контрольную сумму включается весь пакет вместе с данными.
Следующие 2 поля устанавливаются хостом, и поэтому ими мы также не заморачиваемся. Их в эхо-ответе оставляют такими же как и пришли.
В поле данных как правило тестовый набор байтов, который должен возвратиться также неизменным.
Также ICMP-пакеты используются при диагностике маршрутов в трассировке.
В заголовочном файле net.h добавим структуру для заголовка icmp-пакета
//——————————————
typedef struct icmp_pkt {
uint8_t msg_tp; //тип сообщения
uint8_t msg_cd; //код сообщения
uint16_t cs; //контрольная сумма заголовка
uint16_t id; //идентификатор пакета
uint16_t num; //номер пакета
uint8_t data[]; //данные
} icmp_pkt_ptr;
//——————————————
Также добавим типы сообщений
#define IP_UDP 17
//——————————————
#define ICMP_REQ 8
#define ICMP_REPLY 0
В функции icmp_read в файле net.c установим указатель на пакет ICMP
ip_pkt_ptr *ip_pkt = (void*)frame->data;
icmp_pkt_ptr *icmp_pkt = (void*)ip_pkt->data;
Отфильтруем пакет по длине и типу сообщения — эхо-запрос
icmp_pkt_ptr *icmp_pkt = (void*)ip_pkt->data;
//Отфильтруем пакет по длине и типу сообщения — эхо-запрос
if((len >= sizeof(icmp_pkt_ptr)) && (icmp_pkt->msg_tp == ICMP_REQ))
{
}
Выведем сообщение в терминальной программе, что у нас именно такой запрос
if((len >= sizeof(icmp_pkt_ptr)) && (icmp_pkt->msg_tp == ICMP_REQ))
{
sprintf(str1,«ipcmp requestrn»);
USART_TX((uint8_t*)str1,strlen(str1));
}
Соберём код, прошьём контроллер и посмотрим результат
Попробуем ответить. Заменим тип сообщения запрос на ответ
USART_TX((uint8_t*)str1,strlen(str1));
icmp_pkt->msg_tp = ICMP_REPLY;
Посчитаем контрольную сумму
icmp_pkt->msg_tp = ICMP_REPLY;
icmp_pkt->cs=0;
icmp_pkt->cs=checksum((void*)icmp_pkt,len)
Теперь напишем функцию отправки IP-пакета, так как отдельная функция для отправки ICMP-пакета нам не нужна, ибо мы его уже сформировали. Заготовка функции подобна функции отправки пакета
//———————————————————
uint8_t ip_send(enc28j60_frame_ptr *frame, uint16_t len)
{
uint8_t res = 0;
return res;
}
//———————————————————
Вызовем данную функцию в функции icmp_read
icmp_pkt->cs=checksum((void*)icmp_pkt,len);
ip_send(frame,len+sizeof(ip_pkt_ptr));
}
Вернёмся теперь в функцию ip_send и установим указатель на пакет IP
uint8_t res = 0;
ip_pkt_ptr *ip_pkt = (void*)(frame->data);
Заполним заголовок IP
ip_pkt_ptr *ip_pkt = (void*)(frame->data);
// Заполним заголовок пакета IP
ip_pkt->len = be16toword(len);
ip_pkt->fl_frg_of = 0;
ip_pkt->ttl = 128;
ip_pkt->cs = 0;
ip_pkt->ipaddr_dst= ip_pkt->ipaddr_src;
ip_pkt->ipaddr_src = IP_ADDR;
ip_pkt->cs = checksum((void*)ip_pkt, sizeof(ip_pkt_ptr));
И отправим фрейм
ip_pkt->cs = checksum((void*)ip_pkt, sizeof(ip_pkt_ptr));
//отправим фрейм
eth_send(frame, len);
return res;
Соберём код. Прошьём контроллер. Посмотрим результат
Всё работает!
Остальные протоколы мы поизучаем когда-нибудь позже, а то уже и так наше изучение затянулось. Возможно, будет UDP, TCP, DHCP.
А пока всем творческих успехов. Всем спасибо за внимание!
Предыдущая часть Программирование МК AVR Следующий урок
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Приобрести плату Atmega 328p Pro Mini можно здесь.
Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Если всё это освою — буду молодец.
Автору большущая благодарность!!!
Мне понравилась последовательность изложения материала.
И удачи всем в этом очень нужном деле.
Спасибо за тёплый комментарий!
Да большое спасибо ! Ваш проект сразу скомпилировался на Atmel Studio 7 и все взлетело (только на ардуино UNO). Даже толком не успел вникнуть в код….
Все за один день.
Рекомендую!
Тут картинка как подключал : https://kkmspb.ru/development/microcontrollers/Ethernet/ENC28J60.php
Всем привет! Подключаю ENC28J60 к Atmega328P, делаю всё по примеру, но у меня компьютер упорно не хочет видеть IP, который мы задаём в коде. Причем IP под другим номером определяется и пингуется, но он статический, а не динамический. Также на Terminal приходят пакеты IP и arp, а вот arprequest, mac_src, ip_src, mac_dst и ip scr упорно не приходят.
Я уже даже просто скачал готовый проект (Который в описании к этому уроку) и перепрошил контроллер, результат тот же. Что я могу не так делать?
p.s. Спасибо вам за ваши уроки!