Урок 68
Часть 6
LAN. ENC28J60
В предыдущей части нашего урока мы разобрались со структурой кадров Ethernet, написали функцию их приёма и испытали этот процесс в терминальной программе, также познакомились с протоколом ARP.
Так как мы теперь знаем формат протокола ARP, то теперь мы сможем с ним работать. Для этого сначала добавим структуру для заголовка ARP
} enc28j60_frame_ptr;
//--------------------------------------------------
typedef struct arp_msg{
uint16_t net_tp;
uint16_t proto_tp;
uint8_t macaddr_len;
uint8_t ipaddr_len;
uint16_t op;
uint8_t macaddr_src[6];
uint8_t ipaddr_src[4];
uint8_t macaddr_dst[6];
uint8_t ipaddr_dst[4];
} arp_msg_ptr;
//--------------------------------------------------
Вернёмся теперь в файл net.c и добавим функцию для чтения пакета arp чуть выше функции чтения кадра
//--------------------------------------------------
uint8_t arp_read(enc28j60_frame_ptr *frame, uint16_t len)
{
}
//--------------------------------------------------
Возвращать будем 1 в том случае, если модуль узнает свой адрес в запросе.
Вызовем эту функцию в функции eth_read
if(frame->type==ETH_ARP)
{
...
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
arp_read(frame,len-sizeof(enc28j60_frame_ptr));
Добавим переменную для результата и поставим указатель на пакет arp, для этого добавим соответствующий код в функцию arp_read
uint8_t arp_read(enc28j60_frame_ptr *frame, uint16_t len)
{
uint8_t res=0;
arp_msg_ptr *msg=(void*)(frame->data);
Добавим ещё некоторые макросы в файл net.h
#define ETH_IP be16toword(0x0800)
//--------------------------------------------------
#define ARP_ETH be16toword(0x0001)
#define ARP_IP be16toword(0x0800)
#define ARP_REQUEST be16toword(1)
#define ARP_REPLY be16toword(2)
Также в файл net.h добавим макрос для задания желаемого IP-адреса для нашего устройства
#include "enc28j60.h"
//--------------------------------------------------
#define IP_ADDR {192,168,1,197}
Добавим в net.c глобальную переменную ip-адреса
uint8_t net_buf[ENC28J60_MAXFRAME];
uint8_t ipaddr[4]=IP_ADDR;
Вообщем, на данный момент адрес у нас будет статический. Может мы когда-то и дойдём до запроса DHCP к серверу для получения динамического адреса, но пока нам точно до этого далеко.
Добавим в нашу функцию чтения пакета ARP ряд условий для фильтрования лишних пакетов, не соответствующих нашим требований, правда пока этот фильтр пустотелый, а также вернём результат
arp_msg_ptr *msg=(void*)(frame->data);
if (len>=sizeof(arp_msg_ptr))
{
if ((msg->net_tp==ARP_ETH)&&(msg->proto_tp==ARP_IP))
{
if ((msg->op==ARP_REQUEST)&&(!memcmp(msg->ipaddr_dst,ipaddr,4)))
{
}
}
return res;
}
Вообщем, здесь мы хотим работать только с пакетом типа ARP, причём именно с запросом, и также убираем то, что не соответствует нашему сетевому адресу.
Теперь в функции arp_read добавим в тело условия отображение типа запроса, адресов источника и адресов приёмника, и установим результат
if ((msg->op==ARP_REQUEST)&&(!memcmp(msg->ipaddr_dst,ipaddr,4)))
{
sprintf(str1,"requestrnmac_src %02X:%02X:%02X:%02X:%02X:%02Xrn",
msg->macaddr_src[0],msg->macaddr_src[1],msg->macaddr_src[2],msg->macaddr_src[3],msg->macaddr_src[4],msg->macaddr_src[5]);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
sprintf(str1,"ip_src %d.%d.%d.%drn",
msg->ipaddr_src[0],msg->ipaddr_src[1],msg->ipaddr_src[2],msg->ipaddr_src[3]);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
sprintf(str1,"nmac_dst %02X:%02X:%02X:%02X:%02X:%02Xrn",
msg->macaddr_dst[0],msg->macaddr_dst[1],msg->macaddr_dst[2],msg->macaddr_dst[3],msg->macaddr_dst[4],msg->macaddr_dst[5]);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
sprintf(str1,"ip_dst %d.%d.%d.%drn",
msg->ipaddr_dst[0],msg->ipaddr_dst[1],msg->ipaddr_dst[2],msg->ipaddr_dst[3]);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
res=1;
}
Ну тут всё просто. Только есть один вопрос. С какой стати какое-то устройство будет нам посыть ARP-запрос, мы же ведь никому не говорили о нашем IP-адресе.
А мы ему поможем. Мы воспользуемся утилитой ping, встроенной в любую операционную систему. Хотя ping посылает ICMP-запрос, но если он видит, что в таблице соответствия адресов ARP нет нашего IP-адреса, то сначала он посылает ARP-запрос.
Поэтому мы и пошлём ping на назначенный нами IP-адрес из командной строки, понятное дело, собрав перед этим код и прошив контроллер
Мы видим, что наша плата на ping не откликнулась, но это ничего страшного, мы же ведь не писали ответ не только на ICMP-запросы, но и на ARP, но зато мы вот что видим в нашей терминальной программе
Мы видим и физический и сетевой адреса устройства, пославшего нам запрос (это наш основной ПК с адресом 192.168.1.89), а также сетевой адрес приёмника. Так как физический адрес приёмника пока неизвестен, то мы получаем нулевой адрес.
В следующей части нашего урока мы напишем несколько функций для отправки определённых пакетов и проверим ответ на запросы ARP в программе Wireshark.
Предыдущая часть Программирование МК STM32 Следующая часть
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
ı dont understant where is my connect cablo. can you help me?
enc28 to where?
my pc ethernet cablo ?
can you help me
Тем кто будет ставить в структуру «arp_msg» вместо uint8_t ipaddr_src[4] и ipaddr_dst[4] 32-х разрядные числа uint32_t ipaddr_src и ipaddr_dst,
рекомендую использовать выравнивание в структурах данных. Вот например у меня так объявлена структура
typedef struct ARP_msg{
uint16_t net_tp; // протокол канального уровня (Ethernet)
uint16_t ip_tp; // протокол сетевого уровня (IP)
uint8_t mac_addr_len; // lenght MAC address = 6
uint8_t ip_addr_len; // length IP-address = 4
uint16_t type; // тип сообщения (запрос/ответ)
uint8_t MAC_src[6]; // MAC-адрес отправителя
uint32_t ip_addr_src; // IP-адрес отправителя
uint8_t MAC_dst[6]; // MAC-адрес получателя
uint32_t ip_addr_dst; // IP-адрес получателя
} __attribute__ ((__packed__)) ARP_msg_struct;