时隔一年再次用到了单片机的ETH功能需要使用单片机MAC接口使用网络功能,再一次移植,不同的单片机平台,有了新的体会在此分享给各位。一开始接触单片机MAC+LWIP的时候觉得很复杂,有难度,平时也很少用,移植成功后也算是一种自我突破。
1,RT-Thread nano 移植
本人使用的单片机为STM32F407XX,所以操作系统直接用CubeMax生成添加RT-Thread就能直接使用。不做详细介绍。
2,LWIP的移植步骤为另外新建一个CubeMax工程添加LWIP加FreeRtos,然后选择DP83848驱动。之后把FreeRtos版本的所有网络相关文件拷贝到RT-Thread工程。
以上为LWIP相关文件,添加到项目工程。
3,ethernetif.c 文件内容如下
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : ethernetif.c
* Description : This file provides code for the configuration
* of the ethernetif.c MiddleWare.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip/opt.h"
#include "lwip/timeouts.h"
#include "netif/ethernet.h"
#include "netif/etharp.h"
#include "lwip/ethip6.h"
#include "ethernetif.h"
#include "dp83848.h"
#include <string.h>
#include <rtthread.h>
#include "lwip/tcpip.h"
/* Within 'USER CODE' section, code will be kept by default at each generation */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* Private define ------------------------------------------------------------*/
/* The time to block waiting for input. */
#define TIME_WAITING_FOR_INPUT ( portMAX_DELAY )
/* USER CODE BEGIN OS_THREAD_STACK_SIZE_WITH_RTOS */
/* Stack size of the interface thread */
#define INTERFACE_THREAD_STACK_SIZE ( 350 )
/* USER CODE END OS_THREAD_STACK_SIZE_WITH_RTOS */
/* Network interface name */
#define IFNAME0 's'
#define IFNAME1 't'
/* ETH Setting */
#define ETH_DMA_TRANSMIT_TIMEOUT ( 20U )
#define ETH_TX_BUFFER_MAX ((ETH_TX_DESC_CNT) * 2U)
/* ETH_RX_BUFFER_SIZE parameter is defined in lwipopts.h */
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Private variables ---------------------------------------------------------*/
/*
@Note: This interface is implemented to operate in zero-copy mode only:
- Rx buffers will be allocated from LwIP stack memory heap,
then passed to ETH HAL driver.
- Tx buffers will be allocated from LwIP stack memory heap,
then passed to ETH HAL driver.
@Notes:
1.a. ETH DMA Rx descriptors must be contiguous, the default count is 4,
to customize it please redefine ETH_RX_DESC_CNT in ETH GUI (Rx Descriptor Length)
so that updated value will be generated in stm32xxxx_hal_conf.h
1.b. ETH DMA Tx descriptors must be contiguous, the default count is 4,
to customize it please redefine ETH_TX_DESC_CNT in ETH GUI (Tx Descriptor Length)
so that updated value will be generated in stm32xxxx_hal_conf.h
2.a. Rx Buffers number must be between ETH_RX_DESC_CNT and 2*ETH_RX_DESC_CNT
2.b. Rx Buffers must have the same size: ETH_RX_BUFFER_SIZE, this value must
passed to ETH DMA in the init field (heth.Init.RxBuffLen)
2.c The RX Ruffers addresses and sizes must be properly defined to be aligned
to L1-CACHE line size (32 bytes).
*/
/* Data Type Definitions */
typedef enum
{
RX_ALLOC_OK = 0x00,
RX_ALLOC_ERROR = 0x01
} RxAllocStatusTypeDef;
typedef struct
{
struct pbuf_custom pbuf_custom;
uint8_t buff[(ETH_RX_BUFFER_SIZE + 31) & ~31] __ALIGNED(32);
} RxBuff_t;
/* Memory Pool Declaration */
#define ETH_RX_BUFFER_CNT 12U
LWIP_MEMPOOL_DECLARE(RX_POOL, ETH_RX_BUFFER_CNT, sizeof(RxBuff_t), "Zero-copy RX PBUF pool");
/* Variable Definitions */
static uint8_t RxAllocStatus;
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
//osSemaphoreId RxPktSemaphore = NULL; /* Semaphore to signal incoming packets */
//osSemaphoreId TxPktSemaphore = NULL; /* Semaphore to signal transmit packet complete */
static struct rt_semaphore TxPktSemaphore;//osSemaphoreId RxPktSemaphore = NULL; /* Semaphore to signal incoming packets */
static struct rt_semaphore eth_rxin;//osSemaphoreId RxPktSemaphore = NULL; /* Semaphore to signal incoming packets */
/* Global Ethernet handle */
ETH_HandleTypeDef heth;
ETH_TxPacketConfig TxConfig;
/* Private function prototypes -----------------------------------------------*/
int32_t ETH_PHY_IO_Init(void);
int32_t ETH_PHY_IO_DeInit (void);
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal);
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal);
int32_t ETH_PHY_IO_GetTick(void);
dp83848_Object_t DP83848;
dp83848_IOCtx_t DP83848_IOCtx = {ETH_PHY_IO_Init,
ETH_PHY_IO_DeInit,
ETH_PHY_IO_WriteReg,
ETH_PHY_IO_ReadReg,
ETH_PHY_IO_GetTick};
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
/* Private functions ---------------------------------------------------------*/
void pbuf_free_custom(struct pbuf *p);
/**
* @brief Ethernet Rx Transfer completed callback
* @param handlerEth: ETH handler
* @retval None
*/
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *handlerEth)
{
rt_sem_release(ð_rxin); //osSemaphoreRelease(RxPktSemaphore);
}
/**
* @brief Ethernet Tx Transfer completed callback
* @param handlerEth: ETH handler
* @retval None
*/
void HAL_ETH_TxCpltCallback(ETH_HandleTypeDef *handlerEth)
{
rt_sem_release(&TxPktSemaphore); //osSemaphoreRelease(TxPktSemaphore);
}
/**
* @brief Ethernet DMA transfer error callback
* @param handlerEth: ETH handler
* @retval None
*/
void HAL_ETH_ErrorCallback(ETH_HandleTypeDef *handlerEth)
{
if((HAL_ETH_GetDMAError(handlerEth) & ETH_DMASR_RBUS) == ETH_DMASR_RBUS)
{
rt_sem_release(ð_rxin); //osSemaphoreRelease(RxPktSemaphore);
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/*******************************************************************************
LL Driver Interface ( LwIP stack --> ETH)
*******************************************************************************/
/**
* @brief In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
* for this ethernetif
*/
static void low_level_init(struct netif *netif)
{
HAL_StatusTypeDef hal_eth_init_status = HAL_OK;
uint32_t duplex, speed = 0;
int32_t PHYLinkState = 0;
ETH_MACConfigTypeDef MACConf = {0};
/* Start ETH HAL Init */
uint8_t MACAddr[6] ;
heth.Instance = ETH;
MACAddr[0] = 0x00;
MACAddr[1] = 0x80;
MACAddr[2] = 0xE1;
MACAddr[3] = 0x00;
MACAddr[4] = 0x00;
MACAddr[5] = 0x00;
heth.Init.MACAddr = &MACAddr[0];
heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
heth.Init.TxDesc = DMATxDscrTab;
heth.Init.RxDesc = DMARxDscrTab;
heth.Init.RxBuffLen = 1536;
/* USER CODE BEGIN MACADDRESS */
/* USER CODE END MACADDRESS */
hal_eth_init_status = HAL_ETH_Init(&heth);
memset(&TxConfig, 0 , sizeof(ETH_TxPacketConfig));
TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;
/* End ETH HAL Init */
/* create a binary semaphore used for informing ethernetif of frame reception */
rt_sem_init(&TxPktSemaphore, "TxPktSemaphore", 0, RT_IPC_FLAG_FIFO);//TxPktSemaphore = xSemaphoreCreateBinary();
/* create a binary semaphore used for informing ethernetif of frame reception */
rt_sem_init(ð_rxin, "eth_rxin", 0, RT_IPC_FLAG_FIFO);//RxPktSemaphore = xSemaphoreCreateBinary();
/* create the task that handles the ETH_MAC */
rt_thread_t tid = rt_thread_create("eth_rx", ethernetif_input, netif, 1024, 8, 20);//osThreadDef(EthIf, ethernetif_input, osPriorityRealtime, 0, INTERFACE_THREAD_STACK_SIZE);
rt_thread_startup(tid);//osThreadCreate (osThread(EthIf), netif);
/* Initialize the RX POOL */
LWIP_MEMPOOL_INIT(RX_POOL);
#if LWIP_ARP || LWIP_ETHERNET
/* set MAC hardware address length */
netif->hwaddr_len = ETH_HWADDR_LEN;
/* set MAC hardware address */
netif->hwaddr[0] = heth.Init.MACAddr[0];
netif->hwaddr[1] = heth.Init.MACAddr[1];
netif->hwaddr[2] = heth.Init.MACAddr[2];
netif->hwaddr[3] = heth.Init.MACAddr[3];
netif->hwaddr[4] = heth.Init.MACAddr[4];
netif->hwaddr[5] = heth.Init.MACAddr[5];
/* maximum transfer unit */
netif->mtu = ETH_MAX_PAYLOAD;
/* Accept broadcast address and ARP traffic */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
#if LWIP_ARP
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#else
netif->flags |= NETIF_FLAG_BROADCAST;
#endif /* LWIP_ARP */
/* USER CODE BEGIN PHY_PRE_CONFIG */
/* USER CODE END PHY_PRE_CONFIG */
/* Set PHY IO functions */
DP83848_RegisterBusIO(&DP83848, &DP83848_IOCtx);
/* Initialize the DP83848 ETH PHY */
DP83848_Init(&DP83848);
if (hal_eth_init_status == HAL_OK)
{
PHYLinkState = DP83848_GetLinkState(&DP83848);
/* Get link state */
if(PHYLinkState <= DP83848_STATUS_LINK_DOWN)
{
netif_set_link_down(netif);
netif_set_down(netif);
}
else
{
switch (PHYLinkState)
{
case DP83848_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case DP83848_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case DP83848_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
case DP83848_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
default:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
}
/* Get MAC Config MAC */
HAL_ETH_GetMACConfig(&heth, &MACConf);
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
HAL_ETH_SetMACConfig(&heth, &MACConf);
HAL_ETH_Start_IT(&heth);
netif_set_up(netif);
netif_set_link_up(netif);
/* USER CODE BEGIN PHY_POST_CONFIG */
/* USER CODE END PHY_POST_CONFIG */
}
}
else
{
Error_Handler();
}
#endif /* LWIP_ARP || LWIP_ETHERNET */
/* USER CODE BEGIN LOW_LEVEL_INIT */
/* USER CODE END LOW_LEVEL_INIT */
}
/**
* @brief This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become available since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
uint32_t i = 0U;
struct pbuf *q = NULL;
err_t errval = ERR_OK;
ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT] = {0};
memset(Txbuffer, 0 , ETH_TX_DESC_CNT*sizeof(ETH_BufferTypeDef));
for(q = p; q != NULL; q = q->next)
{
if(i >= ETH_TX_DESC_CNT)
return ERR_IF;
Txbuffer[i].buffer = q->payload;
Txbuffer[i].len = q->len;
if(i>0)
{
Txbuffer[i-1].next = &Txbuffer[i];
}
if(q->next == NULL)
{
Txbuffer[i].next = NULL;
}
i++;
}
TxConfig.Length = p->tot_len;
TxConfig.TxBuffer = Txbuffer;
TxConfig.pData = p;
pbuf_ref(p);
HAL_ETH_Transmit_IT(&heth, &TxConfig);
while(rt_sem_take(&TxPktSemaphore, RT_WAITING_FOREVER) != RT_EOK)//while(osSemaphoreWait(TxPktSemaphore, TIME_WAITING_FOR_INPUT)!=osOK)
{
}
HAL_ETH_ReleaseTxPacket(&heth);
}
/**
* @brief Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf * low_level_input(struct netif *netif)
{
struct pbuf *p = NULL;
if(RxAllocStatus == RX_ALLOC_OK)
{
HAL_ETH_ReadData(&heth, (void **)&p);
}
return p;
}
/**
* @brief This function should be called when a packet is ready to be read
* from the interface. It uses the function low_level_input() that
* should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input(void* argument)
{
struct pbuf *p = NULL;
struct netif *netif = (struct netif *) argument;
for( ;; )
{
if(rt_sem_take(ð_rxin, RT_WAITING_FOREVER) == RT_EOK)//if (osSemaphoreWait( RxPktSemaphore, TIME_WAITING_FOR_INPUT)==osOK)
{
do
{
p = low_level_input( netif );
if (p != NULL)
{
if (netif->input( p, netif) != ERR_OK )
{
pbuf_free(p);
}
}
} while(p!=NULL);
}
}
}
#if !LWIP_ARP
/**
* This function has to be completed by user in case of ARP OFF.
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if ...
*/
static err_t low_level_output_arp_off(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
{
err_t errval;
errval = ERR_OK;
/* USER CODE BEGIN 5 */
/* USER CODE END 5 */
return errval;
}
#endif /* LWIP_ARP */
/**
* @brief Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
// MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
#if LWIP_IPV4
#if LWIP_ARP || LWIP_ETHERNET
#if LWIP_ARP
netif->output = etharp_output;
#else
/* The user should write its own code in low_level_output_arp_off function */
netif->output = low_level_output_arp_off;
#endif /* LWIP_ARP */
#endif /* LWIP_ARP || LWIP_ETHERNET */
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
/**
* @brief Custom Rx pbuf free callback
* @param pbuf: pbuf to be freed
* @retval None
*/
void pbuf_free_custom(struct pbuf *p)
{
struct pbuf_custom* custom_pbuf = (struct pbuf_custom*)p;
LWIP_MEMPOOL_FREE(RX_POOL, custom_pbuf);
/* If the Rx Buffer Pool was exhausted, signal the ethernetif_input task to
* call HAL_ETH_GetRxDataBuffer to rebuild the Rx descriptors. */
if (RxAllocStatus == RX_ALLOC_ERROR)
{
RxAllocStatus = RX_ALLOC_OK;
rt_sem_release(ð_rxin); //osSemaphoreRelease(RxPktSemaphore);
}
}
/* USER CODE BEGIN 6 */
/**
* @brief Returns the current time in milliseconds
* when LWIP_TIMERS == 1 and NO_SYS == 1
* @param None
* @retval Current Time value
*/
u32_t sys_now(void)
{
return HAL_GetTick();
}
/* USER CODE END 6 */
/**
* @brief Initializes the ETH MSP.
* @param ethHandle: ETH handle
* @retval None
*/
void HAL_ETH_MspInit(ETH_HandleTypeDef* ethHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(ethHandle->Instance==ETH)
{
/* USER CODE BEGIN ETH_MspInit 0 */
/* USER CODE END ETH_MspInit 0 */
/* Enable Peripheral clock */
__HAL_RCC_ETH_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PB11 ------> ETH_TX_EN
PB12 ------> ETH_TXD0
PB13 ------> ETH_TXD1
*/
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(ETH_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
/* USER CODE BEGIN ETH_MspInit 1 */
/* USER CODE END ETH_MspInit 1 */
}
}
void HAL_ETH_MspDeInit(ETH_HandleTypeDef* ethHandle)
{
if(ethHandle->Instance==ETH)
{
/* USER CODE BEGIN ETH_MspDeInit 0 */
/* USER CODE END ETH_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ETH_CLK_DISABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PB11 ------> ETH_TX_EN
PB12 ------> ETH_TXD0
PB13 ------> ETH_TXD1
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(ETH_IRQn);
/* USER CODE BEGIN ETH_MspDeInit 1 */
/* USER CODE END ETH_MspDeInit 1 */
}
}
/*******************************************************************************
PHI IO Functions
*******************************************************************************/
/**
* @brief Initializes the MDIO interface GPIO and clocks.
* @param None
* @retval 0 if OK, -1 if ERROR
*/
int32_t ETH_PHY_IO_Init(void)
{
/* We assume that MDIO GPIO configuration is already done
in the ETH_MspInit() else it should be done here
*/
/* Configure the MDIO Clock */
HAL_ETH_SetMDIOClockRange(&heth);
return 0;
}
/**
* @brief De-Initializes the MDIO interface .
* @param None
* @retval 0 if OK, -1 if ERROR
*/
int32_t ETH_PHY_IO_DeInit (void)
{
return 0;
}
/**
* @brief Read a PHY register through the MDIO interface.
* @param DevAddr: PHY port address
* @param RegAddr: PHY register address
* @param pRegVal: pointer to hold the register value
* @retval 0 if OK -1 if Error
*/
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal)
{
if(HAL_ETH_ReadPHYRegister(&heth, DevAddr, RegAddr, pRegVal) != HAL_OK)
{
return -1;
}
return 0;
}
/**
* @brief Write a value to a PHY register through the MDIO interface.
* @param DevAddr: PHY port address
* @param RegAddr: PHY register address
* @param RegVal: Value to be written
* @retval 0 if OK -1 if Error
*/
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal)
{
if(HAL_ETH_WritePHYRegister(&heth, DevAddr, RegAddr, RegVal) != HAL_OK)
{
return -1;
}
return 0;
}
/**
* @brief Get the time in millisecons used for internal PHY driver process.
* @retval Time value
*/
int32_t ETH_PHY_IO_GetTick(void)
{
return HAL_GetTick();
}
/**
* @brief Check the ETH link state then update ETH driver and netif link accordingly.
* @retval None
*/
void ethernet_link_thread(void* argument)
{
ETH_MACConfigTypeDef MACConf = {0};
int32_t PHYLinkState = 0;
uint32_t linkchanged = 0U, speed = 0U, duplex = 0U;
struct netif *netif = (struct netif *) argument;
/* USER CODE BEGIN ETH link init */
/* USER CODE END ETH link init */
for(;;)
{
PHYLinkState = DP83848_GetLinkState(&DP83848);
if(netif_is_link_up(netif) && (PHYLinkState <= DP83848_STATUS_LINK_DOWN))
{
HAL_ETH_Stop_IT(&heth);
netif_set_down(netif);
netif_set_link_down(netif);
}
else if(!netif_is_link_up(netif) && (PHYLinkState > DP83848_STATUS_LINK_DOWN))
{
switch (PHYLinkState)
{
case DP83848_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case DP83848_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case DP83848_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
case DP83848_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
default:
break;
}
if(linkchanged)
{
/* Get MAC Config MAC */
HAL_ETH_GetMACConfig(&heth, &MACConf);
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
HAL_ETH_SetMACConfig(&heth, &MACConf);
// HAL_ETH_Start(&heth);
HAL_ETH_Start_IT(&heth);
netif_set_up(netif);
netif_set_link_up(netif);
}
}
/* USER CODE BEGIN ETH link Thread core code for User BSP */
/* USER CODE END ETH link Thread core code for User BSP */
rt_thread_mdelay(100);
}
}
void HAL_ETH_RxAllocateCallback(uint8_t **buff)
{
/* USER CODE BEGIN HAL ETH RxAllocateCallback */
struct pbuf_custom *p = LWIP_MEMPOOL_ALLOC(RX_POOL);
if (p)
{
/* Get the buff from the struct pbuf address. */
*buff = (uint8_t *)p + offsetof(RxBuff_t, buff);
p->custom_free_function = pbuf_free_custom;
/* Initialize the struct pbuf.
* This must be performed whenever a buffer's allocated because it may be
* changed by lwIP or the app, e.g., pbuf_free decrements ref. */
pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, p, *buff, ETH_RX_BUFFER_SIZE);
}
else
{
RxAllocStatus = RX_ALLOC_ERROR;
*buff = NULL;
}
/* USER CODE END HAL ETH RxAllocateCallback */
}
void HAL_ETH_RxLinkCallback(void **pStart, void **pEnd, uint8_t *buff, uint16_t Length)
{
/* USER CODE BEGIN HAL ETH RxLinkCallback */
struct pbuf **ppStart = (struct pbuf **)pStart;
struct pbuf **ppEnd = (struct pbuf **)pEnd;
struct pbuf *p = NULL;
/* Get the struct pbuf from the buff address. */
p = (struct pbuf *)(buff - offsetof(RxBuff_t, buff));
p->next = NULL;
p->tot_len = 0;
p->len = Length;
/* Chain the buffer. */
if (!*ppStart)
{
/* The first buffer of the packet. */
*ppStart = p;
}
else
{
/* Chain the buffer to the end of the packet. */
(*ppEnd)->next = p;
}
*ppEnd = p;
/* Update the total length of all the buffers of the chain. Each pbuf in the chain should have its tot_len
* set to its own length, plus the length of all the following pbufs in the chain. */
for (p = *ppStart; p != NULL; p = p->next)
{
p->tot_len += Length;
}
/* USER CODE END HAL ETH RxLinkCallback */
}
void HAL_ETH_TxFreeCallback(uint32_t * buff)
{
/* USER CODE BEGIN HAL ETH TxFreeCallback */
pbuf_free((struct pbuf *)buff);
/* USER CODE END HAL ETH TxFreeCallback */
}
/* USER CODE BEGIN 8 */
/* USER CODE END 8 */
4,sys_arch.c 文件如下
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <[email protected]>
*
*/
/* lwIP includes. */
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/stats.h"
#if !NO_SYS
/*
#include "cmsis_os.h"
#if defined(LWIP_SOCKET_SET_ERRNO) && defined(LWIP_PROVIDE_ERRNO)
int errno;
#endif
*/
#include <rtthread.h>
/*-----------------------------------------------------------------------------------*/
// Creates an empty mailbox.
/*err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
osMessageQDef(QUEUE, size, void *);
*mbox = osMessageCreate(osMessageQ(QUEUE), NULL);
#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
if (*mbox == NULL)
return ERR_MEM;
return ERR_OK;
}*/
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
static unsigned short counter = 0;
char tname[RT_NAME_MAX];
sys_mbox_t tmpmbox;
RT_DEBUG_NOT_IN_INTERRUPT;
rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_MBOX_NAME, counter);
counter ++;
tmpmbox = rt_mb_create(tname, size, RT_IPC_FLAG_FIFO);
if (tmpmbox != RT_NULL)
{
*mbox = tmpmbox;
return ERR_OK;
}
return ERR_MEM;
}
/*-----------------------------------------------------------------------------------*/
/*
Deallocates a mailbox. If there are messages still present in the
mailbox when the mailbox is deallocated, it is an indication of a
programming error in lwIP and the developer should be notified.
*/
/*void sys_mbox_free(sys_mbox_t *mbox)
{
if( osMessageWaiting(*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.
}
osMessageDelete(*mbox);
#if SYS_STATS
--lwip_stats.sys.mbox.used;
#endif // SYS_STATS
}*/
void sys_mbox_free(sys_mbox_t *mbox)
{
RT_DEBUG_NOT_IN_INTERRUPT;
rt_mb_delete(*mbox);
return;
}
/*-----------------------------------------------------------------------------------*/
// Posts the "msg" to the mailbox.
/*void sys_mbox_post(sys_mbox_t *mbox, void *data)
{
while(osMessagePut(*mbox, (uint32_t)data, osWaitForever) != osOK);
}*/
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
RT_DEBUG_NOT_IN_INTERRUPT;
rt_mb_send_wait(*mbox, (rt_uint32_t)msg, RT_WAITING_FOREVER);
return;
}
/*-----------------------------------------------------------------------------------*/
// Try to post the "msg" to the mailbox.
/*err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
err_t result;
if ( osMessagePut(*mbox, (uint32_t)msg, 0) == osOK)
{
result = ERR_OK;
}
else {
// could not post, queue must be full
result = ERR_MEM;
#if SYS_STATS
lwip_stats.sys.mbox.err++;
#endif // SYS_STATS
}
return result;
}*/
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
if (rt_mb_send(*mbox, (rt_uint32_t)msg) == RT_EOK)
return ERR_OK;
return ERR_MEM;
}
/*-----------------------------------------------------------------------------------*/
// Try to post the "msg" to the mailbox.
err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{
return sys_mbox_trypost(mbox, msg);
}
/*-----------------------------------------------------------------------------------*/
/*
Blocks the thread until a message arrives in the mailbox, but does
not block the thread longer than "timeout" milliseconds (similar to
the sys_arch_sem_wait() function). The "msg" argument is a result
parameter that is set by the function (i.e., by doing "*msg =
ptr"). The "msg" parameter maybe NULL to indicate that the message
should be dropped.
The return values are the same as for the sys_arch_sem_wait() function:
Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
timeout.
Note that a function with a similar name, sys_mbox_fetch(), is
implemented by lwIP.
*/
/*u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
osEvent event;
uint32_t starttime = osKernelSysTick();;
if(timeout != 0)
{
event = osMessageGet (*mbox, timeout);
if(event.status == osEventMessage)
{
*msg = (void *)event.value.v;
return (osKernelSysTick() - starttime);
}
else
{
return SYS_ARCH_TIMEOUT;
}
}
else
{
event = osMessageGet (*mbox, osWaitForever);
*msg = (void *)event.value.v;
return (osKernelSysTick() - starttime);
}
}*/
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
rt_err_t ret;
s32_t t;
u32_t tick;
RT_DEBUG_NOT_IN_INTERRUPT;
/* get the begin tick */
tick = rt_tick_get();
if(timeout == 0)
t = RT_WAITING_FOREVER;
else
{
/* convirt msecond to os tick */
if (timeout < (1000/RT_TICK_PER_SECOND))
t = 1;
else
t = timeout / (1000/RT_TICK_PER_SECOND);
}
ret = rt_mb_recv(*mbox, (rt_ubase_t *)msg, t);
if(ret == -RT_ETIMEOUT)
return SYS_ARCH_TIMEOUT;
else
{
LWIP_ASSERT("rt_mb_recv returned with error!", ret == RT_EOK);
}
/* get elapse msecond */
tick = rt_tick_get() - tick;
/* convert tick to msecond */
tick = tick * (1000 / RT_TICK_PER_SECOND);
if (tick == 0)
tick = 1;
return tick;
}
/*-----------------------------------------------------------------------------------*/
/*
Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll
return with SYS_MBOX_EMPTY. On success, 0 is returned.
*/
/*u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
osEvent event;
event = osMessageGet (*mbox, 0);
if(event.status == osEventMessage)
{
*msg = (void *)event.value.v;
return ERR_OK;
}
else
{
return SYS_MBOX_EMPTY;
}
}*/
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
int ret;
ret = rt_mb_recv(*mbox, (rt_ubase_t *)msg, 0);
if(ret == -RT_ETIMEOUT)
return SYS_ARCH_TIMEOUT;
else
{
if (ret == RT_EOK)
ret = 1;
}
return ret;
}
/*----------------------------------------------------------------------------------*/
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;
}
/*-----------------------------------------------------------------------------------*/
// Creates a new semaphore. The "count" argument specifies
// the initial state of the semaphore.
/*err_t sys_sem_new(sys_sem_t *sem, u8_t count)
{
osSemaphoreDef(SEM);
*sem = osSemaphoreCreate (osSemaphore(SEM), 1);
if(*sem == NULL)
{
#if SYS_STATS
++lwip_stats.sys.sem.err;
#endif // SYS_STATS
return ERR_MEM;
}
if(count == 0) // Means it can't be taken
{
osSemaphoreWait(*sem,0);
}
#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
return ERR_OK;
}*/
err_t sys_sem_new(sys_sem_t *sem, u8_t count)
{
static unsigned short counter = 0;
char tname[RT_NAME_MAX];
sys_sem_t tmpsem;
RT_DEBUG_NOT_IN_INTERRUPT;
rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_SEM_NAME, counter);
counter ++;
tmpsem = rt_sem_create(tname, count, RT_IPC_FLAG_FIFO);
if (tmpsem == RT_NULL)
return ERR_MEM;
else
{
*sem = tmpsem;
return ERR_OK;
}
}
/*-----------------------------------------------------------------------------------*/
/*
Blocks the thread while waiting for the semaphore to be
signaled. If the "timeout" argument is non-zero, the thread should
only be blocked for the specified time (measured in
milliseconds).
If the timeout argument is non-zero, the return value is the number of
milliseconds spent waiting for the semaphore to be signaled. If the
semaphore wasn't signaled within the specified time, the return value is
SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
(i.e., it was already signaled), the function may return zero.
Notice that lwIP implements a function with a similar name,
sys_sem_wait(), that uses the sys_arch_sem_wait() function.
*/
/*u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
uint32_t starttime = osKernelSysTick();
if(timeout != 0)
{
if(osSemaphoreWait (*sem, timeout) == osOK)
{
return (osKernelSysTick() - starttime);
}
else
{
return SYS_ARCH_TIMEOUT;
}
}
else
{
while(osSemaphoreWait (*sem, osWaitForever) != osOK);
return (osKernelSysTick() - starttime);
}
}*/
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
rt_err_t ret;
s32_t t;
u32_t tick;
RT_DEBUG_NOT_IN_INTERRUPT;
/* get the begin tick */
tick = rt_tick_get();
if (timeout == 0)
t = RT_WAITING_FOREVER;
else
{
/* convert msecond to os tick */
if (timeout < (1000/RT_TICK_PER_SECOND))
t = 1;
else
t = timeout / (1000/RT_TICK_PER_SECOND);
}
ret = rt_sem_take(*sem, t);
if (ret == -RT_ETIMEOUT)
return SYS_ARCH_TIMEOUT;
else
{
if (ret == RT_EOK)
ret = 1;
}
/* get elapse msecond */
tick = rt_tick_get() - tick;
/* convert tick to msecond */
tick = tick * (1000 / RT_TICK_PER_SECOND);
if (tick == 0)
tick = 1;
return tick;
}
/*-----------------------------------------------------------------------------------*/
// Signals a semaphore
/*void sys_sem_signal(sys_sem_t *sem)
{
osSemaphoreRelease(*sem);
}*/
void sys_sem_signal(sys_sem_t *sem)
{
rt_sem_release(*sem);
}
/*-----------------------------------------------------------------------------------*/
// Deallocates a semaphore
/*void sys_sem_free(sys_sem_t *sem)
{
#if SYS_STATS
--lwip_stats.sys.sem.used;
#endif // SYS_STATS
osSemaphoreDelete(*sem);
}*/
void sys_sem_free(sys_sem_t *sem)
{
RT_DEBUG_NOT_IN_INTERRUPT;
rt_sem_delete(*sem);
}
/*-----------------------------------------------------------------------------------*/
int sys_sem_valid(sys_sem_t *sem)
{
if (*sem == SYS_SEM_NULL)
return 0;
else
return 1;
}
/*-----------------------------------------------------------------------------------*/
void sys_sem_set_invalid(sys_sem_t *sem)
{
*sem = SYS_SEM_NULL;
}
/*-----------------------------------------------------------------------------------*/
/*osMutexId lwip_sys_mutex;
osMutexDef(lwip_sys_mutex);
// Initialize sys arch
void sys_init(void)
{
lwip_sys_mutex = osMutexCreate(osMutex(lwip_sys_mutex));
}*/
void sys_init(void)
{
/* nothing on RT-Thread porting */
}
/*-----------------------------------------------------------------------------------*/
/* Mutexes*/
/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
#if LWIP_COMPAT_MUTEX == 0
/* Create a new mutex*/
/*err_t sys_mutex_new(sys_mutex_t *mutex) {
osMutexDef(MUTEX);
*mutex = osMutexCreate(osMutex(MUTEX));
// *mutex = xSemaphoreCreateMutex();
if(*mutex == NULL)
{
#if SYS_STATS
++lwip_stats.sys.mutex.err;
#endif // SYS_STATS
return ERR_MEM;
}
#if SYS_STATS
++lwip_stats.sys.mutex.used;
if (lwip_stats.sys.mutex.max < lwip_stats.sys.mutex.used) {
lwip_stats.sys.mutex.max = lwip_stats.sys.mutex.used;
}
#endif // SYS_STATS
return ERR_OK;
}*/
err_t sys_mutex_new(sys_mutex_t *mutex)
{
static unsigned short counter = 0;
char tname[RT_NAME_MAX];
sys_mutex_t tmpmutex;
RT_DEBUG_NOT_IN_INTERRUPT;
rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_MUTEX_NAME, counter);
counter ++;
tmpmutex = rt_mutex_create(tname, RT_IPC_FLAG_FIFO);
if (tmpmutex == RT_NULL)
return ERR_MEM;
else
{
*mutex = tmpmutex;
return ERR_OK;
}
}
/*-----------------------------------------------------------------------------------*/
/* Deallocate a mutex*/
/*void sys_mutex_free(sys_mutex_t *mutex)
{
#if SYS_STATS
--lwip_stats.sys.mutex.used;
#endif // SYS_STATS
osMutexDelete(*mutex);
}*/
void sys_mutex_free(sys_mutex_t *mutex)
{
RT_DEBUG_NOT_IN_INTERRUPT;
rt_mutex_delete(*mutex);
}
/*-----------------------------------------------------------------------------------*/
/* Lock a mutex*/
/*void sys_mutex_lock(sys_mutex_t *mutex)
{
osMutexWait (*mutex, osWaitForever);
}*/
void sys_mutex_lock(sys_mutex_t *mutex)
{
RT_DEBUG_NOT_IN_INTERRUPT;
rt_mutex_take(*mutex, RT_WAITING_FOREVER);
return;
}
/*-----------------------------------------------------------------------------------*/
/* Unlock a mutex*/
/*void sys_mutex_unlock(sys_mutex_t *mutex)
{
osMutexRelease(*mutex);
}*/
void sys_mutex_unlock(sys_mutex_t *mutex)
{
rt_mutex_release(*mutex);
}
#endif /*LWIP_COMPAT_MUTEX*/
/*-----------------------------------------------------------------------------------*/
// TODO
/*-----------------------------------------------------------------------------------*/
/*
Starts a new thread with priority "prio" that will begin its execution in the
function "thread()". The "arg" argument will be passed as an argument to the
thread() function. The id of the new thread is returned. Both the id and
the priority are system dependent.
*/
/*sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)
{
const osThreadDef_t os_thread_def = { (char *)name, (os_pthread)thread, (osPriority)prio, 0, stacksize};
return osThreadCreate(&os_thread_def, arg);
}*/
sys_thread_t sys_thread_new(const char *name,
lwip_thread_fn thread,
void *arg,
int stacksize,
int prio)
{
rt_thread_t t;
RT_DEBUG_NOT_IN_INTERRUPT;
/* create thread */
t = rt_thread_create(name, thread, arg, stacksize, prio, 20);
RT_ASSERT(t != RT_NULL);
/* startup thread */
rt_thread_startup(t);
return t;
}
/*
This optional function does a "fast" critical region protection and returns
the previous protection level. This function is only called during very short
critical regions. An embedded system which supports ISR-based drivers might
want to implement this function by disabling interrupts. Task-based systems
might want to implement this by using a mutex or disabling tasking. This
function should support recursive calls from the same task or interrupt. In
other words, sys_arch_protect() could be called while already protected. In
that case the return value indicates that it is already protected.
sys_arch_protect() is only required if your port is supporting an operating
system.
Note: This function is based on FreeRTOS API, because no equivalent CMSIS-RTOS
API is available
*/
/*sys_prot_t sys_arch_protect(void)
{
osMutexWait(lwip_sys_mutex, osWaitForever);
return (sys_prot_t)1;
}*/
sys_prot_t sys_arch_protect(void)
{
rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
return level;
}
/*
This optional function does a "fast" set of critical region protection to the
value specified by pval. See the documentation for sys_arch_protect() for
more information. This function is only required if your port is supporting
an operating system.
Note: This function is based on FreeRTOS API, because no equivalent CMSIS-RTOS
API is available
*/
/*oid sys_arch_unprotect(sys_prot_t pval)
{
( void ) pval;
osMutexRelease(lwip_sys_mutex);
}*/
void sys_arch_unprotect(sys_prot_t pval)
{
/* enable interrupt */
rt_hw_interrupt_enable(pval);
return;
}
#endif /* !NO_SYS */
5,lwip.c 文件如下
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : LWIP.c
* Description : This file provides initialization code for LWIP
* middleWare.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "lwip.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#if defined ( __CC_ARM ) /* MDK ARM Compiler */
#include "lwip/sio.h"
#endif /* MDK ARM Compiler */
#include "ethernetif.h"
#include <string.h>
/* USER CODE BEGIN 0 */
#include "../user_app/sys_ctl.h"
/* USER CODE END 0 */
/* Private function prototypes -----------------------------------------------*/
static void ethernet_link_status_updated(struct netif *netif);
/* ETH Variables initialization ----------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Variables Initialization */
struct netif gnetif;
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gw;
uint8_t IP_ADDRESS[4];
uint8_t NETMASK_ADDRESS[4];
uint8_t GATEWAY_ADDRESS[4];
/* USER CODE BEGIN OS_THREAD_ATTR_CMSIS_RTOS_V2 */
#define INTERFACE_THREAD_STACK_SIZE ( 1024 )
static rt_thread_t tid1 = RT_NULL;
/* USER CODE END OS_THREAD_ATTR_CMSIS_RTOS_V2 */
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/**
* LwIP initialization function
*/
void MX_LWIP_Init(void)
{
/* IP addresses initialization */
IP_ADDRESS[0] = 192;
IP_ADDRESS[1] = 168;
IP_ADDRESS[2] = 0;
IP_ADDRESS[3] = 245;
NETMASK_ADDRESS[0] = 255;
NETMASK_ADDRESS[1] = 255;
NETMASK_ADDRESS[2] = 255;
NETMASK_ADDRESS[3] = 0;
GATEWAY_ADDRESS[0] = 192;
GATEWAY_ADDRESS[1] = 168;
GATEWAY_ADDRESS[2] = 0;
GATEWAY_ADDRESS[3] = 1;
/* USER CODE BEGIN IP_ADDRESSES */
/* USER CODE END IP_ADDRESSES */
/* Initilialize the LwIP stack with RTOS */
tcpip_init( NULL, NULL );
/* IP addresses initialization without DHCP (IPv4) */
IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
/* add the network interface (IPv4/IPv6) with RTOS */
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
/* Registers the default network interface */
netif_set_default(&gnetif);
if (netif_is_link_up(&gnetif))
{
/* When the netif is fully configured this function must be called */
netif_set_up(&gnetif);
}
else
{
/* When the netif link is down this function must be called */
netif_set_down(&gnetif);
}
/* Set the link callback function, this function is called on change of link status*/
netif_set_link_callback(&gnetif, ethernet_link_status_updated);
/* Create the Ethernet link handler thread */
/* USER CODE BEGIN H7_OS_THREAD_NEW_CMSIS_RTOS_V2 */
/* 初始化线程 2,名称是 thread2,入口是 thread2_entry */
tid1 = rt_thread_create("EthLink",
ethernet_link_thread,
&gnetif,
INTERFACE_THREAD_STACK_SIZE,
24, 5);
/* 如果获得线程控制块,启动这个线程 */
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
/* USER CODE END H7_OS_THREAD_NEW_CMSIS_RTOS_V2 */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
#ifdef USE_OBSOLETE_USER_CODE_SECTION_4
/* Kept to help code migration. (See new 4_1, 4_2... sections) */
/* Avoid to use this user section which will become obsolete. */
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
#endif
/**
* @brief Notify the User about the network interface config status
* @param netif: the network interface
* @retval None
*/
static void ethernet_link_status_updated(struct netif *netif)
{
if (netif_is_up(netif))
{
/* USER CODE BEGIN 5 */
eth_state_get(1);
/* USER CODE END 5 */
}
else /* netif is down */
{
/* USER CODE BEGIN 6 */
eth_state_get(0);
/* USER CODE END 6 */
}
}
#if defined ( __CC_ARM ) /* MDK ARM Compiler */
/**
* Opens a serial device for communication.
*
* @param devnum device number
* @return handle to serial device if successful, NULL otherwise
*/
sio_fd_t sio_open(u8_t devnum)
{
sio_fd_t sd;
/* USER CODE BEGIN 7 */
sd = 0; // dummy code
/* USER CODE END 7 */
return sd;
}
/**
* Sends a single character to the serial device.
*
* @param c character to send
* @param fd serial device handle
*
* @note This function will block until the character can be sent.
*/
void sio_send(u8_t c, sio_fd_t fd)
{
/* USER CODE BEGIN 8 */
/* USER CODE END 8 */
}
/**
* Reads from the serial device.
*
* @param fd serial device handle
* @param data pointer to data buffer for receiving
* @param len maximum length (in bytes) of data to receive
* @return number of bytes actually received - may be 0 if aborted by sio_read_abort
*
* @note This function will block until data can be received. The blocking
* can be cancelled by calling sio_read_abort().
*/
u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
{
u32_t recved_bytes;
/* USER CODE BEGIN 9 */
recved_bytes = 0; // dummy code
/* USER CODE END 9 */
return recved_bytes;
}
/**
* Tries to read from the serial device. Same as sio_read but returns
* immediately if no data is available and never blocks.
*
* @param fd serial device handle
* @param data pointer to data buffer for receiving
* @param len maximum length (in bytes) of data to receive
* @return number of bytes actually received
*/
u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len)
{
u32_t recved_bytes;
/* USER CODE BEGIN 10 */
recved_bytes = 0; // dummy code
/* USER CODE END 10 */
return recved_bytes;
}
#endif /* MDK ARM Compiler */
以上代码添加到工程后LWIP便移植完成,能PING通,客户端程序能正常收发数据。
移植过程中需要注意的问题:
1,创建EthLink线程,检测网络连接状态,通过 netif_is_link_up 函数读取PHY芯片连接状态,发现会经常断连,于是将DP83848通信速率由自适应改为全双工10M速度便可以通信。PHY芯片手册中介绍了可以速率自适应但不知道不行,速度改为100M全双工也不行。
2,如果初始化完毕,单片机会一直进入ETH接收中断(DMAbuffer 错误中断也会进,具体原因不清楚没有深究,但是工作又是正常的)。
3,客户端线程
/*!
\brief tcp_client task
\param[in] arg: user supplied argument
\param[out] none
\retval none
*/
void tcp_client_task(void *arg)
{
int ret;
char *recv_data;
struct hostent *host;
int sock, bytes_received;
struct sockaddr_in server_addr;
const char *url;
int port;
// if (argc < 3)
// {
// rt_kprintf("Usage: tcpclient URL PORT\n");
// rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
// return ;
// }
port = SERVER_PORT;
/* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */
// host = gethostbyname(url);
/* 分配用于存放接收数据的缓冲 */
recv_data = rt_malloc(BUFSZ);
if (recv_data == RT_NULL)
{
rt_kprintf("No memory\n");
return;
}
RETRY:
/* 创建一个socket,类型是SOCKET_STREAM,TCP类型 */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* 创建socket失败 */
rt_kprintf("Socket error\n");
// /* 释放接收缓冲 */
// rt_free(recv_data);
// return;
}
/* 初始化预连接的服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
// server_addr.sin_addr = *((struct in_addr *)host->h_addr);
server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
while(!LiquidF.ucETHState)
{
rt_thread_mdelay(5);
}
/* 连接到服务端 */
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
/* 连接失败,关闭这个连接 */
closesocket(sock);
/* 连接失败 */
rt_kprintf("Connect fail!\n");
goto RETRY;
}
else
{
/* 连接成功 */
rt_kprintf("Connect successful\n");
}
while (1)
{
/* 从sock连接中接收最大BUFSZ - 1字节数据 */
bytes_received = recv(sock, recv_data, BUFSZ - 1, 0);
if (bytes_received < 0)
{
/* 接收失败,关闭这个连接 */
closesocket(sock);
rt_kprintf("\nreceived error,close the socket.\r\n");
goto RETRY;
/* 释放接收缓冲 */
// rt_free(recv_data);
}
else if (bytes_received == 0)
{
/* 默认 recv 为阻塞模式,此时收到0认为连接出错,关闭这个连接 */
closesocket(sock);
rt_kprintf("\nreceived error,close the socket.\r\n");
goto RETRY;
/* 释放接收缓冲 */
// rt_free(recv_data);
}
/* 有接收到数据,把末端清零 */
recv_data[bytes_received] = '\0';
if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0)
{
/* 如果是首字母是q或Q,关闭这个连接 */
closesocket(sock);
rt_kprintf("\n got a 'q' or 'Q',close the socket.\r\n");
/* 释放接收缓冲 */
// rt_free(recv_data);
}
else
{
/* 在控制终端显示收到的数据 */
rt_kprintf("\nReceived data = %s ", recv_data);
}
/* 发送数据到sock连接 */
ret = send(sock, send_data, strlen(send_data), 0);
if (ret < 0)
{
/* 发送失败,关闭这个连接 */
closesocket(sock);
rt_kprintf("\nsend error,close the socket.\r\n");
goto RETRY;
// rt_free(recv_data);
}
else if (ret == 0)
{
/* 打印send函数返回值为0的警告信息 */
rt_kprintf("\n Send warning,send function return 0.\r\n");
}
rt_thread_mdelay(20);
}
}
客户端程序堆栈需>=1024 否则程序可能跑飞或hardfault。
以上便是我移植LWIP+RT-Thread nano的心得体会,深入浅出,如果没有讲到或不理解的地方欢迎一起讨论。共同进步,强大自我,为国内生态做份贡献。