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 с адаптером можно здесь USBASP USBISP 3.3 с адаптером

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

 

 

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

AVR LAN. ENC28J60

 

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

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

*