Наконец дошли руки и до Ethernet на stm32f4 discovery.
Прикупил себе модулек с физическим интерфейсом LAN8720. Он подключается к stm32 по интерфейсу RMII.
ETH_MDIO --------------> PA2 ETH_MDC ---------------> PC1 ETH_RMII_REF_CLK-------> PA1 ETH_RMII_CRS_DV -------> PA7 ETH_RMII_RXD0 -------> PC4 ETH_RMII_RXD1 -------> PC5 ETH_RMII_TX_EN -------> PB11 ETH_RMII_TXD0 -------> PB12 ETH_RMII_TXD1 -------> PB13 ETH_RST_PIN -------> PE2
В сети ходит пример работы с данной микросхемой. Но стек lwip там старый, да и freertos не самый свежий.

За сим предлагаю вам пример на freertos 8.2 и lwip 1.4.1.
В примере с изображения выше все довольно сильно наворочено. Там вам и картинки в Flash памяти контроллера и dhcp, мы пойдем слегка более легким путем.
Нам понадобятся свежие исходники freertos и lwip.
Как добавить в проект freertos.
Из исходников lwip нам понодобятся папки api, core/ipv4, netif.
Также нужен порт.
Портируется lwip довольно просто. Порт включает в себя функции оболочки для создания и управления семафорами и задачами операционной системы, а также низкоуровневые функции работы с периферией. Это два файла sys_arch.c и ethernetif.c. Первый можно добыть в недрах freertos (или написать свой), а второй — часть примеров ST.
Структура проекта в итоге получится вот такая:

При старте нужно инициализировать MAC модуль в STM32. Это делается при помощи функции ETH_BSP_Config() из библиотеки STM32F4x7_ETH_Driver. Она стандартна и доступна на сайте ST. В ней настраиваются GPIO, NVIC и MAC модули.
Сразу оговорка. Нужно аккуратно настраивать NVIC с freertos. Иначе можно улететь в HardFault.
Когда периферия контроллера настроена и готова к работе нужно инициализировать lwip.
Нужно ему сообщить ip адрес, маску и шлюз.
Я настроил на компьютере ip 192.168.1.1, маску 255.255.255.0 и шлюз 192.168.1.1.
В main.h указал такие настройки.
/*Static IP ADDRESS*/ #define IP_ADDR0 192 #define IP_ADDR1 168 #define IP_ADDR2 1 #define IP_ADDR3 2 /*NETMASK*/ #define NETMASK_ADDR0 255 #define NETMASK_ADDR1 255 #define NETMASK_ADDR2 255 #define NETMASK_ADDR3 0 /*Gateway Address*/ #define GW_ADDR0 192 #define GW_ADDR1 168 #define GW_ADDR2 1 #define GW_ADDR3 1
Эти настройки сугубо личные. Можно настроить как угодно, но у меня интернет через WifI и стандартные 192.168.0.1 заняты под интернет, чтобы не переключать постоянно настройки, сделал так.
Вызываем LwIP_Init.
void LwIP_Init(void)
{
ip_addr_t ipaddr;
ip_addr_t netmask;
ip_addr_t gw;
/* Create tcp_ip stack thread */
tcpip_init( NULL, NULL );
/* IP address setting & display on STM32_evalboard LCD*/
#ifdef USE_DHCP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
#else
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif
/* - netif_add(struct netif *netif, struct ip_addr *ipaddr,
struct ip_addr *netmask, struct ip_addr *gw,
void *state, err_t (* init)(struct netif *netif),
err_t (* input)(struct pbuf *p, struct netif *netif))
Adds your network interface to the netif_list. Allocate a struct
netif and pass a pointer to this structure as the first argument.
Give pointers to cleared ip_addr structures when using DHCP,
or fill them with sane numbers otherwise. The state pointer may be NULL.
The init function pointer must point to a initialization function for
your ethernet netif interface. The following code illustrates it's use.*/
netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
/* Registers the default network interface. */
netif_set_default(&xnetif);
/* When the netif is fully configured this function must be called.*/
netif_set_up(&xnetif);
}
В данном примере DHCP не используется. Оставил из старого примера.
Теперь создадим задачу обслуживания соединения. Сделаем простой http сервер.
xTaskCreate(http_server_netconn_thread, "HTTP", configMINIMAL_STACK_SIZE, NULL, 2, ( TaskHandle_t * ) NULL);
В этой задаче мы создаем TCP сокет на 80 порту и ждем соединения.
void http_server_netconn_thread()
{
struct netconn *conn, *newconn;
err_t err;
/* Create a new TCP connection handle */
conn = netconn_new(NETCONN_TCP);
if (conn!= NULL)
{
/* Bind to port 80 (HTTP) with default IP address */
err = netconn_bind(conn, NULL, 80);
if (err == ERR_OK)
{
/* Put the connection into LISTEN state */
netconn_listen(conn);
while(1)
{
/* accept any icoming connection */
err = netconn_accept(conn, &newconn);
/* serve connection */
http_server_serve(newconn);
/* delete connection */
netconn_delete(newconn);
}
}
else
{
printf("can not bind netconn");
}
}
else
{
printf("can not create netconn");
}
}
При появлении запроса вызываем функцию http_server_serve.
void http_server_serve(struct netconn *conn)
{
struct netbuf *inbuf;
err_t res;
char* buf;
u16_t buflen;
size_t len;
static u16_t call_times = 0;
res = netconn_recv(conn, &inbuf);
if (res == ERR_OK)
{
netbuf_data(inbuf, (void**)&buf, &buflen);
if ((buflen >=5) && (strncmp(buf, "GET /", 5) == 0))
{
sprintf(data, "Hello %d times!", call_times++);
len = strlen(data);
netconn_write(conn, (const unsigned char*)(data), (size_t)len, NETCONN_NOCOPY);
}
}
/* Close the connection (server closes in HTTP) */
netconn_close(conn);
/* Delete the buffer (netconn_recv gives us ownership,
so we have to make sure to deallocate the buffer) */
netbuf_delete(inbuf);
}
Я сделал 1 страничку «Hello world».

При каждом обновлении цифра увеличивается на единицу.
Eсли в общем, то главное отличие порта lwip 1.4 от 1.3 в функциях работы с семаформи. В новом lwip работа с ними происходит по указателю, в старом же — функции возвращали семафоры.
Код на GitHub.







