Урок 68
Часть 9
LAN. ENC28J60
В предыдущей части нашего урока мы изучили заголовок IP, написали для него структуру, а также научились рассчитывать контрольную сумму заголовка и написали для этого функцию.
Добавим в функцию чтения IP-пакета фильтрацию по версии протокола, длине заголовка и соответствию адреса получателя нашему IP, и вернём результат
ip_pkt_ptr *ip_pkt = (void*)(frame->data);
if((ip_pkt->verlen==0x45)&&(!memcmp(ip_pkt->ipaddr_dst,ipaddr,4)))
{
}
return res;
}
Вычислим размер данных в байтах
if((ip_pkt->verlen==0x45)&&(!memcmp(ip_pkt->ipaddr_dst,ipaddr,4)))
{
//длина данных
len = be16toword(ip_pkt->len) - sizeof(ip_pkt_ptr);
Сравним контрольную сумму из поля контрольной суммой с расчитанной нами пока в текстовом виде
len = be16toword(ip_pkt->len) - sizeof(ip_pkt_ptr);
sprintf(str1,"rnip_cs 0x%04Xrn", ip_pkt->cs);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
ip_pkt->cs=0;
sprintf(str1,"ip_cs 0x%04Xrn", checksum((void*)ip_pkt,sizeof(ip_pkt_ptr)));
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
Вызовем нашу функцию в функции eth_read
else if(frame->type==ETH_IP)
{
...
ip_read(frame,len-sizeof(ip_pkt_ptr));
}
Соберём код, прошьём контроллер и сверим пришедшую и расчитанную контрольную сумму опять с помощью утилиты ping
Всё сходится. Отлично!
Теперь можно код сравнения удалить. Мы затем будем сравнивать по-другому
sprintf(str1,"rnip_cs 0x%04Xrn", ip_pkt->cs);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
ip_pkt->cs=0;
sprintf(str1,"ip_cs 0x%04Xrn", checksum((void*)ip_pkt,sizeof(ip_pkt_ptr)));
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
Вернёмся в функцию ip_read и узнаем тип протокола
len = be16toword(ip_pkt->len) - sizeof(ip_pkt_ptr);
if (ip_pkt->prt==IP_ICMP)
{
}
else if (ip_pkt->prt==IP_TCP)
{
}
else if (ip_pkt->prt==IP_UDP)
{
}
Добавим функцию чтения ICMP-пакета
//--------------------------------------------------
uint8_t icmp_read(enc28j60_frame_ptr *frame, uint16_t len)
{
}
//--------------------------------------------------
Вызовем данную функцию в функции ip_read
if (ip_pkt->prt==IP_ICMP)
{
icmp_read(frame,len);
}
Теперь разберёмся с пакетами 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-пакета
} ip_pkt_ptr;
//--------------------------------------------------
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,"icmp requestrn");
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
}
Соберём код, прошьём контроллер и посмотрим результат
Попробуем ответить. Заменим тип сообщения запрос на ответ
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
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;
}
//--------------------------------------------------
В следующей части нашего урока мы закончим работу над ответом на пинг нашего модуля ENC28J60.
Предыдущая часть Программирование МК STM32 Следующая часть
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Уважаемый автор, я перелопатил ваши коды и запустил UDP в оба направления. Если интересно, свяжитесь со мной, можем довести проект до логического завершения. Я пока TCP буду делать.
С уважением, Павел
pasha-net@narod.ru
Здравствуйте
Дошел по вашим видео до этого урока, контроллер выводит два сообщенияв терминал и виснет. Контрольную сумму не выводит.Прошу выслать код урока 68.8 по возможности на почту bob_vz@маил.ру или ссылку на скачку.
потратил куча времени а код не работает, нужно разобраться где проблема в желези или коде.
еще кеил подчеркивеет enc28j60_packetReceive и другие функции с ошибкой ( declared implicitly)писал код по урокам
помогите разобраться.
почему у вас нет кодов к урокам ?
Кода к урокам нет потому, что они есть.
В каждой последней части каждого урока есть полный исходный код внизу страницы.