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

本文详细介绍了在STM32F429单片机上使用MDK5和CubeMX开发环境,配置时钟、GPIO、LWIP和FreeRTOS,实现TCP Server与Client功能,以及解决热插拔问题的过程。通过设置静态IP和配置中断接收,确保了网络连接和通信的稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先说一下开发环境: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通信的博客终于告一段落!希望对大家有所帮助。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值