STM32F407 CUBEIDE HAL library implements FREERTOS TCP Echo Server

STM32F407 CUBEIDE HAL library implements FREERTOS TCP Echo Server

The STM32F407 board uses the DP83848 PHY chip. Realize TCP Echo Server under the FREERTOS operating system environment through configuration.

basic configuration

Configure STM32F407 to be phase-locked from an external 25MHz clock to a 168MHz system clock.
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Configure the PA9/PA10 of USART1 as a print information output port.
Insert picture description here
Insert picture description here
Insert picture description here
Configure the Ethernet PHY chip and select the corresponding type. If it is LAN8270A, select the corresponding other option.
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
For configuration, choose FREERTOS and use the default parameters, and then adjust when you need to modify it.
Insert picture description here
Configure two tasks
Insert picture description here
Insert picture description here
. When you choose to use LWIP, you can use the default parameters and you can adjust when you need to modify.
Insert picture description here
Configure the IP address.
Insert picture description here
Save and generate the basic code.

Code

According to the test analysis, the initialization of LWIP should be placed before the task is started. The current cube configuration tool will default the startup of LWIP MX_LWIP_Init() in the default task, which should be adjusted and optimized.

Establish and introduce usart.h and usart.c, realize printf overload
usart.h

#ifndef _USART_H
#define _USART_H

#include "stm32f4xx_hal.h"
#include "stdio.h"	 	  	


int fputc(int ch, FILE *f) ;

#endif

usart.c

#include "usart.h"   


extern UART_HandleTypeDef huart1;   //声明串口

/* USER CODE BEGIN 1 */
#ifdef __GNUC__
  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE

Establish and introduce tcpserver.h and tcpserver.c, implement tcp echo server function code
tcpserver.h

#ifndef _TCPSERVER_H
#define _TCPSERVER_H

#include <stdbool.h>
void tcp_echoserver_init(void *p_arg);

#endif

tcpserver.c

#include <lwip/sockets.h>
#include <lwip/err.h>
#include <lwip/sys.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>								
#include "tcpserver.h"
#include "usart.h"

#define	SERVER_PORT			1030				//配置服务器端口号
extern _Bool TCP_STATUS_UPDATE;
uint8_t data_buffer[100];						//定义接收到的数据Buff大小为100
char tcp_server_recvbuf[300];					//定义数据处理Buff大小为300(大于等于100即可)

//static void tcp_server_thread(void *p_arg)		//定义TCP服务器线程
void tcp_echoserver_init(void *p_arg)
{
	struct sockaddr_in server_addr;				//服务器地址
	struct sockaddr_in conn_addr;				//连接地址
	int sock_fd ;								//服务器的 socked
	int sock_conn;								// 请求的 socked
	socklen_t addr_len;							// 地址长度
	int err;
	int length;
	int num;
	
	sock_fd = socket(AF_INET, SOCK_STREAM, 0);		//建立一个新的socket连接
	if (sock_fd < 0)
		{
		  printf("tcp socket error\r\n") ;
		  TCP_STATUS_UPDATE = 0;
		  return;
		}
	else printf("tcp socket ok\r\n") ;

	memset(&server_addr, 0, sizeof(server_addr));				//将服务器地址清空
	server_addr.sin_family = AF_INET;							//地址家族
	server_addr.sin_addr.s_addr =inet_addr("192.168.1.252");				//注意转化为网络字节序
	server_addr.sin_port = htons(SERVER_PORT);					//使用SERVER_PORT指定为程序头设定的端口号
	memset(server_addr.sin_zero,0,sizeof(server_addr.sin_zero));

	err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));	//建立绑定
	if (err < 0)															    //如果绑定失败则关闭套接字
	{
			closesocket(sock_fd); 												//关闭套接字
			printf("bind error\r\n");
			TCP_STATUS_UPDATE = 0;
			return;

	}
	else printf("tcp socket bind ok\r\n") ;

	err = listen(sock_fd, 1);									//监听连接请求
	if (err < 0) 												//如果监听失败则关闭套接字
	{
			closesocket(sock_fd); 								//关闭套接字
			printf("listen error\r\n");
			TCP_STATUS_UPDATE = 0;
		    return;

	}
	else printf("tcp socket listen ok\r\n") ;

		addr_len = sizeof(struct sockaddr_in);					//将链接地址赋值给addr_len

		sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len);	//对监听到的请求进行连接,状态赋值给sock_conn

		if(sock_conn<0)											//状态小于0代表连接故障,此时关闭套接字
		{
			closesocket(sock_fd);
			printf("sock_conn error\r\n");
			TCP_STATUS_UPDATE = 0;
			return;

		}
		else send(sock_conn, "connect success!\n\r", 20, 0);	//连接成功则发送“connect success!”给客户端

	while (1)
	{
		memset(data_buffer, 0, sizeof(data_buffer));			//清空接收Buff

		length = recv(sock_conn, (unsigned int *)data_buffer, 100, 0);	//将收到的数据放到接收Buff

		for(num=0;num<100;num++)								//接收Buff的数据转移到数据处理Buff,防止之后数据混乱
		{
			tcp_server_recvbuf[num]=data_buffer[num];
		}

        if (length > 0)
        {
        	send(sock_conn, "\ntcp response: ",strlen("\ntcp response: "), 1);	//回复

        	send(sock_conn, tcp_server_recvbuf,length, 1);	//回复

        	send(sock_conn, "\r\n", strlen("\r\n"), 1);	//回复
        }
        else
        {

        	if (errno != EINTR)  //(length<=0)&&(errno!=EINTR) means socket broke
        	{
        	  printf("tcp link broke\r\n");

        		err = listen(sock_fd, 1);									//监听连接请求
        		if (err < 0) 												//如果监听失败则关闭套接字
        		{
        				closesocket(sock_fd); 								//关闭套接字
        				printf("listen error\r\n");
        				TCP_STATUS_UPDATE = 0;
        			    return;

        		}
        		else printf("tcp socket listen ok\r\n") ;

        			addr_len = sizeof(struct sockaddr_in);					//将链接地址赋值给addr_len
        			sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len);	//对监听到的请求进行连接,状态赋值给sock_conn
        			if(sock_conn<0)											//状态小于0代表连接故障,此时关闭套接字
        			{
        				closesocket(sock_fd);
        				printf("sock_conn error\r\n");
        				TCP_STATUS_UPDATE = 0;
        				return;
        			}
        			else send(sock_conn, "connect success!\n\r", 20, 0);	//连接成功则发送“connect success!”给客户端
        	}
        }
        osDelay(1);
	}
}

The code of the main() function, pay attention to delete the code here
Insert picture description here

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "lwip.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "usart.h"
#include "tcpserver.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 ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

osThreadId_t defaultTaskHandle;
osThreadId_t tcpserverTaskHandle;
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
void StartDefaultTask(void *argument);
void tcpserverTaskFunc(void *argument);

/* USER CODE BEGIN PFP */

uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuff[256];		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t	cAlmStr[] = "数据溢出(大于256)\r\n";

_Bool TCP_STATUS_UPDATE = 0;
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
  MX_LWIP_Init();  //must be placed here. remove the one generated by the tool.
  printf("FreeRTOS ready to run!\r\n") ;
  /* USER CODE END 2 */

  osKernelInitialize();

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* 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 */
  const osThreadAttr_t defaultTask_attributes = {
    .name = "defaultTask",
    .priority = (osPriority_t) osPriorityNormal,
    .stack_size = 256
  };
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* definition and creation of tcpserverTask */
  const osThreadAttr_t tcpserverTask_attributes = {
    .name = "tcpserverTask",
    .priority = (osPriority_t) osPriorityHigh4,
    .stack_size = 2048
  };
  tcpserverTaskHandle = osThreadNew(tcpserverTaskFunc, NULL, &tcpserverTask_attributes);

  /* USER CODE BEGIN RTOS_THREADS */

  /* USER CODE END RTOS_THREADS */

  /* Start scheduler */
  osKernelStart();
  
  /* We should never get here as control is now taken by the scheduler */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/* USER CODE BEGIN 4 */
/**
  * @brief  Rx Transfer completed callbacks.
  * @param  huart pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */

	if(Uart1_Rx_Cnt >= 255)  //溢出判断
	{
		Uart1_Rx_Cnt = 0;
		memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
		HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);
	}
	else
	{
		Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存

		if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发出
			Uart1_Rx_Cnt = 0;
			memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空数组
		}
	}

	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
}

/* USER CODE END 4 */

/* 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 *argument)
{
  /* init code for LWIP */

  /* USER CODE BEGIN 5 */

  /* Infinite loop */
  for(;;)
  {
    osDelay(3000);
  }
  /* USER CODE END 5 */ 
}

/* USER CODE BEGIN Header_tcpserverTaskFunc */
/**
* @brief Function implementing the tcpserverTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_tcpserverTaskFunc */
void tcpserverTaskFunc(void *argument)
{
  /* USER CODE BEGIN tcpserverTaskFunc */
 
  /* Infinite loop */
  for(;;)
  {
    if (TCP_STATUS_UPDATE == 0)
    {
    	TCP_STATUS_UPDATE = 1;
    	sys_thread_new("tcp_echoserver_init", tcp_echoserver_init, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO+10);
    }
    osDelay(10);
  }
  /* USER CODE END tcpserverTaskFunc */
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM1 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Connect to the board through a network cable, and manually set the IP address (such as 192.168.1.167) and subnet mask (such as 255.25.255.0) of the network card. After the connection is successful, you can use the TCP test tool to send information to the address 192.168.1.252 and Receive a copy reply.
The IP address of the TCP server can be modified. Note that the port number of the embedded board cannot be set too large, such as setting the TCP Server port to 1030 to be available, and setting to 5000 to be unavailable.
When debugging, pay attention to choosing FreeRTOS
Insert picture description here

-End-

Guess you like

Origin blog.csdn.net/hwytree/article/details/103547919