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日
    浏览(35)
  • RT-Thread Studio配置LAN8720+LWIP+TCP服务器实现

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

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

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

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

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

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

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

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

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

    2024年04月14日
    浏览(62)
  • 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日
    浏览(50)
  • STM32的以太网外设+PHY(LAN8720)使用详解(3):PHY寄存器详解

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

    2024年02月03日
    浏览(47)
  • 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日
    浏览(42)
  • 【STM32】HAL库-以太网外设-LAN8720A-LWIP-无操作系统

    KEIL:MDK_ARM_5.27 MCU:STM32F429IGT6 PHY_IC:LAN8720A LWIP:LWIP2.1.2 STM32CUBEMX:6.6.1 HAL:V1.27.1 LAN8720A使用RMII接口与STM32的ETH外设进行数据通信 STM32使用SMI接口读/写LAN8720A的寄存器 LAN8720A由外部25MHz晶振提供时钟,LED2/NINTSEL引脚配置为下拉,故PHY(LAN8720A)提供50MHz时钟给RMII的NINT/REFCLKO(此时引脚

    2024年02月08日
    浏览(47)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包