AVR Урок 40. LAN. ENC28J60. Часть 4



 

Урок 40

 

Часть 4

 

LAN. ENC28J60

 

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

 

Теперь настроим физический уровень в инициализации

 

  enc28j60_writeRegByte(MAADR0, macaddr[5]);

  //настраиваем физический уровень

  enc28j60_writePhy(PHCON2, PHCON2_HDLDIS);//отключаем loopback

  enc28j60_writePhy(PHLCON, PHLCON_LACFG2| // светодиоды

  PHLCON_LBCFG2|PHLCON_LBCFG1|PHLCON_LBCFG0|

  PHLCON_LFRQ0|PHLCON_STRCH);

 

Ну здесь практически всё ясно из комментариев. На одни светодиоды у нас существует целых 16 бит (отдельный регистр)

 

image11

 

Со светодиодами всё в принципе ясно.

Ну и окончательные настройки в инициализации

 

  PHLCON_LFRQ0|PHLCON_STRCH);

  enc28j60_SetBank(ECON1);

  enc28j60_writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);

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

}

 

В регистре EIE установим биты, разрешающие глобальные прерывания и приём пакетов по ожиданию прерывания.

Также разрешим вообще приём пакетов.

Вот и вся инициализация. Наконец то!

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

Прием пакетов в микросхеме происходит через кольцевой буфер.

Давайте в даташите посмотрим пример приёма пакета

 

image12

 

В буфер ENC28J60 записывает пакет следующим образом. Сначала идут 2 байта со значением указателя на следующий пакет, затем 4 байта со статусом приёма, затем собственно данные пакета с контрольной суммой.

 

 

Создадим функцию для приёма пакета в файле enc28j60.c и создадим в ней локальную переменную для подсчёта длины пакета и последующего его возврата

 

//—————————————-

uint16_t enc28j60_packetReceive(uint8_t *buf, uint16_t buflen)

{

  uint16_t len = 0;

}

//—————————————-

 

В регистре EPKTCNT хранится количество принятых на данный момент пакетов, поэтому проверим его

 

uint16_t len = 0;

if (enc28j60_readRegByte(EPKTCNT) > 0)

{

}

 

Добавим глобальную переменную в файле enc28j60.c

 

static uint8_t Enc28j60Bank;

static int gNextPacketPtr;

 

В регистр ERDPT установим указатель

 

if (enc28j60_readRegByte(EPKTCNT) > 0)

{

  enc28j60_writeReg(ERDPT, gNextPacketPtr);

 

Начнём считывать пакет. Объявим локльную структуру для заголовка

 

enc28j60_writeReg(ERDPT, gNextPacketPtr);

//считаем заголовок

struct {

uint16_t nextPacket;

uint16_t byteCount;

uint16_t status;

} header;

 

Теперь собственно считаем заголовок пакета, содержащий укзатель на следующий пакет, количество байтов и статус пакета

 

} header;

enc28j60_readBuf(sizeof header, (uint8_t*) &header);

 

Запишем значение указателя на следующий пакет в глобальную переменную

 

enc28j60_readBuf(sizeof header, (uint8_t*) &header);

gNextPacketPtr = header.nextPacket;

 

Инициализируем переменную длины пакеты, отрезав от неё контрольную сумму

 

gNextPacketPtr = header.nextPacket;

len = header.byteCount 4; //remove the CRC count

 

Укоротим длину до заданной во входном параметре

 

len = header.byteCount 4; //remove the CRC count

if(len > buflen) len = buflen;

 

Проверим статус и считаем буфер

 

if(len > buflen) len = buflen;

if ((header.status & 0x80)==0) len = 0;

else enc28j60_readBuf(len, buf);

 

 

Завершим буфер нулём

 

else enc28j60_readBuf(len, buf);

buf[len] = 0;

 

Инициализируем указатель буфера на адрес следующего пакета

 

buf[len] = 0;

if (gNextPacketPtr 1 > RXSTOP_INIT)

enc28j60_writeReg(ERXRDPT, RXSTOP_INIT);

else

enc28j60_writeReg(ERXRDPT, gNextPacketPtr 1);

 

Счётчик принятых пакетов также уменьшим на 1 и выйдем из условия

 

  enc28j60_writeReg(ERXRDPT, gNextPacketPtr 1);

  enc28j60_writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);

}

 

Возвратим длину принятого пакета

 

  }

  return len;

}

 

В файле net.c создадим функцию постоянного опроса сети

 

//———————————————————

void net_poll(void)

{

}

//———————————————————

 

Ну и, раз у нас опрос постоянный, то и вызовем данную функцию в бесконечном цикле функции main(), создав предварительно прототип в net.h

 

while (1)

{

  net_poll();

}

 

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

Фреймы из себя представляют вполне себе уже осознанный поток, имеющий определённый протокол.

В настоящее время в большинстве случаев используется пртокол канального уровня Ethernet Version 2. Он состоит из физического (MAC) адреса приёмного устройства, физического адреса устройства-отправителя, 2 байта либо длины поля данных кадра, либо идентификатор протокла (ARP,IP и т.д.), собственно данные, которые будут также на других уровнях иметь свои протоколы, и контрольную сумму в размере 4 байтов, которую рассчитывать мы научимся чуть позже.

Вот так выглядит фрейм более наглядно

 

image13

 

Создадим структуру для фрейма в файле net.h

 

void net_poll(void);

//——————————————

typedef struct enc28j60_frame {

uint8_t addr_dest[6];

uint8_t addr_src[6];

uint16_t type;

uint8_t data[];

} enc28j60_frame_ptr;

 

В файле net.c добавим глобальную переменную

 

#include «net.h»

//———————————————

uint8_t net_buf[ENC28J60_MAXFRAME];

 

 

Создадим две переменных в нашей функции net_poll

 

void net_poll(void)

{

  uint16_t len;

  enc28j60_frame_ptr *frame = (void*)net_buf;

 

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

 

 

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

 

 

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

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

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

 

 

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

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

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

 

 

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

 

AVR LAN. ENC28J60

Один комментарий на “AVR Урок 40. LAN. ENC28J60. Часть 4
  1. Игорь:

    Добрый день. Не могли бы Вы прокомментировать строку
    «enc28j60_frame_ptr *frame = (void*)net_buf;»
    в функции void net_poll(void).
    Я понимаю, что здесь Вы «совмещаете адреса» буфера net_buf и структуры frame, т.о. структура и буфер — одно и тоже. Но я мегу понять, почему компилятор нормально относится к тому, что Вы указателю типа enc28j60_frame_ptr, присваиваете указатель типа void.
    Вот если сделать так:
    int *a = (void*)b;
    то gcc выдаст предупреждение, а g++ вообще ошибку.
    Но в случае со структурой, и тот и другой компилятор вполне себе всё нормально принимают!

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

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

*