Stm32 подключение к ethernet
Итак, если Вы заинтересовались, читайте далее…
1. Драйвер RNDIS
На этапе написания решалось две задачи: подписать наше устройство и поддержать стандарт RNDIS.
Подпись устройства сводится к составлению верных USB-дескрипторов. Значение VID выбрано 0x0483 (STMicroelectronics), значение PID — 0x0123 (произвольное). Само собой, в коммерческом применении так делать не следует.
Device Descriptor | ||||
Offset | Field | Size | Value | Description |
---|---|---|---|---|
0 | bLength | 1 | 12h | |
1 | bDescriptorType | 1 | 01h | Device |
2 | bcdUSB | 2 | 0200h | USB Spec 2.0 |
4 | bDeviceClass | 1 | 02h | CDC Control |
5 | bDeviceSubClass | 1 | 00h | |
6 | bDeviceProtocol | 1 | 00h | |
7 | bMaxPacketSize0 | 1 | 40h | 64 bytes |
8 | idVendor | 2 | 0483h | SGS Thomson Microelectronics |
10 | idProduct | 2 | 0123h | |
12 | bcdDevice | 2 | 0001h | 0.01 |
14 | iManufacturer | 1 | 01h | «Fetisov Sergey» |
15 | iProduct | 1 | 02h | «STM32F4 RNDIS» |
16 | iSerialNumber | 1 | 03h | «00000000123C» |
17 | bNumConfigurations | 1 | 01h | |
Configuration Descriptor 1 | ||||
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 02h | Configuration |
2 | wTotalLength | 2 | 0043h | |
4 | bNumInterfaces | 1 | 02h | |
5 | bConfigurationValue | 1 | 01h | |
6 | iConfiguration | 1 | 00h | |
7 | bmAttributes | 1 | 40h | Self Powered |
8 | bMaxPower | 1 | 01h | 2 mA |
Interface Descriptor 0/0 CDC Control, 1 Endpoint | ||||
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 04h | Interface |
2 | bInterfaceNumber | 1 | 00h | |
3 | bAlternateSetting | 1 | 00h | |
4 | bNumEndpoints | 1 | 01h | |
5 | bInterfaceClass | 1 | 02h | CDC Control |
6 | bInterfaceSubClass | 1 | 02h | Abstract Control Model |
7 | bInterfaceProtocol | 1 | FFh | Vendor-Specific |
8 | iInterface | 1 | 00h | |
Header Functional Descriptor | ||||
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 05h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 00h | Header |
3 | bcdCDC | 2 | 0110h | 1.10 |
Call Management Functional Descriptor | ||||
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 05h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 01h | Call Management |
3 | bmCapabilities | 1 | 00h | |
4 | bDataInterface | 1 | 01h | |
Abstract Control Management Functional Descriptor | ||||
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 04h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 02h | Abstract Control Management |
3 | bmCapabilities | 1 | 00h | Requests/notifications not supported |
Union Functional Descriptor | ||||
Offset | Field | Size | Value | Description |
0 | bFunctionLength | 1 | 05h | |
1 | bDescriptorType | 1 | 24h | CS Interface |
2 | bDescriptorSubtype | 1 | 06h | Union |
3 | bControlInterface | 1 | 00h | |
4 | bSubordinateInterface0 | 1 | 01h | CDC Data |
Endpoint Descriptor 81 1 In, Interrupt, 80 ms | ||||
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 81h | 1 In |
3 | bmAttributes | 1 | 03h | Interrupt |
4 | wMaxPacketSize | 2 | 0008h | 8 bytes |
6 | bInterval | 1 | 50h | 80 ms |
Interface Descriptor 1/0 CDC Data, 2 Endpoints | ||||
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 04h | Interface |
2 | bInterfaceNumber | 1 | 01h | |
3 | bAlternateSetting | 1 | 00h | |
4 | bNumEndpoints | 1 | 02h | |
5 | bInterfaceClass | 1 | 0Ah | CDC Data |
6 | bInterfaceSubClass | 1 | 00h | |
7 | bInterfaceProtocol | 1 | 00h | |
8 | iInterface | 1 | 00h | |
Endpoint Descriptor 82 2 In, Bulk, 64 bytes | ||||
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 82h | 2 In |
3 | bmAttributes | 1 | 02h | Bulk |
4 | wMaxPacketSize | 2 | 0040h | 64 bytes |
6 | bInterval | 1 | 00h | |
Endpoint Descriptor 03 3 Out, Bulk, 64 bytes | ||||
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 03h | 3 Out |
3 | bmAttributes | 1 | 02h | Bulk |
4 | wMaxPacketSize | 2 | 0040h | 64 bytes |
6 | bInterval | 1 | 00h |
Поддержка стандарта осуществлена в соответствии с документацией.
Также в сети присутствует множество RNDIS-драйверов для других платформ (1, 2, 3), что существенно упростило разработку.
В части обмена драйвер повторяет особенность CDC-класса за исключением того, что передаваемые пакеты оборачиваются служебной информацией. Также существует специальный интерфейс для передачи запросов от usb-хоста с целью настройки устройства или получения его статуса. Детально код драйвера можно изучить в модуле usbd_rndis_core.c.
Минимальная настройка драйвера при встраивании заключается в изменении определений в файле usbd_rndis_core.h.
— MAC-адрес устройства (PERMANENT_HWADDR, STATION_HWADDR)
— Идентификатор производителя (RNDIS_VENDOR)
— Значение MTU (ETH_MTU):
Также в файле usbd_desc.h следует изменить название продукта (USBD_PRODUCT_STRING) и производителя (USBD_MANUFACTURER_STRING).
Процесс установки драйвера в ОС Windows:
1. Пункт меню «обновить драйвер»;
2. Выполнить поиск драйвера;
3. Выбрать драйвер из списка;
4. В списке классов устройств выбрать «Сетевые адаптеры»;
5. В списке производителе выбрать «Microsoft Corporation»;
6. И продукт «Remote NDIS based Internet Sharing Device».
Более подробно процесс установки стандартного RNDIS устройства описан по ссылке.
2. Прикручиваем LwIP
Драйвер RNDIS обеспечивает лишь транспортную функцию для кадров стандарта 802.3 (Ethernet). Очевидно, что поддержку всего многообразия пакетов и стандартов требуется возложить на сетевой стек. В его роли было принято решение использовать популярный стек для встраиваемых систем lwip последней (на текущий момент) версии 1.4.1. Надо отдать должное авторам стека, тот получился весьма просто интегрируемым. При всём при этом, код стека богат полезными комментариями и инструкциями.
Запуск стека под stm32 ограничился включением в процесс сборки конкретного набора исходных файлов и вводом определений в файле lwipopts.h:
Нужно отметить, работа по портированию стека на STM32F4 производилась и раньше фирмой STMicroelectronics (приложение STSW-STM32070).
Запускаем DHCP-сервер
Добавление DHCP-сервера в состав библиотеки связано с необходимостью инициализировать сетевой интерфейс на стороне host-а. По умолчанию, создаваемый при подключении устройства интерфейс настроен на автоматическое получение ip-адреса. Библиотека также успешно работает и при статической адресации, но это является не совсем удобным.
К сожалению, среди поставляемых lwip средств, DHCP-сервер отсутствует.
Однако, это не является существенной проблемой, т.к. в своей минимальной реализации DHCP-сервер весьма минималистичен.
В сети присутствует, пожалуй, единственный пример DHCP-сервера, работающего со стеком lwip. Данный источник оказался весьма полезным для изучения, хоть и не пригодным для встраивания по принципу «as-is» по причине отсутствия возможности конфигурирования и использования socket-api.
Поэтому было принято решение написать DHCP сервер.
И вот его скромные возможности:
— выдача адресов на произвольное время
— резервирование адресов по MAC адресу
— настройка DNS-сервера
Подключение сервера в тестовом проекте:
Запускаем DNS-сервер
Желаемый нами результат работы — отображение web-страницы при вводе в браузере некоторого имени ресурса. Однако, это возможно только при наличии DNS-сервера, который будет «знать» о нашем хосте. Конечно, этот результат доступен, если в адресной строке напрямую ввести ip-адрес: 192.168.7.1. Такой адрес имеет по умолчанию наше устройство. Однако, будем более искусны и запустим DNS-сервер.
В отличии от DHCP, текущая реализация DNS-сервера ещё «тоньше». На данный момент она позволяет обработать только стандартные DNS-запросы на одну запись.
Запуск сервера в проекте:
Долго и безрезультатно пришлось бороться с запуском известного сервера из пакета lwip «contrib-1.4.1». До сих пор для меня остаётся загадкой таинственный HardFault, возникающий на, казалось бы, ровном месте. Были проверены все настройки, адреса чтения и записи, глубина стека… Но, увы.
В этой, первой, статье рассмотрим физическое подключение, инициализацию интерфейсов, настройку ENC28J60 и, пожалуй, напишем сразу драйвер для работы с вышеупомянутым контроллером. Все буду описывать по возможности подробнее, но если вдруг возникнут вопросы, добро пожаловать в комментарии, а еще лучше к нам на форум )
Подключение ENC28J60.
Для ENC28J60 у меня среди прочих есть такой модуль, пусть будет он:
Мы будем писать код исходя из соображений возможности его использования и с любым другим железом, так что выбор в данном случае не критичен.
Вспоминаем сетевую модель OSI (здесь в ближайшее время я помещу ссылку на статью об OSI):
У меня внешний кварцевый резонатор на 8 МГц, поэтому в окне тактирования:
После этого можно переходить к генерации проекта, больше в CubeMx ничего менять не будем.
Сразу же зададим адекватную структуризацию в проекте, которой и будем придерживаться по мере его, проекта, развития. Создадим папку Modules, в которую будем помещать наши файлы для работы с тем или иным протоколом или устройством. И сразу добавляем подпапку, а также заголовочный и файл с исходным кодом для драйвера ENC28J60, который и будем реализовывать:
Инициализация и работа с ENC28J60.
Начнем с базовых функций для отправки и приема данных по SPI, которые сопровождаются установкой и сбросом сигнала Chip Select:
Далее последовательно функции для записи/отправки байта, записи нескольких байт, чтения байта:
- управляющие регистры
- регистры для работы с буфером Ethernet
- и регистры PHY
Сегодня наш акцент будет смещен на работу с первой из этих групп. Управляющие регистры распределены по 4-м банкам:
Первые буквы регистров указывают на принадлежность регистров:
Это может показаться очень запутанным, непрозрачным и непонятным, но все встанет на свои места по мере того, как мы будем решать практические задачи. Так что не стоит на этом сильно зацикливаться.
Описание всех регистров и их битов я приводить не буду, поскольку это будет банальным переводом даташита, в этом толку, кроме увеличения объема статьи, никакого нет.
Соответственно, как тут указано, байт команды состоит из кода OpCode (3 бита) и 5 битов, которые либо отвечают за адрес регистра из таблицы выше, либо имеют фиксированное значение для некоторых команд. Разберем, к примеру, команду чтения управляющего регистра (Read Control Register):
В общем, несложная модель, реализуем практически. Для начала определим все существующие регистры. Нюанс тут заключается в том, что помимо адреса каждый регистр имеет еще и свой собственный тип (Ethernet, MAC, MII), а также относится к какому-либо банку. Поэтому для описания каждого из регистров будем использовать 8 битов таким вот образом:
Итак, в заголовочном файле определены enum для банков и типов регистров:
И набор дефайнов:
Все эти определения подчиняются схеме описания регистров, которую мы разобрали. Для номера банка у нас 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).
В итоге имеем изящную систему определения регистров: Register = Address | Type | Bank, где:
Для работы с определенными битами регистров определяем их позиции. Возьмем, к примеру, регистр ECON1:
Перед любыми операциями с регистрами нам нужно будет проверять текущий активный банк в ENC28J60, номер которого хранится в двух младших битах регистра ECON1:
И если нам нужен регистр из другого банка, то банк следует переключить. И чтобы не вычитывать это значение из регистра каждый раз, будем хранить номер текущего банка в переменной:
Функция проверки банка:
Вспоминаем о тех 7-ми командах, которые предоставляет нам ENC28J60, и определяем их:
Опкоды команд, в свою очередь, в массиве:
Функция отправки байта команды:
И на базе этой функции мы создадим все команды. Рассмотрим пару из них, чтение регистра:
Вот итоговый список тех 7-ми функций-команд для взаимодействия с ENC28J60:
И, во-вторых, есть отдельная группа PHY регистров, не относящихся к банкам, которые мы обсудили, имеющих свое собственное адресное пространство.
Их мы просто определяем адресами:
Особенность этих регистров в том, что с ними нельзя работать напрямую, а только через MIREGADR и MIRDL/MIRDH:
База для работы с ENC28J60 полностью готова, так что стартовую статью цикла мы сейчас завершим инициализацией модуля, которую будем использовать и в последующих проектах:
И снова я не буду расписывать биты, копируя эту информацию из даташита, если потребуется, задавайте вопросы в комментариях, буду рад помочь! В следующей статье разберемся с приемным и передающим буферами, которые будут использоваться для приема и отправки Ethernet-фреймов.
Поскольку в этом эксперименте я использовал макетную плату без пайки, все стало выглядеть очень симпатично (по сравнению с предыдущими экспериментами). Поэтому не стыдно выложить фото этой сборки:
Микроконтроллер STM32F103 и модуль Ethernet W5500 в сборе
Как работать с библиотекой я расскажу дальше, а пока поработаем с нашим фото.
Пройдемся по макетке слева направо и посмотрим, что я там разместил.
Макетная сборка
От МК этой плате требуется только одна линия: Tx (отдаем строчки), которая на адаптере будет обозначаться уже как Rx (принимаем строчки). Ну и само собой кабель, который вы видите на торце платы, подключен к USB разъему PC.
Переключатель на плате устанавливаем в положение 3.3В: это будут уровни линий передачи данных, с которыми работает адаптер.
МК STM32F103 Blue Pill
Модуль питания сконфигурирован джамперами на выходное напряжение 3.3В, отбор питания идет по внешним линиям модулями W5500 и МК STM32, которым требуется 3.3В. На фото видны эти желтые и коричневые короткие перемычки, которые идут рядом.
Нюанс: вы пожете подключить к МК вместо 3.3В 5В к соответствующему (другому естественно!) контакту. Эти 5В будут преобразованы в 3.3В.
Внимание! Не подключайте к плате внешнее питание 3.3В и 5В от USB одновременно. Можете потерять МК ) Также по этой причине провод +5В для JTAG от USB PC болтается в воздухе, как может заметить внимательный читатель )
Модуль UART требует электропитание 5В, которое мы обеспечиваем внешним проводком который подключается к пину +5В модуля питания. Есть подозрение, что он прекрасно будет работать и без этого проводка от USB, когда тот подключен (и работает на самом деле).
Конфигурация
Как и раньше, для создания проекта воспользуемся услугами STM32CubeMX. По традиции создаем проект на основе Makefile, чтобы не городить огород с визуальными системами разработки (помните наш проект hardcore? Только Makefile и vim!). Как обычно, включаем пункт Debug:Serial Wire в меню SYS, чтобы иметь возможность прошивки и отладки. Сразу включатся пины PA13, PA14: через них STLINK будет общаться с МК.
Создаем проект и переходим к следующему шагу.
Соединения
Поскольку распиновка модулей W5500 и UART-USB и так известна, а распиновку Blue Pill мы уже получили с помощью Куба, займемся соединениями. Работа приятная, медитативная, навевает мысли о тщете всего сущего, хорошо заниматься этим перед сном, глубокое погружение гарантировано 🙂
Сегодня мы подключим контроллер к локальной сети с помощью модуля на микросхеме ENC28J60.
Так как мы интерфейсом LAN пользовались только используя микроконтроллер AVR, я решил сделать подобный урок и на STM32., причём также с использованием того же модуля, так как именно этот модуль позволяет прочувствовать тот или иной интерфейс LAN изнутри, так как для него у STM готовых библиотек нет.
Данный модуль выглядит вот так
В качестве микроконтроллера был выбран STM32F103RCT6, на который я ещё уроков не давал, но распаковка среди моих роликов на Youtube имеется. Но так как уроков нет, то проект мы будем создавать заново. Контроллер установлен на отладочной плате, которая выглядит вот так (нажмите на картинку для увеличения изображения)
Для программирования нашей отладочной платы мы будем использовать программатор ST-Link-V2, на который есть также распаковка, подключенный по интерфейсу SWD. Как подключать данный программатор, также есть в видео по распаковке здесь.
Сердцем данного модуля является одноименная микросхема ENC28J60, которая представляет собой готовое сетевое решение, в котором присутствует и физический и канальный уровень. Обмен данными с контроллером данная микросхема осуществляет посредством шины SPI. Физический уровень у неё организован по стандарту 10BASE-T. То есть максимальная скорость передачи данных — 10 мегабит в секунду, что хотя и мало по сегодняшним современным меркам скоростей передачи данных по сети, но для микроконтроллера вполне хватит, и я думаю это будет удобнее, чем по USB, так как, находясь в сети и имея свой адрес и канальный и сетевой, контроллер может обращаться к любому компьютеру как в локальной сети, так и в глобальной, что сравнительно-таки неплохо.
Внутренняя структура микросхемы представляет собой следующий вид (нажмите на картинку для увеличения изображения)
Мы видим здесь, что у нас существуют управляющие регистры, в которые мы будем отправлять определённые команды для управления теми или иными действиями и настройками, Также мы видим буфер размером 8 килобайт для получения и отправки данных по сети.
Поставляется микросхема в различных корпусах. На нашем модуле она в корпусе, предназначенном для поверхностного монтажа.
Для подключения модуля имеется следующий разъём
Все обозначения контактов мы видим справа.
К отладочной плате мы подключим модуль следующим образом
ENC28J60 — STM32F103RCT6
VCC — 3.3V
GND — GND
CS — PA4
RESET — 3.3V
SI — PA7 (MOSI)
SCK — PA5
SO — PA6 (MISO)
Кроме всего прочего мы к плате подключим переходник USART, чтобы следить за за данными, которые будут приходить из сети в контроллер.
Регистры в микросхеме организованы следующим образом
Мы видим здесь, что кроме адресов регистров существуют и банки, то есть порядок адресации регистров сегментированный, а начиная с адреса 1Bh обращение к регистрам от банков не зависит, они мапятся на все банки.
Основное назначение регистров можно узнать по их начальным буквам в аббревиатуре
Конкретно с некоторыми регистрами мы будем знакомиться уже при написании исходного кода, так как его будет очень много и к тому моменту, когда нам потребуется тот или иной регистр, мы уже забудем его назначение.
Само собой, чтобы заниматься программированием передачи данных по сети, необходимы знания сетевых уровней, модели OSI и всего, что связано с сетью, так как учить данным вещам я не буду, по этим вопросам есть очень много информации, могу только если что-то посоветовать, так что обращайтесь если что в комментариях. Но некоторые сведения я, конечно, давать буду, не без этого.
Вообще сетевая модель делится на несколько уровней. Основные из них следующие:
1. Прикладной уровень
2. Уровень представления
3. Сеансовый уровень
4. Транспортный уровень
5. Сетевой уровень
6. Канальный уровень
7. Физический уровень
При передаче данных из программы в сетевой провод мы проходим путь сверху вниз по вышенаписанному списку. Сначала мы наши данные объединяем в какие-то удобные последовательности или массивы, понятные для прикладного уровня принимаемой стороны, затем мы их шифруем либо ещё как-то их преобразуем для защиты от несанкционированного использования, затем обёртываем в определённый протокол, который поможет поддержать соединение и не потерять его (сеансовый уровень), затем прицепляем ещё заголовок спереди, который обеспечит передачу определённому порту от другого определённого порта, затем ещё обёртываем в протокол, который обеспечит доставку к определённому устройству, имеющему сетевой адрес (IP), затем ещё обёртываем протокол, который несёт в себе физические адреса устройств MAC (канальный уровень), а последний уровень уже обеспечит непосредственную доставку всех этих несколько раз обёрнутых пакетов (физический уровень), к которому относятся трансформаторы, провода, концентраторы, повторители сигнала и медиаконвертеры.
Вообщем кратко как-то вот так. Поконкретней с протоколами будем знакомиться по мере их использования.
Ну давайте, чтобы от этой всей теории немного отвлечься, создадим наш проект.
Для этого запустим генератор проектов Cube MX создадим новый проект и выберем там наш контроллер (нажмите на картинку для увеличения изображения)
Включим SWD
Затем подключим кварцевый резонатор
Clock Configuration настроим следующим образом
Вернёмся в "Pinout", включим там SPI
Также включим USART
Ещё включим на выход ножки портов PC13 и PA4, так как к перовой подключен красный светодиод, а ко второй мы подключи Chip Select нашего SPI
Идём в Configuration, первым делом включим на обе ножки портов, задействованные выше скорость Medium
Для SPI настроим следующие параметры
А USART мы настроим следующим образом
На всякий случай включим прерывания, лишним не будет
Сохраним проект для Keil5, а назовём его по имени нашей используемой микросхемы незатейливо — ENC28J60 .
Сгенерируем проект для Keil, запустим его, настроим программатор на autoreset и пропробуем проект собрать.
Соединим все наши модули, подключив их согласно распиновке в Cube MX , и посмотрим, что у нас получилось (белый провод от USART можно не подключать, он и так нормально питается)
То есть USART у нас подключен следующим образом:
FDI — STM32F103RCT6
GND — GND
RX — A9
TX — A10
Ножку RESET модуля ENC28J60 желательно подключить к 3.3в через резистор 10 кОм.
В следующей части нашего урока мы добавим несколько макросов и функций по работе с микросхемой ENC28J60, а также немного поработаем над изучением технической документации.
Отладочную плату можно приобрести здесь STM32F103C8T6
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN
Переходник USB to TTL можно приобрести здесь USB to TTL ftdi ft232rl
В состав микроконтроллеров STM32F107xx, STM32F207xx и STM32F217xx входит периферия Ethernet MAC. Основные свойства периферии:
полноценный MAC-уровень с подключением к внешнему физическому уровню;
работа на скоростях 10 и 100 Мбит/с;
полу/полнодуплексные режимы работы;
выделенный DMA-контроллер с очередями приёма и передачи пакетов;
поддержка привязки пакетов во времени;
управление входом/выходом режимов низкого энергопотребления;
интегрированный набор векторов прерываний.
Рассмотрим блок-схему Ethernet MAC в контроллере STM32F107 (см. рис. 1).
Подключение по RMII требует всего восьми портов контроллера, в то время как для подключения физического уровня по стандарту MII требуется 16 портов контроллера. Если перевести количество свободных портов контроллера в стоимость, можно предположить, что соединение по RMII более выгодно. Скорее всего, так и есть, однако имеется ряд нюансов. Интерфейс RMII работает на частоте 50 МГц, что вдвое выше, чем у MII. Соответственно, эмиссия помех такого решения выше. Более того, для генерации 50 МГц и выдаче этой частоты на микросхему физического уровня задействуется один из множителей частоты — рекомендуемое производителем решение по тактированию внешней микросхемы Ethernet PHY. И, наконец, контроллер STM32 может подключить до 32-х внешних микросхем физического уровня по интерфейсу MII и всего лишь одну — по интерфейсу RMII. В любом случае, у разработчика остаётся право выбора, каким образом подключить внешнюю микросхему физического уровня Ethernet.
Построим простое приложение, на базе которого происходит обмен данными по сети Ethernet. Мы не станем описывать настройки портов, тактирования периферии и прочие детали проекта, которые не имеют непосредственного отношения к приёму/передаче данных по Ethernet. Проект будет построен на базе прилагаемых стандартных библиотек для Ethernet.
До задания параметров Ethernet следует установить начальные значения. Выполнить это можно следующим образом:
// сброс настроек в начальное состояние Ethernet
ETH_DeInit();
// сбрасываем Ethernet
ETH_SoftwareReset();
// ждём подтверждения, что Ethernet сбросился
while(ETH_GetSoftwareResetStatus()==SET);
Далее задаем свойства сети:
// конфигурируем Ethernet
ETH_StructInit(Ð_InitStructure);
// Устанавливаем параметры ETH_InitStructure
ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable ;
ETH_InitStructure.ETH_Speed = ETH_Speed_100M;
ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
// Принимать всё подряд
// ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Enable;
ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;
ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;
ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;
ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;
// производим инициализацию параметров
Value = ETH_Init(Ð_InitStructure, PHY_ADDRESS);
Процедура ETH_Init настраивает не только внутренние регистры контроллера, но и внешнюю микросхему PHY-уровня. Далее должно быть выделено пространство буферов приёма, буферов передачи и для описывающих их дескрипторов.
// Будем использовать стандартные библиотеки, поэтому создаём соответствующие структуры
ETH_InitTypeDef ETH_InitStructure;
// Ethernet DMA работает с дескрипторами — указатели массивов данных, которые следует транслировать (+ дополнительная служебная информация)
// Дескрипторы
ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
// массив буферов приёма и отправки данных
// на которые будут ссылаться дескрипторы
u8 Rx_Buff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE], Tx_Buff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE];
Описать дескрипторы можно вручную, заполнив все адреса и прочую служебную информацию. Но можно выполнить это гораздо быстрее, воспользовавшись библиотечной функцией. В данной ситуации создаём цепочную структуру дескрипторов (дескриптор, который описывает одну область памяти и указывает адрес следующего дескриптора):
// Записываем часть параметров дескрипторов (указатели на буфер + на следующий дескриптор)
ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
// Записываем длину буфера в дескриптор
DMATxDscrTab->ControlBufferSize = 100;
// Устанавливаем дескрипторы для приёма
ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
DMARxDscrTab->ControlBufferSize = ETH_MAX_PACKET_SIZE | (1<<14);
Функции в качестве аргументов имеют начальные адреса дескрипторов, начальные адреса буферов данных и длину одного буфера. Аналогичные библиотечные функции имеются для организации кольцевой структуры дескрипторов.
Итак, дескрипторы описаны, свойства сети заданы, запускаем Ethernet:
// Разрешаем приём
DMARxDscrTab->Status = ETH_DMARxDesc_OWN;
// Запускаем Ethernet
ETH_Start();
// а прошлая посылка отправлена?Если отправлена, то
if ((DMATxDscrTab->StatusÐ_DMARxDesc_OWN) == 0)
// Чтобы успешно отправлять, следует сначала отключить передачу (либо успевать обрабатывать все буферы, чтобы DMA не подвешивался).
ETH_DMATransmissionCmd(DISABLE);
//помещаем данные по яркости в 20 и 21-й байт посылки
Tx_Buff[0][20] = Bright_Tx&0xFF;
Tx_Buff[0][21] = Bright_Tx>>8;
// отдаём дескриптор в DMA Ethernet
DMATxDscrTab->Status = ETH_DMARxDesc_OWN | ETH_DMATxDesc_TCH | ETH_DMATxDesc_TTSE | ETH_DMATxDesc_LS | ETH_DMATxDesc_FS;
// разрешаем передачу
ETH_DMATransmissionCmd(ENABLE);
>
В отправляемый пакет помещаем данные. В нашем случае это два байта со смещением 20 и 21 от начала пакета и передаём дескриптор в распоряжение DMA.
// адрес назначения МАССОВАЯ РАССЫЛКА
Tx_Buff[0][0]=0xff;
Tx_Buff[0][1]=0xff;
Tx_Buff[0][2]=0xff;
Tx_Buff[0][3]=0xff;
Tx_Buff[0][4]=0xff;
Tx_Buff[0][5]=0xff;
// адрес источника
Tx_Buff[0][6]=0x17;
Tx_Buff[0][7]=0x11;
Tx_Buff[0][8]=0x12;
Tx_Buff[0][9]=0x13;
Tx_Buff[0][10]=0x14;
Tx_Buff[0][11]=0x15;
// длина пакета
Tx_Buff[0][6]=0x0;
Tx_Buff[0][7]=0x55;
Мы самостоятельно заполняем преамбулу пакета аналогично структуре, описанной в разделе «Ethernet в STM32». При этом мы не заполняем синхронизирующий пакет с командой начала данных — первые восемь байт, а так же контрольную сумму CRC32, которая будет добавлена автоматически.
Если мы собираемся принимать пакеты определённой структуры, то мы можем ввести фильтрацию. Далее показан вариант настройки Ethernet периферии с организацией фильтрации по адресу источника.
// Настройка фильтра приёма
ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;
ETH_InitStructure.ETH_SourceAddrFilter = ETH_SourceAddrFilter_Normal_Enable;
ETH_InitStructure.ETH_DestinationAddrFilter = ETH_DestinationAddrFilter_Normal;
Далее необходимо настроить фильтр. В качестве фильтра воспользуемся MACAddress1:
//настраиваем MAC-адрес устройства
Mac_addr0[0]=0x0;
Mac_addr0[1]=0x55;
Mac_addr0[2]=0x12;
Mac_addr0[3]=0x13;
Mac_addr0[4]=0x14;
Mac_addr0[5]=0x17;
ETH_MACAddressConfig(ETH_MAC_Address1, Mac_addr0);
ETH_MACAddressFilterConfig(ETH_MAC_Address1,ETH_MAC_AddressFilter_SA);
ETH_MACAddressPerfectFilterCmd(ETH_MAC_Address1, ENABLE);
Читайте также: