GD32F450+LAN8720A,#FreeRTOS_Plus_TCP 网络协议栈移植教程

这篇具有很好参考价值的文章主要介绍了GD32F450+LAN8720A,#FreeRTOS_Plus_TCP 网络协议栈移植教程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


(此博文在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。
    gd32f450 ent8720,tcp/ip,单片机
  • 准备好一个能运行FreeRTOS核心库的GD32F450工程;
    gd32f450 ent8720,tcp/ip,单片机
  • 复制FreeRTOS_TCP源文件夹source到工程中,文件夹改名成FreeRTOS_TCP
    gd32f450 ent8720,tcp/ip,单片机
    gd32f450 ent8720,tcp/ip,单片机
  • portable\BufferManagement文件夹里的只保留BufferAllocation_2.c,其它的删除。
    gd32f450 ent8720,tcp/ip,单片机
  • portable\Compiler文件夹里的只保留Keil,其它的删除。
    gd32f450 ent8720,tcp/ip,单片机
  • portable\CompilerNetworkInterface文件夹里的文件全部删除,然后新建NetworkInterface.c文件。这里面是官方给不同的单片机做的接口适配,其中没有适合GD32官方库的,但若是GD32使用ST公司的HAL库开发,倒是可以使用里面给STM32适配的接口文件。
    gd32f450 ent8720,tcp/ip,单片机
  • 移植官网提供demo文件夹里的FreeRTOS_Plus_TCP_Echo_Posix\FreeRTOSIPConfig.h到自己的工程user里,并添加到工程目录中,方便修改里面的参数。
    gd32f450 ent8720,tcp/ip,单片机
    gd32f450 ent8720,tcp/ip,单片机
  • 添加FreeRTOS_TCP里所有的c文件到工程
    gd32f450 ent8720,tcp/ip,单片机
  • 将FreeRTOS_TCP头文件包含在工程中
    gd32f450 ent8720,tcp/ip,单片机
  • 移植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的硬件地址值gd32f450 ent8720,tcp/ip,单片机
  • 以下是需要用户自己要写的东西,在此不再说明,移植成功后,结合官网教程理解
#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
  • 编译,烧录
    gd32f450 ent8720,tcp/ip,单片机
    gd32f450 ent8720,tcp/ip,单片机
  • 移植成功。

资源获取

链接:GD32_FreeRTOS_TCP.zip(工程是UTF-8编码)

补充说明

在局域网中通讯,网段需要相同,工程设置的是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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32踩坑:LAN8720未接网线,上电后再接网线,网络模块无法正常使用

    最近因为做的项目出了BUG,STM32 单片机在未接网线的状态下,上电一段时间后,将网线插入网口后,IP地址ping不通,网络模块无法正常使用,重启一下程序就没问题了。 这次的问题查找全靠网友,把问题扔给百度,然后就能看见一大群网友的评论,都反馈有 LAN8720 在没有接网

    2024年02月03日
    浏览(37)
  • RT-Thread Studio配置LAN8720+LWIP+TCP服务器实现

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 由于项目上需要使用RT-Thread建立TCP服务器实现与客户端的数据交互,查阅了不少资料以及踩了不少坑,这里记录和分享一下实现的过程,希望能帮助到有需要的同学,您的支持是我创作的最大动力,谢谢

    2024年01月25日
    浏览(47)
  • Linux 网络驱动实验(PHY芯片LAN8720)

    和自己工作的IOT项目类似。项目里使用AWS的框架,分为linux系统和freertos系统。Freetros里需使用AT指令驱动4g,不同的模组驱动有差异,需自己实现模组AT驱动然后注册到AWS框架里面。 网络驱动是linux 里面驱动三巨头之一,linux 下的网络功能非常强大,嵌入式linux 中也常常用到网

    2024年02月06日
    浏览(46)
  • Linux 有线网络驱动实验(PHY芯片LAN8720)

    和自己工作的IOT项目类似。项目里使用AWS的框架,分为linux系统和freertos系统。Freetros里需使用AT指令驱动4g,不同的模组驱动有差异,需自己实现模组AT驱动然后注册到AWS框架里面。 网络驱动是linux 里面驱动三巨头之一,linux 下的网络功能非常强大,嵌入式linux 中也常常用到网

    2024年02月04日
    浏览(48)
  • STM32H743+LWIP+LAN8720+STM32cubeMX6.8.0

    最详细 一步到位 带程序 无偿分享   经历一个星期的痛苦,程序终于调试ping通了 网上的H743的博客我一个一个试一个一个试,到最后没有一个正常ping通,写本篇博客的目的就是为了后续再进行调试的人不在踩坑,听懂掌声,哈哈哈。 废话就不多说了,正文开始: 1、打开S

    2024年02月07日
    浏览(43)
  • STM32的以太网外设+PHY(LAN8720)使用详解(2):硬件设计

    在LAN8720上电或复位时会读取一些特定引脚的电平,根据电平来进行硬件配置。LAN8720的引脚分布如下: 注意,LAN8720有些引脚内部自带上/下拉,定义如下: LAN8720的PHYAD[0]用来配置PHY地址的bit0,当接入了多个PHY时可以用来区分不同的PHY。该引脚自带内部下拉,同时我们也只用到

    2024年04月14日
    浏览(63)
  • STM32的以太网外设+PHY(LAN8720)使用详解(5):MAC及DMA配置

    stm32的ETH外设挂载在AHB1总线上,位于RCC_AHB1ENR的bit25-bit27: 相关语句如下: 直接调用ETH_DeInit函数来复位ETH外设 上述语句操作的寄存器如下: 首先设置位25为1复位以太网MAC(复位MAC寄存器到默认值),然后设置为0取消复位。 首先调用ETH_SoftwareReset函数复位MAC的DMA 上述语句操

    2024年02月03日
    浏览(45)
  • STM32的以太网外设+PHY(LAN8720)使用详解(3):PHY寄存器详解

    前面介绍到,站管理接口(SMI)允许应用程序通过2线时钟和数据线访问任意PHY寄存器,同时该接口支持访问最多32个PHY,也就是说PHY地址共有5位。 应用程序可以从32个PHY中选择一个PHY,然后从任意PHY包含的32个寄存器中选择一个寄存器,发送控制数据或接收状态信息。任意给

    2024年02月03日
    浏览(50)
  • STM32的以太网外设+PHY(LAN8720)使用详解(1):ETH和PHY介绍

    STM32F4系列MCU内部集成了一个以太网外设(ETH),可以通过介质独立接口(MII)或简化介质独立接口(RMII)和外部PHY(如LAN8720)相连实现MCU的联网功能。 STM32F4系列的以太网外设(ETH)框图如下: 以太网外设框图中包含了MII、RMII、SMI三种接口,它们的详细介绍如下: 1.2.1 介

    2024年01月23日
    浏览(53)
  • STM32的以太网外设+PHY(LAN8720)使用详解(6):以太网数据接收及发送

    1.1.1 检查是否接收到一帧完整报文 使用轮询的方式接收以太网数据是一种简单但是效率低下的方法,为了保证及时处理以太网数据我们需要在主循环内高频轮询是否接收到了以太网数据。轮询的函数为ETH_CheckFrameReceived,内容如下: 当以太网帧大于我们设置的DMA描述符buffer大

    2024年01月23日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包