- 在stm32f4空工程文件夹下创建FreeRTOS文件夹用来存放FreeRTOS源码,将FreeRTOS文件源码中FreeRTOS\Source路径下的全部文件拷贝到新建的FreeRTOS文件夹中。
- 打开stm32f4空工程,添加分组FreeRTOS/Source,用来存放FreeRTOS源码。添加分组FreeRTOS/Ports,用来存放端口和内存分配方案。将FreeRTOS路径下的文件timers.c、croutine.c、list.c、queue.c、event_groups.c、task.c添加到FreeRTOS/Source中。将FreeRTOS\portable\MemMang路径下的内存管理文件heap_4.c和FreeRTOS\portable\RVDS\ARM_CM4F路径下的接口文件port.c添加到FreeRTOS/Ports中。
- 将路径FreeRTOS\ include和 FreeRTOS\portable\RVDS\ARM_CM4F添加到工程头文件路径中 。
- 将FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SKl路径下的配置文件FreeRTOSConfig.h复制到User文件夹下,并在工程中添加到User分组中。
- 在main.c中添加FreeRTOS.h、task.h、queue.h三个头文件,编译。出现一个错误,提示SystemCoreClock没有定义,如下图。
- 因为__ICCARM__是IAR编译工具链的相关宏定义,而这个工程使用的ARM编译工具链,所以将FreeRTOSConfig.h文件将宏定义__ICCARM__改为__CC_ARM,再次编译,发现出现四个错误。如下图。
- 这四个错误是没有相关回调函数定义,vApplicationIdleHook()是空闲状态回调函数,vApplicationTickHook()是时间片轮转回调函数,vApplicationStackOverflowHook()是堆栈溢出回调函数,vApplicationMallocFailedHook()是内存分配失败回调函数。在main.c中定义这四个回调函数。
#include "stm32f4xx.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
int main(void)
{
while(1);
}
//空闲状态回调函数
void vApplicationIdleHook( void )
{
}
//时间片轮转回调函数
void vApplicationTickHook( void )
{
}
//堆栈溢出回调函数
void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
{
}
//内存分配失败回调函数
void vApplicationMallocFailedHook( void )
{
}
- 再次编译发现没有错误了,移植完成。
- 创建FreeRTOS-TCP文件夹用来存放TCP源码,将FreeRTOS-Plus文件源码中FreeRTOS-Plus\Source\FreeRTOS-Plus-TCP路径下的全部文件拷贝到新建的FreeRTOS-TCP文件夹中。
- 打开工程,添加分组FreeRTOS_TCP,用来存放TCP源码。添加分组FreeRTOS_TCP/Ports,用来存放端口和内存分配方案。将FreeRTOS_TCP路径下的C文件添加到FreeRTOS_TCP分组中。将FreeRTOS-TCP\portable\BufferManagement路径下的内存管理文件BufferAllocation_2.c、FreeRTOS-TCP\portable\NetworkInterface\STM32Fxx路径下的接口文件NetworkInterface.c和FreeRTOS-TCP\portable\NetworkInterface\Common路径下的物理层处理文件添加到FreeRTOS_TCP/Ports中。
- 将路径FreeRTOS-TCP\include和 FreeRTOS-TCP\portable\NetworkInterface\include添加到工程头文件路径中 。
- 将FreeRTOS-Plus\Demo\FreeRTOS_Plus_TCP_Minimal_Windows_Simulator路径下的配置文件FreeRTOSIPConfig.h复制到User文件夹下,并在工程中添加到User分组中。
- 联网需要用到以太网物理层芯片,此案例采用LAN8720,用户需要编写LAN8720驱动函数。这里我们创建bsp_eth.c和bsp_eth.h两个文件,在文件里完成LAN8720驱动。将文件添加到工程中。参考下面代码。
#include "bsp_eth.h"
void LAN8720_Init(void)
{
GPIO_InitTypeDef GPIO_Init;
__HAL_RCC_SYSCFG_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_ETH_CLK_ENABLE();
/*RMII接口引脚
ETH_MDIO -------------------------> PA2
ETH_MDC --------------------------> PC1
ETH_RMII_REF_CLK------------------> PA1
ETH_RMII_CRS_DV ------------------> PA7
ETH_RMII_RXD0 --------------------> PC4
ETH_RMII_RXD1 --------------------> PC5
ETH_RMII_TX_EN -------------------> PG11
ETH_RMII_TXD0 --------------------> PG13
ETH_RMII_TXD1 --------------------> PG14
ETH_RESET-------------------------> PD3*/
GPIO_Init.Mode=GPIO_MODE_AF_PP;
GPIO_Init.Pin=GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
GPIO_Init.Pull=GPIO_NOPULL;
GPIO_Init.Speed=GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_Init.Alternate=GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
GPIO_Init.Pin=GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
HAL_GPIO_Init(GPIOC,&GPIO_Init);
GPIO_Init.Pin=GPIO_PIN_11 | GPIO_PIN_14 | GPIO_PIN_13;
HAL_GPIO_Init(GPIOG,&GPIO_Init);
GPIO_Init.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_Init.Pin=GPIO_PIN_3;
GPIO_Init.Pull=GPIO_NOPULL;
GPIO_Init.Speed=GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOD,&GPIO_Init);
//复位
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_3,GPIO_PIN_RESET);
vTaskDelay(100/portTICK_PERIOD_MS);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_3,GPIO_PIN_SET);
vTaskDelay(100/portTICK_PERIOD_MS);
HAL_NVIC_SetPriority(ETH_IRQn,6,0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
}
TCP协议里需要用到随机数,这个随机数需要用户提供。随机数的创建参考下面代码。
#include "randomnum.h"
/* RNG handler declaration */
RNG_HandleTypeDef RngHandle;
static void Error_Handler()
{
while(1)
{
}
}
void RNG_init(void)
{
__HAL_RCC_RNG_CLK_ENABLE();
/*## Configure the RNG peripheral #######################################*/
RngHandle.Instance = RNG;
/* DeInitialize the RNG peripheral */
if (HAL_RNG_DeInit(&RngHandle) != HAL_OK)
{
/* DeInitialization Error */
Error_Handler();
}
/* Initialize the RNG peripheral */
if (HAL_RNG_Init(&RngHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
}
uint32_t Random_GetNumber(){
uint32_t num;
if (HAL_RNG_GenerateRandomNumber(&RngHandle, &num) != HAL_OK)
{
/* Random number generation error */
Error_Handler();
}
return num;
}
void getRandomNumTo(uint32_t * num){
if (HAL_RNG_GenerateRandomNumber(&RngHandle, num) != HAL_OK)
{
/* Random number generation error */
Error_Handler();
}
}
配置文件FreeRTOSIPConfig.h,参考下面代码。
#ifndef FREERTOS_IP_CONFIG_H
#define FREERTOS_IP_CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32f4xx.h"
#define ipconfigBYTE_ORDER pdFREERTOS_LITTLE_ENDIAN
#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM ( 1 )
#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM ( 1 )
#define ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME ( 1000 )
#define ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME ( 1000 )
#define ipconfigZERO_COPY_RX_DRIVER ( 0 )
#define ipconfigZERO_COPY_TX_DRIVER ( 0 )
#define ipconfigUSE_LLMNR ( 1 )
#define ipconfigUSE_NBNS ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_NAME_LENGTH ( 16 )
#define ipconfigDNS_CACHE_ENTRIES ( 4 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 4 )
#define ipconfigIP_TASK_PRIORITY ( configMAX_PRIORITIES - 2 )
#define ipconfigIP_TASK_STACK_SIZE_WORDS ( configMINIMAL_STACK_SIZE * 5 )
extern uint32_t Random_GetNumber(void);
#define ipconfigRAND32() Random_GetNumber()
#define ipconfigUSE_NETWORK_EVENT_HOOK 1
#define ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ( 5000 / portTICK_PERIOD_MS )
#define ipconfigUSE_DHCP 1
#define ipconfigDHCP_REGISTER_HOSTNAME 1
#define ipconfigDHCP_USES_UNICAST 1
#define ipconfigMAXIMUM_DISCOVER_TX_PERIOD ( pdMS_TO_TICKS( 30000 ) )
#define ipconfigARP_CACHE_ENTRIES 6
#define ipconfigMAX_ARP_RETRANSMISSIONS ( 5 )
#define ipconfigMAX_ARP_AGE 150
#define ipconfigINCLUDE_FULL_INET_ADDR 1
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
#define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ( 25 + 6 )
#else
#define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS 25
#endif
#define ipconfigEVENT_QUEUE_LENGTH ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + 5 )
#define ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND 1
#define ipconfigUDP_TIME_TO_LIVE 128
#define ipconfigTCP_TIME_TO_LIVE 128 /* also defined in FreeRTOSIPConfigDefaults.h */
#define ipconfigUSE_TCP ( 1 )
#define ipconfigUSE_TCP_WIN ( 0 )
#define ipconfigNETWORK_MTU 1500
#define ipconfigUSE_DNS 1
#define ipconfigREPLY_TO_INCOMING_PINGS 1
#define ipconfigSUPPORT_OUTGOING_PINGS 1
#define ipconfigSUPPORT_SELECT_FUNCTION 1
#define ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES 1
#define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1
#define configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY ( 2 / portTICK_PERIOD_MS )
#define ipconfigPACKET_FILLER_SIZE 2
#define ipconfigTCP_WIN_SEG_COUNT 64
#define ipconfigTCP_RX_BUFFER_LENGTH ( 3 * 1460 )
#define ipconfigTCP_TX_BUFFER_LENGTH ( 2 * 1460 )
#define ipconfigIS_VALID_PROG_ADDRESS(x) ( (x) != NULL )
#define ipconfigTCP_HANG_PROTECTION ( 1 )
#define ipconfigTCP_HANG_PROTECTION_TIME ( 30 )
#define ipconfigTCP_KEEP_ALIVE ( 1 )
#define ipconfigTCP_KEEP_ALIVE_INTERVAL ( 20 ) /* in seconds */
#define ipconfigUSE_FTP 0
#define ipconfigUSE_HTTP 0
#define ipconfigFTP_TX_BUFSIZE ( 4 * ipconfigTCP_MSS )
#define ipconfigFTP_TX_WINSIZE ( 2 )
#define ipconfigFTP_RX_BUFSIZE ( 8 * ipconfigTCP_MSS )
#define ipconfigFTP_RX_WINSIZE ( 4 )
#define ipconfigHTTP_TX_BUFSIZE ( 3 * ipconfigTCP_MSS )
#define ipconfigHTTP_TX_WINSIZE ( 2 )
#define ipconfigHTTP_RX_BUFSIZE ( 4 * ipconfigTCP_MSS )
#define ipconfigHTTP_RX_WINSIZE ( 4 )
extern int lUDPLoggingPrintf( const char *pcFormatString, ... );
#define ipconfigHAS_DEBUG_PRINTF 0
#if( ipconfigHAS_DEBUG_PRINTF == 1 )
#define FreeRTOS_debug_printf(X) lUDPLoggingPrintf X
#endif
#define ipconfigHAS_PRINTF 0
#if( ipconfigHAS_PRINTF == 1 )
#define FreeRTOS_printf(X) lUDPLoggingPrintf X
#endif
#define ipconfigDNS_USE_CALLBACKS 1
#define ipconfigSUPPORT_SIGNALS 0
#define TCP_SERVER 0
#define TCP_CLIENT 1
#if( TCP_CLIENT == 1 )
#define CLIENT_PORT 0
#endif
#define configMAC_ADDR0 0x00
#define configMAC_ADDR1 0x51
#define configMAC_ADDR2 0x51
#define configMAC_ADDR3 0x51
#define configMAC_ADDR4 0x51
#ifdef TCP_CLIENT
#define configMAC_ADDR5 0x50
#else
#define configMAC_ADDR5 (0x50+ CLIENT_PORT)
#endif
#define configIP_ADDR0 192
#define configIP_ADDR1 168
#define configIP_ADDR2 31
#define configIP_ADDR3 130
#define configGATEWAY_ADDR0 0
#define configGATEWAY_ADDR1 0
#define configGATEWAY_ADDR2 0
#define configGATEWAY_ADDR3 0
#define configDNS_SERVER_ADDR0 0
#define configDNS_SERVER_ADDR1 0
#define configDNS_SERVER_ADDR2 0
#define configDNS_SERVER_ADDR3 0
#define configNET_MASK0 0
#define configNET_MASK1 0
#define configNET_MASK2 0
#define configNET_MASK3 0
#define configECHO_SERVER_ADDR0 192
#define configECHO_SERVER_ADDR1 168
#define configECHO_SERVER_ADDR2 31
#define configECHO_SERVER_ADDR3 237
#define configHTTP_ROOT "/websrc"
#define configINCLUDE_DEMO_DEBUG_STATS 0
#define configINCLUDE_QUERY_HEAP_COMMAND 0
#define configUDP_LOGGING_NEEDS_CR_LF ( 0 )
#define configUDP_LOGGING_STRING_LENGTH ( 200 )
#define configUDP_LOGGING_MAX_MESSAGES_IN_BUFFER ( 20 )
#define configUDP_LOGGING_TASK_STACK_SIZE ( 512 )
#define configUDP_LOGGING_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 )
#define configUDP_LOGGING_PORT_LOCAL 1499
#define configUDP_LOGGING_PORT_REMOTE 1500
#define configUDP_LOGGING_ADDR0 192
#define configUDP_LOGGING_ADDR1 168
#define configUDP_LOGGING_ADDR2 0
#define configUDP_LOGGING_ADDR3 100
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
- 修改接口文件NetworkInterface.c。具体修改请参考如下代码。
#include "NetworkInterface.h"
BaseType_t xSTM32_PhyRead( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue );
#ifndef BMSR_LINK_STATUS
#define BMSR_LINK_STATUS 0x0004UL
#endif
#ifndef PHY_LS_HIGH_CHECK_TIME_MS
#define PHY_LS_HIGH_CHECK_TIME_MS 15000
#endif
#ifndef PHY_LS_LOW_CHECK_TIME_MS
#define PHY_LS_LOW_CHECK_TIME_MS 1000
#endif
#define EMAC_IF_RX_EVENT 1UL
#define EMAC_IF_TX_EVENT 2UL
#define EMAC_IF_ERR_EVENT 4UL
#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
#define ETH_DMA_ALL_INTS \
( ETH_DMA_IT_TST | ETH_DMA_IT_PMT | ETH_DMA_IT_MMC | ETH_DMA_IT_NIS | ETH_DMA_IT_ER | \
ETH_DMA_IT_FBE | ETH_DMA_IT_RWT | ETH_DMA_IT_RPS | ETH_DMA_IT_RBU | ETH_DMA_IT_R | \
ETH_DMA_IT_TU | ETH_DMA_IT_RO | ETH_DMA_IT_TJT | ETH_DMA_IT_TPS | ETH_DMA_IT_T )
#define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0x0fff ) /* The bits in the two byte IP header field that make up the fragment offset value. */
#if !defined( ipconfigETHERNET_AN_ENABLE )
#define ipconfigETHERNET_AN_ENABLE 1
#endif
#if !defined( ipconfigETHERNET_AUTO_CROSS_ENABLE )
#define ipconfigETHERNET_AUTO_CROSS_ENABLE 1
#endif
#if( ipconfigETHERNET_AN_ENABLE == 0 )
#if !defined( ipconfigETHERNET_CROSSED_LINK )
#define ipconfigETHERNET_CROSSED_LINK 1
#endif
#if !defined( ipconfigETHERNET_USE_100MB )
#define ipconfigETHERNET_USE_100MB 1
#endif
#if !defined( ipconfigETHERNET_USE_FULL_DUPLEX )
#define ipconfigETHERNET_USE_FULL_DUPLEX 1
#endif
#endif /* ipconfigETHERNET_AN_ENABLE == 0 */
#ifndef configEMAC_TASK_STACK_SIZE
#define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE )
#endif
static void prvEMACHandlerTask( void *pvParameters );
static void prvEthernetUpdateConfig( BaseType_t xForce );
static BaseType_t prvNetworkInterfaceInput( void );
static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer );
static void prvDMATxDescListInit( void );
static void prvDMARxDescListInit( void );
static void vClearTXBuffers( void );
#if( ipconfigUSE_LLMNR != 0 )
static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr);
#endif
volatile uint32_t ulISREvents;
static EthernetPhy_t xPhyObject;
static ETH_HandleTypeDef xETH;
static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
#if( ipconfigUSE_LLMNR == 1 )
static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };
#endif
__attribute__ ((aligned (32)))
__attribute__ ((section(".first_data")))
ETH_DMADescTypeDef DMARxDscrTab[ ETH_RXBUFNB ];
#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
__ALIGN_BEGIN uint8_t Rx_Buff[ ETH_RXBUFNB ][ ETH_RX_BUF_SIZE ] __ALIGN_END;
#endif
__attribute__ ((aligned (32)))
__attribute__ ((section(".first_data")))
ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ];
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
__ALIGN_BEGIN uint8_t Tx_Buff[ ETH_TXBUFNB ][ ETH_TX_BUF_SIZE ] __ALIGN_END;
#endif
static __IO ETH_DMADescTypeDef *DMATxDescToClear;
extern const uint8_t ucMACAddress[ 6 ];
static TaskHandle_t xEMACTaskHandle = NULL;
static uint32_t ulPHYLinkStatus = 0;
const PhyProperties_t xPHYProperties =
{
#if( ipconfigETHERNET_AN_ENABLE != 0 )
.ucSpeed = PHY_SPEED_AUTO,
.ucDuplex = PHY_DUPLEX_AUTO,
#else
#if( ipconfigETHERNET_USE_100MB != 0 )
.ucSpeed = PHY_SPEED_100,
#else
.ucSpeed = PHY_SPEED_10,
#endif
#if( ipconfigETHERNET_USE_FULL_DUPLEX != 0 )
.duplex = PHY_DUPLEX_FULL,
#else
.duplex = PHY_DUPLEX_HALF,
#endif
#endif
#if( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 )
.ucMDI_X = PHY_MDIX_AUTO,
#elif( ipconfigETHERNET_CROSSED_LINK != 0 )
.ucMDI_X = PHY_MDIX_CROSSED,
#else
.ucMDI_X = PHY_MDIX_DIRECT,
#endif
};
/*-----------------------------------------------------------*/
void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef *heth )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
ulISREvents |= EMAC_IF_RX_EVENT;
if( xEMACTaskHandle != NULL )
{
vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
}
/*-----------------------------------------------------------*/
void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef *heth )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
ulISREvents |= EMAC_IF_TX_EVENT;
if( xEMACTaskHandle != NULL )
{
vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
}
/*-----------------------------------------------------------*/
static void vClearTXBuffers()
{
__IO ETH_DMADescTypeDef *txLastDescriptor = xETH.TxDesc;
size_t uxCount = ( ( UBaseType_t ) ETH_TXBUFNB ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
NetworkBufferDescriptor_t *pxNetworkBuffer;
uint8_t *ucPayLoad;
#endif
while( ( uxCount > 0 ) && ( ( DMATxDescToClear->Status & ETH_DMATXDESC_OWN ) == 0 ) )
{
if( ( DMATxDescToClear == txLastDescriptor ) && ( uxCount != ETH_TXBUFNB ) )
{
break;
}
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
{
ucPayLoad = ( uint8_t * )DMATxDescToClear->Buffer1Addr;
if( ucPayLoad != NULL )
{
pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad );
if( pxNetworkBuffer != NULL )
{
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ;
}
DMATxDescToClear->Buffer1Addr = ( uint32_t )0u;
}
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
DMATxDescToClear = ( ETH_DMADescTypeDef * )( DMATxDescToClear->Buffer2NextDescAddr );
uxCount--;
/* Tell the counting semaphore that one more TX descriptor is available. */
xSemaphoreGive( xTXDescriptorSemaphore );
}
}
/*-----------------------------------------------------------*/
BaseType_t xNetworkInterfaceInitialise( void )
{
HAL_StatusTypeDef hal_eth_init_status;
//BaseType_t xResult;
if( xEMACTaskHandle == NULL )
{
if( xTXDescriptorSemaphore == NULL )
{
xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB );
configASSERT( xTXDescriptorSemaphore );
}
/* Initialise ETH */
LAN8720_Init();
xETH.Instance = ETH;
xETH.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
xETH.Init.Speed = ETH_SPEED_100M;
xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
/* Value of PhyAddress doesn't matter, will be probed for. */
xETH.Init.PhyAddress = 0;
xETH.Init.MACAddr = ( uint8_t *) ucMACAddress;
xETH.Init.RxMode = ETH_RXINTERRUPT_MODE;
xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
hal_eth_init_status = HAL_ETH_Init( &xETH );
/* Only for inspection by debugger. */
( void ) hal_eth_init_status;
/* Set the TxDesc and RxDesc pointers. */
xETH.TxDesc = DMATxDscrTab;
xETH.RxDesc = DMARxDscrTab;
/* Make sure that all unused fields are cleared. */
memset( &DMATxDscrTab, '\0', sizeof( DMATxDscrTab ) );
memset( &DMARxDscrTab, '\0', sizeof( DMARxDscrTab ) );
/* Initialize Tx Descriptors list: Chain Mode */
DMATxDescToClear = DMATxDscrTab;
/* Initialise TX-descriptors. */
prvDMATxDescListInit();
/* Initialise RX-descriptors. */
prvDMARxDescListInit();
#if( ipconfigUSE_LLMNR != 0 )
{
prvMACAddressConfig( &xETH, ETH_MAC_ADDRESS1, ( uint8_t *) xLLMNR_MACAddress );
}
#endif
prvEthernetUpdateConfig( pdTRUE );
xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );
} /* if( xEMACTaskHandle == NULL ) */
//检查PHY连接状态
xSTM32_PhyRead( 0x00,0x01, &ulPHYLinkStatus);
xPhyObject.ulLinkStatusMask = ulPHYLinkStatus;
return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0;
}
/*-----------------------------------------------------------*/
static void prvDMATxDescListInit()
{
ETH_DMADescTypeDef *pxDMADescriptor;
BaseType_t xIndex;
/* Get the pointer on the first member of the descriptor list */
pxDMADescriptor = DMATxDscrTab;
/* Fill each DMA descriptor with the right values */
for( xIndex = 0; xIndex < ETH_TXBUFNB; xIndex++, pxDMADescriptor++ )
{
/* Set Second Address Chained bit */
pxDMADescriptor->Status = ETH_DMATXDESC_TCH;
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
{
/* Set Buffer1 address pointer */
pxDMADescriptor->Buffer1Addr = ( uint32_t )( Tx_Buff[ xIndex ] );
}
#endif
if( xETH.Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE )
{
/* Set the DMA Tx descriptors checksum insertion for TCP, UDP, and ICMP */
pxDMADescriptor->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL;
}
/* Initialize the next descriptor with the Next Descriptor Polling Enable */
if( xIndex < ETH_TXBUFNB - 1 )
{
/* Set next descriptor address register with next descriptor base address */
pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) ( pxDMADescriptor + 1 );
}
else
{
/* For last descriptor, set next descriptor address register equal to the first descriptor base address */
pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMATxDscrTab;
}
}
/* Set Transmit Descriptor List Address Register */
xETH.Instance->DMATDLAR = ( uint32_t ) DMATxDscrTab;
}
/*-----------------------------------------------------------*/
static void prvDMARxDescListInit()
{
ETH_DMADescTypeDef *pxDMADescriptor;
BaseType_t xIndex;
/*
* RX-descriptors.
*/
/* Get the pointer on the first member of the descriptor list */
pxDMADescriptor = DMARxDscrTab;
/* Fill each DMA descriptor with the right values */
for( xIndex = 0; xIndex < ETH_RXBUFNB; xIndex++, pxDMADescriptor++ )
{
/* Set Buffer1 size and Second Address Chained bit */
pxDMADescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE;
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
{
/* Set Buffer1 address pointer */
NetworkBufferDescriptor_t *pxBuffer;
pxBuffer = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, 100ul );
/* If the assert below fails, make sure that there are at least 'ETH_RXBUFNB'
Network Buffers available during start-up ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) */
configASSERT( pxBuffer != NULL );
if( pxBuffer != NULL )
{
pxDMADescriptor->Buffer1Addr = (uint32_t)pxBuffer->pucEthernetBuffer;
pxDMADescriptor->Status = ETH_DMARXDESC_OWN;
}
}
#else
{
/* Set Buffer1 address pointer */
pxDMADescriptor->Buffer1Addr = ( uint32_t )( Rx_Buff[ xIndex ] );
/* Set Own bit of the Rx descriptor Status */
pxDMADescriptor->Status = ETH_DMARXDESC_OWN;
}
#endif
/* Initialize the next descriptor with the Next Descriptor Polling Enable */
if( xIndex < ETH_RXBUFNB - 1 )
{
/* Set next descriptor address register with next descriptor base address */
pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t )( pxDMADescriptor + 1 );
}
else
{
/* For last descriptor, set next descriptor address register equal to the first descriptor base address */
pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMARxDscrTab;
}
}
/* Set Receive Descriptor List Address Register */
xETH.Instance->DMARDLAR = ( uint32_t ) DMARxDscrTab;
}
/*-----------------------------------------------------------*/
static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr)
{
uint32_t ulTempReg;
/* Calculate the selected MAC address high register. */
ulTempReg = 0x80000000ul | ( ( uint32_t ) Addr[ 5 ] << 8 ) | ( uint32_t ) Addr[ 4 ];
/* Load the selected MAC address high register. */
( *(__IO uint32_t *)( ( uint32_t ) ( ETH_MAC_ADDR_HBASE + ulIndex ) ) ) = ulTempReg;
/* Calculate the selected MAC address low register. */
ulTempReg = ( ( uint32_t ) Addr[ 3 ] << 24 ) | ( ( uint32_t ) Addr[ 2 ] << 16 ) | ( ( uint32_t ) Addr[ 1 ] << 8 ) | Addr[ 0 ];
/* Load the selected MAC address low register */
( *(__IO uint32_t *) ( ( uint32_t ) ( ETH_MAC_ADDR_LBASE + ulIndex ) ) ) = ulTempReg;
}
/*-----------------------------------------------------------*/
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
{
BaseType_t xReturn = pdFAIL;
uint32_t ulTransmitSize = 0;
__IO ETH_DMADescTypeDef *pxDmaTxDesc;
/* Do not wait too long for a free TX DMA buffer. */
const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );
#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
{
ProtocolPacket_t *pxPacket;
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
{
configASSERT( bReleaseAfterSend != 0 );
}
#endif /* ipconfigZERO_COPY_RX_DRIVER */
/* If the peripheral must calculate the checksum, it wants
the protocol checksum to have a value of zero. */
pxPacket = ( ProtocolPacket_t * ) ( pxDescriptor->pucEthernetBuffer );
if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP )
{
pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t )0u;
}
}
#endif
/* Open a do {} while ( 0 ) loop to be able to call break. */
do
{
if( xPhyObject.ulLinkStatusMask != 0 )
{
if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
{
/* Time-out waiting for a free TX descriptor. */
break;
}
/* This function does the actual transmission of the packet. The packet is
contained in 'pxDescriptor' that is passed to the function. */
pxDmaTxDesc = xETH.TxDesc;
/* Is this buffer available? */
configASSERT ( ( pxDmaTxDesc->Status & ETH_DMATXDESC_OWN ) == 0 );
{
/* Is this buffer available? */
/* Get bytes in current buffer. */
ulTransmitSize = pxDescriptor->xDataLength;
if( ulTransmitSize > ETH_TX_BUF_SIZE )
{
ulTransmitSize = ETH_TX_BUF_SIZE;
}
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
{
/* Copy the bytes. */
memcpy( ( void * ) pxDmaTxDesc->Buffer1Addr, pxDescriptor->pucEthernetBuffer, ulTransmitSize );
}
#else
{
/* Move the buffer. */
pxDmaTxDesc->Buffer1Addr = ( uint32_t )pxDescriptor->pucEthernetBuffer;
/* The Network Buffer has been passed to DMA, no need to release it. */
bReleaseAfterSend = pdFALSE_UNSIGNED;
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
/* Ask to set the IPv4 checksum.
Also need an Interrupt on Completion so that 'vClearTXBuffers()' will be called.. */
pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL | ETH_DMATXDESC_IC;
/* Prepare transmit descriptors to give to DMA. */
/* Set LAST and FIRST segment */
pxDmaTxDesc->Status |= ETH_DMATXDESC_FS | ETH_DMATXDESC_LS;
/* Set frame size */
pxDmaTxDesc->ControlBufferSize = ( ulTransmitSize & ETH_DMATXDESC_TBS1 );
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
pxDmaTxDesc->Status |= ETH_DMATXDESC_OWN;
/* Point to next descriptor */
xETH.TxDesc = ( ETH_DMADescTypeDef * ) ( xETH.TxDesc->Buffer2NextDescAddr );
/* Ensure completion of memory access */
__DSB();
/* Resume DMA transmission*/
xETH.Instance->DMATPDR = 0;
iptraceNETWORK_INTERFACE_TRANSMIT();
xReturn = pdPASS;
}
}
else
{
/* The PHY has no Link Status, packet shall be dropped. */
}
}
while( 0 );
/* The buffer has been sent so can be released. */
if( bReleaseAfterSend != pdFALSE )
{
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
}
return xReturn;
}
/*-----------------------------------------------------------*/
static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer )
{
const ProtocolPacket_t *pxProtPacket = ( const ProtocolPacket_t * )pcBuffer;
switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType )
{
case ipARP_FRAME_TYPE:
/* Check it later. */
return pdTRUE;
case ipIPv4_FRAME_TYPE:
/* Check it here. */
break;
default:
/* Refuse the packet. */
return pdFALSE;
}
#if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 )
{
const IPHeader_t *pxIPHeader = &(pxProtPacket->xTCPPacket.xIPHeader);
uint32_t ulDestinationIPAddress;
/* Ensure that the incoming packet is not fragmented (only outgoing packets
* can be fragmented) as these are the only handled IP frames currently. */
if( ( pxIPHeader->usFragmentOffset & FreeRTOS_ntohs( ipFRAGMENT_OFFSET_BIT_MASK ) ) != 0U )
{
return pdFALSE;
}
/* HT: Might want to make the following configurable because
* most IP messages have a standard length of 20 bytes */
/* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes
* 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */
if( pxIPHeader->ucVersionHeaderLength < 0x45 || pxIPHeader->ucVersionHeaderLength > 0x4F )
{
return pdFALSE;
}
ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress;
/* Is the packet for this node? */
if( ( ulDestinationIPAddress != *ipLOCAL_IP_ADDRESS_POINTER ) &&
/* Is it a broadcast address x.x.x.255 ? */
( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xff ) != 0xff ) &&
#if( ipconfigUSE_LLMNR == 1 )
( ulDestinationIPAddress != ipLLMNR_IP_ADDR ) &&
#endif
( *ipLOCAL_IP_ADDRESS_POINTER != 0 ) ) {
FreeRTOS_printf( ( "Drop IP %lxip\n", FreeRTOS_ntohl( ulDestinationIPAddress ) ) );
return pdFALSE;
}
if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP )
{
uint16_t port = pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort;
if( ( xPortHasUDPSocket( port ) == pdFALSE )
#if ipconfigUSE_LLMNR == 1
&& ( port != FreeRTOS_ntohs( ipLLMNR_PORT ) )
#endif
#if ipconfigUSE_NBNS == 1
&& ( port != FreeRTOS_ntohs( ipNBNS_PORT ) )
#endif
#if ipconfigUSE_DNS == 1
&& ( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort != FreeRTOS_ntohs( ipDNS_PORT ) )
#endif
) {
/* Drop this packet, not for this device. */
return pdFALSE;
}
}
}
#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */
return pdTRUE;
}
/*-----------------------------------------------------------*/
static BaseType_t prvNetworkInterfaceInput( void )
{
NetworkBufferDescriptor_t *pxCurDescriptor;
NetworkBufferDescriptor_t *pxNewDescriptor = NULL;
BaseType_t xReceivedLength, xAccepted;
__IO ETH_DMADescTypeDef *pxDMARxDescriptor;
xIPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 );
uint8_t *pucBuffer;
pxDMARxDescriptor = xETH.RxDesc;
if( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_OWN) == 0 )
{
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
xReceivedLength = ( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_FL ) >> ETH_DMARXDESC_FRAMELENGTHSHIFT ) - 4;
pucBuffer = (uint8_t *) pxDMARxDescriptor->Buffer1Addr;
/* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */
/* Chained Mode */
/* Selects the next DMA Rx descriptor list for next buffer to read */
xETH.RxDesc = ( ETH_DMADescTypeDef* )pxDMARxDescriptor->Buffer2NextDescAddr;
}
else
{
xReceivedLength = 0;
}
/* Obtain the size of the packet and put it into the "usReceivedLength" variable. */
/* get received frame */
if( xReceivedLength > 0ul )
{
/* In order to make the code easier and faster, only packets in a single buffer
will be accepted. This can be done by making the buffers large enough to
hold a complete Ethernet packet (1536 bytes).
Therefore, two sanity checks: */
configASSERT( xReceivedLength <= ETH_RX_BUF_SIZE );
if( ( pxDMARxDescriptor->Status & ( ETH_DMARXDESC_CE | ETH_DMARXDESC_IPV4HCE | ETH_DMARXDESC_FT ) ) != ETH_DMARXDESC_FT )
{
/* Not an Ethernet frame-type or a checmsum error. */
xAccepted = pdFALSE;
}
else
{
/* See if this packet must be handled. */
xAccepted = xMayAcceptPacket( pucBuffer );
}
if( xAccepted != pdFALSE )
{
/* The packet wil be accepted, but check first if a new Network Buffer can
be obtained. If not, the packet will still be dropped. */
pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, xDescriptorWaitTime );
if( pxNewDescriptor == NULL )
{
/* A new descriptor can not be allocated now. This packet will be dropped. */
xAccepted = pdFALSE;
}
}
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
{
/* Find out which Network Buffer was originally passed to the descriptor. */
pxCurDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
configASSERT( pxCurDescriptor != NULL );
}
#else
{
/* In this mode, the two descriptors are the same. */
pxCurDescriptor = pxNewDescriptor;
if( pxNewDescriptor != NULL )
{
/* The packet is acepted and a new Network Buffer was created,
copy data to the Network Bufffer. */
memcpy( pxNewDescriptor->pucEthernetBuffer, pucBuffer, xReceivedLength );
}
}
#endif
if( xAccepted != pdFALSE )
{
pxCurDescriptor->xDataLength = xReceivedLength;
xRxEvent.pvData = ( void * ) pxCurDescriptor;
/* Pass the data to the TCP/IP task for processing. */
if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE )
{
/* Could not send the descriptor into the TCP/IP stack, it
must be released. */
vReleaseNetworkBufferAndDescriptor( pxCurDescriptor );
iptraceETHERNET_RX_EVENT_LOST();
}
else
{
iptraceNETWORK_INTERFACE_RECEIVE();
}
}
/* Release descriptors to DMA */
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
{
/* Set Buffer1 address pointer */
if( pxNewDescriptor != NULL )
{
pxDMARxDescriptor->Buffer1Addr = (uint32_t)pxNewDescriptor->pucEthernetBuffer;
}
else
{
/* The packet was dropped and the same Network
Buffer will be used to receive a new packet. */
}
}
#endif /* ipconfigZERO_COPY_RX_DRIVER */
/* Set Buffer1 size and Second Address Chained bit */
pxDMARxDescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE;
pxDMARxDescriptor->Status = ETH_DMARXDESC_OWN;
/* Ensure completion of memory access */
__DSB();
/* When Rx Buffer unavailable flag is set clear it and resume
reception. */
if( ( xETH.Instance->DMASR & ETH_DMASR_RBUS ) != 0 )
{
/* Clear RBUS ETHERNET DMA flag. */
xETH.Instance->DMASR = ETH_DMASR_RBUS;
/* Resume DMA reception. */
xETH.Instance->DMARPDR = 0;
}
}
return ( xReceivedLength > 0 );
}
/*-----------------------------------------------------------*/
BaseType_t xSTM32_PhyRead( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue )
{
uint16_t usPrevAddress = xETH.Init.PhyAddress;
BaseType_t xResult;
HAL_StatusTypeDef xHALResult;
xETH.Init.PhyAddress = xAddress;
xHALResult = HAL_ETH_ReadPHYRegister( &xETH, ( uint16_t )xRegister, pulValue );
xETH.Init.PhyAddress = usPrevAddress;
if( xHALResult == HAL_OK )
{
xResult = 0;
}
else
{
xResult = -1;
}
return xResult;
}
/*-----------------------------------------------------------*/
BaseType_t xSTM32_PhyWrite( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue )
{
uint16_t usPrevAddress = xETH.Init.PhyAddress;
BaseType_t xResult;
HAL_StatusTypeDef xHALResult;
xETH.Init.PhyAddress = xAddress;
xHALResult = HAL_ETH_WritePHYRegister( &xETH, ( uint16_t )xRegister, ulValue );
xETH.Init.PhyAddress = usPrevAddress;
if( xHALResult == HAL_OK )
{
xResult = 0;
}
else
{
xResult = -1;
}
return xResult;
}
/*-----------------------------------------------------------*/
void phy_test()
{
BaseType_t xPhyCount;
BaseType_t xPhyIndex;
vPhyInitialise( &xPhyObject, xSTM32_PhyRead, xSTM32_PhyWrite );
xPhyCount = xPhyDiscover( &xPhyObject );
FreeRTOS_printf( ( "PHY count %ld\n", xPhyCount ) );
for( xPhyIndex = 0; xPhyIndex < xPhyCount; xPhyIndex++ )
{
FreeRTOS_printf( ( "PHY[%d] at address %d ( 0x%08X )\n",
xPhyIndex,
xPhyObject.ucPhyIndexes[ xPhyIndex ],
xPhyObject.ulPhyIDs[ xPhyIndex ] ) );
}
}
void vMACBProbePhy( void )
{
vPhyInitialise( &xPhyObject, xSTM32_PhyRead, xSTM32_PhyWrite );
xPhyDiscover( &xPhyObject );
xPhyConfigure( &xPhyObject, &xPHYProperties );
}
/*-----------------------------------------------------------*/
static void prvEthernetUpdateConfig( BaseType_t xForce )
{
FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n",
xPhyObject.ulLinkStatusMask,
( int )xForce ) );
if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) )
{
/* Restart the auto-negotiation. */
if( xETH.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE )
{
xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) );
/* Configure the MAC with the Duplex Mode fixed by the
auto-negotiation process. */
if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL )
{
xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
}
else
{
xETH.Init.DuplexMode = ETH_MODE_HALFDUPLEX;
}
/* Configure the MAC with the speed fixed by the
auto-negotiation process. */
if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 )
{
xETH.Init.Speed = ETH_SPEED_10M;
}
else
{
xETH.Init.Speed = ETH_SPEED_100M;
}
}
else /* AutoNegotiation Disable */
{
/* Check parameters */
assert_param( IS_ETH_SPEED( xETH.Init.Speed ) );
assert_param( IS_ETH_DUPLEX_MODE( xETH.Init.DuplexMode ) );
if( xETH.Init.DuplexMode == ETH_MODE_FULLDUPLEX )
{
xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF;
}
else
{
xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL;
}
if( xETH.Init.Speed == ETH_SPEED_10M )
{
xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10;
}
else
{
xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100;
}
xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO;
/* Use predefined (fixed) configuration. */
xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) );
}
/* ETHERNET MAC Re-Configuration */
HAL_ETH_ConfigMAC( &xETH, (ETH_MACInitTypeDef *) NULL);
/* Restart MAC interface */
HAL_ETH_Start( &xETH);
}
else
{
/* Stop MAC interface */
HAL_ETH_Stop( &xETH );
}
}
/*-----------------------------------------------------------*/
BaseType_t xGetPhyLinkStatus( void )
{
BaseType_t xReturn;
if( xPhyObject.ulLinkStatusMask != 0 )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
return xReturn;
}
static void prvEMACHandlerTask( void *pvParameters )
{
TimeOut_t xPhyTime;
UBaseType_t uxLastMinBufferCount = 0;
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
UBaseType_t uxLastMinQueueSpace = 0;
#endif
TickType_t xPhyRemTime;
UBaseType_t uxCurrentCount;
BaseType_t xResult;
uint32_t xStatus;
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
/* Remove compiler warnings about unused parameters. */
( void ) pvParameters;
for( ;; )
{
xResult = 0;
uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
if( uxLastMinBufferCount != uxCurrentCount )
{
/* The logging produced below may be helpful
while tuning +TCP: see how many buffers are in use. */
uxLastMinBufferCount = uxCurrentCount;
FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
}
if( xTXDescriptorSemaphore != NULL )
{
static UBaseType_t uxLowestSemCount = ( UBaseType_t ) ETH_TXBUFNB - 1;
uxCurrentCount = uxSemaphoreGetCount( xTXDescriptorSemaphore );
if( uxLowestSemCount > uxCurrentCount )
{
uxLowestSemCount = uxCurrentCount;
FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) );
}
}
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
{
uxCurrentCount = uxGetMinimumIPQueueSpace();
if( uxLastMinQueueSpace != uxCurrentCount )
{
/* The logging produced below may be helpful
while tuning +TCP: see how many buffers are in use. */
uxLastMinQueueSpace = uxCurrentCount;
FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
}
}
#endif /* ipconfigCHECK_IP_QUEUE_SPACE */
if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )
{
/* No events to process now, wait for the next. */
ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
}
if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
{
ulISREvents &= ~EMAC_IF_RX_EVENT;
xResult = prvNetworkInterfaceInput();
if( xResult > 0 )
{
while( prvNetworkInterfaceInput() > 0 )
{
}
}
}
if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
{
/* Code to release TX buffers if zero-copy is used. */
ulISREvents &= ~EMAC_IF_TX_EVENT;
/* Check if DMA packets have been delivered. */
vClearTXBuffers();
}
if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )
{
/* Future extension: logging about errors that occurred. */
ulISREvents &= ~EMAC_IF_ERR_EVENT;
}
if( xResult > 0 )
{
/* A packet was received. No need to check for the PHY status now,
but set a timer to check it later on. */
vTaskSetTimeOutState( &xPhyTime );
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
xResult = 0;
}
else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
{
HAL_ETH_ReadPHYRegister( &xETH, 0x01,&xStatus );
xPhyObject.ulLinkStatusMask = xStatus;
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )
{
ulPHYLinkStatus = xStatus;
FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );
prvEthernetUpdateConfig( pdFALSE );
}
vTaskSetTimeOutState( &xPhyTime );
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
{
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
}
else
{
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
}
}
}
}
/*-----------------------------------------------------------*/
void ETH_IRQHandler( void )
{
HAL_ETH_IRQHandler( &xETH );
}
- TCP还有一些配置函数在接口文件中没有实现,需要用户自己实现。创建netInfoConfig.c和netInfoConfig.h文件来实现TCP需要的一些接口配置函数参考如下代码。
#include "netInfoConfig.h"
const uint8_t ucIPAddress[ 4 ] = { configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3 };
const uint8_t ucNetMask[ 4 ] = { configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3 };
const uint8_t ucGatewayAddress[ 4 ] = { configGATEWAY_ADDR0, configGATEWAY_ADDR1, configGATEWAY_ADDR2, configGATEWAY_ADDR3 };
const uint8_t ucDNSServerAddress[ 4 ] = { configDNS_SERVER_ADDR0, configDNS_SERVER_ADDR1, configDNS_SERVER_ADDR2, configDNS_SERVER_ADDR3 };
const uint8_t ucMACAddress[ 6 ] = { configMAC_ADDR0, configMAC_ADDR1, configMAC_ADDR2, configMAC_ADDR3, configMAC_ADDR4, configMAC_ADDR5 };
//this queue should be create for vApplicationPingReplyHook()
QueueHandle_t xPingReplyQueue;
/* --------------------------FUNCTION MODULE----------------------------- */
//return a rand num
UBaseType_t uxRand(){
return (UBaseType_t) getRandomNum();
}
//Use this name to request IP during DHCP call
const char *pcApplicationHostnameHook( void )
{
return mainHOST_NAME;
}
/*
* The following function should be provided by the user and return true if it
* matches the domain name.
*this func will be used to judge whether *pcName matche DNS request or LLMNR request.
return pdTRUE if matching DNS request,
return pdFALSE if matching LLMNR request.
*/
BaseType_t xApplicationDNSQueryHook( const char *pcName )
{
BaseType_t xReturn;
if( strcmp( pcName, pcApplicationHostnameHook() ) == 0 )
{
xReturn = pdPASS;
}
else if( strcmp( pcName, mainDEVICE_NICK_NAME ) == 0 )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
return xReturn;
}
void vApplicationPingReplyHook( ePingReplyStatus_t eStatus, uint16_t usIdentifier )
{
switch( eStatus )
{
case eSuccess:
xQueueSend( xPingReplyQueue, &usIdentifier, 10 / portTICK_PERIOD_MS );
break;
case eInvalidChecksum :
break;
case eInvalidData :
break;
}
}
void xPingReplyQueueCreate(void)
{
xPingReplyQueue = xQueueCreate( 20, sizeof( uint16_t ) );
}
BaseType_t vSendPing( const char *pcIPAddress )
{
uint16_t usRequestSequenceNumber, usReplySequenceNumber;
uint32_t ulIPAddress;
ulIPAddress = FreeRTOS_inet_addr( pcIPAddress );
if(xPingReplyQueue == NULL)
xPingReplyQueueCreate();
usRequestSequenceNumber = FreeRTOS_SendPingRequest( ulIPAddress, 8, 100 / portTICK_PERIOD_MS );
if( usRequestSequenceNumber == pdFAIL )
{
}
else
{
if( xQueueReceive( xPingReplyQueue, &usReplySequenceNumber, 200 / portTICK_PERIOD_MS ) == pdPASS )
{
if( usRequestSequenceNumber == usReplySequenceNumber )
{
}
}
}
return ulIPAddress;
}
BaseType_t IP_init( void )
{
return FreeRTOS_IPInit( ucIPAddress, ucNetMask, ucGatewayAddress, ucDNSServerAddress, ucMACAddress );
}
/*---------------------------DEBUG ONLY-------------------------------------*/
int lUDPLoggingPrintf( const char *fmt, ... )
{
return 0;
}
void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent )
{
uint32_t ulIPAddress, ulNetMask, ulGatewayAddress, ulDNSServerAddress;
char cBuffer[ 16 ];
static BaseType_t xTasksAlreadyCreated = pdFALSE;
FreeRTOS_printf( ( "vApplicationIPNetworkEventHook: event %ld\n", eNetworkEvent ) );
if( eNetworkEvent == eNetworkUp )
{
if( xTasksAlreadyCreated == pdFALSE )
{
#if( mainCREATE_UDP_LOGGING_TASK == 1 )
{
vUDPLoggingTaskCreate();
}
#endif
#if( ( mainCREATE_FTP_SERVER == 1 ) || ( mainCREATE_HTTP_SERVER == 1 ) )
{
/* Let the server work task now it can now create the servers. */
xTaskNotifyGive( xServerWorkTaskHandle );
}
#endif
#if( TCP_CLIENT == 1 )
{
// vStartTCPEchoClientTasks_SingleTasks( mainECHO_CLIENT_TASK_STACK_SIZE, mainECHO_CLIENT_TASK_PRIORITY );
}
#endif
#if( TCP_SERVER == 1 )
{
// vStartSimpleTCPServerTasks( mainECHO_SERVER_STACK_SIZE, mainECHO_SERVER_TASK_PRIORITY );
}
#endif
#if( mainCREATE_UDP_CLI_TASKS == 1 )
{
vRegisterSampleCLICommands();
vRegisterTCPCLICommands();
vStartUDPCommandInterpreterTask( mainUDP_CLI_TASK_STACK_SIZE, mainUDP_CLI_PORT_NUMBER, mainUDP_CLI_TASK_PRIORITY );
}
#endif
xTasksAlreadyCreated = pdTRUE;
}
/* Print out the network configuration, which may have come from a DHCP
server. */
FreeRTOS_GetAddressConfiguration( &ulIPAddress, &ulNetMask, &ulGatewayAddress, &ulDNSServerAddress );
FreeRTOS_inet_ntoa( ulIPAddress, cBuffer );
FreeRTOS_printf( ( "IP Address: %s\n", cBuffer ) );
FreeRTOS_inet_ntoa( ulNetMask, cBuffer );
FreeRTOS_printf( ( "Subnet Mask: %s\n", cBuffer ) );
FreeRTOS_inet_ntoa( ulGatewayAddress, cBuffer );
FreeRTOS_printf( ( "Gateway Address: %s\n", cBuffer ) );
FreeRTOS_inet_ntoa( ulDNSServerAddress, cBuffer );
FreeRTOS_printf( ( "DNS Server Address: %s\n", cBuffer ) );
}
}
在main.c文件中引用头文件netInfoConfig.h,在main函数中调用初始化函数。参考如下代码。
#include "bsp_clock.h"
#include "randomnum.h"
#include "netInfoConfig.h"
int main(void)
{
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
CLOCLK_Init();
RNG_init();
IP_init();
vTaskStartScheduler();
while(1);
}
- 编译工程,有14个错误。这里是要提供两个编译器内置的命令以取消结构体自动字节对齐,如果用的IDE是keil,那么默认编译器是armcc,需要pack_struct_start.h文件内添加#pragma pack(1),并在pack_struct_end.h文件内添加#pragma pack(),在FreeRTOS-TCP\include路径下创建这两个文件,并将代码添加进去。再次编译。
- 工程编译,有216个错误,报错类型说明符的无效组合,在报错的结构体后面加上“;”。再次编译。
- 工程编译,有35个错误,在FreeRTOS_IP.h文件中引用FreeRTOS.h和list.h头文件
- 工程编译,有1个错误。注释掉phyHandling.c文件下eventLogging.h的引用。再次编译。
- 工程编译,有1个错误,eventLogAdd()函数没被定义。注释掉相关引用。再次编译。无错误。
- 修改stm32f4xx_hal_eth.c文件下函数HAL_ETH_IRQHandler()里的else if,改成if。参考如下代码。
void HAL_ETH_IRQHandler(ETH_HandleTypeDef *heth)
{
/* Frame received */
if (__HAL_ETH_DMA_GET_FLAG(heth, ETH_DMA_FLAG_R))
{
/* Receive complete callback */
HAL_ETH_RxCpltCallback(heth);
/* Clear the Eth DMA Rx IT pending bits */
__HAL_ETH_DMA_CLEAR_IT(heth, ETH_DMA_IT_R);
/* Set HAL State to Ready */
heth->State = HAL_ETH_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(heth);
}
/* Frame transmitted */
if (__HAL_ETH_DMA_GET_FLAG(heth, ETH_DMA_FLAG_T))
{
/* Transfer complete callback */
HAL_ETH_TxCpltCallback(heth);
/* Clear the Eth DMA Tx IT pending bits */
__HAL_ETH_DMA_CLEAR_IT(heth, ETH_DMA_IT_T);
/* Set HAL State to Ready */
heth->State = HAL_ETH_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(heth);
}
/* Clear the interrupt flags */
__HAL_ETH_DMA_CLEAR_IT(heth, ETH_DMA_IT_NIS);
/* ETH DMA Error */
if(__HAL_ETH_DMA_GET_FLAG(heth, ETH_DMA_FLAG_AIS))
{
/* Ethernet Error callback */
HAL_ETH_ErrorCallback(heth);
/* Clear the interrupt flags */
__HAL_ETH_DMA_CLEAR_IT(heth, ETH_DMA_FLAG_AIS);
/* Set HAL State to Ready */
heth->State = HAL_ETH_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(heth);
}
}
- 屏蔽FreeRTOSConfig.h文件下的#define xPortSysTickHandler SysTick_Handler,创建prot.h文件,文件放在FreeRTOS\portable\RVDS\ARM_CM4F路径下。文件内声明xPortSysTickHandler()函数。
- 修改bsp_clock.c文件,加入以下函数。
void SysTick_Handler()
{
HAL_IncTick();
xPortSysTickHandler();
}
- 编译运行,无错误,下载到开发板,连接网线到路由器,登录路由器查看ip地址,ping 开发板IP,正常ping通。
本文为个人爱好所做,欢迎各位大佬指点,如有错误之处还请见谅。