Урок 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 с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добавить комментарий