AVR Урок 40. LAN. ENC28J60. Часть 10

 

 

 

 

Урок 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

 

image12

 

Существует несколько типов сообщений 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));

}

 

Соберём код, прошьём контроллер и посмотрим результат

 

image13

 

Попробуем ответить. Заменим тип сообщения запрос на ответ

 

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;

 

Соберём код. Прошьём контроллер. Посмотрим результат

 

image14

 

Всё работает!

Остальные протоколы мы поизучаем когда-нибудь позже, а то уже и так наше изучение затянулось. Возможно, будет UDP, TCP, DHCP.

А пока всем творческих успехов. Всем спасибо за внимание!

 

Предыдущая часть Программирование МК AVR Следующий урок

 

Исходный код

 

Техническая документация:

Документация на микросхему ENC28J60

Перечень ошибок ENC28J60 (Errata)

 

 

Приобрести плату Atmega 328p Pro Mini можно здесь.

Программатор (продавец надёжный) USBASP USBISP 2.0

Ethernet LAN Сетевой Модуль можно купить здесь (модуль SD SPI в подарок) ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR LAN. ENC28J60

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*