STM32F407+LAN8720A +LWIP +FreeRTOS UDP通讯
上一篇实现了UDP通讯
本篇实现TCP服务器
实现如下功能:
TCP服务器接收数据并把数据发送回去,实现回环。
1.代码
STM32CUBEIDE配置方式和udp相同,只是代码不同。
TCP服务器实现的代码如下:
c
// 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的全部代码如下:
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服务器