这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【STM32F769】移植LWIP

共2条 1/1 1 跳转至

【STM32F769】移植LWIP

工程师
2025-02-25 14:43:35     打赏


【前言】

STM32F769板载了网络接口,这就可以实现联网。由于没有基于stm32cubeMX的示例,我通过配置ETH以及LWIP但是没有成功的获取IP,所以手工进行移植。

【前期准备】

1、在官方的示例中,有基于MDK的联网示例,我是基于vscode的工程,所以只能做为学习的示例。

2、github上面找到了一个基于stm32cubIDE的联网工程,能联上网,但是他是基于mqtt还有http的,他的工程是基于LCD的,所以我也只能拿过来学习。

3、然后我还使用stm32cubeMX配置了FreeRTOS

在这样的前提下,我进行如下的方法,成功的实现了LWIP联网。

【源码的移植】

1、我将STM32CubeIDE的工程中的lwip源码,复制到我工程目录ThirdPart下面

image.png 

2、然后根据Filielists.mk编把工程中的文件夹以及.c添加进CMakeLists.txt中。

image.png 

最后添加好后文件如下:

target_include_directories(ThirdPart INTERFACE
    ./nr_micro_shell/inc
 
    ./lwip/src/include
    ./lwip/src/include/lwip
    ./lwip/src/include/lwip/apps
    ./lwip/src/include/lwip/priv
    ./lwip/src/include/lwip/prot
    ./lwip/src/include/netif
    ./lwip/src/include/posix
    ./lwip/src/include/posix/sys
    ./lwip/system  
    ./lwip/LwIP/system
    ./lwip/src/include/netif/ppp
 
)
 
target_sources(ThirdPart INTERFACE
    ./nr_micro_shell/src/ansi_port.c
    ./nr_micro_shell/src/nr_micro_shell.c
    ./nr_micro_shell/src/ansi.c
 
    ./lwip/src/core/init.c
 ./lwip/src/core/def.c
 ./lwip/src/core/dns.c
 ./lwip/src/core/inet_chksum.c
 ./lwip/src/core/ip.c
 ./lwip/src/core/mem.c
 ./lwip/src/core/memp.c
 ./lwip/src/core/netif.c
 ./lwip/src/core/pbuf.c
 ./lwip/src/core/raw.c
 ./lwip/src/core/stats.c
 ./lwip/src/core/sys.c
 ./lwip/src/core/tcp.c
 ./lwip/src/core/tcp_in.c
 ./lwip/src/core/tcp_out.c
 ./lwip/src/core/timeouts.c
 ./lwip/src/core/udp.c
    ./lwip/src/core/ipv4/autoip.c
 ./lwip/src/core/ipv4/dhcp.c
 ./lwip/src/core/ipv4/etharp.c
 ./lwip/src/core/ipv4/icmp.c
 ./lwip/src/core/ipv4/igmp.c
 ./lwip/src/core/ipv4/ip4_frag.c
 ./lwip/src/core/ipv4/ip4.c
 ./lwip/src/core/ipv4/ip4_addr.c
    ./lwip/src/api/api_lib.c
 ./lwip/src/api/api_msg.c
 ./lwip/src/api/err.c
 ./lwip/src/api/netbuf.c
 ./lwip/src/api/netdb.c
 ./lwip/src/api/netifapi.c
 ./lwip/src/api/sockets.c
 ./lwip/src/api/tcpip.c
    ./lwip/src/netif/ethernet.c
 ./lwip/src/netif/slipif.c
 
    ./lwip/src/apps/snmp/snmp_asn1.c
 ./lwip/src/apps/snmp/snmp_core.c
 ./lwip/src/apps/snmp/snmp_mib2.c
 ./lwip/src/apps/snmp/snmp_mib2_icmp.c
 ./lwip/src/apps/snmp/snmp_mib2_interfaces.c
 ./lwip/src/apps/snmp/snmp_mib2_ip.c
 ./lwip/src/apps/snmp/snmp_mib2_snmp.c
 ./lwip/src/apps/snmp/snmp_mib2_system.c
 ./lwip/src/apps/snmp/snmp_mib2_tcp.c
 ./lwip/src/apps/snmp/snmp_mib2_udp.c
 ./lwip/src/apps/snmp/snmp_msg.c
 ./lwip/src/apps/snmp/snmpv3.c
 ./lwip/src/apps/snmp/snmp_netconn.c
 ./lwip/src/apps/snmp/snmp_pbuf_stream.c
 ./lwip/src/apps/snmp/snmp_raw.c
 ./lwip/src/apps/snmp/snmp_scalar.c
 ./lwip/src/apps/snmp/snmp_table.c
 ./lwip/src/apps/snmp/snmp_threadsync.c
 ./lwip/src/apps/snmp/snmp_traps.c
 ./lwip/src/apps/snmp/snmpv3_mbedtls.c
 ./lwip/src/apps/snmp/snmpv3_dummy.c
    # ./lwip/src/apps/httpd/fs.c
 # ./lwip/src/apps/httpd/httpd.c
    ./lwip/src/apps/sntp/sntp.c
 
    ./lwip/src/apps/netbiosns/netbiosns.c
    ./lwip/src/apps/tftp/tftp_server.c
    ./lwip/src/apps/mqtt/mqtt.c
    ./lwip/system/OS/sys_arch.c
)

 

3、接下来根据示例,编写网卡驱动ethernetif.c

根据原理图:

 image.png

核对示例中的程序是否与原理图相同,编写驱动如下:

void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  
  /* Enable GPIOs clocks */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
 
/* Ethernet pins configuration ************************************************/
  /*
        RMII_REF_CLK ----------------------> PA1
        RMII_MDIO -------------------------> PA2
        RMII_MDC --------------------------> PC1
        RMII_MII_CRS_DV -------------------> PA7
        RMII_MII_RXD0 ---------------------> PC4
        RMII_MII_RXD1 ---------------------> PC5
        RMII_MII_RXER ---------------------> PG2
        RMII_MII_TX_EN --------------------> PG11
        RMII_MII_TXD0 ---------------------> PG13
        RMII_MII_TXD1 ---------------------> PG14
  */
 
  /* Configure PA1, PA2 and PA7 */
  GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
  GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStructure.Pull = GPIO_NOPULL;
  GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
  GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  /* Configure PC1, PC4 and PC5 */
  GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
 
  /* Configure PG2, PG11, PG13 and PG14 */
  GPIO_InitStructure.Pin =  GPIO_PIN_2 | GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
  
  /* Enable the Ethernet global Interrupt */
  HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0);
  HAL_NVIC_EnableIRQ(ETH_IRQn);
  
  /* Enable ETHERNET clock  */
  __HAL_RCC_ETH_CLK_ENABLE();
}

此函数主要核对示例中的官脚与开发板原理图是相同的,不用修改,直接放入core/src目录下面,同时把头文件也添加进工程中。

为是实现中断,需要在stm32f7xx_it.c中添加中断函数:

/* USER CODE BEGIN 1 */
void ETH_IRQHandler(void)
{
  HAL_ETH_IRQHandler(&EthHandle);
}
/* USER CODE END 1 */

同时也要extern 一下extern ETH_HandleTypeDef EthHandle;

 

4、编写用户app_ethernet.c,这里主要实现网络接口状态,如果网络接口已连接,则根据是否需要进行DHCP进行相应的操作

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
 * @brief  通知用户网络接口配置状态
 * @param  netif: the network interface
 * @retval None
 */
void User_notification(struct netif *netif)
{
  if (netif_is_up(netif))
  {
#ifdef USE_DHCP
    /* Update DHCP state machine */
    DHCP_state = DHCP_START;
#else
    uint8_t iptxt[20];
    sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&netif->ip_addr));
    printf ("Static IP address: %s\n", iptxt);
#endif /* USE_DHCP */
  }
  else
  {  
#ifdef USE_DHCP
    /* Update DHCP state machine */
    DHCP_state = DHCP_LINK_DOWN;
#endif  /* USE_DHCP */
    printf ("The network cable is not connected \n");
  }
}

 

5、接下来实现一个DHCP的任务:

#ifdef USE_DHCP
/**
  * @brief  DHCP Process
  * @param  argument: network interface
  * @retval None
  */
void DHCP_thread(void const * argument)
{
  struct netif *netif = (struct netif *) argument;
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t gw;
  struct dhcp *dhcp;
  uint8_t iptxt[20];
 
  for (;;)
  {
    switch (DHCP_state)
    {
    case DHCP_START:
      {
        ip_addr_set_zero_ip4(&netif->ip_addr);
        ip_addr_set_zero_ip4(&netif->netmask);
        ip_addr_set_zero_ip4(&netif->gw);      
        dhcp_start(netif);
        DHCP_state = DHCP_WAIT_ADDRESS;
        printf ("  State: Looking for DHCP server ...\n");
      }
      break;
     
    case DHCP_WAIT_ADDRESS:
      {                
        if (dhcp_supplied_address(netif))
        {
          DHCP_state = DHCP_ADDRESS_ASSIGNED;
         
          sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&netif->ip_addr));
          printf("IP address assigned by a DHCP server: %s\n", iptxt);
        }
        else
        {
          dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
   
          /* DHCP timeout */
          if (dhcp->tries > MAX_DHCP_TRIES)
          {
            DHCP_state = DHCP_TIMEOUT;
           
            /* Stop DHCP */
            dhcp_stop(netif);
           
            /* Static address used */
            IP_ADDR4(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 );
            IP_ADDR4(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
            IP_ADDR4(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
            netif_set_addr(netif, ip_2_ip4(&ipaddr), ip_2_ip4(&netmask), ip_2_ip4(&gw));
           
            sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&netif->ip_addr));
            printf("DHCP Timeout !! \n");
            printf("Static IP address: %s\n", iptxt);
          }
        }
      }
      break;
  case DHCP_LINK_DOWN:
    {
      /* Stop DHCP */
      dhcp_stop(netif);
      DHCP_state = DHCP_OFF;
    }
    break;
    default: break;
    }
   
    /* wait 250 ms */
    osDelay(250);
  }
}


在这个函数中,如果有配置函数,定义的DHCP,则进行DHCP的任务进程,如果是DHCP开始,测清空网卡中的addrnetmaskgw,同时开启dhcp_start任务。

接下来就判断是成功获取到IP地址,如果没有,则按照配置文件的中IP地址、网卡、DNS进行配置。

相关的配置我放到main.h中进行了定义:

image.png 

 

6、需要用到网卡驱动,我们需要到stm32f7xx_hal_conf.h中打开#define HAL_ETH_MODULE_ENABLED配置。

同时还需要添加网络的配置信息,配置信息如下:

 

/* Section 1 : Ethernet peripheral configuration */
 
/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
#define MAC_ADDR0   2U
#define MAC_ADDR1   0U
#define MAC_ADDR2   0U
#define MAC_ADDR3   0U
#define MAC_ADDR4   0U
#define MAC_ADDR5   0U
 
/* Definition of the Ethernet driver buffers size and count */
#define ETH_RX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for receive               */
#define ETH_TX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for transmit              */
#define ETH_RXBUFNB                    ((uint32_t)4U)       /* 4 Rx buffers of size ETH_RX_BUF_SIZE  */
#define ETH_TXBUFNB                    ((uint32_t)4U)       /* 4 Tx buffers of size ETH_TX_BUF_SIZE  */
 
/* Section 2: PHY configuration section */
 /* LAN8742A PHY Address*/
#define LAN8742A_PHY_ADDRESS 0x00U
 /* DP83848_PHY_ADDRESS Address*/
#define DP83848_PHY_ADDRESS
 /* PHY Reset delay these values are based on a 1 ms Systick interrupt*/
#define PHY_RESET_DELAY ((uint32_t)0x000000FFU)
 /* PHY Configuration delay */
#define PHY_CONFIG_DELAY ((uint32_t)0x00000FFFU)
 
#define PHY_READ_TO ((uint32_t)0x0000FFFFU)
#define PHY_WRITE_TO ((uint32_t)0x0000FFFFU)
 
   /* Section 3: Common PHY Registers */
 
#define PHY_BCR ((uint16_t)0x0000U) /*!< Transceiver Basic Control Register   */
#define PHY_BSR ((uint16_t)0x0001U) /*!< Transceiver Basic Status Register    */
 
#define PHY_RESET ((uint16_t)0x8000U)                   /*!< PHY Reset */
#define PHY_LOOPBACK ((uint16_t)0x4000U)                /*!< Select loop-back mode */
#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100U)         /*!< Set the full-duplex mode at 100 Mb/s */
#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000U)         /*!< Set the half-duplex mode at 100 Mb/s */
#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100U)          /*!< Set the full-duplex mode at 10 Mb/s  */
#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000U)          /*!< Set the half-duplex mode at 10 Mb/s  */
#define PHY_AUTONEGOTIATION ((uint16_t)0x1000U)         /*!< Enable auto-negotiation function     */
#define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200U) /*!< Restart auto-negotiation function    */
#define PHY_POWERDOWN ((uint16_t)0x0800U)               /*!< Select the power down mode           */
#define PHY_ISOLATE ((uint16_t)0x0400U)                 /*!< Isolate PHY from MII                 */
 
#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020U) /*!< Auto-Negotiation process completed   */
#define PHY_LINKED_STATUS ((uint16_t)0x0004U)     /*!< Valid link established               */
#define PHY_JABBER_DETECTION ((uint16_t)0x0002U)  /*!< Jabber condition detected            */
 
 /* Section 4: Extended PHY Registers */
#define PHY_SR ((uint16_t)0x1FU) /*!< PHY special control/ status register Offset     */
 
#define PHY_SPEED_STATUS ((uint16_t)0x0004U)  /*!< PHY Speed mask                                  */
#define PHY_DUPLEX_STATUS ((uint16_t)0x0010U) /*!< PHY Duplex mask                                 */
 
#define PHY_ISFR ((uint16_t)0x1DU)        /*!< PHY Interrupt Source Flag register Offset       */
#define PHY_ISFR_INT4 ((uint16_t)0x0010U) /*!< PHY Link down inturrupt                         */

 

image.png 

 

7、接下来在freertos.c中的任务,添加配置初始化与网络开始的代码:

/* USER CODE BEGIN Variables */
static void Netif_Config(void)
{
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t gw;
 
#ifdef USE_DHCP
  ip_addr_set_zero_ip4(&ipaddr);
  ip_addr_set_zero_ip4(&netmask);
  ip_addr_set_zero_ip4(&gw);
#else
  IP_ADDR4(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
  IP_ADDR4(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
  IP_ADDR4(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif /* USE_DHCP */
 
  netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
 
  /*  Registers the default network interface. */
  netif_set_default(&gnetif);
 
  if (netif_is_link_up(&gnetif))
  {
    /* When the netif is fully configured this function must be called.*/
    netif_set_up(&gnetif);
  }
  else
  {
    /* When the netif link is down this function must be called */
    netif_set_down(&gnetif);
  }
}
/* USER CODE END Variables */

这里我们选初始化网络配置,根据是否DHCP进行一些基本配置,同时根据网络是否接入,开启基本配置。

接下来在任务中调用tcpip_init

然后创建一个dhcp的任务。

接下来等待网络接入,DHCP是否就绪,如果就绪则打印出IP地址。

void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  tcpip_init(NULL, NULL);
  Netif_Config();
  User_notification(&gnetif);
#ifdef USE_DHCP
  /* Start DHCPClient */
  osThreadDef(DHCP, DHCP_thread, osPriorityBelowNormal, 0, configMINIMAL_STACK_SIZE * 5);
  osThreadCreate(osThread(DHCP), &gnetif);
#endif
  // 等待网络接口启用
  while (!netif_is_up(&gnetif))
  {
    shell_printf("Waiting for network interface to be up...\n");
    osDelay(1000);
  }
 
  // 等待获取IP地址
  while (ip_addr_isany(netif_ip4_addr(&gnetif)))
  {
    shell_printf("Waiting for IP address...\n");
    osDelay(1000);
  }
 
  shell_printf("IP address obtained: %s\n", ipaddr_ntoa(netif_ip4_addr(&gnetif)));
 
  // 启动TCP客户端
  tcp_client_init();
  for(;;)
  {
    shell_usart_loop();
    osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}

8、tcp_client测试。

我创建了一个tcp_client的测试程序,在程序中创建tcp客户端去连接我在电脑上创建的一个TCP服务器:

image.png 

示例代码如下:

#include "lwip/tcp.h"
#include "lwip/netif.h"
#include "lwip/init.h"
#include "lwip/sys.h"
#include "lwip/ip_addr.h"
#include "shell_uart.h"
#include "string.h"
 
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static err_t tcp_client_send(struct tcp_pcb *tpcb, const char *data, u16_t len);
 
    static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
    if (err != ERR_OK)
    {
        tcp_close(tpcb);
        return err;
    }
    shell_printf("tcp_client connected\r\n");
 
    // Set the receive callback function
    tcp_recv(tpcb, tcp_client_recv);
 
    // Send data to the server
    const char *data = "Hello, Server!\r\n";
    tcp_client_send(tpcb, data, strlen(data));
 
    // Send data or perform other actions upon successful connection
    return ERR_OK;
}
 
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    if (p == NULL)
    {
        tcp_close(tpcb);
        return ERR_OK;
    }
    // Process received data
    shell_printf("Received data: %.*s\n", p->tot_len, (char *)p->payload);
 
    // Indicate that the data has been received
    tcp_recved(tpcb, p->tot_len);
 
    // Free the received pbuf
    pbuf_free(p);
 
    // Continue to receive data
    tcp_recv(tpcb, tcp_client_recv);
    return ERR_OK;
}
 
static void tcp_client_error(void *arg, err_t err)
{
    // Handle error
    shell_printf("TCP error: %d\n", err);
}
 
static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb)
{
    // Handle periodic actions
    return ERR_OK;
}
 
static err_t tcp_client_send(struct tcp_pcb *tpcb, const char *data, u16_t len)
{
    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_POOL);
    if (p == NULL)
    {
        shell_printf("Failed to allocate pbuf\n");
        return ERR_MEM;
    }
    memcpy(p->payload, data, len);
    err_t err = tcp_write(tpcb, p->payload, len, TCP_WRITE_FLAG_COPY);
    if (err != ERR_OK)
    {
        shell_printf("Failed to send data: %d\n", err);
        pbuf_free(p);
        return err;
    }
    err = tcp_output(tpcb);
    if (err != ERR_OK)
    {
        shell_printf("Failed to output data: %d\n", err);
        pbuf_free(p);
        return err;
    }
    pbuf_free(p);
    return ERR_OK;
}
 
void tcp_client_init(void)
{
    struct tcp_pcb *tpcb;
    ip_addr_t server_ip;
 
    IP4_ADDR(&server_ip, 192, 168, 3, 231);
 
    tpcb = tcp_new();
    if (tpcb != NULL)
    {
        tcp_arg(tpcb, NULL);
        tcp_err(tpcb, tcp_client_error);
        tcp_poll(tpcb, tcp_client_poll, 1);
        err_t err = tcp_connect(tpcb, &server_ip, 8000, tcp_client_connected);
        if (err != ERR_OK)
        {
            shell_printf("tcp_client err: %d\r\n", err);
            tcp_close(tpcb);
        }
        else
        {
            shell_printf("tcp_client OK\r\n");
        }
    }
    else
    {
        shell_printf("Failed to create TCP PCB\n");
    }
}

在此示例中,如果成功的连接到了TCP服务器,则循环接收服务端是否发回了消息,如果有,通过串口进行打印。

image.png 





关键词: STM32F769     LWIP    

专家
2025-02-25 21:24:14     打赏
2楼

感谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]