4109|13

36

帖子

1

TA的资源

一粒金砂(中级)

HC32F4A0_FreeRTOS_LwIP移植 [复制链接]

 

     HC32F4A0-FreeRTOS移植 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)

        上篇已经实现了FreeRTOS的移植,经过2天的努力硬肝,实现了LwIP协议栈带FreeRTOS实时系统的移植。板载网络芯片为RTL8021F,可以与单片机以MII或RMII的方式连接。这里,我选择的是RMII的方式,在实际项目中我是用的DP83848,也是使用RMII的方式,RMII方式使用的引脚更少,我们可以留出更多的管脚来控制其他设备。工程代码在文末哦。以下是RMII接口连接图:

 

        以下是开发板网络接口部分原理图,板子兼容了RMII与MII接口,可以通过拨码开关选择接口方式,在编程时选择对应的方式即可。

        移植LwIP协议栈有以下几个重要环节,网卡IO初始化、HC32F4A0内置以太网控制器初始化、网络中断回调函数编写、LwIP底层输入输出函数编写、sys_arch.c接口文件编写。LwIP文件较多,添加到编译器中截图不全,工程在最后上传,可以下载查看。以下是添加的LwIP文件和相关头文件路径。

 

        下面将根据以上几个步骤实现LwIP协议栈移植。

        网卡IO初始化(Ethernet_GpioInit),其中除了RMII接口用到的IO口外,还有网卡芯片的复位IO口,使用的是TCA9539端口2的PIN3。RMII接口用到的IO口为ETH_SMI_MDIO-->PA2、ETH_SMI_MDC--->PC1、ETH_RMII_TX_EN-->PG11、ETH_RMII_TXD0-->PG13、ETH_RMII_TXD1-->PG14、ETH_RMII_REF_CLK-->PA1、ETH_RMII_CRS_DV-->PA7、ETH_RMII_RXD0-->PC4、ETH_RMII_RXD1-->PC5、ETH_RMII_RX_ER-->PI10。在这里,我们只需要设置复用功能就可以了,仅针对HC32F4A0这款芯片,别的芯片可能还需要设置IO口的上下拉、速度等。以下是网口IO初始化代码:

/**************************************************************
  * [url=home.php?mod=space&uid=32621]@name[/url] Ethernet_GpioInit
  * [url=home.php?mod=space&uid=159083]@brief[/url] * @param   None
  * @retval
  * [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
  * [url=home.php?mod=space&uid=34591]@data[/url] 2023-03-27
 **************************************************************/
static void Ethernet_GpioInit( void )
{
    /* ---解保护相关外设--- */
    LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO );
    /* ---网卡芯片复位--- */
    TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN3, TCA9539_DIR_OUT );
    TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN3, TCA9539_PIN_RESET );
    delay_ms( PHY_HW_RST_DELAY );
    TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN3, TCA9539_PIN_SET );
    delay_ms( PHY_HW_RST_DELAY );

    /* Configure MII/RMII selection IO for ETH */
#ifdef ETH_INTERFACE_RMII
    /* Ethernet RMII pins configuration */
    /*
        ETH_SMI_MDIO ----------------> PA2
        ETH_SMI_MDC -----------------> PC1
        ETH_RMII_TX_EN --------------> PG11
        ETH_RMII_TXD0 ---------------> PG13
        ETH_RMII_TXD1 ---------------> PG14
        ETH_RMII_REF_CLK ------------> PA1
        ETH_RMII_CRS_DV -------------> PA7
        ETH_RMII_RXD0 ---------------> PC4
        ETH_RMII_RXD1 ---------------> PC5
        ETH_RMII_RX_ER --------------> PI10
    */
    /* Configure PA1, PA2 and PA7 */
    GPIO_SetFunc( GPIO_PORT_A, ( GPIO_PIN_01 | GPIO_PIN_02 | GPIO_PIN_07 ), GPIO_FUNC_11 );
    /* Configure PC1, PC4 and PC5 */
    GPIO_SetFunc( GPIO_PORT_C, ( GPIO_PIN_01 | GPIO_PIN_04 | GPIO_PIN_05 ), GPIO_FUNC_11 );
    /* Configure PG11, PG13 and PG14 */
    GPIO_SetFunc( GPIO_PORT_G, ( GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14 ), GPIO_FUNC_11 );
    /* Configure PI10 */
    GPIO_SetFunc( GPIO_PORT_I, GPIO_PIN_10, GPIO_FUNC_11 );
#else
    /* Ethernet MII pins configuration */
    /*
        ETH_SMI_MDIO ----------------> PA2
        ETH_SMI_MDC -----------------> PC1
        ETH_MII_TX_CLK --------------> PB6
        ETH_MII_TX_EN ---------------> PG11
        ETH_MII_TXD0 ----------------> PG13
        ETH_MII_TXD1 ----------------> PG14
        ETH_MII_TXD2 ----------------> PB9
        ETH_MII_TXD3 ----------------> PB8
        ETH_MII_RX_CLK --------------> PA1
        ETH_MII_RX_DV ---------------> PA7
        ETH_MII_RXD0 ----------------> PC4
        ETH_MII_RXD1 ----------------> PC5
        ETH_MII_RXD2 ----------------> PB0
        ETH_MII_RXD3 ----------------> PB1
        ETH_MII_RX_ER ---------------> PI10
        ETH_MII_CRS -----------------> PH2
        ETH_MII_COL -----------------> PH3
    */
    /* Configure PA1, PA2 and PA7 */
    GPIO_SetFunc( GPIO_PORT_A, ( GPIO_PIN_01 | GPIO_PIN_02 | GPIO_PIN_07 ), GPIO_FUNC_11 );
    /* Configure PB0, PB1, PB6, PB8 and PB9 */
    GPIO_SetFunc( GPIO_PORT_B, ( GPIO_PIN_00 | GPIO_PIN_01 | GPIO_PIN_06 | GPIO_PIN_08 | GPIO_PIN_09 ), GPIO_FUNC_11 );
    /* Configure PC1, PC4 and PC5 */
    GPIO_SetFunc( GPIO_PORT_C, ( GPIO_PIN_01 | GPIO_PIN_04 | GPIO_PIN_05 ), GPIO_FUNC_11 );
    /* Configure PG11, PG13 and PG14 */
    GPIO_SetFunc( GPIO_PORT_G, ( GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14 ), GPIO_FUNC_11 );
    /* Configure PH2, PH3 */
    GPIO_SetFunc( GPIO_PORT_H, ( GPIO_PIN_02 | GPIO_PIN_03 ), GPIO_FUNC_11 );
    /* Configure PI10 */
    GPIO_SetFunc( GPIO_PORT_I, GPIO_PIN_10, GPIO_FUNC_11 );
#endif

    /* ---解保护相关外设--- */
    LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO );
}

 

        HC32F4A0内置以太网控制器初始化(low_level_init),在这里主要涉及HC32F4A0的内置以太网控制器的设置、DMA的设置和网卡芯片的相关寄存器设置。在这个环节,除以上内容外,还创建了信号量,为系统和以太网数据接收提供桥梁,这个信号量可以是FreeRTOS的二值信号量,也可以是计数信号量。接着创建了网卡数据接收任务,将接收到的数据传给LwIP协议栈。以下是以太网控制器初始化的代码:

/**************************************************************
  * @Name    low_level_init
  * @brief   In this function, the hardware should be initialized.
  * @param   netif: [输入/出] 
  * @retval  LL_OK: Initialize success 
             LL_ERR: Initialize failed
  * @author  Zachary
  * @Data    2023-03-27
 **************************************************************/
static int32_t low_level_init( struct netif *netif )
{
    int32_t i32Ret = LL_ERR;
    stc_eth_init_t stcEthInit;

    LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO );		/* ---解保护相关外设--- */

    /* ---打开网卡时钟--- */
    FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_ETHMAC, ENABLE );
    /* ---初始化网卡GPIO--- */
    Ethernet_GpioInit();
    /* ---网卡缺省设置--- */
    ETH_DeInit();
    /* ---相关寄存器缺省设置--- */
    ETH_CommStructInit( &EthHandle.stcCommInit );
    ETH_StructInit( &stcEthInit );
    
    EthHandle.stcCommInit.u16PhyAddr        = ETH_PHY_ADDR;
    EthHandle.stcCommInit.u32Interface      = ETH_MAC_IF_RMII;
    EthHandle.stcCommInit.u32ChecksumMode   = ETH_MAC_CHECKSUM_MD_SW;
    EthHandle.stcCommInit.u16AutoNego       = ETH_AUTO_NEGO_ENABLE;
    EthHandle.stcCommInit.u32Speed          = ETH_MAC_SPEED_100M;
    EthHandle.stcCommInit.u32DuplexMode     = ETH_MAC_DUPLEX_MD_FULL;
    EthHandle.stcCommInit.u32ReceiveMode    = ETH_RX_MD_INT;
    
    if( LL_OK == ETH_Init( &EthHandle, &stcEthInit ) )
    {
        netif->flags |= NETIF_FLAG_LINK_UP;
    }
    

    /* Initialize Tx Descriptors list: Chain Mode */
    ETH_DMA_TxDescListInit( &EthHandle, EthDmaTxDscrTab, &EthTxBuff[0][0], ETH_TX_BUF_NUM );
    /* Initialize Rx Descriptors list: Chain Mode  */
    ETH_DMA_RxDescListInit( &EthHandle, EthDmaRxDscrTab, &EthRxBuff[0][0], ETH_RX_BUF_NUM );

#if LWIP_ARP
    netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#else
    netif->flags |= NETIF_FLAG_BROADCAST;
#endif /* LWIP_ARP */

    /* 设置MAC地址长度 */
    netif->hwaddr_len = 6U;
    /* 设置MAC地址 地址已经初始化为默认2.0.0.0.0.0 */
    netif->hwaddr[0] = Net_Info.MacAddr[0];
    netif->hwaddr[1] = Net_Info.MacAddr[1];
    netif->hwaddr[2] = Net_Info.MacAddr[2];
    netif->hwaddr[3] = Net_Info.MacAddr[3];
    netif->hwaddr[4] = Net_Info.MacAddr[4];
    netif->hwaddr[5] = Net_Info.MacAddr[5];
    /* maximum transfer unit */
    netif->mtu = 1500U;

    taskENTER_CRITICAL();

    /* ---创建二值信号量或计数信号量--- */
    if ( g_rx_semaphore == NULL )
    {
        //vSemaphoreCreateBinary( g_rx_semaphore );
        //xSemaphoreTake( g_rx_semaphore, 0 );
        g_rx_semaphore = xSemaphoreCreateCounting( 40, 0 );
    }

    /* 创建网卡任务 */
    xTaskCreate( ethernetif_input, "ETHERNETIF_INPUT", ETHERNETIF_INPUT_TASK_STACK_SIZE, netif,
                 ETHERNETIF_INPUT_TASK_PRIO, &xTaskHandleEthernetInput );

    taskEXIT_CRITICAL();

    /* ---使能网卡MAC和DMA传输--- */
    ETH_Start();
    
    /* ---网卡接收中断设置--- */
    Set_EthInterrupt();
    
    LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO );		/* ---解保护相关外设--- */

    return i32Ret;
}

 

        网络中断回调函数编写(Set_EthInterrupt),使用中断方式接收数据,是效率最高的方式。因此,网络数据的接收,我们不适用周期性查询的方式,我们使用以太网中断来实现。代码的实现很简单,跟串口中断的实现方式相同,在这了需要注意的是,根据FreeRTOS可管理的最高优先级来设置以太网中断的优先级,不可高于FreeRTOS可管理的最高优先级。以下是以太网中断设置函数代码和中断回调函数:

/**************************************************************
  * @Name    ETH_RxCpltCallback
  * @brief   
  * @param   None
  * @retval
  * @author  Zachary
  * @Data    2023-03-27
 **************************************************************/
static void ETH_RxCpltCallback( void )
{
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    
    if( SET == ETH_DMA_GetStatus( ETH_DMA_FLAG_RIS ) )
    {
        xSemaphoreGiveFromISR( g_rx_semaphore, &xHigherPriorityTaskWoken );
    }
    
    ETH_DMA_ClearStatus( ETH_DMA_FLAG_RIS );
    ETH_DMA_ClearStatus( ETH_DMA_FLAG_NIS );
    
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

/**************************************************************
  * @Name    Set_EthInterrupt
  * @brief   
  * @param   None
  * @retval
  * @author  Zachary
  * @Data    2023-03-27
 **************************************************************/
static void Set_EthInterrupt( void )
{
    stc_irq_signin_config_t stcIrqSigninConfig;
    
    stcIrqSigninConfig.enIRQn = INT005_IRQn;						/* ---中断向量号--- */
    stcIrqSigninConfig.enIntSrc = INT_SRC_ETH_GLB_INT;				/* ---中断类型--- */
    stcIrqSigninConfig.pfnCallback = Ð_RxCpltCallback;		    /* ---中断回调函数--- */
    INTC_IrqSignIn( &stcIrqSigninConfig );
    NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn );				/* ---清中断--- */
    NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 5UL );				/* ---设置中断优先级--- */
    NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
}

 

        LwIP底层输入输出函数编写(low_level_input、low_level_output),首先是与ST的官方函数基本一致,不同点在于变量的命名、函数的命名不同,这两个函数的实现,不用我们自己写,基本上带以太网控制器的都有配置代码,也可根据ST的改,基本上都是一模一样的。这两个函数是主要作用是底层数据的搬运。以下是完整的代码:


/**************************************************************
  * @Name    low_level_output
  * @brief   
  * @param   netif: [输入/出] 
**			 p: [输入/出] 
  * @retval
  * @author  Zachary
  * @Data    2023-03-27
 **************************************************************/
static err_t low_level_output( struct netif *netif, struct pbuf *p )
{
    int32_t i32Ret;
    struct pbuf *q;
    uint8_t *txBuffer;
    __IO stc_eth_dma_desc_t *DmaTxDesc;
    uint32_t byteCnt;
    uint32_t frameLength = 0UL;
    uint32_t bufferOffset;
    uint32_t payloadOffset;

    DmaTxDesc = EthHandle.stcTxDesc;
    txBuffer = ( uint8_t * )( ( EthHandle.stcTxDesc )->u32Buf1Addr );
    bufferOffset = 0UL;
    /* Copy frame from pbufs to driver buffers */
    for ( q = p; q != NULL; q = q->next )
    {
        /* If this buffer isn't available, goto error */
        if ( 0UL != ( DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN ) )
        {
            i32Ret = LL_ERR;
            goto error;
        }

        /* Get bytes in current buffer */
        byteCnt = q->len;
        payloadOffset = 0UL;
        /* Check if the length of data to copy is bigger than Tx buffer size */
        while ( ( byteCnt + bufferOffset ) > ETH_TX_BUF_SIZE )
        {
            /* Copy data to Tx buffer*/
            memcpy( ( uint8_t * ) & ( txBuffer[bufferOffset] ), ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), ( ETH_TX_BUF_SIZE - bufferOffset ) );
            /* Point to next descriptor */
            DmaTxDesc = ( stc_eth_dma_desc_t * )( DmaTxDesc->u32Buf2NextDescAddr );
            /* Check if the buffer is available */
            if ( 0UL != ( DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN ) )
            {
                i32Ret = LL_ERR;
                goto error;
            }

            txBuffer = ( uint8_t * )( DmaTxDesc->u32Buf1Addr );
            byteCnt = byteCnt - ( ETH_TX_BUF_SIZE - bufferOffset );
            payloadOffset = payloadOffset + ( ETH_TX_BUF_SIZE - bufferOffset );
            frameLength = frameLength + ( ETH_TX_BUF_SIZE - bufferOffset );
            bufferOffset = 0UL;
        }
        /* Copy the remaining bytes */
        memcpy( ( uint8_t * ) & ( txBuffer[bufferOffset] ), ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), byteCnt );
        bufferOffset = bufferOffset + byteCnt;
        frameLength = frameLength + byteCnt;
    }
    /* Prepare transmit descriptors to give to DMA */
    ETH_DMA_SetTransFrame( &EthHandle, frameLength );
    i32Ret = LL_OK;

error:
    /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
    if ( RESET != ETH_DMA_GetStatus( ETH_DMA_FLAG_UNS ) )
    {
        /* Clear DMA UNS flag */
        ETH_DMA_ClearStatus( ETH_DMA_FLAG_UNS );
        /* Resume DMA transmission */
        WRITE_REG32( CM_ETH->DMA_TXPOLLR, 0UL );
    }

    return i32Ret;
}


/**************************************************************
  * @Name    low_level_input
  * @brief   
  * @param   netif: [输入/出] 
  * @retval
  * @author  Zachary
  * @Data    2023-03-27
 **************************************************************/
static struct pbuf * low_level_input( struct netif *netif )
{
    struct pbuf *p = NULL;
    struct pbuf *q;
    uint32_t len;
    uint8_t *rxBuffer;
    __IO stc_eth_dma_desc_t *DmaRxDesc;
    uint32_t byteCnt;
    uint32_t bufferOffset;
    uint32_t payloadOffset;
    uint32_t i;

    /* Get received frame */
    if ( LL_OK != ETH_DMA_GetReceiveFrame( &EthHandle ) )
    {
        return NULL;
    }

    /* Obtain the size of the packet */
    len = ( EthHandle.stcRxFrame ).u32Len;
    rxBuffer = ( uint8_t * )( EthHandle.stcRxFrame ).u32Buf;
    if ( len > 0UL )
    {
        /* Allocate a pbuf chain of pbufs from the buffer */
        //p = ( struct pbuf * )malloc( sizeof( struct pbuf ) + len );
        p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
        if ( NULL != p ) {
            p->next = NULL;
            p->payload = &( ( uint8_t * )p )[sizeof( struct pbuf )];
            p->len = len;
            ( void )memset( p->payload, 0, p->len );
        }
    }
    
    if ( p != NULL )
    {
        DmaRxDesc = ( EthHandle.stcRxFrame ).pstcFSDesc;
        bufferOffset = 0UL;
        for ( q = p; q != NULL; q = q->next )
        {
            byteCnt = q->len;
            payloadOffset = 0UL;

            /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */
            while ( ( byteCnt + bufferOffset ) > ETH_RX_BUF_SIZE )
            {
                /* Copy data to pbuf */
                memcpy( ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), ( uint8_t * ) & ( rxBuffer[bufferOffset] ), ( ETH_RX_BUF_SIZE - bufferOffset ) );
                /* Point to next descriptor */
                DmaRxDesc = ( stc_eth_dma_desc_t * )( DmaRxDesc->u32Buf2NextDescAddr );
                rxBuffer = ( uint8_t * )( DmaRxDesc->u32Buf1Addr );
                byteCnt = byteCnt - ( ETH_RX_BUF_SIZE - bufferOffset );
                payloadOffset = payloadOffset + ( ETH_RX_BUF_SIZE - bufferOffset );
                bufferOffset = 0UL;
            }

            /* Copy remaining data in pbuf */
            memcpy( ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), ( uint8_t * ) & ( rxBuffer[bufferOffset] ), byteCnt );
            bufferOffset = bufferOffset + byteCnt;
        }
    }
    /* Release descriptors to DMA */
    DmaRxDesc = ( EthHandle.stcRxFrame ).pstcFSDesc;
    for ( i = 0UL; i < ( EthHandle.stcRxFrame ).u32SegCount; i++ )
    {
        DmaRxDesc->u32ControlStatus |= ETH_DMA_RXDESC_OWN;
        DmaRxDesc = ( stc_eth_dma_desc_t * )( DmaRxDesc->u32Buf2NextDescAddr );
    }
    /* Clear Segment_Count */
    ( EthHandle.stcRxFrame ).u32SegCount = 0UL;

    /* When Rx Buffer unavailable flag is set, clear it and resume reception */
    if ( RESET != ETH_DMA_GetStatus( ETH_DMA_FLAG_RUS ) )
    {
        /* Clear DMA RUS flag */
        ETH_DMA_ClearStatus( ETH_DMA_FLAG_RUS );
        /* Resume DMA reception */
        WRITE_REG32( CM_ETH->DMA_RXPOLLR, 0UL );
    }

    return p;
}

 

        以上几个步骤所对应的函数,我都放在ethernetif.c中,方便移植,可以理解为专门的网卡驱动.c文件。

 

        sys_arch.c,这个文件是LwIP协议栈与FreeRTOS实时系统的沟通文件,主要有LwIP时基、线程保护、信号量互斥量的创建、释放、任务的创建等。这个文件是LwIP给出的接口文件,我们要根据不同的操作系统,来改写这个文件中的对应函数。代码如下:

#include "lwip/debug.h"

#include <lwip/opt.h>
#include <lwip/arch.h>

#include "lwip/tcpip.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/sio.h"
#include "ethernetif.h"

#if !NO_SYS
#include "sys_arch.h"
#endif
#include <lwip/stats.h>
#include <lwip/debug.h>
#include <lwip/sys.h>
#include "lwip/dhcp.h"
#include <string.h>

int errno;


u32_t lwip_sys_now;

struct sys_timeouts
{
    struct sys_timeo *next;
};

struct timeoutlist
{
    struct sys_timeouts timeouts;
    xTaskHandle pid;
};

#define SYS_THREAD_MAX 4

static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX];

static u16_t s_nextthread = 0;

u32_t
sys_jiffies(void)
{
    lwip_sys_now = xTaskGetTickCount();
    return lwip_sys_now;
}

u32_t
sys_now(void)
{
    lwip_sys_now = xTaskGetTickCount();
    return lwip_sys_now;
}

void
sys_init(void)
{
    int i;
    // Initialize the the per-thread sys_timeouts structures
    // make sure there are no valid pids in the list
    for(i = 0; i < SYS_THREAD_MAX; i++)
    {
        s_timeoutlist[i].pid = 0;
        s_timeoutlist[i].timeouts.next = NULL;
    }
    // keep track of how many threads have been created
    s_nextthread = 0;
}

struct sys_timeouts *sys_arch_timeouts(void)
{
    int i;
    xTaskHandle pid;
    struct timeoutlist *tl;
    pid = xTaskGetCurrentTaskHandle( );
    for(i = 0; i < s_nextthread; i++)
    {
        tl = &(s_timeoutlist[i]);
        if(tl->pid == pid)
        {
            return &(tl->timeouts);
        }
    }
    return NULL;
}

sys_prot_t sys_arch_protect(void)
{
    vPortEnterCritical();
    return 1;
}

void sys_arch_unprotect(sys_prot_t pval)
{
    ( void ) pval;
    vPortExitCritical();
}

#if !NO_SYS

//test_sys_arch_waiting_fn the_waiting_fn;

//void
//test_sys_arch_wait_callback(test_sys_arch_waiting_fn waiting_fn)
//{
//  the_waiting_fn = waiting_fn;
//}

err_t
sys_sem_new(sys_sem_t *sem, u8_t count)
{
    /* 创建 sem */
    if(count <= 1)
    {
        *sem = xSemaphoreCreateBinary();
        if(count == 1)
        {
            sys_sem_signal(sem);
        }
    }
    else
        *sem = xSemaphoreCreateCounting(count,count);

#if SYS_STATS
    ++lwip_stats.sys.sem.used;
    if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used)
    {
        lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
    }
#endif /* SYS_STATS */

    if(*sem != SYS_SEM_NULL)
        return ERR_OK;
    else
    {
#if SYS_STATS
        ++lwip_stats.sys.sem.err;
#endif /* SYS_STATS */
        printf("[sys_arch]:new sem fail!\n");
        return ERR_MEM;
    }
}

void
sys_sem_free(sys_sem_t *sem)
{
#if SYS_STATS
    --lwip_stats.sys.sem.used;
#endif /* SYS_STATS */
    /* 删除 sem */
    vSemaphoreDelete(*sem);
    *sem = SYS_SEM_NULL;
}


int sys_sem_valid(sys_sem_t *sem)
{
    return (*sem != SYS_SEM_NULL);
}


void
sys_sem_set_invalid(sys_sem_t *sem)
{
    *sem = SYS_SEM_NULL;
}

/*
 如果timeout参数不为零,则返回值为
 等待信号量所花费的毫秒数。如果
 信号量未在指定时间内发出信号,返回值为
 SYS_ARCH_TIMEOUT。如果线程不必等待信号量
 该函数返回零。 */
u32_t
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
    u32_t wait_tick = 0;
    u32_t start_tick = 0 ;

    //看看信号量是否有效
    if(*sem == SYS_SEM_NULL)
        return SYS_ARCH_TIMEOUT;

    //首先获取开始等待信号量的时钟节拍
    start_tick = xTaskGetTickCount();

    //timeout != 0,需要将ms换成系统的时钟节拍
    if(timeout != 0)
    {
        //将ms转换成时钟节拍
        wait_tick = timeout / portTICK_PERIOD_MS;
        if (wait_tick == 0)
            wait_tick = 1;
    }
    else
        wait_tick = portMAX_DELAY;  //一直阻塞

    //等待成功,计算等待的时间,否则就表示等待超时
    if(xSemaphoreTake(*sem, wait_tick) == pdTRUE)
        return ((xTaskGetTickCount()-start_tick)*portTICK_RATE_MS);
    else
        return SYS_ARCH_TIMEOUT;
}

void
sys_sem_signal(sys_sem_t *sem)
{
    if(xSemaphoreGive( *sem ) != pdTRUE)
        printf("[sys_arch]:sem signal fail!\n");
}

err_t
sys_mutex_new(sys_mutex_t *mutex)
{
    /* 创建 sem */
    *mutex = xSemaphoreCreateMutex();
    if(*mutex != SYS_MRTEX_NULL)
        return ERR_OK;
    else
    {
        printf("[sys_arch]:new mutex fail!\n");
        return ERR_MEM;
    }
}

void
sys_mutex_free(sys_mutex_t *mutex)
{
    vSemaphoreDelete(*mutex);
}

void
sys_mutex_set_invalid(sys_mutex_t *mutex)
{
    *mutex = SYS_MRTEX_NULL;
}

void
sys_mutex_lock(sys_mutex_t *mutex)
{
    xSemaphoreTake(*mutex,/* 互斥量句柄 */
                   portMAX_DELAY); /* 等待时间 */
}

void
sys_mutex_unlock(sys_mutex_t *mutex)
{
    xSemaphoreGive( *mutex );//给出互斥量
}


sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
{
    sys_thread_t handle = NULL;
    BaseType_t xReturn = pdPASS;
    /* 创建MidPriority_Task任务 */
    xReturn = xTaskCreate((TaskFunction_t )function,  /* 任务入口函数 */
                          (const char*    )name,/* 任务名字 */
                          (uint16_t       )stacksize,  /* 任务栈大小 */
                          (void*          )arg,/* 任务入口函数参数 */
                          (UBaseType_t    )prio, /* 任务的优先级 */
                          (TaskHandle_t*  )&handle);/* 任务控制块指针 */
    if(xReturn != pdPASS)
    {
        printf("[sys_arch]:create task fail!err:%#lx\n",xReturn);
        return NULL;
    }
    return handle;
}

err_t
sys_mbox_new(sys_mbox_t *mbox, int size)
{
    /* 创建Test_Queue */
    *mbox = xQueueCreate((UBaseType_t ) size,/* 消息队列的长度 */
                         (UBaseType_t ) sizeof(void *));/* 消息的大小 */
#if SYS_STATS
    ++lwip_stats.sys.mbox.used;
    if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used)
    {
        lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
    }
#endif /* SYS_STATS */
    if(NULL == *mbox)
        return ERR_MEM;

    return ERR_OK;
}

void
sys_mbox_free(sys_mbox_t *mbox)
{
    if( uxQueueMessagesWaiting( *mbox ) )
    {
        /* Line for breakpoint.  Should never break here! */
        portNOP();
#if SYS_STATS
        lwip_stats.sys.mbox.err++;
#endif /* SYS_STATS */

        // TODO notify the user of failure.
    }

    vQueueDelete(*mbox);

#if SYS_STATS
    --lwip_stats.sys.mbox.used;
#endif /* SYS_STATS */
}

int sys_mbox_valid(sys_mbox_t *mbox)
{
    if (*mbox == SYS_MBOX_NULL)
        return 0;
    else
        return 1;
}

void
sys_mbox_set_invalid(sys_mbox_t *mbox)
{
    *mbox = SYS_MBOX_NULL;
}

void
sys_mbox_post(sys_mbox_t *q, void *msg)
{
    while(xQueueSend( *q, /* 消息队列的句柄 */
                      &msg,/* 发送的消息内容 */
                      portMAX_DELAY) != pdTRUE); /* 等待时间 */
}

err_t
sys_mbox_trypost(sys_mbox_t *q, void *msg)
{
    if(xQueueSend(*q,&msg,0) == pdPASS)
        return ERR_OK;
    else
        return ERR_MEM;
}

err_t
sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg)
{
    return sys_mbox_trypost(q, msg);
}

u32_t
sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout)
{
    void *dummyptr;
    u32_t wait_tick = 0;
    u32_t start_tick = 0 ;

    if ( msg == NULL )  //看看存储消息的地方是否有效
        msg = &dummyptr;

    //首先获取开始等待信号量的时钟节拍
    start_tick = sys_now();

    //timeout != 0,需要将ms换成系统的时钟节拍
    if(timeout != 0)
    {
        //将ms转换成时钟节拍
        wait_tick = timeout / portTICK_PERIOD_MS;
        if (wait_tick == 0)
            wait_tick = 1;
    }
    //一直阻塞
    else
        wait_tick = portMAX_DELAY;

    //等待成功,计算等待的时间,否则就表示等待超时
    if(xQueueReceive(*q,&(*msg), wait_tick) == pdTRUE)
        return ((sys_now() - start_tick)*portTICK_PERIOD_MS);
    else
    {
        *msg = NULL;
        return SYS_ARCH_TIMEOUT;
    }
}

u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg)
{
    void *dummyptr;
    if ( msg == NULL )
        msg = &dummyptr;

    //等待成功,计算等待的时间
    if(xQueueReceive(*q,&(*msg), 0) == pdTRUE)
        return ERR_OK;
    else
        return SYS_MBOX_EMPTY;
}

#if LWIP_NETCONN_SEM_PER_THREAD
#error LWIP_NETCONN_SEM_PER_THREAD==1 not supported
#endif /* LWIP_NETCONN_SEM_PER_THREAD */

#endif /* !NO_SYS */

 

        以上都是底层移植步骤,接着是LwIP的初始化与网卡的挂载,都是调用LwIP的相关API就可以了。为方便验证,我这里使用的是静态IP,没有做DHCP。以下是代码:

/**
  ************************************* Copyright ******************************
  *
  *                 (C) Copyright 2022,Zachary ,HPU, China.
  *                            All Rights Reserved
  *
  * FileName   : netconf.c
  * Version    : v1.0
  * Author     : Zachary
  * Date       : 2022-02-17
  * Description:
  ******************************************************************************
 */
/* Includes --------------------------------------------------------------*/
#include <stdio.h>
#include "bsp_NT35510.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/dhcp.h"
#include "ethernetif.h"
#include "app/netconf.h"
#include "lwip/tcpip.h"
#include "lwip/errno.h"
#include "os_includes.h"

/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
struct netif g_mynetif;
struct Network_Info Net_Info;
__IO uint8_t NetCable_STATUS;
/* --- Functions ---------------------------------------------------------*/
static void ethernetif_notify_conn_changed( struct netif *s_netif );
/*------------------------------------------------------------------------*/


/**************************************************************
  * @Name    LWIP_Init                                    
  * @brief   初始化网卡&LwIP协议栈
  * @param   None
  * @retval
  * @author  Zachary
  * @Data    2022-05-02
 **************************************************************/
LwIP_StatusTypeDef LWIP_Init( void )
{
    char DispBuf[50];
    struct netif *Netif_Init_Flag;
    ip_addr_t ipaddr;
    ip_addr_t netmask;
    ip_addr_t gw;

    tcpip_init( NULL, NULL );

    LwIP_Set_Default_IP_Info( &Net_Info );

#if LWIP_DHCP
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;
#else
    IP4_ADDR( &ipaddr, Net_Info.LocalIP[0], Net_Info.LocalIP[1], Net_Info.LocalIP[2], Net_Info.LocalIP[3] );
    IP4_ADDR( &netmask, Net_Info.NetMask[0], Net_Info.NetMask[1], Net_Info.NetMask[2], Net_Info.NetMask[3] );
    IP4_ADDR( &gw, Net_Info.GateWay[0], Net_Info.GateWay[1], Net_Info.GateWay[2], Net_Info.GateWay[3] );
    
    LCD_ShowString( 10, 110, lcddev.width, lcddev.height, 24, ( uint8_t * )"IP:", WHITE, BLACK );
    LCD_ShowString( 10, 140, lcddev.width, lcddev.height, 24, ( uint8_t * )"NetMask:", WHITE, BLACK );
    LCD_ShowString( 10, 170, lcddev.width, lcddev.height, 24, ( uint8_t * )"GateWay:", WHITE, BLACK );
    LCD_ShowString( 10, 200, lcddev.width, lcddev.height, 24, ( uint8_t * )"MacAddr:", WHITE, BLACK );
    sprintf( DispBuf, "%03d.%03d.%03d.%03d", Net_Info.LocalIP[0], Net_Info.LocalIP[1], Net_Info.LocalIP[2], Net_Info.LocalIP[3] );
    LCD_ShowString( 120, 110, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
    sprintf( DispBuf, "%03d.%03d.%03d.%03d", Net_Info.NetMask[0], Net_Info.NetMask[1], Net_Info.NetMask[2], Net_Info.NetMask[3] );
    LCD_ShowString( 120, 140, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
    sprintf( DispBuf, "%03d.%03d.%03d.%03d", Net_Info.GateWay[0], Net_Info.GateWay[1], Net_Info.GateWay[2], Net_Info.GateWay[3] );
    LCD_ShowString( 120, 170, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
    sprintf( DispBuf, "%03d-%03d-%03d-%03d", Net_Info.MacAddr[0], Net_Info.MacAddr[1], Net_Info.MacAddr[2], Net_Info.MacAddr[3] );
    LCD_ShowString( 120, 200, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
    
    LCD_ShowString( 10, 240, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP Stack Initializing...", WHITE, BLACK );
    
#if NET_DEBUG
    printf( "网卡的MAC地址为...................%x.%x.%x.%x.%x.%x\r\n", Net_Info.MacAddr[0], Net_Info.MacAddr[1], Net_Info.MacAddr[2], Net_Info.MacAddr[3], Net_Info.MacAddr[4], Net_Info.MacAddr[5] );
    printf( "静态IP地址........................%d.%d.%d.%d\r\n", Net_Info.LocalIP[0], Net_Info.LocalIP[1], Net_Info.LocalIP[2], Net_Info.LocalIP[3] );
    printf( "子网掩码..........................%d.%d.%d.%d\r\n", Net_Info.NetMask[0], Net_Info.NetMask[1], Net_Info.NetMask[2], Net_Info.NetMask[3] );
    printf( "默认网关..........................%d.%d.%d.%d\r\n", Net_Info.GateWay[0], Net_Info.GateWay[1], Net_Info.GateWay[2], Net_Info.GateWay[3] );
#endif	/* #if NET_DEBUG */
#endif
    Netif_Init_Flag = netif_add( &g_mynetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input );
	
	/* registers the default network interface */
	netif_set_default( &g_mynetif );
    
	if( Netif_Init_Flag != NULL )
    {
		/* when the netif is fully configured this function must be called */
		netif_set_up( &g_mynetif );
    }
    else
    {
		netif_set_down( &g_mynetif );
    }
	
	netif_set_link_callback( &g_mynetif, ethernetif_notify_conn_changed );
	
#if	LWIP_DHCP
    /* ---添加DHCP任务--- */
#endif
    return NETIF_INIT_OK;
}

/**************************************************************
  * @Name    ethernetif_notify_conn_changed                                    
  * @brief  
  * @param   Info: [输入/出] 
  * @retval
  * @author  Zachary
  * @Data    2022-08-03
 **************************************************************/
static void ethernetif_notify_conn_changed( struct netif *s_netif )
{		
	if( netif_is_link_up( s_netif ) )
	{
		NetCable_STATUS = 1;
		netif_set_up( s_netif );
	}
	else
	{
		NetCable_STATUS = 0;
		netif_set_down( s_netif );
	}
}

/**************************************************************
  * @Name    LwIP_Set_Default_IP_Info                                    
  * @brief  
  * @param   Info: [输入/出] 
  * @retval
  * @author  Zachary
  * @Data    2022-05-02
 **************************************************************/
void LwIP_Set_Default_IP_Info( struct Network_Info *Info )
{
    Info->LocalIP[0] = 192;
    Info->LocalIP[1] = 168;
    Info->LocalIP[2] = 123;
    Info->LocalIP[3] = 200;

    Info->NetMask[0] = 255;
    Info->NetMask[1] = 255;
    Info->NetMask[2] = 255;
    Info->NetMask[3] = 0;

    Info->GateWay[0] = 192;
    Info->GateWay[1] = 168;
    Info->GateWay[2] = 123;
    Info->GateWay[3] = 1;

    Info->RemoteIP[0] = 192;
    Info->RemoteIP[1] = 168;
    Info->RemoteIP[2] = 123;
    Info->RemoteIP[3] = 100;
    
    Info->MacAddr[0] = 0x02;
    Info->MacAddr[1] = 0x00;
    Info->MacAddr[2] = 0x00;
    Info->MacAddr[3] = 0x00;
    Info->MacAddr[4] = 0x00;
    Info->MacAddr[5] = 0x00;
    
}


 

        接着我们在上篇创建的Task1中初始化以太网与LwIP协议栈,其代码如下:

/**************************************************************
  * @Name    xTask1
  * @brief   
  * @param   pvParameters: [输入/出] 
  * @retval
  * @author  Zachary
  * @Data    2023-03-23
 **************************************************************/
static void xTask1( void *pvParameters )
{
    LCD_ShowString( 10, 10, lcddev.width, lcddev.height, 24, ( uint8_t * )"HC32F4A0SITB", WHITE, BLACK );
    LCD_ShowString( 10, 40, lcddev.width, lcddev.height, 24, ( uint8_t * )"FreeRTOS v10.3.0", WHITE, BLACK );
    LCD_ShowString( 10, 70, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP v2.1.4", WHITE, BLACK );
    
    if( NETIF_INIT_OK == LWIP_Init() )
    {
        LCD_ShowString( 10, 270, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP Stack Initialized Successfully...", WHITE, BLACK );
    }
    else
    {
        LCD_ShowString( 10, 270, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP Stack Initialized Failed...", WHITE, BLACK );
    }
    
    
    
    for( ;; )
    {
        TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN5, TCA9539_PIN_SET );
        vTaskDelay( 1000 );
        TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN5, TCA9539_PIN_RESET );
        vTaskDelay( 1000 );
    }
}

 

以下是视频验证:

lwip

 

以下是工程代码:

HC32F4A0_FreeRTOS_LwIP.7z (1.84 MB, 下载次数: 79, 售价: 2 分芯积分)

最新回复

顶顶顶顶顶   详情 回复 发表于 2024-7-15 09:42

回复
举报

36

帖子

1

TA的资源

一粒金砂(中级)

RE: HC32F4A0_FreeRTOS_LwIP移植

本帖最后由 Zachary_yo 于 2024-4-28 21:43 编辑

原工程中找不到port.c,但工程中实际是有的,只需要Remove一下port.c再添加一下就可以了。

现上传新工程。

游客,如果您要查看本帖隐藏内容请回复

回复

7284

帖子

18

TA的资源

五彩晶圆(中级)

谢谢分享,期待后续

个人签名

默认摸鱼,再摸鱼。2022、9、28


回复

1

帖子

0

TA的资源

一粒金砂(初级)

能否告知最终占用的硬件资源?是否使用了片外的Flash/SRAM/SDRAM?


回复

1

帖子

0

TA的资源

一粒金砂(初级)

博主你好 想问下你的视频里面是通过什么方式验证测试的?

我刷写了程序到华大的板子里面  Ping不同192.168.123.200这个地址  talnet也连不上  求指导下

点评

开发板直连PC,然后检查一下电脑的IP地址,也要在192.168.123.xxx段  详情 回复 发表于 2024-1-2 17:47

回复

36

帖子

1

TA的资源

一粒金砂(中级)

dddddqwdqdqwd 发表于 2024-1-2 16:06 博主你好 想问下你的视频里面是通过什么方式验证测试的? 我刷写了程序到华大的板子里面  Ping不同 ...

开发板直连PC,然后检查一下电脑的IP地址,也要在192.168.123.xxx段


回复

6

帖子

0

TA的资源

一粒金砂(中级)

来看看如何做到的,一直想移植,一直没有成功过


回复

2

帖子

0

TA的资源

一粒金砂(初级)

我用自动获取   获取IP成功,但是接受一次数据后,就什么也接收不到了


回复

3

帖子

0

TA的资源

一粒金砂(初级)

谢谢分享,期待后续,解压后port文件找不到



回复

1

帖子

0

TA的资源

一粒金砂(初级)

好的


回复

3

帖子

0

TA的资源

一粒金砂(初级)

GOOD


回复

1

帖子

0

TA的资源

一粒金砂(初级)

谢谢分享,期待后续


回复

1

帖子

0

TA的资源

一粒金砂(初级)

本帖最后由 huex 于 2024-6-13 08:05 编辑

楼主。你目前测试断流不?我买的第三方开发板,配合dp8348和lan8720都是一样的,ping不了多久就断了,感觉是以太网中断数据那里low_level_input数据处理过程中出现新的中断,导致memcpy拷贝问题? 发现很多次数据包突然来个11880大小,然后应该卡在input那里一直whlie处理数据,开始ping延迟变大,最后断流。


回复

1

帖子

0

TA的资源

一粒金砂(初级)

顶顶顶顶顶


回复
您需要登录后才可以回帖 登录 | 注册

猜你喜欢
随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
PIC中档单片机系列-10位A-D转换器

以前说的是8位单片机的AD转换器本文将详细的介绍一下10位系列单片机的转换器

机器人讲座 第二讲

机器人讲座 第二讲

赛前作品——扫频仪(未完成)

本帖最后由 paulhyde 于 2014-9-15 03:38 编辑 0~500MHZ数字扫频仪,步进5KHZ,这是赛前最后一个大型作品了,采用积木式设计。射 ...

没想到还真有出版的程序员健康指南

偶然看见有本书叫程序员健康指南,http://www.ituring.com.cn/book/1295 没有找到PDF版,不过看到了百度文库有个PPT讲的程序员 ...

平头哥RVB2601板子-GPIO

对于嵌入式应用来说最简单的测试就是点亮LED了,下面我就做一下使用GPIO来点亮LED的试验。从有限的资料来看,CH2601使用了E906内 ...

观电路-adc与系统(2)

观电路-ADC与系统(2) Full scale error 满量程误差 满量程误差指的是当ADC读取的最大值和理想最大值的差距,比如说12bi ...

选择压敏电阻时需要注意的几个关键参数

691665 压敏电阻是一种能够在电路中防止过电压损害的元器件。它们在电路中起到限制电压和保护其他元器件的作用。作为 ...

新能源车124串电池包行驶实测曲线实图,判断电池性能优良好坏。

718573 718574 718575 718576 718577

WIFI的配置指令中有一个配置MAC地址的指令,MAC地址不是固化到硬件中的码,为什么配置

WIFI的配置指令中有一个配置MAC地址的指令,MAC地址不是固化到硬件中的全球唯一的编码吗,为什么还能随意配置,如下图所示 ...

测评颁奖: 安信可BW16-Kit

首先感谢网友参与>>安信可BW16-Kit开发板测评,以下是审核结果。 *积分奖励将于本日内发送; *需寄回的网友,请于2024 ...

关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表