Wiznet W5500 — это Ethernet-контроллер с интерфейсом SPI . Чип поддерживает стандарты 10baseT и 100baseT. Характерной особенностью контроллера является то, что он имеет аппаратную реализацию TCP/IPv4. Это позволяет существенно разгрузить работающий с ним микроконтроллер. Wiznet W5500 поддерживает до 8 сокетов и имеет суммарно 16 Кб памяти на прием и еще 16 Кб на передачу. Эта память может быть распределена между сокетами произвольным образом. Давайте же разберемся, как работать с этим чипом на примере МК STM32.

Примечание: В этом контексте вас может заинтересовать статья Изучаем Ethernet-фреймы с помощью осциллографа , если вдруг вы ее пропустили.

В свои экспериментах я использовал такой модуль:

Цена устройства на eBay составляет около 4$. На модуле есть стабилизатор напряжения, благодаря чему его можно питать как от 5 В, так и от 3.3 В. Сам же чип прекрасно работает как с 3.3-вольтовой, так и 5-иволтовой логикой. Таким образом, его можно без труда использовать с любым другим микроконтроллером, не обязательно STM32, или даже с FPGA . В частности, для Arduino существует библиотека Ethernet2 от Adafruit.

Fun fact! На момент написания этих строк, Sigrok не имел декодера протокола для Wiznet W5500. Между тем, протокол этот довольно несложен, и описан в даташите W5500 [PDF] . Отличная возможность законтрибьютить в опенсорс 😉

Существует также официальная библиотека от самого производителя под названием ioLibrary_Driver , которой мы и воспользуемся. Библиотека имеет лицензию MIT. Она не завязана на конкретный микроконтроллер, и может быть с тем же успехом использована с STM8, AVR, 8051 или PIC.

Код библиотеки был скопирован в каталог Lib/ioLibrary_Driver проекта. В Makefile было дописано следующее:

# …

C_SOURCES =  
Lib / ioLibrary_Driver / Ethernet / wizchip_conf.c
Lib / ioLibrary_Driver / Ethernet / socket.c
Lib / ioLibrary_Driver / Ethernet / W5500 / w5500.c
Lib / ioLibrary_Driver / Internet / DHCP / dhcp.c
Lib / ioLibrary_Driver / Internet / DNS / dns.c
# … прочие файлы были здесь и раньше …

# …

C_INCLUDES =  
-ILib / ioLibrary_Driver / Ethernet
-ILib / ioLibrary_Driver / Internet / DHCP
-ILib / ioLibrary_Driver / Internet / DNS
# … прочие каталоги были здесь и раньше …

# …

Правим Lib/ioLibrary_Driver/Ethernet/wizchip_conf.h:

#define _WIZCHIP_    W5500

Основной код проекта рассмотрим по частям. Первым делом добавляем необходимые инклудники:

#include «socket.h»
#include «dhcp.h»
#include «dns.h»

Мы будем использовать три сокета — один для DHCP, один для DNS, и еще один для хождения по HTTP:

#define DHCP_SOCKET     0
#define DNS_SOCKET      1
#define HTTP_SOCKET     2

Для работы DHCP и DNS нам понадобятся временные буферы:

// 1K should be enough, see https://forum.wiznet.io/t/topic/1612/2
uint8_t dhcp_buffer [ 1024 ] ;
// 1K seems to be enough for this buffer as well
uint8_t dns_buffer [ 1024 ] ;

Также объявим процедуры, через которые библиотека будет ходить в SPI:

void W5500_Select ( void ) {
HAL_GPIO_WritePin ( W5500_CS_GPIO_Port , W5500_CS_Pin ,
GPIO_PIN_RESET ) ;
}

void W5500_Unselect ( void ) {
HAL_GPIO_WritePin ( W5500_CS_GPIO_Port , W5500_CS_Pin ,
GPIO_PIN_SET ) ;
}

void W5500_ReadBuff ( uint8_t * buff , uint16_t len ) {
HAL_SPI_Receive ( & hspi1 , buff , len , HAL_MAX_DELAY ) ;
}

void W5500_WriteBuff ( uint8_t * buff , uint16_t len ) {
HAL_SPI_Transmit ( & hspi1 , buff , len , HAL_MAX_DELAY ) ;
}

uint8_t W5500_ReadByte ( void ) {
uint8_t byte ;
W5500_ReadBuff ( & byte , sizeof ( byte ) ) ;
return byte ;
}

void W5500_WriteByte ( uint8_t byte ) {
W5500_WriteBuff ( & byte , sizeof ( byte ) ) ;
}

Эти процедуры должны быть зарегистрированы таким образом:

UART_Printf ( «Registering W5500 callbacks… r n » ) ;
reg_wizchip_cs_cbfunc ( W5500_Select , W5500_Unselect ) ;
reg_wizchip_spi_cbfunc ( W5500_ReadByte , W5500_WriteByte ) ;
reg_wizchip_spiburst_cbfunc ( W5500_ReadBuff , W5500_WriteBuff ) ;

Далее распределяем память между сокетами. Пусть у каждого сокета будет по 2 Кб на прием и передачу:

UART_Printf ( «Calling wizchip_init()… r n » ) ;
uint8_t rx_tx_buff_sizes [ ] = { 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 } ;
wizchip_init ( rx_tx_buff_sizes , rx_tx_buff_sizes ) ;

Инициализируем клиент DHCP:

UART_Printf ( «Calling DHCP_init()… r n » ) ;
wiz_NetInfo net_info = {
. mac = { 0xEA , 0x11 , 0x22 , 0x33 , 0x44 , 0xEA } ,
. dhcp = NETINFO_DHCP
} ;
// set MAC address before using DHCP
setSHAR ( net_info. mac ) ;
DHCP_init ( DHCP_SOCKET , dhcp_buffer ) ;

Ему также нужно несколько колбэков, роль которых очевидна из названия:

volatile bool ip_assigned = false ;

void Callback_IPAssigned ( void ) {
UART_Printf ( «Callback: IP assigned! Leased time: %d sec r n » ,
getDHCPLeasetime ( ) ) ;
ip_assigned = true ;
}

void Callback_IPConflict ( void ) {
UART_Printf ( «Callback: IP conflict! r n » ) ;
}

Эти колбэки регистрируются следующим образом:

UART_Printf ( «Registering DHCP callbacks… r n » ) ;
reg_dhcp_cbfunc (
Callback_IPAssigned ,
Callback_IPAssigned ,
Callback_IPConflict
) ;

Согласно документации, мы также должны раз в секунду вызывать процедуру DHCP_time_handler . Для этого проще всего отредактировать Src/stm32f4xx_it.c:

# …
#include «dhcp.h»

# …

void SysTick_Handler ( void ) {
/* USER CODE BEGIN SysTick_IRQn 0 */
static uint16_t ticks = 0 ;
ticks ++;
if ( ticks == 1000 ) {
DHCP_time_handler ( ) ;
ticks = 0 ;
}
/* USER CODE END SysTick_IRQn 0 */

/* … прочий код был здесь и раньше … */
}

Возвращаемся к основному коду программы. Получаем IP-адрес и информацию о сети по DHCP:

UART_Printf ( «Calling DHCP_run()… r n » ) ;
// actually should be called in a loop, e.g. by timer
uint32_t ctr = 10000 ;
while ( ( ! ip_assigned ) && ( ctr > 0 ) ) {
DHCP_run ( ) ;
ctr —;
}
if ( ! ip_assigned ) {
UART_Printf ( » r n IP was not assigned 🙁 r n » ) ;
return ;
}

В общем случае, DHCP_run должна вызываться регулярно, например, по таймеру .

Выводим полученную информацию, а также передаем ее самой библиотеке:

getIPfromDHCP ( net_info. ip ) ;
getGWfromDHCP ( net_info. gw ) ;
getSNfromDHCP ( net_info. sn ) ;

uint8_t dns [ 4 ] ;
getDNSfromDHCP ( dns ) ;

UART_Printf (
«IP:  %d.%d.%d.%d r n »
«GW:  %d.%d.%d.%d r n »
«Net: %d.%d.%d.%d r n »
«DNS: %d.%d.%d.%d r n » ,
net_info. ip [ 0 ] , net_info. ip [ 1 ] , net_info. ip [ 2 ] , net_info. ip [ 3 ] ,
net_info. gw [ 0 ] , net_info. gw [ 1 ] , net_info. gw [ 2 ] , net_info. gw [ 3 ] ,
net_info. sn [ 0 ] , net_info. sn [ 1 ] , net_info. sn [ 2 ] , net_info. sn [ 3 ] ,
dns [ 0 ] , dns [ 1 ] , dns [ 2 ] , dns [ 3 ]
) ;

UART_Printf ( «Calling wizchip_setnetinfo()… r n » ) ;
wizchip_setnetinfo ( & net_info ) ;

Далее резолвим доменное имя «remontka.com»:

UART_Printf ( «Calling DNS_init()… r n » ) ;
DNS_init ( DNS_SOCKET , dns_buffer ) ;

uint8_t addr [ 4 ] ;
{
char domain_name [ ] = «remontka.com» ;
UART_Printf ( «Resolving domain name » %s «» r n «» domain_name ) ;
int8_t res = DNS_run ( dns ( uint8_t * ) & domain_name addr ) ;
if ( res != 1 ) {
UART_Printf ( «»DNS_run() failed

admin

Share
Published by
admin

Recent Posts

Консоль удаленного рабочего стола(rdp console)

Клиент удаленного рабочего стола (rdp) предоставляет нам возможность войти на сервер терминалов через консоль. Что…

2 месяца ago

Настройка сети в VMware Workstation

В VMware Workstation есть несколько способов настройки сети гостевой машины: 1) Bridged networking 2) Network…

2 месяца ago

Логи брандмауэра Windows

Встроенный брандмауэр Windows может не только остановить нежелательный трафик на вашем пороге, но и может…

2 месяца ago

Правильный способ отключения IPv6

Вопреки распространенному мнению, отключить IPv6 в Windows Vista и Server 2008 это не просто снять…

2 месяца ago

Ключи реестра Windows, отвечающие за параметры экранной заставки

Параметры экранной заставки для текущего пользователя можно править из системного реестра, для чего: Запустите редактор…

2 месяца ago

Как управлять журналами событий из командной строки

В этой статье расскажу про возможность просмотра журналов событий из командной строки. Эти возможности можно…

2 месяца ago