Всем доброго времени суток! Начинаем долгожданный цикл статей по практической работе с Ethernet. Пройдемся с самого низкого уровня по всему стеку протоколов - ARP, IP, UDP, TCP... В качестве целевой платформы возьмем традиционно STM32 и в качестве микросхемы физического уровня Ethernet контроллер ENC28J60, как один из наиболее распространенных и доступных.
В этой, первой, статье рассмотрим физическое подключение, инициализацию интерфейсов, настройку ENC28J60 и, пожалуй, напишем сразу драйвер для работы с вышеупомянутым контроллером. Все буду описывать по возможности подробнее, но если вдруг возникнут вопросы, добро пожаловать в комментарии, а еще лучше к нам на форум.
Подключение ENC28J60.
Итак, STM32... Я возьму STM32F103C8T6, опять же в силу его невероятной распространенности и популярности, чему мы можем быть благодарны плате Blue Pill:

Для ENC28J60 у меня среди прочих есть такой модуль, пусть будет он:

Мы будем писать код исходя из соображений возможности его использования и с любым другим железом, так что выбор в данном случае не критичен.
Вспоминаем сетевую модель OSI (здесь в ближайшее время я помещу ссылку на статью об OSI):

Самый низкий уровень, физический, в данном случае это 10BASE-T полностью берет на себя ENC28J60. Переходим на уровень выше - канальный уровень, который отвечает за передачу фреймов между узлами сети, основан на использовании MAC-адресов. Его также отчасти возьмет на себя ENC28J60, благо возможности позволяют. А вот работу на уровнях выше мы будем реализовывать программно - к примеру, на сетевом уровне, будет протокол IP, на транспортном - UDP/TCP, но не будем забегать вперед. Возвращаемся к подключению модулей.
И подключать контроллер мы будем по интерфейсу SPI. Собственно, схематично это представляет из себя следующее:

В разных проектах бывает по-разному, но в данном случае я не вижу ни малейших причин не использовать STM32CubeMx и HAL, так что создаем новый проект и сразу включаем "базово-необходимые" вещи, такие как внешнее тактирование и SWD интерфейс. Кроме того, пара выводов для работы с ENC28J60, в данном случае, это сигналы Chip Select, Reset и, конечно, SPI1:

У меня внешний кварцевый резонатор на 8 МГц, поэтому в окне тактирования:

Ну и напоследок, конечно же, настраиваем SPI. Как изображено на схеме выше, используем ножки PA5 - SPI SCK, PA6 - SPI MISO и PA7 - SPI MOSI. Настройки для ENC28J60 такие:

После этого можно переходить к генерации проекта, больше в CubeMx ничего менять не будем.
Сразу же зададим адекватную структуризацию в проекте, которой и будем придерживаться по мере его, проекта, развития. Создадим папку Modules, в которую будем помещать наши файлы для работы с тем или иным протоколом или устройством. И сразу добавляем подпапку, а также заголовочный и файл с исходным кодом для драйвера ENC28J60, который и будем реализовывать:

Проект целиком можно будет как и всегда скачать в конце статьи, так же в конце статьи размещу полный код этих файлов для максимально удобного прочтения. А пока начинаем поэтапно разбираться.
Инициализация и работа с ENC28J60.
Начнем с базовых функций для отправки и приема данных по SPI, которые сопровождаются установкой и сбросом сигнала Chip Select:
/*----------------------------------------------------------------------------*/
static void SetCS(ENC28J60_CS_State state)
{
HAL_GPIO_WritePin(ENC28J60_CS_PORT, ENC28J60_CS_PIN, (GPIO_PinState)state);
}
/*----------------------------------------------------------------------------*/
Аргументы функции, а также порты STM32, используемые для Chip Select'а и Reset'а объявлены в заголовочном файле enc28j60.h:
#define ENC28J60_CS_PORT GPIOB
#define ENC28J60_CS_PIN GPIO_PIN_3
#define ENC28J60_RESET_PORT GPIOB
#define ENC28J60_RESET_PIN GPIO_PIN_6
typedef enum
{
CS_LOW = 0,
CS_HIGH = 1,
} ENC28J60_CS_State;
Далее последовательно функции для записи/отправки байта, записи нескольких байт, чтения байта:
/*----------------------------------------------------------------------------*/
static void WriteByte(uint8_t data)
{
HAL_StatusTypeDef res = HAL_SPI_Transmit(&hspi1, &data, 1, ENC28J60_SPI_TIMEOUT);
}
/*----------------------------------------------------------------------------*/
static void WriteBytes(uint8_t* data, uint16_t size)
{
HAL_StatusTypeDef res = HAL_SPI_Transmit(&hspi1, data, size, ENC28J60_SPI_TIMEOUT);
}
/*----------------------------------------------------------------------------*/
static uint8_t ReadByte()
{
uint8_t txData = 0x00;
uint8_t rxData = 0x00;
HAL_StatusTypeDef res = HAL_SPI_TransmitReceive(&hspi1, &txData, &rxData, 1, ENC28J60_SPI_TIMEOUT);
return rxData;
}
/*----------------------------------------------------------------------------*/
ENC28J60 поддерживает стандартный механизм взаимодействия с внешним контроллером - через чтение и запись значений в регистры, отвечающие за ту или иную функцию. Все регистры делятся на три основные группы:
- управляющие регистры
- регистры для работы с буфером Ethernet
- и регистры PHY
Сегодня наш акцент будет смещен на работу с первой из этих групп. Управляющие регистры распределены по 4-м банкам:

Соответственно, если у нас активен банк 0, то при записи регистра с адресом 0x00 мы изменим значение регистра ERDPTL, если банк 1 - то EHT0. В конце каждого из банков есть пять одинаковых регистров, которые являются общими, то есть, какой бы банк не был активен, эти адреса (0x1B - 0x1F) будут принадлежать одним и тем же регистрам EIE, EIR, ESTAT, ECON2, ECON1.
Первые буквы регистров указывают на принадлежность регистров:
- E - регистры Ethernet
- MA - группа регистров MAC
- MII - группа регистров MII
Это может показаться очень запутанным, непрозрачным и непонятным, но все встанет на свои места по мере того, как мы будем решать практические задачи. Так что не стоит на этом сильно зацикливаться.
Описание всех регистров и их битов я приводить не буду, поскольку это будет банальным переводом даташита, в этом толку, кроме увеличения объема статьи, никакого нет.
Работа с ENC28J60 построена на следующем формате - контроллер отправляет команду длиной один байт, за которой следуют данные. В зависимости от типа команды за ней следует либо передача данных в ENC28J60, либо чтение данных из нее же. Всего этих команд 7 штук:

Соответственно, как тут указано, байт команды состоит из кода OpCode (3 бита) и 5 битов, которые либо отвечают за адрес регистра из таблицы выше, либо имеют фиксированное значение для некоторых команд. Разберем, к примеру, команду чтения управляющего регистра (Read Control Register):

Все в точности, как мы и ожидали - байт команды (3 бита опкода + 5 битов адреса), за которыми следуют данные со значением регистра, передаваемые из ENC28J60. Команда записи аналогична, за исключением того, что после команды мы уже сами передаем в ENC28J60 данные для записи в регистр:

В общем, несложная модель, реализуем практически. Для начала определим все существующие регистры. Нюанс тут заключается в том, что помимо адреса каждый регистр имеет еще и свой собственный тип (Ethernet, MAC, MII), а также относится к какому-либо банку. Поэтому для описания каждого из регистров будем использовать 8 битов таким вот образом:

- 5 младших битов - адрес регистра
- 2 бита для хранения информации о номере банка - 0b00 - Bank0, 0b01 - Bank1, 0b10 - Bank2, 0b11 - Bank3
- и старший бит - флаг о принадлежности регистра либо к группе Ethernet (бит равен 0), либо к одной из групп MAC или MII (бит равен 1)
В коде это реализовано по следующей схеме. Напоминаю, что полный код файлов и проект целиком будут в конце статьи, здесь же для поддержания целостности изложения будут лишь отрывки для передачи основной сути. На любые возникшие дополнительные вопросы с радостью отвечу 👍
Итак, в заголовочном файле определены enum для банков и типов регистров:
typedef enum
{
BANK_0,
BANK_1,
BANK_2,
BANK_3,
} ENC28J60_RegBank;
typedef enum
{
ETH_REG,
MAC_MII_REG,
} ENC28J60_RegType;
И набор дефайнов:
#define ENC28J60_REG_BANK_OFFSET 5 #define ENC28J60_REG_TYPE_OFFSET 7 #define ENC28J60_REG_BANK_MASK 0x60 #define ENC28J60_REG_TYPE_MASK 0x80 #define ENC28J60_REG_ADDR_MASK 0x1F #define ENC28J60_BANK_0_BITS (BANK_0 << ENC28J60_REG_BANK_OFFSET) #define ENC28J60_BANK_1_BITS (BANK_1 << ENC28J60_REG_BANK_OFFSET) #define ENC28J60_BANK_2_BITS (BANK_2 << ENC28J60_REG_BANK_OFFSET) #define ENC28J60_BANK_3_BITS (BANK_3 << ENC28J60_REG_BANK_OFFSET) #define ENC28J60_BANK_COMMON_BITS (BANK_0 << ENC28J60_REG_BANK_OFFSET) #define ENC28J60_COMMON_REGS_ADDR 0x1B #define ENC28J60_ETH_REG_BIT (ETH_REG << ENC28J60_REG_TYPE_OFFSET) #define ENC28J60_MAC_MII_REG_BIT (MAC_MII_REG << ENC28J60_REG_TYPE_OFFSET)
Все эти определения подчиняются схеме описания регистров, которую мы разобрали. Для номера банка у нас 5-ый и 6-ой из 8 битов, соответственно, ENC28J60_REG_BANK_OFFSET = 5 и ENC28J60_REG_BANK_MASK = 0x60 (0b01100000). Аналогично для бита, который отвечает за тип регистра (он у нас 7-ой).
Также для тех 5-ти регистров, которые не относятся к конкретному банку мы будем использовать такое же значение битов, как для нулевого банка (ENC28J60_BANK_COMMON_BITS). Но для их идентификации будем использовать не значения битов, как для других регистров, а непосредственно значение адреса. Поскольку мы знаем, что эти уникальные регистры расположены по адресам старше, чем ENC28J60_COMMON_REGS_ADDR (0x1B).
Битовые маски помогут извлечь из 8-ми битного значения данные о банке и типе регистра. В результате определение каждого из регистров у нас будет выглядеть следующим образом. Возьмем несколько из разных банков для наглядности:
#define EIE (0x1B | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_COMMON_BITS) #define EWRPTL (0x02 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS) #define MACON4 (0x03 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS) #define EBSTCSH (0x09 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
В итоге имеем изящную систему определения регистров: Register = Address | Type | Bank, где:
- Address - значение адреса из таблицы
- Type -
ENC28J60_ETH_REG_BITилиENC28J60_MAC_MII_REG_BIT - Bank -
ENC28J60_BANK_0_BITS-ENC28J60_BANK_3_BITS
Для работы с определенными битами регистров определяем их позиции. Возьмем, к примеру, регистр ECON1:
#define ECON1_TXRST_BIT (1 << 7) #define ECON1_RXRST_BIT (1 << 6) #define ECON1_DMAST_BIT (1 << 5) #define ECON1_CSUMEN_BIT (1 << 4) #define ECON1_TXRTS_BIT (1 << 3) #define ECON1_RXEN_BIT (1 << 2) #define ECON1_BSEL1_BIT (1 << 1) #define ECON1_BSEL0_BIT (1 << 0)
На этом все, переходим к созданию функций. Группа функций, извлекающих из 8-битного значения регистра данные о его типе, банке и адресе:
/*----------------------------------------------------------------------------*/
static ENC28J60_RegType getRegType(uint8_t reg)
{
ENC28J60_RegType type = (ENC28J60_RegType)((reg & ENC28J60_REG_TYPE_MASK) >> ENC28J60_REG_TYPE_OFFSET);
return type;
}
/*----------------------------------------------------------------------------*/
static ENC28J60_RegBank getRegBank(uint8_t reg)
{
ENC28J60_RegBank bank = (ENC28J60_RegBank)((reg & ENC28J60_REG_BANK_MASK) >> ENC28J60_REG_BANK_OFFSET);
return bank;
}
/*----------------------------------------------------------------------------*/
static uint8_t getRegAddr(uint8_t reg)
{
uint8_t addr = (reg & ENC28J60_REG_ADDR_MASK);
return addr;
}
/*----------------------------------------------------------------------------*/
Перед любыми операциями с регистрами нам нужно будет проверять текущий активный банк в ENC28J60, номер которого хранится в двух младших битах регистра ECON1:

И если нам нужен регистр из другого банка, то банк следует переключить. И чтобы не вычитывать это значение из регистра каждый раз, будем хранить номер текущего банка в переменной:
static ENC28J60_RegBank curBank = BANK_0;
Функция проверки банка:
/*----------------------------------------------------------------------------*/
static void CheckBank(uint8_t reg)
{
uint8_t regAddr = getRegAddr(reg);
if (regAddr < ENC28J60_COMMON_REGS_ADDR)
{
ENC28J60_RegBank regBank = getRegBank(reg);
if (curBank != regBank)
{
uint8_t econ1Addr = getRegAddr(ECON1);
// Clear bank bits
SetCS(CS_LOW);
WriteCommand(BIT_FIELD_CLEAR, econ1Addr);
WriteByte(ECON1_BSEL1_BIT | ECON1_BSEL0_BIT);
SetCS(CS_HIGH);
// Set bank bits
SetCS(CS_LOW);
WriteCommand(BIT_FIELD_SET, econ1Addr);
WriteByte(regBank);
SetCS(CS_HIGH);
curBank = regBank;
}
}
}
/*----------------------------------------------------------------------------*/
Если банк регистра, который передан аргументом, не соответствует текущему - выполняем переключение банка при помощи функции WriteCommand(), которую обсудим чуть ниже. При этом весь этот механизм проверки нужен только для регистров, значения адресов которых численно меньше ENC28J60_COMMON_REGS_ADDR.
Вспоминаем о тех 7-ми командах, которые предоставляет нам ENC28J60, и определяем их:
typedef enum
{
READ_CONTROL_REG,
READ_BUFFER_MEM,
WRITE_CONTROL_REG,
WRITE_BUFFER_MEM,
BIT_FIELD_SET,
BIT_FIELD_CLEAR,
SYSTEM_RESET,
COMMANDS_NUM,
} ENC28J60_Command;
Опкоды команд, в свою очередь, в массиве:
static uint8_t commandOpCodes[COMMANDS_NUM] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07};
Функция отправки байта команды:
/*----------------------------------------------------------------------------*/
static void WriteCommand(ENC28J60_Command command, uint8_t argData)
{
uint8_t data = 0;
data = (commandOpCodes[command] << ENC28J60_OP_CODE_OFFSET) | argData;
WriteByte(data);
}
/*----------------------------------------------------------------------------*/
И на базе этой функции мы создадим все команды. Рассмотрим пару из них, чтение регистра:
/*----------------------------------------------------------------------------*/
static uint8_t ReadControlReg(uint8_t reg)
{
uint8_t data = 0;
ENC28J60_RegType regType = getRegType(reg);
uint8_t regAddr = getRegAddr(reg);
CheckBank(reg);
SetCS(CS_LOW);
WriteCommand(READ_CONTROL_REG, regAddr);
if (regType == MAC_MII_REG)
{
ReadByte();
}
data = ReadByte();
SetCS(CS_HIGH);
return data;
}
/*----------------------------------------------------------------------------*/
Аргумент - значение регистра в том формате, который мы определили для описания регистров контроллера. Из этого значения затем мы получаем банк и тип регистра. Обратите внимания, что для MAC/MII регистров нужно считать дополнительно байт, не несущий никакой смысловой нагрузки, так устроено в ENC28J60.
Команда сброса:
/*----------------------------------------------------------------------------*/
static void SystemReset()
{
SetCS(CS_LOW);
WriteCommand(SYSTEM_RESET, ENC28J60_RESET_COMMAND_ARG);
SetCS(CS_HIGH);
curBank = BANK_0;
HAL_Delay(1);
}
/*----------------------------------------------------------------------------*/
Вот итоговый список тех 7-ми функций-команд для взаимодействия с ENC28J60:
void BitFieldSet(uint8_t reg, uint8_t regData); void BitFieldClear(uint8_t reg, uint8_t regData); uint8_t ReadControlReg(uint8_t reg); void WriteControlReg(uint8_t reg, uint8_t regData); void WriteBufferMem(uint8_t *data, uint16_t size); void ReadBufferMem(uint8_t *data, uint16_t size); static void SystemReset();
Но этим ограничиться не получится. Во-первых, многие регистры представляют из себя комплементарную пару, то есть 16-битное значение хранится в 2-х регистрах (например, EWRPTL и EWRPTH). Старшие 8 бит - в EWRPTH, младшие - в EWRPTL. Так что нам необходимо будет работать и с ними:
/*----------------------------------------------------------------------------*/
static void WriteControlRegPair(uint8_t reg, uint16_t regData)
{
WriteControlReg(reg, (uint8_t)regData);
WriteControlReg(reg + 1, (uint8_t)(regData >> 8));
}
/*----------------------------------------------------------------------------*/
static uint16_t ReadControlRegPair(uint8_t reg)
{
uint16_t data = 0;
data = (uint16_t)ReadControlReg(reg) | ((uint16_t)ReadControlReg(reg + 1) << 8);
return data;
}
/*----------------------------------------------------------------------------*/
И, во-вторых, есть отдельная группа PHY регистров, не относящихся к банкам, которые мы обсудили, имеющих свое собственное адресное пространство. Их мы просто определяем адресами:
#define PHCON1 (0x00) #define PHSTAT1 (0x01) #define PHID1 (0x02) #define PHID2 (0x03) // ...
Особенность этих регистров в том, что с ними нельзя работать напрямую, а только через MIREGADR и MIRDL/MIRDH:
/*----------------------------------------------------------------------------*/
static uint16_t ReadPhyReg(uint8_t reg)
{
uint16_t data = 0;
uint8_t regAddr = getRegAddr(reg);
WriteControlReg(MIREGADR, regAddr);
BitFieldSet(MICMD, MICMD_MIIRD_BIT);
while((ReadControlReg(MISTAT) & MISTAT_BUSY_BIT) != 0);
BitFieldClear(MICMD, MICMD_MIIRD_BIT);
data = ReadControlRegPair(MIRDL);
return data;
}
/*----------------------------------------------------------------------------*/
static void WritePhyReg(uint8_t reg, uint16_t regData)
{
uint8_t regAddr = getRegAddr(reg);
WriteControlReg(MIREGADR, regAddr);
WriteControlRegPair(MIWRL, regData);
while((ReadControlReg(MISTAT) & MISTAT_BUSY_BIT) != 0);
}
/*----------------------------------------------------------------------------*/
Для чтения PHY регистра - заносим его адрес в MIREGADR и выставляем бит MICMD_MIIRD_BIT в регистре MICMD. Бит MISTAT_BUSY_BIT регистра MISTAT сигнализирует о выполнении операции, ожидаем его сброса. После этого очищаем MICMD_MIIRD_BIT и считываем значение PHY регистра из пары MIRDL/MIRDH. В общем, все четко по коду.
База для работы с ENC28J60 полностью готова, так что стартовую статью цикла мы сейчас завершим инициализацией модуля, которую будем использовать и в последующих проектах:
/*----------------------------------------------------------------------------*/
void ENC28J60_Init()
{
HAL_GPIO_WritePin(ENC28J60_RESET_PORT, ENC28J60_RESET_PIN, GPIO_PIN_RESET);
HAL_Delay(50);
HAL_GPIO_WritePin(ENC28J60_RESET_PORT, ENC28J60_RESET_PIN, GPIO_PIN_SET);
HAL_Delay(50);
SystemReset();
// Rx/Tx buffers
WriteControlRegPair(ERXSTL, ENC28J60_RX_BUF_START);
WriteControlRegPair(ERXNDL, ENC28J60_RX_BUF_END);
WriteControlRegPair(ERDPTL, ENC28J60_RX_BUF_START);
// MAC address
WriteControlReg(MAADR1, macAddr[0]);
WriteControlReg(MAADR2, macAddr[1]);
WriteControlReg(MAADR3, macAddr[2]);
WriteControlReg(MAADR4, macAddr[3]);
WriteControlReg(MAADR5, macAddr[4]);
WriteControlReg(MAADR6, macAddr[5]);
WriteControlReg(MACON1, MACON1_TXPAUS_BIT | MACON1_RXPAUS_BIT | MACON1_MARXEN_BIT);
WriteControlReg(MACON3, MACON3_PADCFG0_BIT | MACON3_TXCRCEN_BIT | MACON3_FRMLNEN_BIT);
WriteControlRegPair(MAIPGL, ENC28J60_NBB_PACKET_GAP);
WriteControlReg(MABBIPG, ENC28J60_BB_PACKET_GAP);
WriteControlRegPair(MAMXFLL, ENC28J60_FRAME_DATA_MAX);
// PHY resisters
WritePhyReg(PHCON2, PHCON2_HDLDIS_BIT);
ENC28J60_StartReceiving();
}
/*----------------------------------------------------------------------------*/
И снова я не буду расписывать биты, копируя эту информацию из даташита, если потребуется, задавайте вопросы в комментариях, буду рад помочь. В следующей статье разберемся с приемным и передающим буферами, которые будут использоваться для приема и отправки Ethernet-фреймов.
/**
******************************************************************************
* @file : enc28j60.c
* @brief : ENC28J60 driver
* @author : MicroTechnics (microtechnics.ru)
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "enc28j60.h"
/* Declarations and definitions ----------------------------------------------*/
uint8_t macAddr[MAC_ADDRESS_BYTES_NUM] = {0x00, 0x17, 0x22, 0xED, 0xA5, 0x01};
static uint8_t commandOpCodes[COMMANDS_NUM] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07};
static ENC28J60_RegBank curBank = BANK_0;
extern SPI_HandleTypeDef hspi1;
/* Functions -----------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static void SetCS(ENC28J60_CS_State state)
{
HAL_GPIO_WritePin(ENC28J60_CS_PORT, ENC28J60_CS_PIN, (GPIO_PinState)state);
}
/*----------------------------------------------------------------------------*/
static void WriteBytes(uint8_t* data, uint16_t size)
{
HAL_StatusTypeDef res = HAL_SPI_Transmit(&hspi1, data, size, ENC28J60_SPI_TIMEOUT);
}
/*----------------------------------------------------------------------------*/
static void WriteByte(uint8_t data)
{
HAL_StatusTypeDef res = HAL_SPI_Transmit(&hspi1, &data, 1, ENC28J60_SPI_TIMEOUT);
}
/*----------------------------------------------------------------------------*/
static uint8_t ReadByte()
{
uint8_t txData = 0x00;
uint8_t rxData = 0x00;
HAL_StatusTypeDef res = HAL_SPI_TransmitReceive(&hspi1, &txData, &rxData, 1, ENC28J60_SPI_TIMEOUT);
return rxData;
}
/*----------------------------------------------------------------------------*/
static ENC28J60_RegType getRegType(uint8_t reg)
{
ENC28J60_RegType type = (ENC28J60_RegType)((reg & ENC28J60_REG_TYPE_MASK) >> ENC28J60_REG_TYPE_OFFSET);
return type;
}
/*----------------------------------------------------------------------------*/
static ENC28J60_RegBank getRegBank(uint8_t reg)
{
ENC28J60_RegBank bank = (ENC28J60_RegBank)((reg & ENC28J60_REG_BANK_MASK) >> ENC28J60_REG_BANK_OFFSET);
return bank;
}
/*----------------------------------------------------------------------------*/
static uint8_t getRegAddr(uint8_t reg)
{
uint8_t addr = (reg & ENC28J60_REG_ADDR_MASK);
return addr;
}
/*----------------------------------------------------------------------------*/
static void WriteCommand(ENC28J60_Command command, uint8_t argData)
{
uint8_t data = 0;
data = (commandOpCodes[command] << ENC28J60_OP_CODE_OFFSET) | argData;
WriteByte(data);
}
/*----------------------------------------------------------------------------*/
static void CheckBank(uint8_t reg)
{
uint8_t regAddr = getRegAddr(reg);
if (regAddr < ENC28J60_COMMON_REGS_ADDR)
{
ENC28J60_RegBank regBank = getRegBank(reg);
if (curBank != regBank)
{
uint8_t econ1Addr = getRegAddr(ECON1);
// Clear bank bits
SetCS(CS_LOW);
WriteCommand(BIT_FIELD_CLEAR, econ1Addr);
WriteByte(ECON1_BSEL1_BIT | ECON1_BSEL0_BIT);
SetCS(CS_HIGH);
// Set bank bits
SetCS(CS_LOW);
WriteCommand(BIT_FIELD_SET, econ1Addr);
WriteByte(regBank);
SetCS(CS_HIGH);
curBank = regBank;
}
}
}
/*----------------------------------------------------------------------------*/
static void BitFieldSet(uint8_t reg, uint8_t regData)
{
uint8_t regAddr = getRegAddr(reg);
CheckBank(reg);
SetCS(CS_LOW);
WriteCommand(BIT_FIELD_SET, regAddr);
WriteByte(regData);
SetCS(CS_HIGH);
}
/*----------------------------------------------------------------------------*/
static void BitFieldClear(uint8_t reg, uint8_t regData)
{
uint8_t regAddr = getRegAddr(reg);
CheckBank(reg);
SetCS(CS_LOW);
WriteCommand(BIT_FIELD_CLEAR, regAddr);
WriteByte(regData);
SetCS(CS_HIGH);
}
/*----------------------------------------------------------------------------*/
static uint8_t ReadControlReg(uint8_t reg)
{
uint8_t data = 0;
ENC28J60_RegType regType = getRegType(reg);
uint8_t regAddr = getRegAddr(reg);
CheckBank(reg);
SetCS(CS_LOW);
WriteCommand(READ_CONTROL_REG, regAddr);
if (regType == MAC_MII_REG)
{
ReadByte();
}
data = ReadByte();
SetCS(CS_HIGH);
return data;
}
/*----------------------------------------------------------------------------*/
static void WriteControlReg(uint8_t reg, uint8_t regData)
{
uint8_t regAddr = getRegAddr(reg);
CheckBank(reg);
SetCS(CS_LOW);
WriteCommand(WRITE_CONTROL_REG, regAddr);
WriteByte(regData);
SetCS(CS_HIGH);
}
/*----------------------------------------------------------------------------*/
static void WriteControlRegPair(uint8_t reg, uint16_t regData)
{
WriteControlReg(reg, (uint8_t)regData);
WriteControlReg(reg + 1, (uint8_t)(regData >> 8));
}
/*----------------------------------------------------------------------------*/
static uint16_t ReadControlRegPair(uint8_t reg)
{
uint16_t data = 0;
data = (uint16_t)ReadControlReg(reg) | ((uint16_t)ReadControlReg(reg + 1) << 8);
return data;
}
/*----------------------------------------------------------------------------*/
static void WriteBufferMem(uint8_t *data, uint16_t size)
{
SetCS(CS_LOW);
WriteCommand(WRITE_BUFFER_MEM, ENC28J60_BUF_COMMAND_ARG);
WriteBytes(data, size);
SetCS(CS_HIGH);
}
/*----------------------------------------------------------------------------*/
static void ReadBufferMem(uint8_t *data, uint16_t size)
{
SetCS(CS_LOW);
WriteCommand(READ_BUFFER_MEM, ENC28J60_BUF_COMMAND_ARG);
for (uint16_t i = 0; i < size; i++)
{
*data = ReadByte();
data++;
}
SetCS(CS_HIGH);
}
/*----------------------------------------------------------------------------*/
static void SystemReset()
{
SetCS(CS_LOW);
WriteCommand(SYSTEM_RESET, ENC28J60_RESET_COMMAND_ARG);
SetCS(CS_HIGH);
curBank = BANK_0;
HAL_Delay(100);
}
/*----------------------------------------------------------------------------*/
static uint16_t ReadPhyReg(uint8_t reg)
{
uint16_t data = 0;
uint8_t regAddr = getRegAddr(reg);
WriteControlReg(MIREGADR, regAddr);
BitFieldSet(MICMD, MICMD_MIIRD_BIT);
while((ReadControlReg(MISTAT) & MISTAT_BUSY_BIT) != 0);
BitFieldClear(MICMD, MICMD_MIIRD_BIT);
data = ReadControlRegPair(MIRDL);
return data;
}
/*----------------------------------------------------------------------------*/
static void WritePhyReg(uint8_t reg, uint16_t regData)
{
uint8_t regAddr = getRegAddr(reg);
WriteControlReg(MIREGADR, regAddr);
WriteControlRegPair(MIWRL, regData);
while((ReadControlReg(MISTAT) & MISTAT_BUSY_BIT) != 0);
}
/*----------------------------------------------------------------------------*/
void ENC28J60_Init()
{
HAL_GPIO_WritePin(ENC28J60_RESET_PORT, ENC28J60_RESET_PIN, GPIO_PIN_RESET);
HAL_Delay(50);
HAL_GPIO_WritePin(ENC28J60_RESET_PORT, ENC28J60_RESET_PIN, GPIO_PIN_SET);
HAL_Delay(50);
SystemReset();
// Rx/Tx buffers
WriteControlRegPair(ERXSTL, ENC28J60_RX_BUF_START);
WriteControlRegPair(ERXNDL, ENC28J60_RX_BUF_END);
WriteControlRegPair(ERDPTL, ENC28J60_RX_BUF_START);
// MAC address
WriteControlReg(MAADR1, macAddr[0]);
WriteControlReg(MAADR2, macAddr[1]);
WriteControlReg(MAADR3, macAddr[2]);
WriteControlReg(MAADR4, macAddr[3]);
WriteControlReg(MAADR5, macAddr[4]);
WriteControlReg(MAADR6, macAddr[5]);
WriteControlReg(MACON1, MACON1_TXPAUS_BIT | MACON1_RXPAUS_BIT | MACON1_MARXEN_BIT);
WriteControlReg(MACON3, MACON3_PADCFG0_BIT | MACON3_TXCRCEN_BIT | MACON3_FRMLNEN_BIT);
WriteControlRegPair(MAIPGL, ENC28J60_NBB_PACKET_GAP);
WriteControlReg(MABBIPG, ENC28J60_BB_PACKET_GAP);
WriteControlRegPair(MAMXFLL, ENC28J60_FRAME_DATA_MAX);
// PHY resisters
WritePhyReg(PHCON2, PHCON2_HDLDIS_BIT);
ENC28J60_StartReceiving();
}
/*----------------------------------------------------------------------------*/
void ENC28J60_StartReceiving()
{
BitFieldSet(ECON1, ECON1_RXEN_BIT);
}
/*----------------------------------------------------------------------------*/
/**
******************************************************************************
* @file : enc28j60.h
* @brief : ENC28J60 driver interface
* @author : MicroTechnics (microtechnics.ru)
******************************************************************************
*/
#ifndef ENC28J60_H
#define ENC28J60_H
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
/* Declarations and definitions ----------------------------------------------*/
#define ENC28J60_CS_PORT GPIOB
#define ENC28J60_CS_PIN GPIO_PIN_3
#define ENC28J60_RESET_PORT GPIOB
#define ENC28J60_RESET_PIN GPIO_PIN_6
#define MAC_ADDRESS_BYTES_NUM 6
#define IP_ADDRESS_BYTES_NUM 4
#define ENC28J60_SPI_TIMEOUT 10
#define ENC28J60_OP_CODE_OFFSET 5
#define ENC28J60_REG_BANK_OFFSET 5
#define ENC28J60_REG_TYPE_OFFSET 7
#define ENC28J60_TX_BUF_START 0x0000
#define ENC28J60_RX_BUF_START 0x0600
#define ENC28J60_RX_BUF_END 0x1FFF
#define ENC28J60_FRAME_RX_OK_MASK 0x80
#define ENC28J60_REG_BANK_MASK 0x60
#define ENC28J60_REG_TYPE_MASK 0x80
#define ENC28J60_REG_ADDR_MASK 0x1F
#define ENC28J60_BUF_COMMAND_ARG 0x1A
#define ENC28J60_RESET_COMMAND_ARG 0x1F
#define ENC28J60_FRAME_DATA_MAX 1024
#define ENC28J60_BB_PACKET_GAP 0x15
#define ENC28J60_NBB_PACKET_GAP 0x0C12
#define ENC28J60_BANK_0_BITS (BANK_0 << ENC28J60_REG_BANK_OFFSET)
#define ENC28J60_BANK_1_BITS (BANK_1 << ENC28J60_REG_BANK_OFFSET)
#define ENC28J60_BANK_2_BITS (BANK_2 << ENC28J60_REG_BANK_OFFSET)
#define ENC28J60_BANK_3_BITS (BANK_3 << ENC28J60_REG_BANK_OFFSET)
#define ENC28J60_BANK_COMMON_BITS (BANK_0 << ENC28J60_REG_BANK_OFFSET)
#define ENC28J60_COMMON_REGS_ADDR 0x1B
#define ENC28J60_ETH_REG_BIT (ETH_REG << ENC28J60_REG_TYPE_OFFSET)
#define ENC28J60_MAC_MII_REG_BIT (MAC_MII_REG << ENC28J60_REG_TYPE_OFFSET)
// Common bank registers
#define EIE (0x1B | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_COMMON_BITS)
#define EIR (0x1C | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_COMMON_BITS)
#define ESTAT (0x1D | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_COMMON_BITS)
#define ECON2 (0x1E | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_COMMON_BITS)
#define ECON1 (0x1F | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_COMMON_BITS)
#define EIR_PKTIF_BIT (1 << 6)
#define EIR_DMAIF_BIT (1 << 5)
#define EIR_LINKIF_BIT (1 << 4)
#define EIR_TXIF_BIT (1 << 3)
#define EIR_TXERIF_BIT (1 << 1)
#define EIR_RXERIF_BIT (1 << 0)
#define ECON2_AUTOINC_BIT (1 << 7)
#define ECON2_PKTDEC_BIT (1 << 6)
#define ECON2_PWRSV_BIT (1 << 5)
#define ECON2_VRPS_BIT (1 << 3)
#define ECON1_TXRST_BIT (1 << 7)
#define ECON1_RXRST_BIT (1 << 6)
#define ECON1_DMAST_BIT (1 << 5)
#define ECON1_CSUMEN_BIT (1 << 4)
#define ECON1_TXRTS_BIT (1 << 3)
#define ECON1_RXEN_BIT (1 << 2)
#define ECON1_BSEL1_BIT (1 << 1)
#define ECON1_BSEL0_BIT (1 << 0)
// Bank 0 registers
#define ERDPTL (0x00 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERDPTH (0x01 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EWRPTL (0x02 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EWRPTH (0x03 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ETXSTL (0x04 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ETXSTH (0x05 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ETXNDL (0x06 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ETXNDH (0x07 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXSTL (0x08 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXSTH (0x09 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXNDL (0x0A | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXNDH (0x0B | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXRDPTL (0x0C | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXRDPTH (0x0D | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXWRPTL (0x0E | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define ERXWRPTH (0x0F | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMASTL (0x10 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMASTH (0x11 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMANDL (0x12 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMANDH (0x13 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMADSTL (0x14 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMADSTH (0x15 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMACSL (0x16 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
#define EDMACSH (0x17 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_0_BITS)
// Bank 1 registers
#define EHT0 (0x00 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EHT1 (0x01 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EHT2 (0x02 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EHT3 (0x03 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EHT4 (0x04 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EHT5 (0x05 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EHT6 (0x06 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EHT7 (0x07 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM0 (0x08 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM1 (0x09 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM2 (0x0A | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM3 (0x0B | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM4 (0x0C | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM5 (0x0D | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM6 (0x0E | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMM7 (0x0F | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMCSL (0x10 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMCSH (0x11 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMOL (0x14 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define EPMOH (0x15 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define ERXFCON (0x18 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
#define ERXFCON_UCEN_BIT (1 << 7)
#define ERXFCON_ANDOR_BIT (1 << 6)
#define ERXFCON_CRCEN_BIT (1 << 5)
#define ERXFCON_PMEN_BIT (1 << 4)
#define ERXFCON_MPEN_BIT (1 << 3)
#define ERXFCON_HTEN_BIT (1 << 2)
#define ERXFCON_MCEN_BIT (1 << 1)
#define ERXFCON_BCEN_BIT (1 << 0)
#define EPKTCNT (0x19 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_1_BITS)
// Bank 2 registers
#define MACON1 (0x00 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MACON1_TXPAUS_BIT (1 << 3)
#define MACON1_RXPAUS_BIT (1 << 2)
#define MACON1_PASSALL_BIT (1 << 1)
#define MACON1_MARXEN_BIT (1 << 0)
#define MACON3 (0x02 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MACON3_PADCFG2_BIT (1 << 7)
#define MACON3_PADCFG1_BIT (1 << 6)
#define MACON3_PADCFG0_BIT (1 << 5)
#define MACON3_TXCRCEN_BIT (1 << 4)
#define MACON3_PHDRLEN_BIT (1 << 3)
#define MACON3_HFRMEN_BIT (1 << 2)
#define MACON3_FRMLNEN_BIT (1 << 1)
#define MACON3_FULDPX_BIT (1 << 0)
#define MACON4 (0x03 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MABBIPG (0x04 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MAIPGL (0x06 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MAIPGH (0x07 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MACLCON1 (0x08 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MACLCON2 (0x09 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MAMXFLL (0x0A | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MAMXFLH (0x0B | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MICMD (0x12 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MICMD_MIIRD_BIT (1 << 0)
#define MIREGADR (0x14 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MIWRL (0x16 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MIWRH (0x17 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MIRDL (0x18 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
#define MIRDH (0x19 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_2_BITS)
// Bank 3 registers
#define MAADR5 (0x00 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_3_BITS)
#define MAADR6 (0x01 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_3_BITS)
#define MAADR3 (0x02 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_3_BITS)
#define MAADR4 (0x03 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_3_BITS)
#define MAADR1 (0x04 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_3_BITS)
#define MAADR2 (0x05 | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_3_BITS)
#define EBSTSD (0x06 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define EBSTCON (0x07 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define EBSTCSL (0x08 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define EBSTCSH (0x09 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define MISTAT (0x0A | ENC28J60_MAC_MII_REG_BIT | ENC28J60_BANK_3_BITS)
#define MISTAT_BUSY_BIT (1 << 0)
#define EREVID (0x12 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define ECOCON (0x15 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define EFLOCON (0x17 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define EPAUSL (0x18 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
#define EPAUSH (0x19 | ENC28J60_ETH_REG_BIT | ENC28J60_BANK_3_BITS)
// PHY registers
#define PHCON1 (0x00)
#define PHSTAT1 (0x01)
#define PHID1 (0x02)
#define PHID2 (0x03)
#define PHCON2 (0x10)
#define PHCON2_FRCLNK_BIT (1 << 14)
#define PHCON2_TXIS_BIT (1 << 13)
#define PHCON2_JABBER_BIT (1 << 10)
#define PHCON2_HDLDIS_BIT (1 << 8)
#define PHSTAT2 (0x11)
#define PHIE (0x12)
#define PHIR (0x13)
#define PHLCON (0x14)
#define PHLCON_LACFG3_BIT (1 << 11)
#define PHLCON_LACFG2_BIT (1 << 10)
#define PHLCON_LACFG1_BIT (1 << 9)
#define PHLCON_LACFG0_BIT (1 << 8)
#define PHLCON_LBCFG3_BIT (1 << 7)
#define PHLCON_LBCFG2_BIT (1 << 6)
#define PHLCON_LBCFG1_BIT (1 << 5)
#define PHLCON_LBCFG0_BIT (1 << 4)
#define PHLCON_LFRQ1_BIT (1 << 3)
#define PHLCON_LFRQ0_BIT (1 << 2)
#define PHLCON_STRCH_BIT (1 << 1)
#define MISTAT_BUSY_BIT (1 << 0)
typedef enum
{
READ_CONTROL_REG,
READ_BUFFER_MEM,
WRITE_CONTROL_REG,
WRITE_BUFFER_MEM,
BIT_FIELD_SET,
BIT_FIELD_CLEAR,
SYSTEM_RESET,
COMMANDS_NUM,
} ENC28J60_Command;
typedef enum
{
CS_LOW = 0,
CS_HIGH = 1,
} ENC28J60_CS_State;
typedef enum
{
BANK_0,
BANK_1,
BANK_2,
BANK_3,
} ENC28J60_RegBank;
typedef enum
{
ETH_REG,
MAC_MII_REG,
} ENC28J60_RegType;
typedef struct ENC28J60_Frame
{
uint16_t nextPtr;
uint16_t length;
uint16_t status;
uint8_t data[ENC28J60_FRAME_DATA_MAX];
uint32_t checkSum;
} ENC28J60_Frame;
/* Functions -----------------------------------------------------------------*/
extern void ENC28J60_Init();
extern void ENC28J60_StartReceiving();
#endif // #ifndef ENC28J60_H
Ссылка на проект: MT_ENC28J60_Part_1




А почему не w5100 или w5500?
Что-то даже и не помню... W5500 тоже где-то лежат вроде бы.
Можете помочь пожалуйста наладить соединение stm32 с ноутбуком при помощи ethernet? (не за бесплатно) Сейчас есть проект при котором аналоговый сигнал снимается с датчиков по 4 пинам и преобразовывается со скоростью 20 000 значений по каждому канала, все это пишется на sd карту, потом ксрта перетыкается в ноут, где строятся графики, нужно чтобы значения сразу сыпались в ноут
Плата китайская на базе stm32f407vet6, есть w5100
Добрый день! У меня традиционно со временем свободным плохо совсем... Попробуй в группе или на форуме клич кинуть.
А, вот есть ли STM32 со встроенным Gigabit Ethernet? Или на худой конец внешний модуль, но именно Gigabit?
Здравствуйте! Подскажите, пожалуйста, почему в функции ReadPhyReg читается младший байт MIRDL, когда нужно читать старший байт, а когда сразу два? В документации написано, что данные лежат в MIRDL и MIRDH регистрах.
Доброго дня! Там через ReadControlRegPair() читается пара регистров сразу.
Спасибо, разобрался
Всем привет, нашел маленькую ошибку в коде:
static void WriteBytes(uint8_t* data, uint8_t size){
HAL_StatusTypeDef res = HAL_SPI_Transmit(&hspi1, data, size, ENC28J60_SPI_TIMEOUT);
}
функция принимает размер пакета вида uint8_t size для последующей отправки в ENC через SPI, а это ограничивает размер пакета до 255 байт, исправил на uint16_t size, пакет отправился со всеми 1000 байт данными корректно.
Супер! Блин только проекты перезалить все придется )
Подскажите, с Ethernet встроенным существует готовая плата уровня F405 и выше?
А насколько выше? У меня лежит NUCLEO-H743ZI:
IPv6 работает на этой плате? На STM32F74x, STM32F76x силиконовые баги в части IPv6
Это же от стека зависит, а не от чипа
не факт! в зов-ти от того какие уровни реализованы в "сетевом чипе", если в enc28j60 все делается "руками", то W5500 почти все рутины реализованы в самом чипе
Тут под "чипом" имелся ввиду микроконтроллер (STM32xx).
итого, зовисит:
Именно, а про аппаратные баги непосредственно микроконтроллера я не слышал, поэтому и спрашивал.
что за баги упоминаются?
Здравствуйте, во всех схемах, питание подключают к 3,3В. Почему Вы подключили к 5 В?
Добрый день! Ошибся на картинке похоже... Спасибо, сейчас поправлю.