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 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

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

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

 

 

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

STM LAN. ENC28J60

 

 

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

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 не будет опубликован. Обязательные поля помечены *

*