STM Урок 68. LAN. ENC28J60. Часть 4

 

 

 

 

Урок 68

 

Часть 4

 

LAN. ENC28J60

 

 

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

 

Включим broadcast, вдруг пригодится

 

 //Enable Broadcast
 enc28j60_writeRegByte(ERXFCON,enc28j60_readRegByte(ERXFCON)|ERXFCON_BCEN);

 

Также можно включить фильтрацию пакетов с проверкой контрольной суммы с помощью определённых регистров EPMM0-EPMM7 и EPMMCS, но пока мы этого делать не будем, мы хотим видеть все пакеты. Если будет впоследствии это нужно, то мы всегда успеем отфильтровать. Просто я хочу отметить, что наша микросхема очень хорошо умеет фильтровать пакеты по маске и контрольной сумме.

Добавим в наш файл глобальную переменную нашего физического адреса

 

static uint8_t Enc28j60Bank;
uint8_t macaddr[6]=MAC_ADDR;

 

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

 

 //настраиваем канальный уровень
 enc28j60_writeRegByte(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
 enc28j60_writeRegByte(MACON2,0x00);
 enc28j60_writeOp(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
 enc28j60_writeReg(MAIPG,0x0C12);
 enc28j60_writeRegByte(MABBIPG,0x12);//промежуток между фреймами
 enc28j60_writeReg(MAMXFL,MAX_FRAMELEN);//максимальный размер фрейма
 enc28j60_writeRegByte(MAADR5,macaddr[0]);//Set MAC addres
 enc28j60_writeRegByte(MAADR4,macaddr[1]);
 enc28j60_writeRegByte(MAADR3,macaddr[2]);
 enc28j60_writeRegByte(MAADR2,macaddr[3]);
 enc28j60_writeRegByte(MAADR1,macaddr[4]);
 enc28j60_writeRegByte(MAADR0,macaddr[5]);

 

Кратко о том, что здесь происходит.

У нас есть четыре регистра для настройки данного уровня MACON1-MACON4, хотя в документации только 3, второго нет

 

image24

 

Поэтому во второй мы отправим все нули.

В первом регистре мы включим следующие биты: MARXEN, разрешающий принимать пакеты, а также TXPAUS и RXPAUS, которые включают аппаратное управление потоком.

В третьем регистре включим паддинг, или выравнивание. Для этого есть три бита PADCFG, в них мы включим 001 для выравнивание пакета нулями до 60 байт и добавления контрольной суммы 4 байта. Также здесь мы включаем биты FRMLNEN, который автоматически проверяет длину пакетов и TXCRCEN, который автоматически добавляет к пакету контрольную сумму.

Дальше идут стандартные значения регистров MAIPG и MABBIPG, устанавливающих для полнодуплекстного режима определённые задержки.

Затем MAMXFL, который устанавливает значения максимального размера фрейма (кадра).

Ну а дальше, я думаю, всем понятно, что мы обязаны занести в регистры наш MAC-адрес.

Дальше работаем с физическим уровнем.

Здесь не так всё просто.

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

У нас есть такой регистр MIREGADR, в который мы сначала обязаны занести значение адреса того или иного регистра PHY. А затем данные, предназначенные для этого регистра, мы заносим в регистры MIWRL (младший байт) и MIWRH (старший байт), причём именно в такой последовательности. Затем читаем регистр MISTAT на предмет сброса бита BUSY. Вот так вот.

Поэтому напишем отдельную функцию для записи регистра PHY

 

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

static void enc28j60_writePhy(uint8_t addres,uint16_t data)

{

  enc28j60_writeRegByte(MIREGADR, addres);

  enc28j60_writeReg(MIWR, data);

  while(enc28j60_readRegByte(MISTAT)&MISTAT_BUSY)

  ;

}

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

 

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

 

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 бит (отдельный регистр)

 

image25

 

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

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

 

    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 установим биты, разрешающие глобальные прерывания и приём пакетов по ожиданию прерывания.

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

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

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

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

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

 

image26

 

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

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

 

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

uint16_t enc28j60_packetReceive(uint8_t *buf,uint16_t buflen)

{

}

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

 

 

В регистре 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);

}

 

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

 

    enc28j60_writeOp(ENC28J60_BIT_FIELD_SET,ECON2,ECON2_PKTDEC);

  }

  return len;

}

 

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

 

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

 

 

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

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

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

 

 

Отладочную плату можно приобрести здесь STM32F103C8T6

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

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

 

 

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

 

STM LAN. ENC28J60

 

3 комментария на “STM Урок 68. LAN. ENC28J60. Часть 4
  1. Alex:

    static int gNextPacketPtr; - Объявлена

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

    if(enc28j60_readRegByte(EPKTCNT)>0)

    {

      enc28j60_writeReg(ERDPT,gNextPacketPtr); - Передана в функцию.

    Вопрос, какое значение переменной gNextPacketPtr мы передаем при первом вхождении в функцию?

  2. Роман:

    Зачем такая сложость?

    if(gNextPacketPtr-1>RXSTOP_INIT)

    enc28j60_writeReg(ERXRDPT,RXSTOP_INIT);

    else

    enc28j60_writeReg(ERXRDPT,gNextPacketPtr-1);
    Может просто так:?
    enc28j60_writeReg(ERXRDPT,gNextPacketPtr-1);

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

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

*