STM Урок 68. LAN. ENC28J60. Часть 6

 

 

 

 

Урок 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-адрес из командной строки, понятное дело, собрав перед этим код и прошив контроллер

 

image31

 

Мы видим, что наша плата на ping не откликнулась, но это ничего страшного, мы же ведь не писали ответ не только на ICMP-запросы, но и на ARP, но зато мы вот что видим в нашей терминальной программе

 

image32

 

Мы видим и физический и сетевой адреса устройства, пославшего нам запрос (это наш основной ПК с адресом 192.168.1.89), а также сетевой адрес приёмника. Так как физический адрес приёмника пока неизвестен, то мы получаем нулевой адрес.

 

В следующей части нашего урока мы напишем несколько функций для отправки определённых пакетов и проверим ответ на запросы ARP в программе Wireshark.

 

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

 

 

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

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

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

 

 

Отладочную плату можно приобрести здесь STM32F103C8T6

Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN

Переходник USB to TTL можно приобрести здесь USB to TTL ftdi ft232rl

 

 

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

 

STM LAN. ENC28J60

 

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

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

*