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 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

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

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

 

 

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

STM LAN. ENC28J60

 

 

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

STM LAN. ENC28J60

 

6 комментариев на “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);

  3. ductu:

    else enc28j60_readBuf(len, buf);//

    buf[len]=0;//почему удалть знаение??

  4. serg_555:

    if((header.status&0x80)==0) какой флаг проверяется ?

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

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

*