天天看点

Stm32F407 FreeRTOS+TCP 移植

FreeRTOS 版本: FreeRTOSv10.2.1

HAL 版本:  Keil.STM32F4xx_DFP_HAL.2.13.0

版本无所谓,只是记录下,FreeRTOS 10.0之后会多实现一个网络序列化回调函数。

FreeRTOSv10.2.1 TCP 文件目录

Stm32F407 FreeRTOS+TCP 移植

官方使用手册:https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/index.html

移植好FreeRTOS系统。因为TCP是多任务。

需要用到的文件:

1.上图9个C文件。

2.include目录头文件。

3.portable\ BufferManagement\ BufferAllocation_2.c 内存管理方案。

4.portable\Compiler\GCC\pack_struct_end.h和pack_struct_start.h 字节对齐(对应编译器)可以直接放到自己的头文件目录中去。

     start.h 添加 一句 #pragma pack(1)

     end.h 添加一句 __attribute__( (packed) );  #pragma pack()

5.portable\NetworkInterface\STM32Fxx\NetworkInterface.c (对应架构) stm32fxx系列网络接口文件。

6.portable\NetworkInterface\Common\phyHandling.c和portable\NetworkInterface\include\phyHandling.h

Stm32F407 FreeRTOS+TCP 移植
Stm32F407 FreeRTOS+TCP 移植

需要新建添加FreeRTOSIPConfig.h 配置头文件,主要是定义功能开关,物理地址,默认IP、网关等。直接复制。

(占篇幅 分开链接) https://blog.csdn.net/wy212670/article/details/105968537

工程添加完文件编译,当然会有很多错误。

主要是NetworkInterface.c 接口文件,首先修改引入#include "stm32f4xx_hal.h",#include "stm32f4xx_hal_eth.h"头文件,缺什么引什么。

有几个函数体内函数找不到,不重要的可以屏蔽。

修改了xNetworkInterfaceInitialise(),添加PHY 初始化函数,使用的是LAN8720。

// 网络接口初始化,其中初始化硬件,然后初始化发送和接收描述符,最后开启MAC层中断处理任务
BaseType_t xNetworkInterfaceInitialise( void )
{
HAL_StatusTypeDef hal_eth_init_status;
BaseType_t xResult;

	if( xEMACTaskHandle == NULL )
	{
			if( xTXDescriptorSemaphore == NULL )
			{
				xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB );
				configASSERT( xTXDescriptorSemaphore );
			}

		/* Initialise ETH */

		//Lan8720初始化
		Lan8720_Init();
			
		xETH.Instance = ETH;
		xETH.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
		xETH.Init.Speed = ETH_SPEED_100M;
		xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
		/* Value of PhyAddress doesn't matter, will be probed for. */
		xETH.Init.PhyAddress = 0;

		xETH.Init.MACAddr = ( uint8_t *)FreeRTOS_GetMACAddress();
		xETH.Init.RxMode = ETH_RXINTERRUPT_MODE;

		/* using the ETH_CHECKSUM_BY_HARDWARE option:
		both the IP and the protocol checksums will be calculated
		by the peripheral. */
		xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;

//		#if( ipconfigUSE_RMII != 0 )
//		{
//			xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
//		}
//		#else
//		{
//		xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII;
//		}
//		#endif /* ipconfigUSE_RMII */
		xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
		hal_eth_init_status = HAL_ETH_Init( &xETH );

		/* Only for inspection by debugger. */
		( void ) hal_eth_init_status;

		/* Set the TxDesc and RxDesc pointers. */
		xETH.TxDesc = DMATxDscrTab;
		xETH.RxDesc = DMARxDscrTab;

		/* Make sure that all unused fields are cleared. */
		memset( &DMATxDscrTab, '\0', sizeof( DMATxDscrTab ) );
		memset( &DMARxDscrTab, '\0', sizeof( DMARxDscrTab ) );

			/* Initialize Tx Descriptors list: Chain Mode */
			DMATxDescToClear = DMATxDscrTab;

		/* Initialise TX-descriptors. */
		prvDMATxDescListInit();

		/* Initialise RX-descriptors. */
		prvDMARxDescListInit();

		#if( ipconfigUSE_LLMNR != 0 )
		{
			/* Program the LLMNR address at index 1. */
			prvMACAddressConfig( &xETH, ETH_MAC_ADDRESS1, ( uint8_t *) xLLMNR_MACAddress );
		}
		#endif

		/* Force a negotiation with the Switch or Router and wait for LS. */
		prvEthernetUpdateConfig( pdTRUE );

		/* The deferred interrupt handler task is created at the highest
		possible priority to ensure the interrupt handler can return directly
		to it.  The task's handle is stored in xEMACTaskHandle so interrupts can
		notify the task when there is something to process. */
		xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle );
	} /* if( xEMACTaskHandle == NULL ) */
	
//检查PHY连接状态
	xSTM32_PhyRead( 0x00,0x01, &ulPHYLinkStatus);
	xPhyObject.ulLinkStatusMask = ulPHYLinkStatus;
	return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0;
	
//	if( xPhyObject.ulLinkStatusMask != 0 )
//	{
//		xETH.Instance->DMAIER |= ETH_DMA_ALL_INTS;
//		xResult = pdPASS;
//		FreeRTOS_printf( ( "Link Status is high\n" ) ) ;
//	}
//	else
//	{
//		/* For now pdFAIL will be returned. But prvEMACHandlerTask() is running
//		and it will keep on checking the PHY and set 'ulLinkStatusMask' when necessary. */
//		xResult = pdFAIL;
//		FreeRTOS_printf( ( "Link Status still low\n" ) ) ;
//	}
//	/* When returning non-zero, the stack will become active and
//    start DHCP (in configured) */
//	return xResult;
}
           

 解决了函数问题,就剩下回调函数需要实现,再次编译会报错9个函数未定义,我们自己实现就好了。

Stm32F407 FreeRTOS+TCP 移植

新建NetWorkConfig.c 实现这些函数,其中ulApplicationGetNextSequenceNumber()和 uxRand()需要使用随机数,所以自己添加RNG生成随机数返回。完整代码如下。

(NetWorkConfig.c)https://blog.csdn.net/wy212670/article/details/105968368

总结:

    1.修改官方文件 NetworkInterface.c ,添加LAN8720初始化。

     2.需要我们自己添加的有,PHY初始化函数,RNG随机数生成函数,9个回调函数实现。

问题:

     1.调试发现收发冲突导致不能发送,修改stm32f4xx_hal_eth.c 的 HAL_ETH_IRQHandler中断函数,if( )..else if( )改成 if( )..if( )。

      2.FreeRTOSConfig.h 中需要注意的问题:

              #define configMAX_PRIORITIES       ( 12 )   任务优先级适当调大 默认5,任务多了不够用

              #define configTOTAL_HEAP_SIZE     ( ( size_t ) ( 50 * 1024 ) )  堆空间默认20*1024,太小网络任务无法运行但是不报错。

到此需要的工作都做完,编译应该没错了。就可以试试能不能用。

插网线,连接到路由器网络获取IP。

调用IP_Init(), RNG_Init(),初始化网络相关,会自动通过路由器获取DHCP静态地址。所以需要查看是否获取的地址,可以调试查看 xDefaultPartUDPPacketHeader. ulWords(搜索变量名查看位置) 变量第五个字节保存的是获取到的地址。

或者通过路由器界面,例如 192.168.31.1查看连接设备是否有开发板。在回调文件NetWorkConfig.c 中定义开发板名称,例如我的是“WY-STM32”,如果有并且地址也分配了,网口灯也亮了,说明移植成功。

下面开始网络数据收发。也就是编写TCPClient和TCPServer通过网络调试助手进行数据收发。

TCPClient客户端代码如下:创建两个任务,发送和接收任务。发送时创建套接字,连接服务器,连接成功循环发送数据,接收任务中通过发送创建的套接字循环接收数据。

相关API函数查看官方手册。

1.创建套接字,xSocket =FreeRTOS_socket(FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP)。

2.设置接收、发送超时时间,FreeRTOS_setsockopt()。

3.if ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND is set to 1 in FreeRTOSIPConfig.h 就不需要bind 绑定函数,直接使用连接函数FreeRTOS_connect(套接字,服务器地址,地址长度)。

4.连接成功后,FreeRTOS_send()发送数据。

5.连接成功后,FreeRTOS_recv()接收数据。

(代码链接 TCPClient.c)https://blog.csdn.net/wy212670/article/details/105970402

用网络调试助手开启服务器,执行vStartTCPClientTasks(512, 2),开启客户端。如果连接成功,服务器串口循环打印客户端发过来的数据,服务器发送stop客户端接收停止发送。

Stm32F407 FreeRTOS+TCP 移植

TCPServer 服务器代码如下:开启任务创建套接字,绑定服务器地址,监听套接字,循环接收监听,当都客户端连接成功,开启任务循环处理接收客户端发送过来的数据。

1.创建套接字,xListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );

2.设置连接超时,FreeRTOS_setsockopt()。

3.绑定,FreeRTOS_bind( xListeningSocket, &xBindAddress, sizeof( xBindAddress ) );

4.监听,FreeRTOS_listen( xListeningSocket, xBacklog );

5.接收,xConnectedSocket = FreeRTOS_accept( xListeningSocket, &xClient, &xSize );

6.接收到客户端套接字,处理客户端接收和发送。

             FreeRTOS_recv( xConnectedSocket, pucRxBuffer, ipconfigTCP_MSS, 0 );,

             FreeRTOS_send( xConnectedSocket, pucRxBuffer, lBytes - lTotalSent, 0 );

(TCPServer.c)https://blog.csdn.net/wy212670/article/details/105971657

网络调试助手开启客户端连接服务器成功,客户端发送数据给服务器,服务器接收到再重新发给客户端。

Stm32F407 FreeRTOS+TCP 移植

继续阅读