STM32F407+LAN8720A +LWIP +FreeRTOS 实现TCP服务器

news/2024/12/18 6:47:38/

STM32F407+LAN8720A +LWIP +FreeRTOS UDP通讯
上一篇实现了UDP通讯

本篇实现TCP服务器

实现如下功能:

TCP服务器接收数据并把数据发送回去,实现回环。

1.代码

STM32CUBEIDE配置方式和udp相同,只是代码不同。
TCP服务器实现的代码如下:

// tcp接收发送缓存
//#define TCP_RX_LEN  8192
uint8_t TCP_RX_BUF[TCP_RX_LEN];
volatile uint16_t TCP_RX_STA = 0;  /* bit15:有无数据标志位        bit14-0:数据量计数 */
struct tcp_pcb* tcppcb = NULL;static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{if (p == NULL) /* 接收到空包表示对方断开连接 */{tcp_server_disconnect(tpcb);err = ERR_CLSD;}else if (err != ERR_OK) /* 收到非空包但是出现错误 */{pbuf_free(p);}else /* 接收数据正常,遍历pbuf拷贝出接收到的数据 */{struct pbuf* it = p;if ((TCP_RX_STA & 0x8000) == 0) /* 当前缓存为空  */{for (it = p; it != NULL; it = it->next){if (TCP_RX_STA + it->len > TCP_RX_LEN) /* 缓存满了 */break;memcpy(TCP_RX_BUF + TCP_RX_STA, it->payload, it->len);  /* 将接收到的数据拷贝到自己的缓存中  */TCP_RX_STA += it->len;}TCP_RX_STA |= 0x8000;    /* 标记有数据收到 */}tcp_recved(tpcb, p->tot_len); /* 滑动TCP窗口 */pbuf_free(p); /* 释放pbuf */}return err;
}static err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{if (tcppcb == NULL){if (err == ERR_OK){tcppcb = newpcb;tcp_arg(newpcb, NULL);tcp_recv(newpcb, recv_callback);printf("%s:%d connect.\r\n", inet_ntoa(newpcb->remote_ip), newpcb->remote_port);}else{tcp_server_disconnect(newpcb);}}else{tcp_abort(newpcb);printf("already connected.\r\n");}return err;
}static void tcp_server_disconnect(struct tcp_pcb *tpcb)
{tcp_arg(tpcb, NULL);tcp_recv(tpcb, NULL);tcp_abort(tpcb); /* 关闭连接并释放tpcb控制块 */tcppcb = NULL;printf("disconnected.\r\n");
}static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len)
{uint32_t nwrite = 0, total = 0;const uint8_t* p = (const uint8_t *) buf;err_t err = ERR_OK;if (!tpcb)return 0;while ((err == ERR_OK) && (len != 0) && (tcp_sndbuf(tpcb) > 0)){nwrite = tcp_sndbuf(tpcb) >= len ? len : tcp_sndbuf(tpcb);err = tcp_write(tpcb, p, nwrite, 1);if (err == ERR_OK){len -= nwrite;total += nwrite;p += nwrite;}tcp_output(tpcb);}return total;
}//extern int tcp_server_start(uint16_t port);
//extern int user_senddata(const void* buf,uint32_t len);
//extern int transfer_data();/*** 启动TCP服务器* @param  port 本地端口号* @return      成功返回0*/
int tcp_server_start(uint16_t port)
{int ret = 0;struct tcp_pcb* pcb = NULL;err_t err = ERR_OK;/* create new TCP PCB structure */pcb = tcp_new();if (!pcb){printf("Error creating PCB. Out of Memory\r\n");ret = -1;goto __exit;}/* bind to specified @port */err = tcp_bind(pcb, IP_ADDR_ANY, port);if (err != ERR_OK){printf("Unable to bind to port %d: err = %d\r\n", port, err);ret = -2;goto __exit;}/* listen for connections */pcb = tcp_listen(pcb);if (!pcb){printf("Out of memory while tcp_listen\r\n");ret = -3;}/* specify callback to use for incoming connections */tcp_accept(pcb, accept_callback);/* create success */printf("TCP echo server started @ port %d\r\n", port);return ret;__exit:if (pcb)memp_free(MEMP_TCP_PCB, pcb);return ret;
}/*** TCP发送数据* @param  buf 待发送的数据* @param  len 数据长度* @return     返回实际发送的字节数*/
int user_senddata(const void* buf, uint32_t len)
{return tcp_server_send(tcppcb, buf, len);
}/*** 轮询函数,放置于main函数的while死循环中* @return 无*/
int transfer_data(void)
{uint32_t nsend = 0;if (tcppcb != NULL && tcppcb->state == ESTABLISHED) /* 连接有效 */{if (TCP_RX_STA & 0x8000)   /* 有数据收到  */{nsend = user_senddata(TCP_RX_BUF, TCP_RX_STA & 0x7FFF);    /* 将接收到的数据发回去  */TCP_RX_STA = 0;printf("send %d bytes success\r\n", nsend);}}return 0;
}

freertos.c的全部代码如下:

/* USER CODE BEGIN Header */
/********************************************************************************* File Name          : freertos.c* Description        : Code for freertos applications******************************************************************************* @attention** Copyright (c) 2024 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"#include <string.h>
#include <stdint.h>
#include "lwip/tcp.h"
#include "lwip/err.h"
#include "lwip/memp.h"
#include "lwip/inet.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
// tcp接收发送缓存
//#define TCP_RX_LEN  8192
uint8_t TCP_RX_BUF[TCP_RX_LEN];
volatile uint16_t TCP_RX_STA = 0;  /* bit15:有无数据标志位        bit14-0:数据量计数 */
struct tcp_pcb* tcppcb = NULL;
/* USER CODE END Variables */
osThreadId defaultTaskHandle;
osThreadId LEDTaskHandle;
osThreadId UARTTask03Handle;
osSemaphoreId uartBinarySem01Handle;/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
static void tcp_server_disconnect(struct tcp_pcb *tpcb);
static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len);
int transfer_data(void);
int user_senddata(const void* buf, uint32_t len);
int tcp_server_start(uint16_t port);
static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len);
static void tcp_server_disconnect(struct tcp_pcb *tpcb);
static err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err);
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);/* USER CODE END FunctionPrototypes */void StartDefaultTask(void const * argument);
void StartTask_LED(void const * argument);
void StartTask_UART(void const * argument);extern void MX_LWIP_Init(void);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) *//* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{*ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;*ppxIdleTaskStackBuffer = &xIdleStack[0];*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;/* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY *//*** @brief  FreeRTOS initialization* @param  None* @retval None*/
void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init *//* USER CODE END Init *//* USER CODE BEGIN RTOS_MUTEX *//* add mutexes, ... *//* USER CODE END RTOS_MUTEX *//* Create the semaphores(s) *//* definition and creation of uartBinarySem01 */osSemaphoreDef(uartBinarySem01);uartBinarySem01Handle = osSemaphoreCreate(osSemaphore(uartBinarySem01), 1);/* USER CODE BEGIN RTOS_SEMAPHORES *//* add semaphores, ... *//* USER CODE END RTOS_SEMAPHORES *//* USER CODE BEGIN RTOS_TIMERS *//* start timers, add new ones, ... *//* USER CODE END RTOS_TIMERS *//* USER CODE BEGIN RTOS_QUEUES *//* add queues, ... *//* USER CODE END RTOS_QUEUES *//* Create the thread(s) *//* definition and creation of defaultTask */osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 2048);defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);/* definition and creation of LEDTask */osThreadDef(LEDTask, StartTask_LED, osPriorityIdle, 0, 256);LEDTaskHandle = osThreadCreate(osThread(LEDTask), NULL);/* definition and creation of UARTTask03 */osThreadDef(UARTTask03, StartTask_UART, osPriorityIdle, 0, 512);UARTTask03Handle = osThreadCreate(osThread(UARTTask03), NULL);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *//* USER CODE END RTOS_THREADS */}/* USER CODE BEGIN Header_StartDefaultTask */
/*** @brief  Function implementing the defaultTask thread.* @param  argument: Not used* @retval None*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{/* init code for LWIP */MX_LWIP_Init();/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */int ret =  tcp_server_start(6001);if(ret !=0) return;for(;;){transfer_data();osDelay(100);}/* USER CODE END StartDefaultTask */
}/* USER CODE BEGIN Header_StartTask_LED */
/**
* @brief Function implementing the LEDTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask_LED */
void StartTask_LED(void const * argument)
{/* USER CODE BEGIN StartTask_LED *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(GPIOE, LED0_Pin|LED1_Pin);osDelay(300);}/* USER CODE END StartTask_LED */
}/* USER CODE BEGIN Header_StartTask_UART */
/**
* @brief Function implementing the UARTTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask_UART */
void StartTask_UART(void const * argument)
{/* USER CODE BEGIN StartTask_UART *//* Infinite loop */
//printf("freertos start\r\n");for(;;){osDelay(1000);}/* USER CODE END StartTask_UART */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{if (p == NULL) /* 接收到空包表示对方断开连接 */{tcp_server_disconnect(tpcb);err = ERR_CLSD;}else if (err != ERR_OK) /* 收到非空包但是出现错误 */{pbuf_free(p);}else /* 接收数据正常,遍历pbuf拷贝出接收到的数据 */{struct pbuf* it = p;if ((TCP_RX_STA & 0x8000) == 0) /* 当前缓存为空  */{for (it = p; it != NULL; it = it->next){if (TCP_RX_STA + it->len > TCP_RX_LEN) /* 缓存满了 */break;memcpy(TCP_RX_BUF + TCP_RX_STA, it->payload, it->len);  /* 将接收到的数据拷贝到自己的缓存中  */TCP_RX_STA += it->len;}TCP_RX_STA |= 0x8000;    /* 标记有数据收到 */}tcp_recved(tpcb, p->tot_len); /* 滑动TCP窗口 */pbuf_free(p); /* 释放pbuf */}return err;
}static err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{if (tcppcb == NULL){if (err == ERR_OK){tcppcb = newpcb;tcp_arg(newpcb, NULL);tcp_recv(newpcb, recv_callback);printf("%s:%d connect.\r\n", inet_ntoa(newpcb->remote_ip), newpcb->remote_port);}else{tcp_server_disconnect(newpcb);}}else{tcp_abort(newpcb);printf("already connected.\r\n");}return err;
}static void tcp_server_disconnect(struct tcp_pcb *tpcb)
{tcp_arg(tpcb, NULL);tcp_recv(tpcb, NULL);tcp_abort(tpcb); /* 关闭连接并释放tpcb控制块 */tcppcb = NULL;printf("disconnected.\r\n");
}static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len)
{uint32_t nwrite = 0, total = 0;const uint8_t* p = (const uint8_t *) buf;err_t err = ERR_OK;if (!tpcb)return 0;while ((err == ERR_OK) && (len != 0) && (tcp_sndbuf(tpcb) > 0)){nwrite = tcp_sndbuf(tpcb) >= len ? len : tcp_sndbuf(tpcb);err = tcp_write(tpcb, p, nwrite, 1);if (err == ERR_OK){len -= nwrite;total += nwrite;p += nwrite;}tcp_output(tpcb);}return total;
}/*** 启动TCP服务器* @param  port 本地端口号* @return      成功返回0*/
int tcp_server_start(uint16_t port)
{int ret = 0;struct tcp_pcb* pcb = NULL;err_t err = ERR_OK;/* create new TCP PCB structure */pcb = tcp_new();if (!pcb){printf("Error creating PCB. Out of Memory\r\n");ret = -1;goto __exit;}/* bind to specified @port */err = tcp_bind(pcb, IP_ADDR_ANY, port);if (err != ERR_OK){printf("Unable to bind to port %d: err = %d\r\n", port, err);ret = -2;goto __exit;}/* listen for connections */pcb = tcp_listen(pcb);if (!pcb){printf("Out of memory while tcp_listen\r\n");ret = -3;}/* specify callback to use for incoming connections */tcp_accept(pcb, accept_callback);/* create success */printf("TCP echo server started @ port %d\r\n", port);return ret;__exit:if (pcb)memp_free(MEMP_TCP_PCB, pcb);return ret;
}/*** TCP发送数据* @param  buf 待发送的数据* @param  len 数据长度* @return     返回实际发送的字节数*/
int user_senddata(const void* buf, uint32_t len)
{return tcp_server_send(tcppcb, buf, len);
}/*** 轮询函数,放置于main函数的while死循环中* @return 无*/
int transfer_data(void)
{uint32_t nsend = 0;if (tcppcb != NULL && tcppcb->state == ESTABLISHED) /* 连接有效 */{if (TCP_RX_STA & 0x8000)   /* 有数据收到  */{nsend = user_senddata(TCP_RX_BUF, TCP_RX_STA & 0x7FFF);    /* 将接收到的数据发回去  */TCP_RX_STA = 0;printf("send %d bytes success\r\n", nsend);}}return 0;
}
/* USER CODE END Application */

2.下载运行测试

下载完成后,串口输出:TCP echo server started @ port 6001
netif_up
点击网络调试助手的连接按钮,显示连接成功,串口输出:192.168.111.11:57991 connect.
点击网络调试助手的发送,网络调试助手的接收区显示收到相同的数据,同时串口打印接收字节数
点击网络调试助手的断开,串口输出:disconnected.
再次点击连接后可以继续通讯。
在这里插入图片描述
代码部分参考了 这篇文章
STM32F407 + LAN8720A + LWIP 实现TCP服务器


http://www.ppmy.cn/news/1556060.html

相关文章

请确保 $(OutDir)、$(TargetName) 和 $(TargetExt) 属性值与 %(Link.OutputFile) 中指定的值匹配

vs版本升级时&#xff0c;编译时会出现上述问题&#xff0c;如原来在2017下编译的程序&#xff0c;后来改用2019&#xff0c;出现上述问题。需要在解决方案-通用属性-调试源文件下变更相应设置。

Windows server服务器之网络按管理(创建IPsec保护网络安全)

14.2 任务&#xff1a;创建IPsec保护网络安全 对IPsec 两个方面的作用。&#xff08;必须清楚&#xff09; 任务实施的步骤&#xff1a; 1.创建本地安全策略 2.创建管理IP筛选器列表和筛选器操作 3.打开IP地址添加向导 4.筛选器列表和筛选向导中的内容要对应 5.完成IP筛…

探索 Cesium 的未来:3D Tiles Next 标准解析

探索 Cesium 的未来&#xff1a;3D Tiles Next 标准解析 随着地理信息系统&#xff08;GIS&#xff09;和 3D 空间数据的快速发展&#xff0c;Cesium 作为领先的开源 3D 地球可视化平台&#xff0c;已成为展示大规模三维数据和进行实时渲染的强大工具。近年来&#xff0c;随着…

练习题:一维数组

练习题 第一题 键盘录入一组数列&#xff0c;利用冒泡排序将数据由大到小排序 代码 #include <stdio.h>int arr_home01() {int arr[10];int i,j,temp;printf("请输入10个测试整数&#xff1a;\n");int len sizeof(arr) / sizeof(arr[0]);for(i 0;i < …

《统计方形(数据加强版)》

题目背景 1997年普及组第一题 题目描述 有一个 nmnm 方格的棋盘&#xff0c;求其方格包含多少正方形、长方形&#xff08;不包含正方形&#xff09;。 输入格式 一行&#xff0c;两个正整数 n,mn,m&#xff08;n≤5000,m≤5000n≤5000,m≤5000&#xff09;。 输出格式 一…

不能通过 ip 直接访问 共享盘 解决方法

from base_config.config import OpenSMB, SMB import os, time, calendar, requests, decimal, platform, fs.smbfsinfo_dict SMB.EPDI_dict info_dict[host] (FS03,10.6.12.182) info_dict[direct_tcp] True# smb OpenSMB(info_dict)print(ok)# 根据 ip 查询电脑名 impor…

Linux系统 —— 进程系列 - 程序地址空间:虚拟地址空间

接前文&#xff1a; Linux系统 —— 进程系列 - 进程优先级与进程切换-CSDN博客https://blog.csdn.net/hedhjd/article/details/144404639?spm1001.2014.3001.5502 目录 前言 1. 虚拟地址空间和进程地址空间 1.1 什么是虚拟地址空间&#xff1f; 结论 1.2 虚拟地址空间的结…

【Ubuntu】安装QQ

1.1 标题安装 从网页下载QQ 查看Ubuntu系统架构 uname -a 下载文件并安装 sudo apt install libgtk2.0-0 sudo apt install -y ./QQ_3.2.15_241210_amd64_01.deb 1.2 卸载 sudo rpm -e linuxqq sudo dpkg -r linuxqq