(此博文在2023年10月写完后,在2023年11月有更新,更新内容包括博文内容与代码文件,代码效果不变,但更新后的逻辑更合理)
前言
- FreeRTOS-Plus-TCP 是一种适用于 FreeRTOS 的可扩展的开源和线程安全 TCP/IP 堆栈。
- FreeRTOS-Plus-TCP 提供了一个熟悉的 基于标准 Berkeley 套接字的接口, 简单易用,便于快速学习。 高级用户
还可以使用替代回调接口。 FreeRTOS-Plus-TCP 的功能和 RAM占用空间 完全可扩展,使 FreeRTOS-Plus-TCP
既适用于较小的低吞吐量微控制器, 也适用于较大的高吞吐量 微处理器。 - 相对于LWIP,FreeRTOS-Plus-TCP的源代码量更小,可使代码编译更快,更适用于 FreeRTOS等优点。
移植流程
- 去官网下载FreeRTOS_TCP源文件文件;,当前版本为202212.01。
- 准备好一个能运行FreeRTOS核心库的GD32F450工程;
- 复制FreeRTOS_TCP源文件夹source到工程中,文件夹改名成FreeRTOS_TCP
- portable\BufferManagement文件夹里的只保留BufferAllocation_2.c,其它的删除。
- portable\Compiler文件夹里的只保留Keil,其它的删除。
- portable\CompilerNetworkInterface文件夹里的文件全部删除,然后新建NetworkInterface.c文件。这里面是官方给不同的单片机做的接口适配,其中没有适合GD32官方库的,但若是GD32使用ST公司的HAL库开发,倒是可以使用里面给STM32适配的接口文件。
- 移植官网提供demo文件夹里的FreeRTOS_Plus_TCP_Echo_Posix\FreeRTOSIPConfig.h到自己的工程user里,并添加到工程目录中,方便修改里面的参数。
- 添加FreeRTOS_TCP里所有的c文件到工程
- 将FreeRTOS_TCP头文件包含在工程中
- 移植GD32例程里面的随机数发生器代码
#include "gd32f4xx.h"
ErrStatus trng_ready_check(void)
{
uint32_t timeout = 0;
FlagStatus trng_flag = RESET;
ErrStatus reval = SUCCESS;
/* check wherther the random data is valid */
do{
timeout++;
trng_flag = trng_flag_get(TRNG_FLAG_DRDY);
}while((RESET == trng_flag) &&(0xFFFF > timeout));
if(RESET == trng_flag)
{
/* ready check timeout */
printf_network("Error: TRNG can't ready \r\n");
trng_flag = trng_flag_get(TRNG_FLAG_CECS);
printf_network("Clock error current status: %d \r\n", trng_flag);
trng_flag = trng_flag_get(TRNG_FLAG_SECS);
printf_network("Seed error current status: %d \r\n", trng_flag);
reval = ERROR;
}
/* return check status */
return reval;
}
static ErrStatus trng_config(void)
{
ErrStatus reval = SUCCESS;
/* TRNG module clock enable */
rcu_periph_clock_enable(RCU_TRNG);
/* TRNG registers reset */
trng_deinit();
trng_enable();
/* check TRNG work status */
reval = trng_ready_check();
return reval;
}
void trng_init(void)
{
uint8_t retry = 0;
while((ERROR == trng_config()) && retry < 3)
{
printf_network("TRNG init fail \r\n");
printf_network("TRNG init retry \r\n");
retry++;
}
}
//获取32位随机数
uint32_t trng_random_range_get(void)
{
if(SUCCESS == trng_ready_check()) {
return abs(trng_get_true_random_data()) ;
} else {
return 0;
}
}
- 新建enet.c和enet.h文件,将下面的代码复制粘贴。
注意:该工程是使用GD32F450本身的时钟输出功能CK_OUT0作为LAN8720A的时钟。
enet.c
#include "enet.h"
#include "gd32f4xx_enet.h"
#define configMAC_ADDR0 2
#define configMAC_ADDR1 0xA
#define configMAC_ADDR2 0xC
#define configMAC_ADDR3 0x5
#define configMAC_ADDR4 0x6
#define configMAC_ADDR5 0x6
extern enet_descriptors_struct txdesc_tab[ENET_TXBUF_NUM];/*ENET TxDMA描述符, 地址会被存入DMX_RDTADDR寄存器中*/
extern enet_descriptors_struct rxdesc_tab[ENET_RXBUF_NUM];/*ENET RxDMA描述符,地址会被存入DMX_TDTADDR寄存器中*/
static void eth_rmii_gpio_conifg(RMII_GPIO_NUM gpio_periph,RMII_GPIO_PIN pin);
static void enet_gpio_config(void);
/*设置以太网系统(GPIOs, clocks, MAC, DMA, systick)*/
ErrStatus InitialiseNetwork(void)
{
ErrStatus flag = ERROR;
/*配置嵌套矢量中断控制器*/
nvic_irq_enable(ENET_IRQn, 6 , 0);
enet_gpio_config(); /* 配置以太网引脚的GPIO端口 */
/* 开启以太网时钟 */
rcu_periph_clock_enable(RCU_ENET);
rcu_periph_clock_enable(RCU_ENETTX);
rcu_periph_clock_enable(RCU_ENETRX);
enet_deinit(); /* 在AHB总线上重置以太网 */
enet_software_reset(); //重置位于CLK_TX和CLK_RX的所有核心内部寄存器
/*用硬件计算和验证IP、UDP、TCP和ICMP的校验和*/
if (enet_init(ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS))
{
flag = SUCCESS;
}
enet_interrupt_enable(ENET_DMA_INT_NIE); //使能ENET MAC/MSC/DMA中断,正常中断汇总启用
enet_interrupt_enable(ENET_DMA_INT_RIE); //接收中断启用
enet_mac_address_set(ENET_MAC_ADDRESS0,ucMACAddress);//写入mac地址
enet_descriptors_chain_init(ENET_DMA_TX);//初始化DMA接收/发送描述符为链模式
enet_descriptors_chain_init(ENET_DMA_RX);
for(uint8_t i=0; i<ENET_RXBUF_NUM; i++)//当接收完成时,立即置位ENET_DMA_STAT寄存器的RS位
enet_rx_desc_immediate_receive_complete_interrupt(&rxdesc_tab[i]);
for(uint8_t i=0; i < ENET_TXBUF_NUM; i++)
enet_transmit_checksum_config(&txdesc_tab[i], ENET_CHECKSUM_TCPUDPICMP_FULL);
enet_enable(); //ENET Tx/Rx功能使能(包括ENET外设内的MAC和DMA模块)
return flag;
}
/*协议栈初始化成功后会调用 vApplicationIPNetworkEventHook() ,所有的IP_task需在里面创建 ethernet_task_creation 放在里面创建*/
/*配置RMII端口*/
static void enet_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
/*CK_OUT0配置,项目没有使用晶振,直接使用MCU输出时钟到PHY,并同步到REF_CLK*/
/* 选择DIV2在CKOUT0引脚(PA8)上从200MHz获得50MHz到PHY,PLLP:200*/
gpio_af_set(CK_OUT0_GPIO_NUM, GPIO_AF_0, CK_OUT0_PIN);
gpio_mode_set(CK_OUT0_GPIO_NUM, GPIO_MODE_AF, GPIO_PUPD_NONE, CK_OUT0_PIN);
gpio_output_options_set(CK_OUT0_GPIO_NUM, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, CK_OUT0_PIN);
// rcu_ckout0_config(RCU_CKOUT0SRC_PLLP, RCU_CKOUT0_DIV4);//50M
rcu_ckout0_config(RCU_CKOUT0SRC_HXTAL, RCU_CKOUT0_DIV1);//25M
rcu_periph_clock_enable(RCU_SYSCFG); /* 使能SYSCFG时钟 */
syscfg_enet_phy_interface_config(SYSCFG_ENET_PHY_RMII);//配置以太网MAC的PHY接口
eth_rmii_gpio_conifg(RMII_REF_CLK_GPIO_NUM , RMII_REF_CLK_PIN);
eth_rmii_gpio_conifg(RMII_MDIO_GPIO_NUM , RMII_MDIO_PIN);
eth_rmii_gpio_conifg(RMII_CRS_DV_GPIO_NUM , RMII_CRS_DV_PIN);
eth_rmii_gpio_conifg(RMII_TX_EN_GPIO_NUM , RMII_TX_EN_PIN);
eth_rmii_gpio_conifg(RMII_TXD0_GPIO_NUM , RMII_TXD0_PIN);
eth_rmii_gpio_conifg(RMII_TXD1_GPIO_NUM , RMII_TXD1_PIN);
eth_rmii_gpio_conifg(RMII_MDC_GPIO_NUM , RMII_MDC_PIN);
eth_rmii_gpio_conifg(RMII_RXD0_GPIO_NUM , RMII_RXD0_PIN);
eth_rmii_gpio_conifg(RMII_RXD1_GPIO_NUM , RMII_RXD1_PIN);
}
/*网口RMII配置,统一是复用模式 GPIO_AF_11 ,无上下拉推挽输出,速度最大*/
static void eth_rmii_gpio_conifg(RMII_GPIO_NUM gpio_periph,RMII_GPIO_PIN pin)
{
gpio_mode_set(gpio_periph, GPIO_MODE_AF, GPIO_PUPD_NONE, pin);
gpio_output_options_set(gpio_periph, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, pin);
gpio_af_set(gpio_periph, GPIO_AF_11, pin);
}
enet.h
#ifndef _ENET_H_
#define _ENET_H_
#include "main.h"
/* 设置以太网系统(GPIOs, clocks, MAC, DMA, systick) */
void InitialiseNetwork(void);
typedef enum
{
RMII_REF_CLK_GPIO_NUM=GPIOA,
RMII_MDIO_GPIO_NUM =GPIOA,
RMII_CRS_DV_GPIO_NUM =GPIOA,
RMII_TX_EN_GPIO_NUM =GPIOB,
RMII_TXD0_GPIO_NUM =GPIOB,
RMII_TXD1_GPIO_NUM =GPIOB,
RMII_MDC_GPIO_NUM =GPIOC,
RMII_RXD0_GPIO_NUM =GPIOC,
RMII_RXD1_GPIO_NUM =GPIOC
}RMII_GPIO_NUM;
typedef enum
{
RMII_REF_CLK_PIN =GPIO_PIN_1,
RMII_MDIO_PIN =GPIO_PIN_2,
RMII_CRS_DV_PIN =GPIO_PIN_7,
RMII_TX_EN_PIN =GPIO_PIN_11,
RMII_TXD0_PIN =GPIO_PIN_12,
RMII_TXD1_PIN =GPIO_PIN_13,
RMII_MDC_PIN =GPIO_PIN_1,
RMII_RXD0_PIN =GPIO_PIN_4,
RMII_RXD1_PIN =GPIO_PIN_5
}RMII_GPIO_PIN;
#define CK_OUT0_GPIO_NUM GPIOA
#define CK_OUT0_PIN GPIO_PIN_8
#endif
- 全局搜索PHY_TYPE,将官方默认的宏定义DP83848改成LAN8700,并修改PHY_ADDRESS为自己PHY的硬件地址值
- 以下是需要用户自己要写的东西,在此不再说明,移植成功后,结合官网教程理解
#include "main.h"
#include "FreeRTOSConfig.h"
#include "FreeRTOS_IP.h"
#include "trng.h"
#include "enet.h"
TaskHandle_t xEMACTaskHandle = NULL; /* 保存用作延迟中断处理器的任务句柄。使用句柄可以将所有EMAC/DMA相关中断的直接通知发送到任务。 */
QueueHandle_t xPingReplyQueue = NULL;
void eth_rece_data_task( void *pvParameters );
void ethernet_task_creation(void)/*创建以太网任务*/
{
printf("Ethernet is negotiating \r\n");
BaseType_t xReturn;
xPingReplyQueue = xQueueCreate( 20, sizeof( uint16_t ) ); /*Ping创建应答队列*/
xReturn=xTaskCreate( eth_rece_data_task, "eth_rece_data_task", 640, NULL, 31, &xEMACTaskHandle );//以太网接口接收数据任务,不需要它直接做什么
if(xReturn==errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY )
printf("eth_rece_data_task errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY\r\n");
}
/*协议栈初始化,会调用xNetworkInterfaceInitialise来初始化硬件*/
BaseType_t xNetworkInterfaceInitialise( void )
{
if(InitialiseNetwork())
{
printf("enet_init ok\r\n");
return pdPASS ;
}
return pdFAIL ;
}
/*以太网中断服务函数*/
void ENET_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* 帧收到 */
if(SET == enet_interrupt_flag_get(ENET_DMA_INT_FLAG_RS))
{
/* 唤醒 接收任务 。 */
if( xEMACTaskHandle != NULL )
{
vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken );//发送通知
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
}
/* 清除enet DMA Rx中断挂起位 */
enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_RS_CLR);
enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_NI_CLR);
/* 必要时切换任务 */
if(pdFALSE != xHigherPriorityTaskWoken) {
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
}
/*网络接口输出*/
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t xReleaseAfterSend )
{
enet_frame_transmit( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
/*调用标准跟踪宏来记录发送事件。*/
iptraceNETWORK_INTERFACE_TRANSMIT();
if( xReleaseAfterSend != pdFALSE )
{
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
}
return pdTRUE;
}
/*简单的接收处理程序示例*/
/*延迟中断处理程序是一个标准的RTOS任务。FreeRTOS的集中延迟中断处理能力也可以使用。*/
/*GD32的描述符结构体:enet_descriptors_struct */
void eth_rece_data_task( void *pvParameters )
{
NetworkBufferDescriptor_t *pxBufferDescriptor;//网络缓冲区描述符
size_t xBytesReceived; //接收字节数
IPStackEvent_t xRxEvent; /* 用于指示由于以太网接收事件而调用 xSendEventStructToIPTask() 。 */
while(1)
{
/* 等待以太网MAC中断来表明已经接收到另一个数据包。
任务通知的使用方式与计数信号量类似,用于计数Rx事件,但比信号量更有效。*/
ulTaskNotifyTake( pdFALSE, portMAX_DELAY );
xBytesReceived = enet_rxframe_size_get();/*查看接收了多少数据*/
if( xBytesReceived > 0 )
{
/*分配一个网络缓冲区描述符,该描述符指向一个足够大的缓冲区,以容纳接收到的帧。
由于这是一个简单而不是有效的例子,接收到的数据将仅被复制到这个缓冲区中。 */
pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );
if( pxBufferDescriptor != NULL )//表示以获取网络缓冲区的描述符的指针
{
/*pxBufferDescriptor->pucEthernetBuffer 现在指向一个足够大的以太网缓冲区,用来容纳接收到的数据。
将接收到的数据复制到 pcNetworkBuffer->pucEthernetBuffer中 */
enet_frame_receive( pxBufferDescriptor->pucEthernetBuffer ,xBytesReceived);//将接收到的数据作为函数参数传入的缓冲区中。
pxBufferDescriptor->xDataLength = xBytesReceived;
/*查看是否需要处理接收到的以太网帧中的数据。
注意!最好是在中断服务例程本身中执行此操作,这样就不用为无需处理的数据包解除阻塞任务。 */
if( eConsiderFrameForProcessing( pxBufferDescriptor->pucEthernetBuffer )== eProcessBuffer )
{
xRxEvent.eEventType = eNetworkRxEvent;/* 即将被发送到TCP/IP的事件是Rx事件。*/
xRxEvent.pvData = ( void * ) pxBufferDescriptor; /* pvData用于指向现在引用接收到的数据的网络缓冲区描述符。*/
if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )/* 将数据发送到TCP/IP堆栈中。 */
{
vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );/* 缓冲区无法发送到IP任务,因此必须释放缓冲区。 */
iptraceETHERNET_RX_EVENT_LOST(); /* 调用标准跟踪宏来记录发生的情况。*/
}
else
{
iptraceNETWORK_INTERFACE_RECEIVE(); /* 日志含义成功发送消息到TCP/IP栈。调用标准跟踪宏来记录事件的发生。 */
}
}
else
{
vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );/*可以丢弃以太网帧,但是必须释放以太网缓冲区。*/
}
}
else
{
iptraceETHERNET_RX_EVENT_LOST();/* 事件丢失,因为网络缓冲区不可用。调用标准跟踪宏来记录事件的发生。*/
printf("eth buf error\r\n");
}
}
}
}
void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent )
{
uint32_t ulIPAddress, ulNetMask, ulGatewayAddress, ulDNSServerAddress;
static BaseType_t xTasksAlreadyCreated = pdFALSE;//已创建的任务
char cBuffer[ 16 ];
/*检查这是网络启动,而不是网络关闭。*/
if( eNetworkEvent == eNetworkUp ) //eNetworkUp:如果网络配置完成
{
/* 如果尚未创建使用TCP/IP堆栈的任务,则创建这些任务。 */
if( xTasksAlreadyCreated == pdFALSE ) //注意,此处是协议栈初始化成功,并不代表已经连接上网络
{ /*在这里创建任务*/
ethernet_task_creation();/*创建以太网任务*/
xTasksAlreadyCreated = pdTRUE;
}
else if( xTasksAlreadyCreated == pdTRUE )
{
printf("xTasksAlreadyCreated == pdTRUE\r\n");
}
/* 网络已启动并配置完毕。打印配置,该配置可能是从DHCP服务器获得的。 */
FreeRTOS_GetAddressConfiguration( &ulIPAddress, &ulNetMask, &ulGatewayAddress, &ulDNSServerAddress );
/*将IP地址转换为字符串,然后打印出来。 */
FreeRTOS_inet_ntoa( ulIPAddress, cBuffer );//将ip地址的32位表示转换为点分十进制。
printf( "IP Address: %s\r\n", cBuffer );
/* 将网关的IP地址转换为字符串,然后打印出来。 */
FreeRTOS_inet_ntoa( ulGatewayAddress, cBuffer );
printf( "Gateway IP: %s\r\n", cBuffer );
/* 将子网掩码转换为字符串,然后打印出来。 */
FreeRTOS_inet_ntoa( ulNetMask, cBuffer );
printf( "Subnet Mask: %s\r\n", cBuffer );
}
else if ( eNetworkEvent == eNetworkDown)//网络断开,但并非所有驱动程序都能实现此功能
{
printf("Disconnected from the network\r\n");
}
}
/*日志打印*/
void vLoggingPrintf( const char *pcFormatString, ... )
{
}
/*获取随机数*/
BaseType_t xApplicationGetRandomNumber( uint32_t * pulNumber )
{
*(pulNumber) = uxRand();
return pdTRUE;
}
/*用于 检查接收到的 LLMNR 或 NBNS 名称是否与正在查找的设备相同。*/
/*DNS或LLMNR请求的查询回调*/
BaseType_t xApplicationDNSQueryHook( const char *pcName )
{
return pdPASS;
}
/*为每个RFC生成一个32位的随机TCP初始序列号。*/
uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress, uint16_t usSourcePort,uint32_t ulDestinationAddress, uint16_t usDestinationPort )
{
( void ) ulSourceAddress;
( void ) usSourcePort;
( void ) ulDestinationAddress;
( void ) usDestinationPort;
return uxRand();
}
UBaseType_t uxRand(void)//typedef unsigned long UBaseType_t
{
return (UBaseType_t) trng_random_range_get();
}
- 在main函数中单独用一个任务来初始化随机数生成器和协议栈,初始化完成后删除任务。在start_task中创建别的任务,创建完成后删除任务。
main.c
#include "main.h"
#include "trng.h"
uint8_t ucMACAddress[ 6 ] = { configMAC_ADDR0, configMAC_ADDR1, configMAC_ADDR2, configMAC_ADDR3, configMAC_ADDR4, configMAC_ADDR5 };
uint8_t ucIPAddress[ 4 ] = { configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3 };
uint8_t ucNetMask[ 4 ] = { configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3 };
uint8_t ucGatewayAddress[ 4 ] = { configGATEWAY_ADDR0, configGATEWAY_ADDR1, configGATEWAY_ADDR2, configGATEWAY_ADDR3 };
uint8_t ucDNSServerAddress[ 4 ] = { configDNS_SERVER_ADDR0, configDNS_SERVER_ADDR1, configDNS_SERVER_ADDR2, configDNS_SERVER_ADDR3 };
void start_task(void * pvParameters)
{
printf("hello\r\n");
...
...
...
vTaskDelete(NULL);
}
void enet_inti_task(void * pvParameters)//单独用一个任务来初始协议栈
{
trng_init(); //初始化随机数发生器,在FreeRTOS_IPInit之前
FreeRTOS_IPInit( ucIPAddress, ucNetMask, ucGatewayAddress, ucDNSServerAddress, ucMACAddress ); /* 初始化网络栈*/
vTaskDelete(NULL);
}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
uart0_init(115200);
xTaskCreate(enet_inti_task , "enet_inti_task" , 128, NULL , 10 , NULL);
xTaskCreate(start_task , "start_task" , 128, NULL , 10 , NULL);
vTaskStartScheduler();
while(1)
{
printf("freertos error\r\n");
}
}
main.h
#ifndef _MAIN_H_
#define _MAIN_H_
#include "gd32f4xx.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "FreeRTOS_IP.h"
#include "uart0.h"
int fputc(int ch, FILE *f);
/* 默认MAC地址配置。*/
#define configMAC_ADDR0 2
#define configMAC_ADDR1 0xA
#define configMAC_ADDR2 0xC
#define configMAC_ADDR3 0x5
#define configMAC_ADDR4 0x6
#define configMAC_ADDR5 0x6
/* 默认IP地址配置。*/
#define configIP_ADDR0 192
#define configIP_ADDR1 168
#define configIP_ADDR2 1
#define configIP_ADDR3 11
/* 默认网关IP地址配置。 */
#define configGATEWAY_ADDR0 192
#define configGATEWAY_ADDR1 168
#define configGATEWAY_ADDR2 1
#define configGATEWAY_ADDR3 1
/* 默认的子网掩码配置。*/
#define configNET_MASK0 255
#define configNET_MASK1 255
#define configNET_MASK2 255
#define configNET_MASK3 0
/* 默认DNS服务器配置。
OpenDNS地址为208.67.222.222和208.67.220.220。*/
#define configDNS_SERVER_ADDR0 208
#define configDNS_SERVER_ADDR1 67
#define configDNS_SERVER_ADDR2 222
#define configDNS_SERVER_ADDR3 222
extern uint8_t ucMACAddress[ 6 ] ;
extern uint8_t ucIPAddress[ 4 ];
extern uint8_t ucNetMask[ 4 ];
extern uint8_t ucGatewayAddress[ 4 ];
extern uint8_t ucDNSServerAddress[ 4 ] ;
#endif
- 博主移植成功时,用的源文件是FreeRTOSv202112.00版本的,现暂时还不想去更新写好的工程,所以需要在FreeRTOSConfig.h加入下面宏定义,以便使用FreeRTOSv202212.01版本已弃用的API 。若想使用最新版本的API,请参考freertos官网教程。
#define ipconfigIPv4_BACKWARD_COMPATIBLE 1
- 编译,烧录
- 移植成功。
资源获取
链接:GD32_FreeRTOS_TCP.zip(工程是UTF-8编码)文章来源:https://www.toymoban.com/news/detail-793762.html
补充说明
在局域网中通讯,网段需要相同,工程设置的是192.168.1网段,可以在main.h里修改GD32的IP和网关。
为什么不用DHCP?是因为当时移植的时候,不知道缺了什么地方,设置DHCP无效,后来因为时间问题,也没有去深究。可参照官方最新说明去增加DHCP。文章来源地址https://www.toymoban.com/news/detail-793762.html
到了这里,关于GD32F450+LAN8720A,#FreeRTOS_Plus_TCP 网络协议栈移植教程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!