Подключение микроконтроллера к локальной сети: работаем с ENC28J60

Кому-то она может показаться тупым копированием даташита. Но это не совсем так, тут есть и примеры кода и описание различных граблей.
Но даташит всё равно может пригодится. А так же еррата.
Краткое содержание:
- Включение ENC28J60
- Архитектура ENC28J60
- Обмен данными по SPI
- Инициализация
- Отправка пакетов
- Приём пакетов
- Заключение
Примеры кода написаны под AVR. Впрочем из платформенно-зависимых вещей тут только работа со SPI.
ENC28J60 — Ethernet-адаптер (проще говоря, «сетевая карточка») на одном чипе, разработанный вражеской компанией Microchip. Микросхемка не требует для работы много обвязки из внешних компонентов, к МК подключается с помощью SPI. Полностью соответствует спецификации IEEE 802.3 и, кроме того, поддерживает много дополнительных прикольных фич (например, аппаратную фильтрацию пакетов).
А теперь, немного о грустном. Количество багов в ENC28J60 трудно описать печатными словами. Из-за них половина фич либо работает нестабильно, либо может нарушать работу других важных модулей. Хотя, главное, что принимать и отправлять пакеты девайс всё-таки умеет. :)
Подключаемся
Микросхемка выпускается в 28-ножечных DIP, SOIC и QFN корпусах. Попадаются и готовые модули со всей обвязкой и разъёмом для сетевого кабеля.
Вот стандартная схема включения ENC28J60 (распиновка для DIP корпуса):

Увеличить схему
Питание — 3.3 В. Но входы микросхемы совместимы с 5-вольтовыми TTL уровнями.
Потребляет микросхемка прилично — 250 мА. Нужно столько для питания драйверов передатчика. Есть режим «пониженного энергопотребления», когда вся «силовая» часть отключается.
VCAP — выход встроенного преобразователя на 2,5 В (именно такое напряжение используется при передачи данных по сетевому кабелю). К этому выводу нужно подключить конденсатор на 10 мкф. Даташит не рекомендует питать от этого вывода что-то ещё.
R7 (RBIAS) — резистор для какой-то балансировки. В даташите указан номинал 2 кОм с допуском 1%. Однако в ENC28J60 есть баг, из-за которого в ревизиях микросхемы 1 и 4 нужно использовать резистор на 2,7 кОм. А в ревизиях 5 и 7 — на 2,32 кОм. Иначе выходной сигнал не будет соответствовать спецификации IEEE. Западло, однако. Ревизию можно определить только чтением соответствующего регистра. Мне попалась ревизия 7 — судя по всему, последняя. Так что, стоит запаять резистор на 2,32 кОм, прочитать ревизию, и, если вдруг попадётся другая ревизия, резистор заменить. Где взять резистор на такой необычный номинал? Можно бегать по магазинам и искать, или просто взять горсточку обычных неточных резисторов на 2,2 кОм и методом тыка выбрать наиболее близкий к требуемому номиналу. Впрочем, когда я только начинал экспериментировать с ENC28J60, об этом баге не знал и ставил резистор на 2 кОм. Проблем не было. :)
TR1 и TR2 — не абы какие трансформаторы, а специальные Ethernet-фильтры (Ethernet magnetics). Представляют собой систему из нескольких катушек на ферритовых колечках. Обычно выпускаются в виде готовых сборок (оба фильтра в одном корпусе, совместимом с DIP-16). Нужны они, судя по всему, для развязки, защиты от статики, etc. (сетевой кабель может иметь длину до 100, а то и 300 м — представь какой статический потенциал может быть между девайсами на таком расстоянии). Где их взять? Вариант номер раз — взять мёртвую/ненужную (или хотя-бы дешёвую) сетевую карточку, и выпаять из неё. Кстати, в ней же найдутся точные резисторы на 50 Ом, розеточка для сетевого кабеля и, если повезёт, кварц на 25 МГц. Вариант номер два — использовать сетевой разъём с уже встроенными фильтрами («MagJack»). Деталька редкая и дорогая. Впрочем, достать её можно из трупика материнской платы — обычно там используются именно они. Правда, чтобы выпаять такую массивную деталь из толстенной платы с кучей слоёв меди, понадобится паяльная лампа или что-то в этом роде. К тому же часто деталька идёт в виде неразделимого блока с USB-A розеточками, которые будут вносить неприятную энтропию в конструкцию (если, конечно, в ней не будет USB-хоста, хе-хе).
Катушка L1 — ферритовое колечко диаметром 5мм с несколькими витками проволоки. Если колечка под рукой нет, можно поставить дроссель, например, на 100 мкГн.
ENC28J60 автоматически определяет полярность подключенных светодиодов. Причём полярность светодиода, подключенного к выводу LEDB влияет на дуплексный режим работы микросхемы. Если светодиод подключен как показано на схеме, катодом к микрухе — ENC28J60 инициализируется в полнодуплексном режиме. Соответственно, если подключен анодом — то в полудуплексном. Если светодиод не подключен — состояние не определено. Впрочем, дуплексный режим можно изменить при инициализации.
Конденсатор C2 на 2 кВ служит для разрядки статики при подключении кабеля. Естественно, можно поставить конденсатор и на меньшее напряжение. Ни на что это не повлияет, разве что твой девайс не будет формально соответствовать стандарту.

Как выяснилось, вход RESET у ENC28J60, несмотря на то, что сказано в даташите, не подтянут! Его обязательно нужно соединить с питанием, иначе микруха может сброситься в самый неудачный момент из-за любой наводки.
Выходы прерываний использовать не обязательно, и, как мне кажется, и не нужно. Забрать принятый пакет или загрузить пакет для отправки — слишком длительная процедура, чтобы выполнять её в обработчике прерывания. Лучше делать это из главного цикла.
С выхода CLKOUT можно снимать тактовый сигнал (с настраиваемым делителем). Правда, из-за бага, при входе в спящий режим сигнал пропадает (хотя главные часики продолжают тикать). Блин, инженеры из Microchip совсем забивают на тестирование?!
Таким образом, для связи с микроконтроллером можно использовать только 4 провода — стандартную шину SPI.
Архитектура ENC28J60
На этой картинке я нарисовал основные блоки ENC28J60:

- PHY — физический уровень. Приёмник, передатчик, драйверы, etc. В общем, всё, что необходимо для работы с определённой средой передачи данных (medium). В данном случае — с витой парой, по стандарту 10BASE-T. Доступ к PHY происходит исключительно через MII — Medium Independent Interface. MII задуман так, чтобы следующий (канальный) уровень мог абстрагироваться от типа среды передачи данных. PHY имеет свой набор 16-битных регистров (специфичных для среды передачи данных), доступ к которым осуществляется через MII. Не нужно пугаться аббревиатуры MII — это всего лишь набор регистров, через которые управляется PHY.
- MAC (Medium Access Controller) — канальный уровень. В него входит вся логика, необходимая для отправки и приёма пакетов в сети Ethernet. MAC занимается адресацией, рассчётом контрольной суммы, фильтрацией принимаемых пакетов, разрешением коллизий (в полудуплескном режиме), etc. Обменивается со следующим, сетевым уровнем готовыми пакетами, а с физическим — отправляемыми и принимаемыми «сырыми» байтами.
- Управляющая логика занимается всем остальным. В том числе, обслуживает буфер, из которого MAC берёт отправляемые данные и складывает принятые. Управляет режимами энергопотребления, etc.
Вся память в ENC28J60 делится на буфер для данных, управляющие регистры и регистры PHY.
Буфер для данных
В ENC28J60 есть буфер размером 8 КБ. Часть этого буфера обычно выделяется для приёма пакетов, остальное можно использовать как угодно. Например, для отправляемых данных.
Управляющие регистры
Управляющие регистры делятся на 4 банка (ну нравится Microchip'овским инженерам сегментированное адресное пространство). Каждый банк имеет размер в 32 регистра, причём последние 5 ячеек (0x1b..0x1f) всегда мапятся на одни и те же регистры, вне зависимости от того, какой банк выбран.
Страшно?

Пугаться количества не нужно. Сейчас всё структурируем, и станет просто.
Основная часть регистров имеет префикс E (Ethernet). Регистры MAC — с префиксом MA, регистры MII — с префиксом MI.
Регистры можно разделить на функциональные группы.

Основное
Назначение отдельных бит, как правило, понятно из их названий. :)
Здесь я опишу лишь то, что нам реально понадобится. Остальные биты описаны в даташите. Однако перед использованием той или иной фичи, нужно заглянуть в еррату и проверить нет ли с данной фичей проблем. Например, DMA (биты DMAST и CSUMEN) еррата использовать не рекомендует вообще. Так-то!

- BSEL1:BSEL0 — выбор банка регистров.
- RXEN — разрешает приём данных.
- TXRTS — разрешает отправку пакета (автоматически сбрасывается после того, как отправка пакета будет завершена).

- VRPS — разрешает перевод стабилизатора питания в экономичный режим при включении режима пониженного энергопотребления (бит PWRSV). Данный бит можно установить при инициализации и забыть про него.
- PWRSV — включает режим пониженного энергопотребления. Прежде чем устанавливать этот бит, следует запретить приём новых пакетов и убедиться что приём данных завершён. После выхода из режима пониженного энергопотребления, нужно подождать 1 мс, чтобы PHY вошёл в рабочий режим.
- PKTDEC — при установке этого бита значение счётчика пакетов уменьшается на 1.
- AUTOINC — включает автоматическое инкрементирование указателей чтения и записи буфера (для удобства последовательного чтения и записи данных). Этот бит установлен после сброса, и трогать его ни к чему.

- TXABRT — флаг завершения передачи с ошибкой.
- RXBUSY — признак работы приёмника (установлен, если принимаются данные).
Регистр EPKTCNT — счётчик принятых пакетов. Автоматически инкрементируется при успешно принятом пакете. Уменьшается вручную, установкой бита PKTDEC в регистре ECON2. Вручную записывать в этот регистр ничего нельзя, т.к. все операции с ним должны выполняться атомарно.
Указатели буфера
Регистры, указывающие куда приёмник будет складывать данные, откуда данные будет брать передатчик, etc. Каждый указатель занимает два регистра. Например, младший байт ERDPT хранится в регистре ERDPTL, а старший — в ERDPTH.
ERDPT и EWRPT — указатели чтения и записи буфера. Указывают по какому адресу данные будут считываться из буфера или записываться в буфер микроконтроллером.
Если ECON2.AUTOINC установлен, данные будут считываться и записываться последовательно (соответствующий указатель будет инкрементироваться после каждого байта).

ETXST и ETXND — начало и конец отправляемого пакета. Например, если мы хотим отравить пакет размером 256 байт, лежащий в буфере по адресу 0x1800, устанавливаем ETXST в 0x1800 и ETXND в 0x18ff.
ERXST и ERXND — начло и конец кольцевого буфера, в который будут приниматься пакеты. Из-за бага в ENC28J60, в ERXST можно записывать только 0. Например, если мы хотим выделить 4096 байт под приём пакетов, пишем в ERXST 0, а в ERXND 0x0fff. Когда приём пакетов разрешён, трогать эти регистры нельзя.
ERXRDPT и ERXWRPT — указатели кольцевого буфера. Доходя до конца буфера (ERXND), указатель затем перемещается на начало (ERXST).
ERXWRPT — указывает на место, куда приёмник положит следующий принятый пакет. Этот указатель доступен только для чтения. Он автоматически инициализируется вместе с ERXST и автоматически обновляется после приёма пакета.
ERXRDPT — указывает на то место, откуда микроконтроллер будет забирать принятые пакеты.
Если микроконтроллер долго не будет забирать пакеты из буфера, ERXWRPT может «догнать» ERXRDPT. В таком случае приёмнику некуда будет складывать данные и приходящие данные начнут выбрасываться. Чтобы освобождать место в буфере, микроконтроллер, после того, как заберёт пакет из буфера, должен перемещать ERXRDPT к следующему пакету.
DMA
В ENC28J60 есть модуль DMA, позволяющий выполнять операции над блоками. А именно, копирование блока внутри буфера и аппаратное вычисление контрольной суммы для IP и других протоколов. Второе даже могло бы пригодиться. Но из-за имеющегося бага, использование этой фичи может привести к потере входящих пакетов. Обидно.
Фильтрация пакетов
ENC28J60 отлично умеет фильтровать пакеты. Это важно, особенно, если сеть на хабах. :)
Правила фильтрации пакетов устанавливает регистр ERXFCON.

- UCEN включает фильтр Unicast-пакетов. Пакет проходит фильтр, если адрес получателя в нём равен нашему MAC-адресу.
- MCEN включает фильтр Multicast-пакетов. Пакет проходит фильтр, если является Multicast-пакетом.
- BCEN включает фильтр Broadcast-пакетов. Пакет проходит фильтр, если является широковещательным.
- MPEN включает фильтр волшебных пакетов. Пакет проходит фильтр, если является волшебным и направлен на наш MAC-адрес.
- PMEN включает фильтрацию по шаблону.
- HTEN включает фильтрацию по хэш-таблице.
- ANDOR — группировка фильтров. Если бит установлен — пакет принимается только при прохождении всех выбраных фильтров. Если сброшен — прохождения одного фильтра достаточно.
- CRCEN разрешает проверку контрольной суммы. Если установлен, принимаются пакеты только с корректной контрольной суммой.
Фильтрация по шаблону заключается в следующем. Из принятого пакета, по смещению, записанному в регистрах EPMO, берётся окно размером 64 байта. Из этого окна выбираются байты по маске, записанной в регистрах EPMM (например, если бит 0 в регистре EPMM0 установлен, выбирается байт 0 из окна, etc.). От выбранных байт рассчитывается контрольная сумма. Если она совпадает со значением в регистрах EPMCS, фильтр пройден.
При фильтрации по хэш-таблице, рассчитывается хэш от адреса получателя, указанного в заголовке пакета. Берётся соответствующий бит из регистров EHT. Например, если хэш равен 0x5, берётся бит 5 из регистра EHT0. Если бит установлен, фильтр пройден.
MAC-адрес
Те самые 6 байт, которые будут идентифицировать наш девайс в локальной сети. Нужны ENC28J60 для фильтрации входящих пакетов. Хранятся в обратном порядке, т.е. для адреса 01:23:45:67:89:ab в MAADR0 пишем 0xab, в MAADR1 — 0x89, etc.
Регистры MAC

- MARXEN — разрешить MAC принимать пакеты.
- TXPAUS, RXPAUS — включают аппаратное управление потоком.

- FULLDPX — включить полнодуплексный режим. Дуплексный режим PHY (PHCON1.PDPXMD) должен быть таким же. Значение после сброса зависит от полярности подключения светодиода к ножке LEDB.
- FRMLNEN — включить автоматическую проверку длины принимаемых и отправляемых фреймов.
- TXCRCEN — включить автоматическое добавление контрольной суммы к фрейму.
- PADCFG2:PADCFG0 — настройка паддинга фреймов:
- 001 — выравнять пакет нулями до 60 байт, затем добавить контрольную сумму (4 байта). Бит TXCRCEN также должен быть установлен.
- 000 — не выравнивать пакеты.
Регистры MAMXF — максимальная длина принимаемого и отправляемого пакета. Обычно 1518 байт или меньше. Ставим столько, сколько сможет утащить наш МК. Пакеты большего размера будут отбрасываться.
MABBIPG, MAIPGL и MAIPGH — задержка (gap) между отправляемыми пакетами. Стандартные значения:
- MABBIPG — 0x15 (в полнодуплексном режиме) или 0x12 (в полудуплексном).
- MAIPGL — 0x12.
- MAIPGH — 0x0c.
MACLCON1 и MACLCON2 — настройка задержки и ретрансмиссий при возникновении коллизии. Оставляем по умолчанию.
Регистры MII
Регистры MII служат для доступа к регистрам PHY. Во как!

Для чтения регистра PHY:
- Выставляем его адрес в регистре MIREGADR.
- Устанавливаем бит MICMD.MIIRD.
- Ждём, пока MISTAT.BUSY очистится.
- Вручную очищаем MICMD.MIIRD.
- Забираем данные из регистров MIRD.
Для записи в регистр PHY:
- Выставляем его адрес в регистре MIREGADR.
- Записываем данные в регистры MIWR. Сначала MIWRL, затем MIWRH.
- Ждём, пока MISTAT.BUSY очистится.
Управление ножкой CLKOUT
В ENC28J60 можно брать тактовый сигнал (с делителем) с ножки CLKOUT. Из-за бага, сигнал может пропадать при входе в режим пониженного энергопотребления.

Биты ECOCON2:0 устанавливают делитель:
- 000 — ножка CLKOUT подтянута к земле.
- 001 — делитель на 1 (25 МГц).
- 010 — делитель на 2 (12,5 МГц).
- 011 — делитель на 3 (8,333333 МГц).
- 100 — делитель на 4 (6,25 МГц).
- 101 — делитель на 8 (3,125 МГц).
Регистры PHY
Регистры PHY раположены в отдельном адресном пространстве. Получить к ним доступ можно через регистры MII. Размер адресного пространства — 32 регистра, всего заюзано 9 адресов.

Регистры PHY 16-битные. Используются для различных настроек PHY. Целый регистр выделен под настройки светодиодов. Эстетика!

- PDPXMD — дуплексный режим PHY. Должен соответствовать дуплексному режиму MAC (MACON3.FULLDPX). Начальное значение зависит от полярности светодиода, подключенного к ножке LEDB.

- HDLDIS — запрещает «заворот назад» (loopback) отправляемых данных в полудуплексном режиме.

- LLSTAT — «асинхронный» бит сосотяния линка. Читается как 1 если линк есть и не пропадал с момента предыдущего чтения этого бита.

- LSTAT — состояние линка. Бит установлен, если линк есть.

Управление светодиодиками.
- STRCH — разрешает «растягивание» событий. Если бит включен, события будут отмечаться вспышкой светодиода определённой длительности. Если выключен — светодиод будет зажигаться только во время события (передача/приём данных, etc.).
- LFRQ — длительность вспышки светодиода:
- 00 — 40 мс.
- 01 — 73 мс.
- 10 — 139 мс.
- LACFG и LBCFG — выбираем, что именно будут показывать светодиоды, подключенные к ножкам LEDA и LEDB:
- 0001 — передача.
- 0010 — приём.
- 0100 — состояние линка.
- 0101 — дуплексный режим.
- 0111 — приём и передача.
- 1000 — включен.
- 1001 — выключен.
- 1100 — приём и состояние линка.
- 1101 — приём, передача и состояние линка.
В общем, большая часть регистров используется исключительно для конфигурации и после завершения инициализации не трогается.
SPI
Обмен по SPI ведётся в режиме 0 (CPOL=0, CPHA=0). ENC28J60 поддерживает скорость передачи данных по SPI до 10 мбит/с.
// Указываем как у нас подключено
#define ENC28J60_SPI_DDR DDRB
#define ENC28J60_SPI_PORT PORTB
#define ENC28J60_SPI_CS (1<<PB4)
#define ENC28J60_SPI_MOSI (1<<PB5)
#define ENC28J60_SPI_MISO (1<<PB6)
#define ENC28J60_SPI_SCK (1<<PB7)
#define enc28j60_select() ENC28J60_SPI_PORT &= ~ENC28J60_SPI_CS
#define enc28j60_release() ENC28J60_SPI_PORT |= ENC28J60_SPI_CS
// Инициализация ENC28J60
void enc28j60_init()
{
// Настроим ножки
ENC28J60_SPI_DDR |= ENC28J60_SPI_CS|ENC28J60_SPI_MOSI|ENC28J60_SPI_SCK;
ENC28J60_SPI_DDR &= ~ENC28J60_SPI_MISO;
enc28j60_release();
// Максимальная скорость SPI (CLK/2)
SPCR = (1<<SPE)|(1<<MSTR);
SPSR |= (1<<SPI2X);
// Остальная инициализация
// ...
}
// Передача данных через SPI
uint8_t enc28j60_rxtx(uint8_t data)
{
SPDR = data;
while(!(SPSR & (1<<SPIF)))
;
return SPDR;
}
Обмен данными с ENC28J60 выполняется транзакциями. Транзакция начинается с отправки микроконтроллером команды. Затем идут опциональные данные (приём или передача). Завершается транзакция «поднятием» ножки CS.
Чтение:

При чтении данных уровень на линии MOSI не имеет значения.
Запись:

При записи линия MISO находится в Z-состоянии (т.е. не подключена ни к чему).
#define enc28j60_rx() enc28j60_rxtx(0xff)
#define enc28j60_tx(data) enc28j60_rxtx(data)
Команда состоит из опкода и аргумента. При чтении или записи регистра, аргумент содержит адрес регистра.

Операции с регистрами
Для чтения регистра контроллер отправляет ENC28J60 команду чтения регистра и забирает значение. При чтении регистров MAC или MII, контроллер должен пропустить 1 «ложный» байт, затем прочитать значение.
// Операция чтения
uint8_t enc28j60_read_op(uint8_t cmd, uint8_t adr)
{
uint8_t data;
// Низкий уровень на CS
enc28j60_select();
// Отправляем команду
enc28j60_tx(cmd | (adr & 0x1f));
// При необходимости, пропускаем "ложный" байт
if(adr & 0x80)
enc28j60_rx();
// Читаем данные
data = enc28j60_rx();
// Высокий уровень на ножке CS
enc28j60_release();
return data;
}
// Операция записи
void enc28j60_write_op(uint8_t cmd, uint8_t adr, uint8_t data)
{
enc28j60_select();
// Отправляем команду
enc28j60_tx(cmd | (adr & 0x1f));
// Отправляем значение
enc28j60_tx(data);
enc28j60_release();
}
Перед тем, как осуществлять доступ к определённому регистру, нужно выбрать банк. Чтобы не отправлять команду переключения банка при каждой операции с регистром, можно закэшировать текущий банк и переключать банк только при необходимости.
Также, нам понадобится заголовочный файл с определениями регистров. Для удобства, в определение регистра можно включить также адрес банка и признак регистра MII/MAC.
// С этого адреса начинаются глобальные для всех банков регистры
#define ENC28J60_COMMON_CR 0x1B
// Банк 0
#define ERDPTL 0x00
#define ERDPTH 0x01
#define ERDPT ERDPTL
//...
// Банк 1
#define EHT0 (0x00 | 0x20)
#define EHT1 (0x01 | 0x20)
//...
// Банк 2, регистры MAC/MII
#define MACON1 (0x00 | 0x40 | 0x80)
//...
// Банк 3
#define EREVID (0x12 | 0x60)
//...
Кстати, быстро превратить таблицы из даташита в заголовочный файл поможет любой офисный пакет с редактором электронных таблиц. :)
#define ENC28J60_SPI_RCR 0x00
#define ENC28J60_SPI_WCR 0x40
#define ENC28J60_SPI_BFS 0x80
#define ENC28J60_SPI_BFC 0xA0
uint8_t enc28j60_current_bank = 0;
// Выбор банка регистров
void enc28j60_set_bank(uint8_t adr)
{
uint8_t bank;
// Регистр относится к определённому банку?
if( (adr & ENC28J60_ADDR_MASK) < ENC28J60_COMMON_CR )
{
// Получаем номер банка
bank = (adr >> 5) & 0x03; //BSEL1|BSEL0=0x03
// Если выбран "не тот" банк
if(bank != enc28j60_current_bank)
{
// Выбираем банк
enc28j60_write_op(ENC28J60_SPI_BFC, ECON1, 0x03);
enc28j60_write_op(ENC28J60_SPI_BFS, ECON1, bank);
enc28j60_current_bank = bank;
}
}
}
// Чтение регистра
uint8_t enc28j60_rcr(uint8_t adr)
{
enc28j60_set_bank(adr);
return enc28j60_read_op(ENC28J60_SPI_RCR, adr);
}
// Чтение пары регистров (L и H)
uint16_t enc28j60_rcr16(uint8_t adr)
{
enc28j60_set_bank(adr);
return enc28j60_read_op(ENC28J60_SPI_RCR, adr) |
(enc28j60_read_op(ENC28J60_SPI_RCR, adr+1) << 8);
}
// Запись регистра
void enc28j60_wcr(uint8_t adr, uint8_t arg)
{
enc28j60_set_bank(adr);
enc28j60_write_op(ENC28J60_SPI_WCR, adr, arg);
}
// Запись пары регистров (L и H)
void enc28j60_wcr16(uint8_t adr, uint16_t arg)
{
enc28j60_set_bank(adr);
enc28j60_write_op(ENC28J60_SPI_WCR, adr, arg);
enc28j60_write_op(ENC28J60_SPI_WCR, adr+1, arg>>8);
}
// Очистка битов в регистре (reg[adr] &= ~mask)
void enc28j60_bfc(uint8_t adr, uint8_t mask)
{
enc28j60_set_bank(adr);
enc28j60_write_op(ENC28J60_SPI_BFC, adr, mask);
}
// Установка битов в регистре (reg[adr] |= mask)
void enc28j60_bfs(uint8_t adr, uint8_t mask)
{
enc28j60_set_bank(adr);
enc28j60_write_op(ENC28J60_SPI_BFS, adr, mask);
}
Чтение и запись буфера
Для чтения буфера отправляем команду чтения, затем читаем столько байт, сколько нам нужно. Завершается операция поднятием линии CS.
#define ENC28J60_SPI_RBM 0x3A
// Чтение данных из буфера (по адресу в регистрах ERDPT)
void enc28j60_read_buffer(uint8_t *buf, uint16_t len)
{
enc28j60_select();
enc28j60_tx(ENC28J60_SPI_RBM);
while(len--)
*(buf++) = enc28j60_rx();
enc28j60_release();
}
Запись происходит аналогично. Команда, передача данных, поднятие CS.
#define ENC28J60_SPI_WBM 0x7A
// Запись данных в буфер (по адресу в регистрах EWRPT)
void enc28j60_write_buffer(uint8_t *buf, uint16_t len)
{
enc28j60_select();
enc28j60_tx(ENC28J60_SPI_WBM);
while(len--)
enc28j60_tx(*(buf++));
enc28j60_release();
}
Мягкий сброс
Сброс выполняется отправкой команды 0xff. После сброса ждём 1 мс, чтобы ENC28J60 мог выполнить внутреннюю инициализацию.
#define ENC28J60_SPI_SC 0xFF
void enc28j60_soft_reset()
{
// Отправляем команду
enc28j60_select();
enc28j60_tx(ENC28J60_SPI_SC);
enc28j60_release();
// Ждём, пока ENC28J60 инициализируется
_delay_ms(1);
// Не забываем про банк
enc28j60_current_bank = 0;
}
Инициализация
Типичная последовательность инициализации ENC28J60 выглядит примерно так:
- Настраиваем размер FIFO для приёма данных (ERXST, ERXND), инициализируем указатель для чтения данных из FIFO (ERXRDPT).
- Настраиваем фильтрацию входящих пакетов. По умолчанию, ENC28J60 пропускает пакеты, приходящие на наш MAC-адрес и широковещательные пакеты. В принципе, можно так и оставить.
- Настраиваем MAC:
- Очищаем MACON2.MARST чтобы снять сброс MAC.
- Устанавливаем MACON1.MARXEN чтобы разрешить приём данных MAC.
- Устанавливаем MACON1.RXPAUS и MACON1.TXPAUS для включения аппаратного упралвения потоком.
- Настраиваем биты PADCFG, TXCRCEN в MACON3. Для большинства приложений подойдёт выравнивание пакета до 60 байт и автоматическое добавление контрольной суммы.
- Устанавливаем максимальный размер фрейма в регистрах MAMXF.
- Устанавливаем размер промежутка между фреймами в регистрах MABBIPG, MAIPGL и MAIPGH.
- Устанавливаем MAC-адрес в регистрах MAADR.
- Настраиваем PHY:
- Включаем бит PHCON2.HDLDIS, если не хотим получать свои пакеты обратно в полудуплексном режиме.
- Выбираем как на различные события будут реагировать светодиоды LEDA и LEDB в регистре PHLCON.
- Настраиваем дуплексный режим, если хотим переопределить значение, определяемое полярностью светодиода LEDB. Для включения полного дуплекса устанавливаем биты PHCON1.PDPXMD и MACON3.FULDPX.
- Разрешаем приём пакетов
#define ENC28J60_SPI_DDR DDRB
#define ENC28J60_SPI_PORT PORTB
#define ENC28J60_SPI_CS (1<<PB4)
#define ENC28J60_SPI_MOSI (1<<PB5)
#define ENC28J60_SPI_MISO (1<<PB6)
#define ENC28J60_SPI_SCK (1<<PB7)
#define ENC28J60_BUFSIZE 0x2000
#define ENC28J60_RXSIZE 0x1A00
#define ENC28J60_MAXFRAME 1500
#define ENC28J60_RXSTART 0
#define ENC28J60_RXEND (ENC28J60_RXSIZE-1)
#define ENC28J60_TXSTART ENC28J60_RXSIZE
#define ENC28J60_BUFEND (ENC28J60_BUFSIZE-1)
uint16_t enc28j60_rxrdpt = 0;
void enc28j60_init(uint8_t *macadr)
{
// Настраиваем SPI
ENC28J60_SPI_DDR |= ENC28J60_SPI_CS|ENC28J60_SPI_MOSI|ENC28J60_SPI_SCK;
ENC28J60_SPI_DDR &= ~ENC28J60_SPI_MISO;
enc28j60_release();
SPCR = (1<<SPE)|(1<<MSTR);
SPSR |= (1<<SPI2X);
// Выполняем сброс
enc28j60_soft_reset();
// Настраиваем размер буфера для приёма пакетов
enc28j60_wcr16(ERXST, ENC28J60_RXSTART);
enc28j60_wcr16(ERXND, ENC28J60_RXEND);
// Указатель для чтения принятых пакетов
enc28j60_wcr16(ERXRDPT, ENC28J60_RXSTART);
enc28j60_rxrdpt = ENC28J60_RXSTART;
// Настраиваем MAC
enc28j60_wcr(MACON2, 0); // очищаем сброс
enc28j60_wcr(MACON1, MACON1_TXPAUS|MACON1_RXPAUS| // включаем управление потоком
MACON1_MARXEN); // разрешаем приём данных
enc28j60_wcr(MACON3, MACON3_PADCFG0| // разрешаем паддинг
MACON3_TXCRCEN| // разрешаем рассчёт контрольной суммы
MACON3_FRMLNEN| //разрешаем контроль длины фреймов
MACON3_FULDPX);// включаем полный дуплекс
enc28j60_wcr16(MAMXFL, ENC28J60_MAXFRAME); // устанавливаем максимальный размер фрейма
enc28j60_wcr(MABBIPG, 0x15); // устанавливаем промежуток между фреймами
enc28j60_wcr(MAIPGL, 0x12);
enc28j60_wcr(MAIPGH, 0x0c);
enc28j60_wcr(MAADR5, macadr[0]); // устанавливаем MAC-адрес
enc28j60_wcr(MAADR4, macadr[1]);
enc28j60_wcr(MAADR3, macadr[2]);
enc28j60_wcr(MAADR2, macadr[3]);
enc28j60_wcr(MAADR1, macadr[4]);
enc28j60_wcr(MAADR0, macadr[5]);
// Настраиваем PHY
enc28j60_write_phy(PHCON1, PHCON1_PDPXMD); // включаем полный дуплекс
enc28j60_write_phy(PHCON2, PHCON2_HDLDIS); // отключаем loopback
enc28j60_write_phy(PHLCON, PHLCON_LACFG2| // настраиваем светодиодики
PHLCON_LBCFG2|PHLCON_LBCFG1|PHLCON_LBCFG0|
PHLCON_LFRQ0|PHLCON_STRCH);
// разрешаем приём пакетов
enc28j60_bfs(ECON1, ECON1_RXEN);
}
Отправка пакетов
Для отправки пакета, записываем указатели на его начало и конец в регистры ETXST и ETXND. Перед пакетом должен находиться управляющий байт, в котором можно переопределить некоторые настройки MAC для отправки этого пакета. После того, как пакет будет отправлен, после его конца будет записан блок, содержащий статус передачи.

Если хотим отправить пакет с настройками по-умолчанию, младший бит (POVERRIDE) управляющего байта должен быть сброшен.
void enc28j60_send_packet(uint8_t *data, uint16_t len)
{
// Ждём готовности передатчика
while(enc28j60_rcr(ECON1) & ECON1_TXRTS)
;
// Записываем пакет в буфер
enc28j60_wcr16(EWRPT, ENC28J60_TXSTART);
enc28j60_write_buffer((uint8_t*)"\x00", 1);
enc28j60_write_buffer(data, len);
// Устанавливаем указатели ETXST и ETXND
enc28j60_wcr16(ETXST, ENC28J60_TXSTART);
enc28j60_wcr16(ETXND, ENC28J60_TXSTART + len);
// Разрешаем отправку
enc28j60_bfs(ECON1, ECON1_TXRTS);
}
Правда, в ENC28J60 есть баг, из-за которого бит TXRTS может не сбрасываться при серьёной ошибке передачи пакета. Соответственно, готовности передатчика мы не дождёмся. Еррата рекомендует проверять бит EIR.ERIF, и, если он установлен, выполнять сброс передатчика. Для этого изменим код вот так:
//...
while(enc28j60_rcr(ECON1) & ECON1_TXRTS)
{
// При ошибке, сбрасываем передатчик
if(enc28j60_rcr(EIR) & EIR_TXERIF)
{
enc28j60_bfs(ECON1, ECON1_TXRST);
enc28j60_bfc(ECON1, ECON1_TXRST);
}
}
//...
Приём пакетов
ENC28J60 записывает пакеты в кольцевой буфер в виде связанного списка:

Адрес первого непрочитанного пакета храниться в регистрах ERXRDPT. Забрав пакет, микроконтроллер записывает в ERXRDPT адрес следующего пакета. После этого место, которое занимал пакет считается свободным и ENC28J60 может использовать его для приёма новых пакетов.
Статус приёма — длина пакета (2 байта) и различные флаги (тоже 2 байта). Из флагов нас интересует только бит 7 — приём успешно завершён.
Все принятые пакеты ENC28J60 записывает в буфер с выравниванием на 2 байта. Таким образом, адрес пакета всегда чётный.
Для того, чтобы забрать принятый пакет, микроконтроллер делает следующее:
- Смотрит сколько принято пакетов (в регистре EPKTCNT).
- Читает пакет из буфера (по адресу ERXRDPT).
- Записывает в ERXRDPT адрес следующего пакета.
- Уменьшает значение счётчика пакетов установкой бита ECON2.PKTDEC.
Ну и последний на сегодня баг ENC28J60 — при записи чётного значения в регистр ERXRDPT, ENC28J60 может повредить данные в буфере (кстати, адрес пакета всегда чётный из-за выравнивания). Еррата рекомендует записывать в ENC28J60 всегда нечётное значение. Стоп, а как же мы узнаем откуда брать новый пакет? Придётся хранить это значение в памяти микроконтроллера. Но ERXRDPT мы всё равно должны записывать, чтобы ENC28J60 знал сколько памяти доступно для приёма пакетов. Только записывать будем не адрес следующего пакета, а на адрес на 1 байт выше.
// "Правильное" значение ERXRDPT
uint16_t enc28j60_rxrdpt = 0;
uint16_t enc28j60_recv_packet(uint8_t *buf, uint16_t buflen)
{
uint16_t len = 0, rxlen, status, temp;
// Есть ли принятые пакеты?
if(enc28j60_rcr(EPKTCNT))
{
// Считываем заголовок
enc28j60_wcr16(ERDPT, enc28j60_rxrdpt);
enc28j60_read_buffer((void*)&enc28j60_rxrdpt, sizeof(enc28j60_rxrdpt));
enc28j60_read_buffer((void*)&rxlen, sizeof(rxlen));
enc28j60_read_buffer((void*)&status, sizeof(status));
// Пакет принят успешно?
if(status & 0x80)
{
// Выбрасываем контрольную сумму
len = rxlen - 4;
// Читаем пакет в буфер (если буфера не хватает, пакет обрезается)
if(len > buflen) len = buflen;
enc28j60_read_buffer(buf, len);
}
// Устанавливаем ERXRDPT на адрес следующего пакета - 1
temp = (enc28j60_rxrdpt - 1) & ENC28J60_BUFEND;
enc28j60_wcr16(ERXRDPT, temp);
// Уменьшаем счётчик пакетов
enc28j60_bfs(ECON2, ECON2_PKTDEC);
}
return len;
}
Заключение
Готовую библиотеку можно взять здесь.
Уфф, ну вот вроде и всё про ENC28J60 :)
В следующей части напишем простенькое приложение работающее с компом по UDP.
update: Микрочип время от времени обновляет даташит. Последнюю версию можно найти здесь. Статья написана на основе документа ревизии A.

Все статьи цикла
- Подключение микроконтроллера к локальной сети: Теория
- Подключение микроконтроллера к локальной сети: работаем с ENC28J60
- Подключение микроконтроллера к локальной сети: UDP-сервер
- Подключение микроконтроллера к локальной сети: UDP-клиент
- Подключение микроконтроллера к локальной сети: Широковещательные сообщения и DHCP
- Подключение микроконтроллера к локальной сети: TCP-клиент
- Подключение микроконтроллера к локальной сети: HTTP и CGI
- Подключение микроконтроллера к локальной сети: TCP и HTTP (продолжение)
- Подключение микроконтроллера к локальной сети: HTTP и CGI (заключение)
- Подключение микроконтроллера к локальной сети: тесты производительности и краткое описание API стека
- Подключение микроконтроллера к локальной сети: Заключение
- Веб сервер на Tiny2313. Чисто ради лулзов
- +21
- 29 марта 2011, 13:40
- Lifelover
Спасибо, информативно :)
Почитал Errata, 15 багов 8) Но если взять правильные бубны тогда баги превращаются в новые фичи…
Почитал Errata, 15 багов 8) Но если взять правильные бубны тогда баги превращаются в новые фичи…
по теме ENC28J60+Errata нашел подобные комменты на roboforum-е:
ENC28J60 — жутко глючная штука, не советую с ней даже связываться. Использовали первое время в преобразователе Ethernet — RS485, так эта зараза при непонятном стечении обстоятельств отваливалась сама собой. Даже после выполнения всех безумных рекомендаций из Errata (типа тактирования микроконтроллера только от самой ENC)- лечилась исключительно перезагрузкой.никто не сталкивался?
А у этого контроллера вообще что-то работает без глюков и кучки условий мелким текстом? =)
Что-то пока читаю статью, везде то то не работает, то резистор подбирается в зависимости от фазы луны =D
Что-то пока читаю статью, везде то то не работает, то резистор подбирается в зависимости от фазы луны =D
Спасибо за статью, она безусловно будет полезна многим.
Сам недавно купил эту микруху, смущает только одно — она работает только с IPv4, а ведь на подходе IPv6 (может даже по оптволокну) и ставить ее в новые устройства вродь как уже не имеет особого смысла. Разве что попрактиковаться в создании интернет-устройств, для этого ее и купил.
Сам недавно купил эту микруху, смущает только одно — она работает только с IPv4, а ведь на подходе IPv6 (может даже по оптволокну) и ставить ее в новые устройства вродь как уже не имеет особого смысла. Разве что попрактиковаться в создании интернет-устройств, для этого ее и купил.
А можно поподробнее про трансформаторы TR1 и TR2? Я так понял, что H16105DF-R — это и есть эти фильтры, так? Просто дохлых/ненужных материнок/сетевых карт у меня нет — и их по-любому придется покупать. Вот из этих любой подойдет? Или лучше сразу magjack искать?
Судя по параметрам, они все одинаковые там, отличается только распиновка)
Ну если серийно нужно, конечно можно купить magjack или блок трансформаторов. Но, например, у себя поблизости я не нашёл, так что нужно искать/заказывать/ждать.
А для просто поиграться — проще взять с дохлой платы. Не думаю, что так сложно найти.
Ну если серийно нужно, конечно можно купить magjack или блок трансформаторов. Но, например, у себя поблизости я не нашёл, так что нужно искать/заказывать/ждать.
А для просто поиграться — проще взять с дохлой платы. Не думаю, что так сложно найти.
В терре разъем для СМД монтажа с трансами и светодиодами стоит примерно 150 рублей. Есть и не СМД. Так что найти и заказать — не большая проблема. В других магазинах тоже их видел.
Окей, все равно скоро собирался в митино съездить — там и посмотрю, что будет в наличии/дешевле; заказывать и ждать совсем не хочется, тем более за 5 евро+доставка. А насчет сетевых карт — у меня никогда ее не было и как-то не думал, что они бывают настолько дешевые))
посмотрите не дорогие
www.promelec.ru/vitrina_new/406/1262/1263/page3/
www.promelec.ru/vitrina_new/406/1262/1263/page3/
А почему именно ENC? В чем плюсы по сравнению с другими аналогичными микрухами, дешевыми RTL, например?
В заключительной статье было упоминание про RTL8201BL, но при более внимательном рассмотрении, как я понял, этот RTL8201BL реализует только физический уровень, в то время как в ENC есть еще и канальный. Поправьте, если ошибаюсь. И все же интересно чем обоснован выбор именно этого Ethernet-контроллера, ведь он наверно не один в природе? Или этот самый популярный и доступный?
Пытаюсь повторить проект в IAR. Все собралось нормально кроме непонятки с функцией sei(). Что это такое? очевидно это относится к AVR Studio. Какова ее роль?
Это включает прерывания)
ЕМНИП аналог в иаре — __enable_interrupt(); (хедер intrinsic.h).
Алсо всё-таки юзать avr-gcc. =)
ЕМНИП аналог в иаре — __enable_interrupt(); (хедер intrinsic.h).
Алсо всё-таки юзать avr-gcc. =)
Спасибо. Все понял.
Да я и не против, но на IAR уже столько потрачено времени… Надежная штука. Надеюсь что он не подведет и в этом случае.
Да, дабы не менять все пришлось в файле enc28j60.h сделать так
Надеюсь это не должно вызвать у компилятора вопросов…
Алсо всё-таки юзать avr-gcc. =)
Да я и не против, но на IAR уже столько потрачено времени… Надежная штука. Надеюсь что он не подведет и в этом случае.
Да, дабы не менять все пришлось в файле enc28j60.h сделать так
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;
Надеюсь это не должно вызвать у компилятора вопросов…
Уговорили. Если сразу пример (имеется ввиду пример UDP из следующего поста) на IAR не покатит попробую запустить его на AVR Studio. Я так понимаю именно на ней выполнен проект? Кстати спасибо за посты. Все очень интересно и профессионально.
Пока подключил эту штуку starterkit.ru/html/index.php?name=shop&op=view&id=57 к ATMega128.
Кварц 14.7MHz Может всеже сменить кварц на 8? Или и так должно пойти…
Пока подключил эту штуку starterkit.ru/html/index.php?name=shop&op=view&id=57 к ATMega128.
Кварц 14.7MHz Может всеже сменить кварц на 8? Или и так должно пойти…
А почему таблица регистров из статьи отличается от таблица регистров из даташита?
narod.ru/disk/28068013001/ENC29J60_%D0%BC%D0%B8%D0%BA%D1%80%D0%BE%D1%81%D1%85%D0%B5%D0%BC%D0%B0.pdf.html — смотри page 12
narod.ru/disk/28068013001/ENC29J60_%D0%BC%D0%B8%D0%BA%D1%80%D0%BE%D1%81%D1%85%D0%B5%D0%BC%D0%B0.pdf.html — смотри page 12
Непонимаю что тебе не нравится. Где ошибка-то?
Алсо зачем заливать шит на говнообменники, заставляющие вводить капчу, ждать, впаривающие какие-то сраные бары. Если религия не позволяет дать прямую ссылку на шит, залей на нормальный обменник.
Алсо зачем заливать шит на говнообменники, заставляющие вводить капчу, ждать, впаривающие какие-то сраные бары. Если религия не позволяет дать прямую ссылку на шит, залей на нормальный обменник.
На вскидку: в банке 4 по адресам 0х00...0х05 например, или банк 3 0х01.
Если внимательно посмотреть может еще чего найдется. Может это не принципиально, но всеже.
Если внимательно посмотреть может еще чего найдется. Может это не принципиально, но всеже.
А, да. У тебя просто другая ревизия даташита. Приниципальных различий вроде бы нет. Добавил в конец статьи.
Подскажите пожалуйста инф. по схеме:
1) в даташите на ENC28J60 светодиоды подключены на GND (здесь они подключены на VNC 3.3В)- на что это влияет?
2) в качестве трансформатора H16105DF-R можно ли использовать какой нить из этих (тоже стоят в сетевых карточках): 16PT8515, LP-164C, FB2022.
3) использование резисторов (R3,R4,R5,R6) с 3-5% (вместо 1%) погрешностью критично?
1) в даташите на ENC28J60 светодиоды подключены на GND (здесь они подключены на VNC 3.3В)- на что это влияет?
2) в качестве трансформатора H16105DF-R можно ли использовать какой нить из этих (тоже стоят в сетевых карточках): 16PT8515, LP-164C, FB2022.
3) использование резисторов (R3,R4,R5,R6) с 3-5% (вместо 1%) погрешностью критично?
1) МС автоматически определяет полярность диодов. Одновременно полярность определяет режим при запуске — полно- или полудуплексный (но это не очень существенно, т.к. можно потом программно поменять на нужный). Кстати, в этом модуле тоже есть ошибка и полярность может определиться неправильно. Как бороться — в эррате.
2) Скорее всего можно, а вообще-то в даташите указаны параметры требуемого трансформатора. Хотя у меня магджек с заметно отличающимия параметрами (конкретно — коэфф. трансформации RX 2.5:1 вместо 1:1), но работает. Возможно кстати, что резисторы R5 и R6 мне нужно поставить другие, но и с этими работает.
3) Вообще работает даже есть поставить 51 ом 5%. Но при этом форма сигнала не соответствует требованиям стандарта. Возможно снизится помехоустойчивость или максимальная длина кабеля (сопротивление резисторов намекает на то, что они согласующие, а рассогласование увеличивает потери сигнала из-за отражения). То же относится и к R7 — работает даже при больших отклонениях. Ну, по крайней мере на 10-метровом кабеле.
2) Скорее всего можно, а вообще-то в даташите указаны параметры требуемого трансформатора. Хотя у меня магджек с заметно отличающимия параметрами (конкретно — коэфф. трансформации RX 2.5:1 вместо 1:1), но работает. Возможно кстати, что резисторы R5 и R6 мне нужно поставить другие, но и с этими работает.
3) Вообще работает даже есть поставить 51 ом 5%. Но при этом форма сигнала не соответствует требованиям стандарта. Возможно снизится помехоустойчивость или максимальная длина кабеля (сопротивление резисторов намекает на то, что они согласующие, а рассогласование увеличивает потери сигнала из-за отражения). То же относится и к R7 — работает даже при больших отклонениях. Ну, по крайней мере на 10-метровом кабеле.
А в ST802RT1 только PHY? Т.е его можно использовать в связке с контроллером у которого есть MAC на борту.
Datasheet: www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATA_BRIEF/CD00216985.pdf
Datasheet: www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATA_BRIEF/CD00216985.pdf
"… сетевой разъём с уже встроенными фильтрами («MagJack»). Деталька редкая и дорогая..." Позавчера выкупил подобное HY991101C в smd.ru за 109руб./шт. Но у меня другой вопрос: есть функциональный аналог CP220x Ethernet Controller ICs от silabs.com. Errata состоит из трех пунктов, что обнадеживает да и цена сопоставима с ENC28J60. Корпус QFN-28 или LQFP-48, что вполне приемлемо. Никто с подобными не работал? Хочу заложить их в следующий свой проект.
С ценой/доступностью/функционалом все хорошо, например, в Контесте (elecont.ru) CP2200-GQR LQFP-48 163.76р. — 573щт.; CP2201-GM QFN-28 222.55 р. — 35шт.; CP2201-GMR QFN-28 127р. — 691шт. В elitan.ru: CP2201-GMR@SILABS QFN28 1шт — 116р.; от 10шт. — 86,3р.; от 14шт. — 80.6р.; sbros — 73,4р./шт. Ethernet контроллер IEEE802.3MAC and 10BASE-T PHY (Vcc=3,1-3,6V, 8kb Flash, выводы совмест. с 5V I/O, LED-драйвер
Добавить в проект
Добавить в проект
Доброго!
А кто нить запускал пример на STM32? Пробую iteadstudio.com/produce/enc28j60-ethernet-module-and-demo-codes/ свиду очень похоже. Ничего не работает, Мак не считывается. Может кто подскажет как быть?
А кто нить запускал пример на STM32? Пробую iteadstudio.com/produce/enc28j60-ethernet-module-and-demo-codes/ свиду очень похоже. Ничего не работает, Мак не считывается. Может кто подскажет как быть?
Очень хочется печатку из статьи желательно в layout, хотя это не принципиально. Спасибо тем кто может поделиться, если уже сам реализовывал этот проект.
- dreamyslesh
- 14 февраля 2012, 11:32
- ↓
Спасибо за статью, сильно жизнь облегчила)). Вопросик есть. Набросал код по аналогии и смотрю, счетчик пакетов=0 и все тут. Пакеты начали поступать после отключения проверки crc в ERXFCON. Соответственно флаги в принятых пакетах тоже нифига не ОК. 90% жалуются на crc и 10% на несоответствие длины пакета. Это нормально? В принципе, заголовки у пакетов адекватные. В еррате вроде по данному вопросу не видать ничего. Проверил на 2х устройствах, одном самопальном и одном китайско-заводском. Результат один. Не встречалась такая ситуация никому?
А откуда посылаете пакеты на девайс?
С PC?
Это ненормально. Ревизия чипа какая?
С PC?
Это ненормально. Ревизия чипа какая?
- Martovskij
- 14 декабря 2012, 13:10
- ↑
- ↓
Что-то не вкурю никак… не отправляет пакеты и все тут. Пытаюсь ARP-ответ сделать. Пакет формирую из запроса, отправляю, линк отправки моргает, флаг ошибки отправки не появляется, но Wireshark ответа не видит… Косяк с контрольной суммой так и остался из предыдущего поста. Вот мысль есть, enc28 добавляет контрольную сумму к пакету, так вот если эта сумма будет тоже не верна Wireshark такой пакет покажет?
Здравствуйте. Помогите понять почему код не работает.
#define SPI_PORT PORTB
#define SPI_DDR DDRB
#define SPI_CS 4
#define SPI_MOSI 5
#define SPI_MISO 6
#define SPI_SCK 7
#include <avr/io.h>
#include «enc28j60.h»
int main(void)
{
DDRA = 0xFF;
PORTA = 0x00;
SPI_DDR = (1<<SPI_CS)|(1<<SPI_MOSI)|(0<<SPI_MISO)|(1<<SPI_SCK);
SPI_PORT = (1<<SPI_CS)|(1<<SPI_MOSI)|(1<<SPI_MISO)|(1<<SPI_SCK); — Выстявляем ноги
SPCR = (1<<SPE)|(1<<MSTR)|(0<<CPOL)|(0<<CPHA)|(1<<DORD)|(0<<SPR1)|(0<<SPR0);
SPSR = (0<<SPI2X); -----------РЕЖИМ 0 и двойная корость
SPI_PORT &= ~(1<<SPI_CS); — Начинаем передачу.
SPDR = (0b01000000|ECON1); — Говорим что хочем писать в ECON1 (выбирать банк)
while (!(SPSR & (1<<SPIF)));
SPDR = (0b00000000 | 0х03); — Передаем что банк 3.
while (!(SPSR & (1<<SPIF)));
unsigned char report;
SPDR = (0b00000000|EREVID); — говорим что хочу читать ревизию
while (!(SPSR & (1<<SPIF)));
report = SPDR; — приняли ревизию.
SPI_PORT |= (1<<SPI_CS); — Завершили передачу.
while(1){ — Так как у меня нету JTAG то проверяю принял что-то или нет просто засветив светодиод.
for (unsigned char i=0x00; i<0xFF; i++){
if (dt==i){
PORTA=0xFF;
}
}
}
return 0;
}
Прикол в том, что он не светит. Если написать i <= 0xFF (включительно) в условии цикла — то светодиод горит. Скажите что это может быть? Микроконтроллер работает на 16000000.
#define SPI_PORT PORTB
#define SPI_DDR DDRB
#define SPI_CS 4
#define SPI_MOSI 5
#define SPI_MISO 6
#define SPI_SCK 7
#include <avr/io.h>
#include «enc28j60.h»
int main(void)
{
DDRA = 0xFF;
PORTA = 0x00;
SPI_DDR = (1<<SPI_CS)|(1<<SPI_MOSI)|(0<<SPI_MISO)|(1<<SPI_SCK);
SPI_PORT = (1<<SPI_CS)|(1<<SPI_MOSI)|(1<<SPI_MISO)|(1<<SPI_SCK); — Выстявляем ноги
SPCR = (1<<SPE)|(1<<MSTR)|(0<<CPOL)|(0<<CPHA)|(1<<DORD)|(0<<SPR1)|(0<<SPR0);
SPSR = (0<<SPI2X); -----------РЕЖИМ 0 и двойная корость
SPI_PORT &= ~(1<<SPI_CS); — Начинаем передачу.
SPDR = (0b01000000|ECON1); — Говорим что хочем писать в ECON1 (выбирать банк)
while (!(SPSR & (1<<SPIF)));
SPDR = (0b00000000 | 0х03); — Передаем что банк 3.
while (!(SPSR & (1<<SPIF)));
unsigned char report;
SPDR = (0b00000000|EREVID); — говорим что хочу читать ревизию
while (!(SPSR & (1<<SPIF)));
report = SPDR; — приняли ревизию.
SPI_PORT |= (1<<SPI_CS); — Завершили передачу.
while(1){ — Так как у меня нету JTAG то проверяю принял что-то или нет просто засветив светодиод.
for (unsigned char i=0x00; i<0xFF; i++){
if (dt==i){
PORTA=0xFF;
}
}
}
return 0;
}
Прикол в том, что он не светит. Если написать i <= 0xFF (включительно) в условии цикла — то светодиод горит. Скажите что это может быть? Микроконтроллер работает на 16000000.
Это случайно не сперли статью?
suli-company.org.ua/electro/1128-podklyuchenie-mikrokontrollera-k-lokalnoy-seti-chast-1.html
suli-company.org.ua/electro/1128-podklyuchenie-mikrokontrollera-k-lokalnoy-seti-chast-1.html
Можете мне помочь по этой теме?
У меня есть железка
DP83848, как подключить к STM32F4Discovery? Буду очень-очень благодарен за помощь. Если можно, напишите как можно с обширными пояснениями.
Спасибо.
У меня есть железка
Спасибо.
Добрый день!
пытаюсь разобраться с микросхемой. не понимаю
Это же по сути мягкий сброс? мы ведь отправляем 11111111. Как же осуществляется чтение? Чую, что я не понимаю))
пытаюсь разобраться с микросхемой. не понимаю
#define enc28j60_rx() enc28j60_rxtx(0xff)
Это же по сути мягкий сброс? мы ведь отправляем 11111111. Как же осуществляется чтение? Чую, что я не понимаю))
- dpbondarenko
- 21 мая 2014, 17:18
- ↓
Это мягкий сброс если идет в позиции команды. Скажем если выбрать енку (цс=0) и сразуже вызвать enc28j60_rx, то да, мы получим мягкий сброс.
Но здесь вызов enc28j60_rx предполагается не в позиции команды, а в позиции данных (после команды). С начала передается комманда, а уже затем в ответ на команду енка будет выдавать результат. Но т.к. спи 2-направленный, то что бы что-то считать мы должны что-то записать. Вкачестве «что-то записать» и используется 0xFF.
Если попробывать представить обмен, то будет выглядить так (передача произходит одновременно по 2-м линиям):
MOSI: CMDn 0xFF 0xFF
MISO: xxxx DATA DATA
При этом CMDn != 0xFF, и сами 0xFF в этом случае интерпретируются не как команда а как данные (которые в конкретном случае игнорируются).
Но здесь вызов enc28j60_rx предполагается не в позиции команды, а в позиции данных (после команды). С начала передается комманда, а уже затем в ответ на команду енка будет выдавать результат. Но т.к. спи 2-направленный, то что бы что-то считать мы должны что-то записать. Вкачестве «что-то записать» и используется 0xFF.
Если попробывать представить обмен, то будет выглядить так (передача произходит одновременно по 2-м линиям):
MOSI: CMDn 0xFF 0xFF
MISO: xxxx DATA DATA
При этом CMDn != 0xFF, и сами 0xFF в этом случае интерпретируются не как команда а как данные (которые в конкретном случае игнорируются).
спасибо большое! то есть заместо 0xff может спокойно быть всякая ересь))
постепенно врубаюсь в происходящее))
постепенно врубаюсь в происходящее))
- dpbondarenko
- 24 мая 2014, 03:22
- ↑
- ↓
При подключении к незапитанной платой на сетевухе комповой и в компе появляется линк, я имею ввиду «Подключение по локальной сети» включено. Это нормально? Если запитать плату, то светодиод на плате один из двух горит постоянно, что с вставленным кабелем, что с вытащенным. Тот светодиод настроен на индикацию состояния линка. В конфигурации менял, чтоб дугой индицировал это состояние — индицирует также, горит (второй в это момент не светится). При чтении бита LSTAT показывает, что нет линка (равен 0). Правда трансформатор использовал внешний ML8512 от сетевухи. Даташита именно на него не нашел, нашел только очень похожий на 16РТ8512B. Пинга нет (проект из статьи про UDP-сервер). Вот ищу где ещё собака зарыта…
Здравствуйте. Статья и в правду интересная. Вот такой вопрос: не нашёл функцию enc28j60_write_phy(); в инициализации она используется, а вот что из себя представляет?
С уважением Алексей.
С уважением Алексей.
Видимо, эту функцию следует искать в приложенных сырках (ссылка в «заключении»). Как именно она работает кратко описано в разделе «Регистры MII».
Спасибо, нашёл. Я работаю в программе Code Vision AVR, поэтому пришлось немного редактировать. Вот такой ещё вопрос: функция инициализации void enc28j60_init(тут должен быть MAC адрес)? Если так, то почему 8-битная переменная объявлена (uint8_t *macadr)? А в сомой функции macadr как массив?
enc28j60_wcr(MAADR5, macadr[0]); // Set MAC address
enc28j60_wcr(MAADR4, macadr[1]);
enc28j60_wcr(MAADR3, macadr[2]);
enc28j60_wcr(MAADR2, macadr[3]);
enc28j60_wcr(MAADR1, macadr[4]);
enc28j60_wcr(MAADR0, macadr[5]);
enc28j60_wcr(MAADR5, macadr[0]); // Set MAC address
enc28j60_wcr(MAADR4, macadr[1]);
enc28j60_wcr(MAADR3, macadr[2]);
enc28j60_wcr(MAADR2, macadr[3]);
enc28j60_wcr(MAADR1, macadr[4]);
enc28j60_wcr(MAADR0, macadr[5]);
Я с Вами согласен, но в Си (по крайней мере в CodeVision) массив указывается с размерностью (нумерацией), то есть macadr[6] это значит что массив состоит из шести значений. В данном случае этого нет, поэтому не ясно массив или переменная, если массив то из скольких значений? В функции указывается нумерация массива macadr[0], macadr[1] и т.д. а в инициализации enc28j60_init(uint8_t *macadr) — указывается просто 8-битная переменная macadr. Возможно это понятно для AVR Studio, но не людям. В любой литературе по Си написано что массив указывается с размерностью, в данном случае этого нет, поэтому такой вопрос и был. Так что это массив или переменная?
Это указатель на uint8_t. В данном случае он трактуется как указатель на массив uint8_t (си не делает различий между массивами и указателями).
uint8_t
u — unsigned — беззнаковое
int — integer — целое
8 — кол-во битов
_t — обозначение того что эта аббревиатура не макрос и не функция и не процедура а ТиП!
О массиве и слова нет.
Производные типы от стандартных в языке Си для компилятора AVR-GCC
uint8_t — unsigned char, но негде ни слова нет что это массив. Откройте библиотеку AVR-GCC, и посмотрите,
u — unsigned — беззнаковое
int — integer — целое
8 — кол-во битов
_t — обозначение того что эта аббревиатура не макрос и не функция и не процедура а ТиП!
О массиве и слова нет.
Производные типы от стандартных в языке Си для компилятора AVR-GCC
uint8_t — unsigned char, но негде ни слова нет что это массив. Откройте библиотеку AVR-GCC, и посмотрите,
Это указатель на uint8_t. В данном случае он трактуется как указатель на массив uint8_t— не соглашусь, даже в AVR Studio массив обозначается uint8_t tmp[10]={....}; То есть это простая 8-битная переменная, а в функции указывается массив. Вы мне не ответили на вопрос. Извиняюсь за свою назойлевость, но это факт.
Это указатель на uint8_t. В данном случае он трактуется как указатель на массив uint8_t— это не ответ, и не подтверждение что относится к разряду массива.
uint8_t *macadr — это указатель на конкретный байт в памяти, в этом же месте памяти (с этого же байта) начинается массив (поименованная область памяти) macadr, т.е. это указатель на macadr[0] — 1-й байт массива macadr.
- well-man2000
- 12 декабря 2015, 20:23
- ↑
- ↓
Тем, кто писал до этого на java/javascript или Basic — это не понятно, но тому, кто работал с массивами(и вообще, любыми областями памяти) на asm — эта адресная арифметика Си — яснса, как божий день, т.к. все дешевые фокусы Си взяты из ассемблера.
- well-man2000
- 12 декабря 2015, 20:28
- ↑
- ↓
Alexl81, вам надо пописать на денек-два на asm — и все с пониманием Си встанет после этого не место. Лучше всего (именно в учебных целях понимания сути) для MSP430 — его asm и методы адресации CPU чем-то похожа на ones в древней машине PDP-11, на которой и писался этот самый Си.
- well-man2000
- 12 декабря 2015, 20:44
- ↑
- ↓
Доброго времени! специально зарегился сказать спасибо, ну и коротко опишу что делал.
Проц у меня «вражеский)» — PIC18F27K40. Это из разряда «новья». Задавшись целью подключить к нему ЕНЦ, интересней показалось пошукать по рунету, чем по сайту производителя.
В целом процесс выглядил так:
-настроил SPI ПИКа(включая проверку что он успешно включился).
-включил в проект приведенные библиотеки и заголовочные файлы.
-внимательно рассмотрел работу с SPI и переделал на свою.
-проверил что типы uint.._t описаны.
-еще раз перепроверил вышенаписанное.
После этого проект откомпилился! Сразу.
Далее разобрал как использовать lan_poll(). При этом перепроверил еще раз параметры сети.
Все это отняло день. Без фанатизма, долго читал, потом внимательно подготавливал код. И самое интересное что пинг пошел сразу!!! Подтверждаю, стек рабочий, сколочен добротно!!!
Сказалось отсутствие выравнивания структур у компилятора ХС8. Вообще не потребовалось в синтаксисе ничего переделывать, завелось один в один! Почти. Объявление «extern net_buf[]» заработало некорректно, потребовалось указать размерность. Все же разные компиляторы.
Ну и о граблях. Думаю, это ДАЛЕКО ЕЩЕ НЕ ВСЕ сюрпризы чипа ЕНЦ, но попарило мозг одно явление,
если ресет всего изделия делать с подачи питания или же программным сбросом — для ЕНЦ оказывается не одно и то же, с какого «начального момента» получать soft reset. Короче потом задержки в 1мс хватало если изделие рестартовало «с рубильника» и чаще всего не хватало если программно! Короче — посылаю я в него команду «ресет», МК делает сброс, и после этого все виснет! Вылечилось добавлением времени после soft reset.
Сейчас изделия на основе этого процессора и стека имеют у меня уже по многу сот часов аптайма, ну и выявилось достаточно интересного, напишу еще обязательно, но тут для начала хотел еще раз поблагодарить!!!
Проц у меня «вражеский)» — PIC18F27K40. Это из разряда «новья». Задавшись целью подключить к нему ЕНЦ, интересней показалось пошукать по рунету, чем по сайту производителя.
В целом процесс выглядил так:
-настроил SPI ПИКа(включая проверку что он успешно включился).
-включил в проект приведенные библиотеки и заголовочные файлы.
-внимательно рассмотрел работу с SPI и переделал на свою.
-проверил что типы uint.._t описаны.
-еще раз перепроверил вышенаписанное.
После этого проект откомпилился! Сразу.
Далее разобрал как использовать lan_poll(). При этом перепроверил еще раз параметры сети.
Все это отняло день. Без фанатизма, долго читал, потом внимательно подготавливал код. И самое интересное что пинг пошел сразу!!! Подтверждаю, стек рабочий, сколочен добротно!!!
Сказалось отсутствие выравнивания структур у компилятора ХС8. Вообще не потребовалось в синтаксисе ничего переделывать, завелось один в один! Почти. Объявление «extern net_buf[]» заработало некорректно, потребовалось указать размерность. Все же разные компиляторы.
Ну и о граблях. Думаю, это ДАЛЕКО ЕЩЕ НЕ ВСЕ сюрпризы чипа ЕНЦ, но попарило мозг одно явление,
если ресет всего изделия делать с подачи питания или же программным сбросом — для ЕНЦ оказывается не одно и то же, с какого «начального момента» получать soft reset. Короче потом задержки в 1мс хватало если изделие рестартовало «с рубильника» и чаще всего не хватало если программно! Короче — посылаю я в него команду «ресет», МК делает сброс, и после этого все виснет! Вылечилось добавлением времени после soft reset.
Сейчас изделия на основе этого процессора и стека имеют у меня уже по многу сот часов аптайма, ну и выявилось достаточно интересного, напишу еще обязательно, но тут для начала хотел еще раз поблагодарить!!!
Комментарии (111)
RSS свернуть / развернуть