STM32F407的板子采用DP83848 PHY芯片。通过配置实现FREERTOS操作系统环境下的TCP Echo Server。
配置STM32F407由外部25MHz时钟锁相到168MHz系统时钟
配置USART1的PA9/PA10,作为打印信息输出端口
配置以太网PHY芯片,选择对应类型, 如果是LAN8270A,选择对应另外的选项即可。
配置选择使用FREERTOS,采用默认参数即可,需要修改时再调整
配置两项任务
配置选择使用LWIP,采用默认参数即可,需要修改时再调整
配置IP地址
保存并生成基本代码
根据测试分析,LWIP的初始化应放置在任务启动之前,目前的cube配置工具,会默认将LWIP的启动MX_LWIP_Init()被放在默认任务里,应调整优化。
建立并引入usart.h和usart.c, 实现printf重载
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
建立并引入tcpserver.h和tcpserver.c, 实现tcp echo server函数代码
tcpserver.h
#ifndef _TCPSERVER_H
#define _TCPSERVER_H
#include
void tcp_echoserver_init(void *p_arg);
#endif
tcpserver.c
#include
#include
#include
#include
#include
#include
#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);
}
}
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* © Copyright (c) 2019 STMicroelectronics.
* All rights reserved.
*
* 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
#include
#include
#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****/
通过网线与板子连接,并手动设置网卡的IP地址(如192.168.1.167)和子网掩码(如255.25.255.0)后,连接成功后,就可以通过TCP测试工具,向地址192.168.1.252发送信息和接收副本回复。
TCP sever的IP地址可以修改,留意对于嵌入式板子端口号不能设置太大,如设置TCP Server端口为1030可用,设置为5000不可用。
调试的时候,留意选择FreeRTOS
-End-