从零开始Cubemx配置STM32搭载freeRTOS以及lwip实现tcp网络通信(二)

本文介绍如何使用CubeMX配置STM32F407,结合FreeRTOS和LWIP实现TCP回环功能。首先配置ETH为RMII模式,然后设置LWIP,通过创建TCPECHO接收和接受函数,实现TCP连接的回环。在FreeRTOS环境下,可以使用熟悉的socket编程,简化网络开发。

引言

看见名字自己就笑了,标题的长度已经快赶上日本轻小说了,正好最近在追无职转生。
说回正题,上篇文章介绍了如何从零开始使用CubeMX生成一个带有freeRTOS操作系统的程序,嵌入式进阶指南以及必备知识学习路线,朋友们可以去看一下,这篇文章我将开始重头戏,如何在STM32F407上移植lwip实现TCP通信,最终实现一个httpd服务器。

自己写的另外两篇文章
从零开始Cubemx配置STM32搭载freeRTOS实现多路ADC(一)
从零开始使用CubeMX配置STM32使用lwip实现httpd服务器(三)
CubeMX配置STM32实现httpd服务器CGI功能并使用网页控制STM32单片机(四)

CubeMX配置以太网以及LWIP实现一个回环功能(裸机)

实现这个功能需要配置2部分,一部分的EHT也就是以太网的配置,另一部分是lwip协议栈的配置

ETH配置

ETH的配置主要有三个器件,单片机,物理网卡(在本文中我们使用的是LAN8720A芯片)以及网口。在这里面最重要的是物理网卡,单片机ETH的配置主要以物理网卡为主,与其电路连接有关系。LAN8720A的知识主要看这一篇文章 https://blog.youkuaiyun.com/sinat_20265495/article/details/79628283?spm=1001.2014.3001.5506
在这里插入图片描述
现在开始使用Cubemx配置ETH,在Connectivity选择中选择ETH
在这里插入图片描述
MII(Medium Independent Interface)是IEEE802.3u规定的一种介质无关接口,主要作用是连接介质访问控制层(MAC)子层与物理层(PH-Y)之间的标准以太网接口,负责MAC和PHY之间的通信。由于MII需要多达16根信号线,由此产生的I/O口需求及功耗较大,有必要对MII引脚数进行简化,因此提出了RMII(Reduced Medium Independent Interface,精简的介质无关接口),即简化了的MII。更详细的看这篇文章 https://blog.youkuaiyun.com/fun_tion/article/details/70270632
采用MII接口,PYH的时钟频率要求25M,不需要与MAC层时钟一致。
采用RMII接口,PYH的时钟频率要求50M,需与MAC层时钟一致,通常从MAC层获取该时钟源。
现在一般都用RMII模式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

LWIP配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接下来生成代码就可以了
在主函数中,添加这两个函数
在这里插入图片描述
实现回环代码,新建一个tcpecho.c文件,代码

#include "tcpecho.h"
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"
#include "netif/etharp.h"
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include <stdio.h>	
#include <string.h>


static err_t tcpecho_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
  if (p != NULL) 
  {        
	/* 更新窗口*/
	tcp_recved(tpcb, p->tot_len);
    
    /* 返回接收到的数据*/  
  tcp_write(tpcb, p->payload, p->tot_len, 1);
    
  memset(p->payload, 0 , p->tot_len);
  pbuf_free(p);
    
  } 
  else if (err == ERR_OK) 
  {
    return tcp_close(tpcb);
  }
  return ERR_OK;
}

static err_t tcpecho_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{     

  tcp_recv(newpcb, tcpecho_recv);
  return ERR_OK;
}

void TCP_Echo_Init(void)
{
  struct tcp_pcb *pcb = NULL;	            		
  
  /* 创建一个TCP控制块  */
  pcb = tcp_new();	  
  
  /* 绑定TCP控制块 */
  tcp_bind(pcb, IP_ADDR_ANY, TCP_ECHO_PORT);       


  /* 进入监听状态 */
  pcb = tcp_listen(pcb);				

  /* 处理连接 */	
  tcp_accept(pcb, tcpecho_accept);   
}

这样裸机就成功了,可以ping成功
在这里插入图片描述在这里插入图片描述

CubeMX配置以太网以及LWIP实现一个回环功能(freeRTOS)

在CubeMX中打开freeRTOS后,lwip会自动切换到带有os的模式,其他的和裸机一样。不懂如何移植freeRTOS的去看我上一篇文章。
在这里插入图片描述
生成代码,可以看到LWIP初始化函数以及被加入默认线程中了。
在这里插入图片描述
搭载freeRTOS后就可以使用我们最熟悉的socket编程了,这时tcpecho.c可以写成,这个代码,使用过socket编程的人应该就很熟悉了,实验效果和裸机一样。推荐使用带操作系统的框架进行网络编程。

#include "tcpecho.h"

#include "lwip/opt.h"

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

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

#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);
}

结论

还是那个观点,在进行stm32开发的时候cubemx可以省去大量的库移植的操作,可以大幅缩减开发周期,把时间更集中在任务上,在这篇文章中,本文分别在裸机以及带有freeRTOS的情况下实现了TCP连接,并简单做了回环,推荐使用操作系统进行网络开发,因为可以用socket编程,方便在Linux等系统上进行使用。
在下一篇文章中,我将实现一个httpd服务器,可以用浏览器直接访问。

### STM32CubeMX ETH 配置指南 #### 1. 初始化项目设置 在启动STM32CubeMX软件后,创建新工程并选择目标微控制器型号。完成基本参数设定之后进入外设配置阶段。 #### 2. 启用以太网模块 于左侧菜单栏找到`Connectivity`分类下的`Ethernet (ETH)`选项,并勾选激活此功能[^2]。此时右侧会出现一系列关于以太网的具体属性等待调整。 #### 3. 物理层(PHY)接口的选择 对于物理层连接方式而言,需依据实际使用的PHY芯片来决定采用何种标准——MII、RMII或是RGMII。如果选用的是裕太YT8512这类支持RMII协议的器件,则应在界面中确认已开启`ETH Ref Clock Input`选项后再指定为RMII模式。 #### 4. 引脚分配优化 根据所选MCU的不同,可能涉及到特定引脚映射至以太网MAC/PHY之间的关系定义。确保所有必要的信号线(如TX/RX数据通道、时钟输入等)都被正确关联到对应的硬件资源上。 #### 5. 中断和服务例程管理 为了使能网络事件响应机制,在中断配置区应适当安排与以太网相关的IRQ触发条件;同时也要考虑是否要引入DMA传输服务或其他辅助特性增强性能表现[^3]。 #### 6. 软件栈集成准备 当上述硬件层面的工作完成后,下一步就是着手规划轻量级TCP/IP协议栈(LwIP)或者其他高级应用框架如何嵌入当前系统架构之中。 ```c // 示例代码片段展示初始化部分操作 void MX_ETH_Init(void){ /* USER CODE BEGIN ETH_Init_PreTreatment */ // 这里可以加入自定义预处理逻辑 /* USER CODE END ETH_Init_PreTreatment */ heth.Instance = ETH; heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; // 自动协商启用 heth.Init.PhyAddress = LAN8742_PHY_ADDRESS; // 设置PHY地址 heth.Init.MACAddr = MAC_ADDR_DEFAULT; // 默认MAC地址赋值 heth.Init.RxMode = ETH_RXPOLLING_MODE; // 接收模式选择轮询 heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; // 校验和计算交给硬件处理 heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; // 使用RMII介质接口 } ```
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值