野火挑战者V2开发板CubeMX+LWIP+FreeRTOS+TCP_Server+TCP_Client实现

首先说一下开发环境:MDK5版本为5.26.2.0,CubeMX版本为6.6.1,FreeRTOS API选择的是 CMSIS v1,LWIP版本为2.1.2。单片机型号为STM32F429IGT6,以太网芯片为LAN8720A。

接下来开始进入正题,首先是基础的时钟、GPIO、LWIP方面的配置,具体流程如下:

1、时钟选择外部高速时钟(25M),系统时钟180M,SYS设置界面的Timebase Source项应当设置为SysTick以外的任何一项,因为SysTick将作为FreeRTOS系统的时钟源。

2、设置LED引脚PH10。

3、选择一组串口作为日志输出串口,打开串口,允许中断

4、配置ETH:

        选择RMII连接方式。

        勾上Ethernet global interrupt中断

        修改ETH_TXD0、ETH_TXD1引脚为PG13 PG14

        修改PHY引脚为  LAN8742A_PHY_ADDRESS 控制。

        打开LWIP功能,关闭LWIP_DHCH,设置静态IP、子网掩码、网关。

        进入Platform Settings页面选择网络芯片为LAN_8742A。

        进入Key  Options页面使能LWIP_NETIF_STATUS_CALLBACK。

在完成基础工程的配置以后再进入FreeRTOS的配置。进入FreeRTOS选项卡后选择CMSIS v1。相关设置直接默认,不需要改。直接进入Tasks and Queues选项卡设置任务。进入时会有一个默认任务,LWIP任务会自动加入该任务栈。双击该任务行任意位置,在弹出的界面中将Stack Size (Words)项的值改为256(更大也行),该项是设置该任务的内存大小,随后点击OK保存。为了测试方便我还创建了个Task2,任务名为LED_Task,其余默认,,随后点击OK保存。

最后 设置工程名称和保存地址,生成工程,打开工程进行如下操作:

5、首先修改MDK5的一些设置,最重要的是把这个 Use MicroLIB 勾上,不勾跑不起来:

6、全局搜索“LAN8742A_PHY_ADDRESS ”宏定义,将其的值改为0,或者main函数开头添加以下语句:

	#ifdef LAN8742A_PHY_ADDRESS           
	#undef LAN8742A_PHY_ADDRESS
	#define LAN8742A_PHY_ADDRESS 0U
	#endif

7、如有需要可以添加串口重定向:

首先加入头文件:#include "stdio.h"

随后在工程任意位置加入以下代码:

int fputc(int ch, FILE *f)
{
	//具体哪个串口可以更改huart1为其它串口
	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0x0f);
	return ch;
}

8、如果需要,可以在添加LWIP日志管理代码,粘贴于lwipopts.h中

#define LWIP_DEBUG 1
#if LWIP_DEBUG
#define LWIP_DBG_TYPES_ON               LWIP_DBG_ON
/* USER CODE BEGIN 1 */
#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_OFF
//#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_WARNING
//#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_SERIOUS
//#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_SEVERE
  
#define ETHARP_DEBUG                    LWIP_DBG_ON     
#define NETIF_DEBUG                     LWIP_DBG_ON     
#define PBUF_DEBUG                      LWIP_DBG_ON
#define API_LIB_DEBUG                   LWIP_DBG_ON
#define API_MSG_DEBUG                   LWIP_DBG_ON
#define SOCKETS_DEBUG                   LWIP_DBG_ON
#define ICMP_DEBUG                      LWIP_DBG_ON
#define IGMP_DEBUG                      LWIP_DBG_ON
#define INET_DEBUG                      LWIP_DBG_ON
#define IP_DEBUG                        LWIP_DBG_ON     
#define IP_REASS_DEBUG                  LWIP_DBG_ON
#define RAW_DEBUG                       LWIP_DBG_ON
#define MEM_DEBUG                       LWIP_DBG_ON
#define MEMP_DEBUG                      LWIP_DBG_ON
#define SYS_DEBUG                       LWIP_DBG_ON
#define TCP_DEBUG                       LWIP_DBG_ON
#define TCP_INPUT_DEBUG                 LWIP_DBG_ON
#define TCP_FR_DEBUG                    LWIP_DBG_ON
#define TCP_RTO_DEBUG                   LWIP_DBG_ON
#define TCP_CWND_DEBUG                  LWIP_DBG_ON
#define TCP_WND_DEBUG                   LWIP_DBG_ON
#define TCP_OUTPUT_DEBUG                LWIP_DBG_ON
#define TCP_RST_DEBUG                   LWIP_DBG_ON
#define TCP_QLEN_DEBUG                  LWIP_DBG_ON
#define UDP_DEBUG                       LWIP_DBG_ON     
#define TCPIP_DEBUG                     LWIP_DBG_ON
#define PPP_DEBUG                       LWIP_DBG_ON
#define SLIP_DEBUG                      LWIP_DBG_ON
#define DHCP_DEBUG                      LWIP_DBG_ON     
#define AUTOIP_DEBUG                    LWIP_DBG_ON
#define SNMP_MSG_DEBUG                  LWIP_DBG_ON
#define SNMP_MIB_DEBUG                  LWIP_DBG_ON
#define DNS_DEBUG                       LWIP_DBG_ON
#endif //LWIP_DEBUG

       若需关闭,将#define LWIP_DBG_TYPES_ON               LWIP_DBG_ON  修改为

#define LWIP_DBG_TYPES_ON               LWIP_DBG_OFF

至此工程已经配置完成,编译工程下到开发板中,可以ping通,如下图:

在写这篇文章之前笔者就踩了大坑,那就是默认任务没有加大内存,也只设置了128 Words,然后一直ping不通,困了几天,后面是请教别的大佬才顺利解决。

第二天续写:

如果要实现TCP Server功能只需要新建两个文件,将其加入工程中(别忘了编译路径,我是放在了LWIP/APP目录下),代码如下:

tcpecho.c代码:


#include "tcpecho.h"

#include "lwip/opt.h"

#if LWIP_SOCKET
#include <lwip/sockets.h>

#include "lwip/sys.h"
#include "lwip/api.h"
/*-----------------------------------------------------------------------------------*/
#define LOCAL_PORT         5001
#define RECV_DATA         (1024)

static void 
tcpecho_thread(void *arg)
{
  int sock = -1,connected;
  char *recv_data;
  struct sockaddr_in server_addr,client_addr;
  socklen_t sin_size;
  int recv_data_len;
  
  printf("本地端口号是%d\n\n",LOCAL_PORT);
  
  recv_data = (char *)pvPortMalloc(RECV_DATA);
  if (recv_data == NULL)
  {
      printf("No memory\n");
      goto __exit;
  }
  
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
  {
      printf("Socket error\n");
      goto __exit;
  }
  
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = INADDR_ANY;
  server_addr.sin_port = htons(LOCAL_PORT);
  memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
  
  if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
  {
      printf("Unable to bind\n");
      goto __exit;
  }
  
  if (listen(sock, 5) == -1)
  {
      printf("Listen error\n");
      goto __exit;
  }
  
  while(1)
  {
    sin_size = sizeof(struct sockaddr_in);

    connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);

    printf("new client connected from (%s, %d)\n",
            inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    {
      int flag = 1;
      
      setsockopt(connected,
                 IPPROTO_TCP,     /* set option at TCP level */
                 TCP_NODELAY,     /* name of option */
                 (void *) &flag,  /* the cast is historical cruft */
                 sizeof(int));    /* length of option value */
    }
    
    while(1)
    {
      recv_data_len = recv(connected, recv_data, RECV_DATA, 0);
      
      if (recv_data_len <= 0) 
        break;
      
      printf("recv %d len data\n",recv_data_len);
      
      write(connected,recv_data,recv_data_len);
      
    }
    if (connected >= 0) 
      closesocket(connected);
    
    connected = -1;
  }
__exit:
  if (sock >= 0) closesocket(sock);
  if (recv_data) free(recv_data);
}
/*-----------------------------------------------------------------------------------*/
void
tcpecho_init(void)
{
  sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 512, 4);
}
/*-----------------------------------------------------------------------------------*/

#endif /* LWIP_NETCONN */

tcpecho.h代码如下;

#ifndef LWIP_TCPECHO_H
#define LWIP_TCPECHO_H

void tcpecho_init(void);

#endif /* LWIP_TCPECHO_H */

之后在main.h添加代码头文件(其他地方也行,只要不报错):#include "tcpecho.h"

最后再找到FreeRTOS的第一个任务,将"tcpecho_init();"语句添加到语句 "MX_LWIP_Init();的后面即可",随后编译,再用网络调试助手进行TCP连接已经可以连接上,并且发送的信息还可以回显,如下图:

第三天续写:

如果要实现TCP_Client功能,只需在第一天的基础上做些许改动,具体步骤如下:

新建两个文件,将其加入工程中(别忘了编译路径,我是放在了LWIP/APP目录下),代码如下:

"client.c"代码如下:

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 * 
 * Author: Adam Dunkels <adam@sics.se>
 *
 */
#include "client.h"

#include "lwip/opt.h"

#include "lwip/sys.h"
#include "lwip/api.h"

#include <lwip/sockets.h>


#define DEST_IP_ADDR0               192
#define DEST_IP_ADDR1               168
#define DEST_IP_ADDR2                 0
#define DEST_IP_ADDR3               15

#define DEST_PORT                  5001

static void client(void *thread_param)
{
  int sock = -1;
  struct sockaddr_in client_addr;
  
  ip4_addr_t ipaddr;
  
  uint8_t send_buf[]= "This is a TCP Client test...\n";
  
  printf("目地IP地址:%d.%d.%d.%d \t 端口号:%d\n\n",      \
          DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3,DEST_PORT);
  
  printf("请将电脑上位机设置为TCP Server.在User/arch/sys_arch.h文件中将目标IP地址修改为您电脑上的IP地址\n\n");
  
  printf("修改对应的宏定义:DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3,DEST_PORT\n\n");
  
  IP4_ADDR(&ipaddr,DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3);
  while(1)
  {
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
//      printf("Socket error\n");
      vTaskDelay(10);
      continue;
    } 

    client_addr.sin_family = AF_INET;      
    client_addr.sin_port = htons(DEST_PORT);   
    client_addr.sin_addr.s_addr = ipaddr.addr;
    memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));    

    if (connect(sock, 
               (struct sockaddr *)&client_addr, 
                sizeof(struct sockaddr)) == -1) 
    {
//        printf("Connect failed!\n");
        closesocket(sock);
        vTaskDelay(10);
        continue;
    }                                           
    
    printf("Connect to server successful!\n");
    
    while (1)
    {
      if(write(sock,send_buf,sizeof(send_buf)) < 0)
        break;
   
      vTaskDelay(1000);
    }
    
    closesocket(sock);
  }

}

void
client_init(void)
{
  sys_thread_new("client", client, NULL, 512, 4);
}

"client.h"代码如下:

#ifndef _CLIENT_H
#define _CLIENT_H

void client_init(void);

#endif /* _CLIENT_H */

之后在main.h添加代码头文件(其他地方也行,只要不报错):#include "tcpecho.h"

最后再找到FreeRTOS的第一个任务,将"tcpecho_init();"语句添加到语句 "client_init();的后面即可",随后编译,再用网络调试助手创建TCP_Server,将代码下入开发板一会可以看到已经可以连接上,定时接收到开发板发送的消息,并且电脑发送的信息还可以回显,如下图:

至此LWIP+FreeRTOS进行TCP连接的功能就已经全部实现了,但是我还发现一个问题就是当程序正常运行时如果按下复位键重启,任务可以正常运行,LED灯可以正常闪烁,但是LWIP不能正常运行,电脑也ping不通,除非开发板断电重启。这个问题我也是刚发现,具体问题还需后面继续查找,待找到原因后将会更新后续。 

事隔接近10天后,终于把剩下的问题解决了,最近大降温,同时也比较忙,终于有时间把它解决了。

之前遗留的问题其实就是热插拔的问题。这段时间我也参考了很多大佬的解决办法,但是我尝试了适合我们这种静态IP的解决办法的就是增加3个字符:找到ethernetif.c文件,将其中的ethernet_link_thread函数的倒数第三条语句“HAL_ETH_Start(&heth);”修改成“HAL_ETH_Start_IT(&heth);”。其实就是在括号前加了“_IT”这三个字符。虽然只是简单的换了字符,但是启动方式却不一样,采用中断接收后热插拔的功能也就实现了。

至此STM32F4 LWIP+FreeRTOS实现TCP通信的博客终于告一段落!希望对大家有所帮助。

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: FreeRTOS是一个开源的实时操作系统内核,而LwIP是一个轻量级的开源TCP/IP协议栈。在FreeRTOS中,可以使用LwIP作为其TCP/IP组件,实现网络通信功能。 而DHCP(Dynamic Host Configuration Protocol)是用于动态分配IP地址的协议,DHCP客户端是指能够自动获取IP地址、子网掩码、网关以及DNS服务器等网络配置信息的设备。 在FreeRTOS中使用LwIP的DHCP客户端功能,可以通过以下步骤实现: 1. 首先,配置LwIP以支持DHCP客户端功能。这包括在LwIP配置文件中启用DHCP客户端(如开启LWIP_DHCP选项),以及设置网络接口(网卡)以使用DHCP客户端功能。 2. 在FreeRTOS应用程序中,创建一个任务或者在一个现有任务中加入DHCP客户端代码。这个任务会负责与DHCP服务器通信,发送DHCP请求并接收响应。 3. 在任务中,通过调用LwIP提供的API来控制DHCP客户端功能。例如,可以调用dhcp_start()函数来启动DHCP客户端,或者调用dhcp_release()函数来释放获得的IP地址。 4. 随后,DHCP客户端会与DHCP服务器进行通信,尝试获取IP地址和其他配置信息。一旦成功获取到这些信息,DHCP客户端会更新LwIP的网络接口配置,使得设备能够正常进行网络通信。 5. 运行过程中,DHCP客户端会定期与DHCP服务器进行通信,以保持IP地址有效性。如果DHCP服务器不再响应,DHCP客户端会尝试重新获取IP地址。 总的来说,使用FreeRTOSLwIP搭配实现DHCP客户端的功能可以使得设备能够自动获取并更新网络配置,简化了网络管理的过程,提高了网络通信的效率。 ### 回答2: FreeRTOS是一个开源的实时操作系统内核,而lwIP是一个轻量级的TCP/IP协议栈。在使用FreeRTOSlwIP时,可以通过lwIP的DHCP(Dynamic Host Configuration Protocol)客户端来实现自动获取IP地址的功能。 DHCP是一种网络协议,允许设备在网络上动态获得IP地址、子网掩码、网关地址等参数。在使用lwIP时,可以将其配置成DHCP客户端模式,这样设备上电或连接网络时,就会自动向网络中的DHCP服务器发送请求,以获取它所需的网络配置信息。 对于FreeRTOSlwIP的集成,需要进行一些配置和初始化工作。首先,在FreeRTOS的任务中,需要创建一个专门的线程来处理网络事件,并调用lwIP提供的函数进行初始化。其次,需要配置lwIP的网络参数,如设置网络接口、启用DHCP客户端等。最后,可以通过调用lwIP提供的API函数,来获取DHCP客户端的状态和获取网络配置信息。 在使用FreeRTOSlwIP的过程中,应注意以下几点: 1. 确保网络硬件设备的驱动程序已正确集成到FreeRTOSlwIP中。 2. 需要适当处理并发访问的问题,如使用互斥锁保护共享资源。 3. 可以根据实际需求,监听DHCP客户端的状态变化,并采取相应的操作。 综上所述,使用FreeRTOSlwIP的DHCP客户端,可以方便地实现设备的自动获取IP地址的功能,提高了设备的灵活性和便利性。但在使用过程中,需要注意合适的配置和初始化,以及处理并发访问的问题。 ### 回答3: FreeRTOS 是一个流行的实时操作系统,LWIP 是一个轻量级的 TCP/IP 协议栈,而 DHCP 是一种用于自动分配 IP 地址的网络协议。 FreeRTOS 提供了一个名为 LwIP 的轻量级 TCP/IP 协议栈的端口,用于在 FreeRTOS实现网络通信功能。在使用 LwIP 进行网络通信时,可以选择使用 DHCP 客户端来自动获取 IP 地址。 LwIP 提供了一个现成的 DHCP 客户端实现,可以用来向 DHCP 服务器发送请求,获取可用的 IP 地址、网关、DNS 服务器和子网掩码等网络配置参数。通过配置 DHCP 客户端,可以在网络连接建立后自动获得所需的网络设置。 使用 FreeRTOS 中的 LwIP DHCP 客户端可以帮助我们简化网络配置过程。通过启用 DHCP 客户端,我们不再需要手动配置每个设备的静态 IP 地址,而是允许网络中的 DHCP 服务器自动分配可用的 IP 地址。这大大减轻了手动管理网络 IP 地址的工作量,提高了网络配置的效率。 要使用 FreeRTOS LwIP DHCP 客户端,我们需要在应用程序中进行相应的配置和初始化,确保 DHCP 客户端能够发送请求并接收到响应。一旦 DHCP 客户端成功获取到 IP 地址等网络配置参数,我们就可以使用这些设置来进行网络通信。 总而言之,FreeRTOS LwIP DHCP 客户端是一个方便的工具,可以帮助我们在 FreeRTOS实现自动获取 IP 地址和其他网络配置参数的功能,简化了网络配置过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值