Урок 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, второго нет
Поэтому во второй мы отправим все нули.
В первом регистре мы включим следующие биты: 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 бит (отдельный регистр)
Со светодиодами всё в принципе ясно.
Ну и окончательные настройки в инициализации
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 установим биты, разрешающие глобальные прерывания и приём пакетов по ожиданию прерывания.
Также разрешим вообще приём пакетов.
Вот и вся инициализация. Наконец то!
Теперь нам нужно научиться принимать пакеты, так как весь обмен по локальной сети делится на пакеты.
Прием пакетов в микросхеме происходит через кольцевой буфер.
Давайте в даташите посмотрим пример приёма пакета
В буфер 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 (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
static int gNextPacketPtr; - Объявлена
В регистр ERDPT установим указатель
if(enc28j60_readRegByte(EPKTCNT)>0)
{
enc28j60_writeReg(ERDPT,gNextPacketPtr); - Передана в функцию.
Вопрос, какое значение переменной
gNextPacketPtr мы передаем при первом вхождении в функцию?
Ноль, так как функция глобальная.
Зачем такая сложость?
if(gNextPacketPtr-1>RXSTOP_INIT)
enc28j60_writeReg(ERXRDPT,RXSTOP_INIT);
else
enc28j60_writeReg(ERXRDPT,gNextPacketPtr-1);
Может просто так:?
enc28j60_writeReg(ERXRDPT,gNextPacketPtr-1);
else enc28j60_readBuf(len, buf);//
buf[len]=0;//почему удалть знаение??
ductu, у Вас не читаются пакеты?
if((header.status&0x80)==0) какой флаг проверяется ?