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

 

2 комментария на “STM Урок 68. LAN. ENC28J60. Часть 6
  1. Ahmet:

    ı dont understant where is my connect cablo. can you help me?

    enc28 to where?
    my pc ethernet cablo ?

    can you help me

  2. Сергей:

    Тем кто будет ставить в структуру «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;

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

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

*