AVR Урок 41. LAN. ENC28J60. ARP. Часть 1

 

 

 

 

Урок 41

 

Часть 1

 

LAN. ENC28J60. ARP

 

 

Сегодня мы продолжим начатую в предыдущем уроке тему по программированию модуля LAN на микросхеме ENC28J60, который позволяет нам изучить программирование сетевых протоколов вручную, преследуя при этом цель — "Понять работу локальной сети и всех её уровней и протоколов изнутри — на низком уровне". Если мы достигнем этой цели, то в дальнейшем на будет гораздо проще ориентироваться в программировании локальных сетей с использованием готовых стеков, реализованных в тех или иных библиотеках.

Поэтому продолжим изучать глубже протокол ARP, который позволяет обмениваться пакетами между устройствами на более нижних сетевых уровнях, не используя при этом верхние уровни, а также, как мы знаем уже из предыдущего занятия, позволяет узнать физический адрес интересующего нас устройства, локальный адрес которого мы уже знаем.

Напрашивается при этом вполне логичный вопрос: "А зачем нам это, мы же это уже проделали на пршлом занятии?". А на вопрос есть ответ. Мы это делали в одном направлении. То есть мы обеспечивали выполнение этой задачи только в том случае, если внешнему устройству нужно было узнать физический адрес нашего устройства. То есть мы создали своего рода ARP-сервер. Но нужен нам также и ARP-клиент, с помощью которого уже наш модуль будет пытаться узнать сетевой адрес какого-то внешнего устройства. Причём клиент на практике возможно будет даже нужен чаще, так как без этого мы не сможем ничего запросить у других устройств, так как ни один физический адрес внешних устройств мы не знаем. То есть мы даже передать ничего не сможем, так как, учитывая вышенаписанное, нам и передать-то некуда. Так что вот такой ответ. Поэтому продолжаем.

Но для начала давайте исправим некоторые недочёты, которые были допущены на прошлом занятии, а также немного усовершенствуем наш код, для чего мы вынесем весь код, связанный с ARP запросами и ответами в отдельный модуль.

Но сначала недочёты.

Во-первых, чтобы у нас сохранился проект предыдущего занятия, давайте теперь дадим проекту, который мы будем продолжать на сегодняшнем занятии, новое имя. Создадим новый проект и назовём его ENC28J60_ARP. Все файлы с исходными кодами, кроме main.c, мы скопируем из предыдущего проекта ENC28J60 и подключим их проект, а код из файла main.c прошлого проекта мы скопируем в одноимённый файл нового проекта, перед этим полностью очистив его от нового текста.

Попробуем собрать код. Если всё нормально и без ошибок собралось, то как и договорились, начнём исправлять недочёты, для чего перейдём в файл enc28j60.c, найдём в нём функцию enc28j60_readOp и увидим в ней, что ложный байт мы вовсе и не пропустили. Мы сначала использовали соответствующую функцию SPI_SendByte, которая отправила 0 в модуль, и если пройти по коду данной функции, то она в регистр SPDR записала значение из модуля, то есть всё равно обмен произошел, а дальше мы просто считали значение из регистра, тем самым мы ничего не пропустили, никаких ложных байтов. Поэтому изменим код на следующий.

Сначала заранее объявим локальную переменную

 

static uint8_t enc28j60_readOp(uint8_t op,uint8_t addres)

{

  uint8_t result;

 

А затем уже исправим код

 

//пропускаем ложный байт

if(addres & 0x80) SPI_ReceiveByte();

result=SPI_ReceiveByte();

SS_DESELECT();

 

Вот теперь в случае присутствия логической 1 в байте 7 мы уже проделаем обмен дважды, а в обычном случае — только единожды.

Теперь перейдём в хедер-файл enc28j60.h и добавим там ещё один регистр в 3 банк

 

#define MISTAT (0x0A|0x60|0x80)

#define ECOCON (0x15|0x60)

//--------------------------------------------------

 

вернёмся в enc28j60.c и в функции инициализации в самом конце мы уменьшим вдвое частоту генератора и после этого немного подождём

 

  enc28j60_writeOp(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN);//разрешаем приём пакетов

  //Включим делитель частоты генератора 2, то есть частота будет 12,5 MHz

  enc28j60_writeRegByte(ECOCON,0x02);

  _delay_us(15);

}

 

Согласно технической документации там вообще можно подождать меньше микросекунды, но нам спешить некуда и мы подождём на всякий случай побольше.

Перейдём теперь в функцию отправки пакетов enc28j60_packetSend и там также добавим некоторую задержку в самом конце. Она необязательна, но в загруженных сетях с ней стало работать лучше, что подтвердилось эксперементальным путём

 

  enc28j60_writeOp(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS);

  //небольшая задержка, почему-то без неё не работает в загруженных сетях

  _delay_ms(1);

}

 

Пока с поправками всё.

 

 

Давайте теперь как-то весь функционал ARP вынесем в отдельный модуль, для чего создадим файлы arp.c и arp.h и подключим их к проекту.

Первоначальное содержание данных файлов стандартное:

 

arp.c

 

#include "arp.h"

//--------------------------------------------------

 

arp.h

 

#ifndef ARP_H_

#define ARP_H_

//--------------------------------------------------

#include <string.h>

#include "enc28j60.h"

#include "net.h"

#include "usart.h"

//--------------------------------------------------

//--------------------------------------------------

#endif /* ARP_H_ */

 

Теперь нам эту новую библиотеку нам надо как-то подключить. А почему как-то, потому что необычно, иначе будут ошибки.

Перейдём в заголовочный файл net.h и и подключим наш новый модуль в самом низу файла (это очень важно)

 

//--------------------------------------------------

#include "arp.h"

//--------------------------------------------------

#endif /* NET_H_ */

 

Теперь из файла net.c в файл arp.c перенесём сначала весь код функции arp_read, конечно удалив затем этот код из файла-источника, также в заголовочном файле arp.h создадим на данную функцию прототип. Теперь, чтобы данная функция работала, мы также в файл arp.c добавим глобальную переменную, вернее мы подключим готовую, чтобы не занимать много памяти

 

#include "arp.h"

//--------------------------------------------------

extern char str1[60];

//--------------------------------------------------

 

То же самое проделаем и с функцией arp_send и также добавляем на неё прототип. Но так как данная функция использует вызов функции eth_send, которая осталась в другом файле, то на неё мы также сделаем прототип в файле net.h причём внизу файла перед подключением файла arp.h

 

//--------------------------------------------------

void eth_send(enc28j60_frame_ptr *frame, uint16_t len);

//--------------------------------------------------

#include "arp.h"

 

Также давайте подключим переменную с MAC-адресом в этот же файл arp.c

 

extern char str1[60];
extern uint8_t macaddr[6];
//--------------------------------------------------

 

В следующей части нашего занятия мы обработаем приём строки с IP-адресом из шины USART и произведём её преобразование в числовую величину.

 

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

 

 

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

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

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

 

 

Приобрести плату Atmega 328p Pro Mini можно здесь.

Программатор (продавец надёжный) USBASP USBISP 2.0

Ethernet LAN Сетевой Модуль можно купить здесь (модуль SD SPI в подарок) ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

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

 

AVR LAN. ENC28J60. ARP

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

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

*