Ethernet в STM32 LAN8720 + lwip 1.4 + freertos 8

Наконец дошли руки и до 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, &ethernetif_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.

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий