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
以下是工程代码:
|