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

2023-05-16

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

野火挑战者V2开发板CubeMX+LWIP+FreeRTOS+TCP_Server+TCP_Client实现 的相关文章

  • matplotlab不能显示中文解决(windows)

    汉字字体 xff0c 优先使用楷体 xff0c 找不到则使用黑体 plt rcParams 39 font sans serif 39 61 39 Kaitt 39 39 SimHei 39 正常显示负号 plt rcParams 39 a
  • ORB与CCM-SLAM

    入口函数 mono euroc cc int main int argc char argv 图像序列的文件名字符串序列 vector lt string gt vstrImageFilenames 时间戳 vector lt double
  • 在国际期刊发表人工智能和机器人文章的诀窍(WILEY)

    该次学术讨论主要讲述了如何去写论文 如何投稿 并介绍了WILEY论文投稿工具 xff0c 该工具 提供了论文撰写 xff08 包括 xff1a 图表 摘要图的生成 xff09 投稿期刊会议分析及建议等 链接 xff1a https gcb
  • ROS下进行串口通讯

    文章目录 前言一 安装串口功能包二 串口接受程序三 查看串口号 前言 在使用工控机时 xff0c 经常需要和其他主控进行信息交流 xff0c 串口通讯作为使用最为广泛的形式 xff0c 因此 xff0c 了解如何在ros下进行串口的接发数据
  • ubuntu系统分区详细教程

    1 xff1a swap交换空间 xff0c 这个也就是虚拟内存的地方 xff0c 选择主分区和空间起始位置 如果你给Ubuntu系统分区容量足够的话 xff0c 最好是能给到你物理内存的2倍大小 xff0c 像我8GB内存 xff0c 就
  • 前端面试题 - 如何将 Set 集合转化为一个数组

    什么是 Set 集合 xff1a Set 集合类似于一个数组 xff0c 但是它要求里面是元素不可以有重复 const set 61 new Set 39 welcome 39 39 you 39 39 39 39 you 39 conso
  • Ubuntu系统解压文件后乱码解决方法

    Ubuntu系统使用解压文件常见问题 在Ubuntu18 04下 xff0c 如果直接解压压缩文件 xff1a 右键选择提取到此处 发生乱码 乱码原因 xff1a Windows下生成的zip文件中的编码是GBK GB2312等 xff0c
  • Ubuntu下切换默认的python版本

    Ubuntu系统自带python2 7 xff08 默认 xff09 和python3 5 xff0c 所以不需要自己安装python 可以使用命令 python version 来查看默认的python版本 smile 64 smile
  • (完整体系教程)使用 Arduino 控制伺服电机

    1 什么是伺服电机 xff1f 伺服电机是一个闭环系统 xff0c 它使用位置反馈来控制其运动和最终位置 伺服电机有多种类型 xff0c 其主要特点是能够精确控制其轴的位置 在工业型伺服电机中 xff0c 位置反馈传感器通常是高精度编码器
  • ubuntu18.04安装vscode用于开发c++环境下的opencv

    微软官方下没有为ubuntu给出Visual Studio2022之类的安装方法 xff0c 但是Visual Studio Code却有 xff0c window下 xff0c Visual Studio 20xx下配置opencv的C
  • 【SpringBoot实战系列】阿里云OSS接入上传图片实战

    大家好 xff0c 我是工藤学编程 x1f989 一个正在努力学习的小博主 xff0c 期待你的关注作业侠系列最新文章 x1f609 Java实现聊天程序SpringBoot实战系列 x1f437 SpringBoot实战系列之图形验证码开
  • springboot kafka消费者启动/停止监听控制,启动时只消费此时之后的数据

    1 业务需求 在springboot项目中 xff0c 使用spring kafka消费kafka数据 希望能够控制消费者 xff08 KafkaConsumer xff09 启动或停止消费 xff0c 并且在启动消费时只消费当前时刻以后生
  • Ubuntu双系统安装(一次安装成功)

    Ubuntu双系统安装主要有关键地两步 xff1a 一 制作启动硬盘 二 为Ubuntu分配磁盘空间 第二点是安装过程中非常重要的一步 制作启动硬盘 xff1a 1 下载Ubuntu LTS xff0c 可以去官网下载 2 下载UltraI
  • R语言实现 黑箱方法——神经网络

    用人工神经网络对混泥土的强度strength进行建模 Step 1 Exploring and preparing the data 探索与准备数据 以下有8个描述混合物成分的特征 read in data and examine stru
  • Docker的无法访问dockerHub,无法使用search命令

    因为hub docker com是在国外的 xff0c 所以访问速度很慢 xff0c 导致无法访问该网址 xff0c 所以 xff0c 需要配置国内镜像来加速访问 一般的docker安装都是默认安装的 xff0c 所以 xff0c 找到da
  • Windows 添加新用户,并授予该用户远程登录权限

    注 xff1a 以Windows Server 2019举例子 xff08 该机器本身是一台轻量应用服务器 xff09 首先添加新用户 xff1a 以管理员身份登录 xff0c 选择 更改账户设置 xff1a 选择 其他用户 gt 将其他人
  • MySQL的错误日志查看

    在对MySQL数据库进行配置的过程中 xff0c 懂得如何查看错误日志对成功配置有着至关重要的作用 MySQL查看错误日志的方法如下 xff1a 1 查询错误日志所在位置 在MySQL运行以下命令 xff1a SHOW VARIABLES
  • Mysql磁盘IO占用过高的一种解决办法

    背景 xff1a 在之前的主从同步过程中 xff08 Mysql的多级复制 xff09 xff0c 从数据库Z存在磁盘IO占用过高的问题 磁盘IO在同步期间占用率达到100 xff0c 且数据存在滞后 xff0c 不能实现实时更新 从数据库
  • 电脑重装操作系统——使用U盘安装(简略步骤)

    前言 xff1a 这是使用U盘安装操作系统的一种较为通用的方式 首先使用UltraISO制作系统安装U盘 xff1b 然后修改系统启动项 xff0c 设置为优先从U盘启动 xff1b 然后插入U盘 xff0c 重启电脑安装系统 xff1b
  • 电脑重装Windows操作系统的几种方法

    在安装操作系统前 xff0c 首先要下载Windows ISO镜像 xff0c 本次以Windows 10的安装做为示例 无论采用以下哪一种方法 xff0c 都需要先将Windows ISO镜像下载下来 xff0c 然后再采用下列任意一种方

随机推荐