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服务器