FreeRTOS+TCP模块移植

上一版本移植并没有写的很详细,只是将改好的代码贴上去,今天更新一版,附带资源。上一版本用的是FreeRTOS V10.0.1.这一版采用了最新的FreeRTOS V10.3.1

在正确移植FreeRTOS的工程中Libraries文件夹下创建FreeRTOS-TCP文件夹用来存放TCP源码,将FreeRTOS-Plus文件源码中FreeRTOS-Plus\Source\FreeRTOS-Plus-TCP路径下的全部文件拷贝到新建的FreeRTOS-TCP文件夹中。

打开工程,添加分组FreeRTOS_TCP/Source,用来存放TCP源码。将FreeRTOS_TCP路径下的C文件添加到FreeRTOS_TCP分组中。将FreeRTOS-TCP\portable\BufferManagement路径下的内存管理文件BufferAllocation_c、FreeRTOS-TCP\portable\NetworkInterface\STM32Fxx路径下的接口文件NetworkInterface.c和FreeRTOS-TCP\portable\NetworkInterface\Common路径下的物理层处理文件phyHandling.c添加到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驱动。将文件添加到工程中。使能ETH、DMA库。参考下面代码。

#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协议里需要用到随机数,这个随机数需要用户提供。使能RNG库。随机数的创建参考下面代码。

#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();     

      }

}

 

修改接口文件NetworkInterface.c下的初始化函数,引用TCP配置文件FreeRTOSIPConfig.h。引用LAN8720初始化函数。媒体接口改为RMII。引用头文件bsp_eth.h。删除PHY_AUTONEGO_COMPLETE宏定义。具体修改请参考如下代码。

BaseType_t xNetworkInterfaceInitialise( void )

{

HAL_StatusTypeDef hal_eth_init_status;

BaseType_t xResult;



  if( xEMACTaskHandle == NULL )

  {

      #if( ipconfigZERO_COPY_TX_DRIVER != 0 )

      {

          if( xTXDescriptorSemaphore == NULL )

          {

              xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB );

              configASSERT( xTXDescriptorSemaphore );

          }

      }

      #endif /* ipconfigZERO_COPY_TX_DRIVER */



      /* 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;

      xETH.Init.PhyAddress = 0;



      xETH.Init.MACAddr = ( uint8_t *) ucMACAddress;

      xETH.Init.RxMode = ETH_RXINTERRUPT_MODE;



      /* using the ETH_CHECKSUM_BY_HARDWARE option:

      both the IP and the protocol checksums will be calculated

      by the peripheral. */

      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 ) );



      #if( ipconfigZERO_COPY_TX_DRIVER != 0 )

      {

          /* Initialize Tx Descriptors list: Chain Mode */

          DMATxDescToClear = DMATxDscrTab;

      }

      #endif /* ipconfigZERO_COPY_TX_DRIVER */



      /* Initialise TX-descriptors. */

      prvDMATxDescListInit();



      /* Initialise RX-descriptors. */

      prvDMARxDescListInit();



      #if( ipconfigUSE_LLMNR != 0 )

      {

          /* Program the LLMNR address at index 1. */

          prvMACAddressConfig( &xETH, ETH_MAC_ADDRESS1, ( uint8_t *) xLLMNR_MACAddress );

      }

      #endif



      /* Force a negotiation with the Switch or Router and wait for LS. */

      prvEthernetUpdateConfig( pdTRUE );



      /* The deferred interrupt handler task is created at the highest

      possible priority to ensure the interrupt handler can return directly

      to it.  The task's handle is stored in xEMACTaskHandle so interrupts can

      notify the task when there is something to process. */

      xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );

  } /* if( xEMACTaskHandle == NULL ) */

 

  if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )

  {

      xETH.Instance->DMAIER |= ETH_DMA_ALL_INTS;

      xResult = pdPASS;

      FreeRTOS_printf( ( "Link Status is high\n" ) ) ;

  }

  else

  {

      /* For now pdFAIL will be returned. But prvEMACHandlerTask() is running

      and it will keep on checking the PHY and set ulPHYLinkStatus when necessary. */

      xResult = pdFAIL;

      FreeRTOS_printf( ( "Link Status still low\n" ) ) ;

  }

  /* When returning non-zero, the stack will become active and

    start DHCP (in configured) */

  return xResult;

}

 

TCP还有一些配置函数在接口文件中没有实现,需要用户自己实现。创建netInfoConfig.c和netInfoConfig.h文件用来实现TCP需要的一些接口配置函数。将文件添加在FreeRTOS_TCP/Ports组下。参考如下代码。

#include "netInfoConfig.h" 

#include "FreeRTOS.h"

#include "task.h"

#include "queue.h"

#include "semphr.h"

#include "string.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 };



QueueHandle_t xPingReplyQueue;



UBaseType_t uxRand(){ 

    return (UBaseType_t) getRandomNum(); 

} 



const char *pcApplicationHostnameHook( void )

{

  return mainHOST_NAME;

}

 

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 ); 

} 





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( mainCREATE_UDP_CLI_TASKS == 1 )

          {

              vRegisterSampleCLICommands();

              vRegisterTCPCLICommands();

              vStartUDPCommandInterpreterTask( mainUDP_CLI_TASK_STACK_SIZE, mainUDP_CLI_PORT_NUMBER, mainUDP_CLI_TASK_PRIORITY );

          }

          #endif

          xTasksAlreadyCreated = pdTRUE;

      }



      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 ) );

  }

}



extern uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress,

                                                  uint16_t usSourcePort,

                                                  uint32_t ulDestinationAddress,

                                                  uint16_t usDestinationPort )

{

  ( void ) ulSourceAddress;

  ( void ) usSourcePort;

  ( void ) ulDestinationAddress;

  ( void ) usDestinationPort;



  return uxRand();

}





BaseType_t xApplicationGetRandomNumber(uint32_t* pulNumber)

{

  *(pulNumber) = uxRand();

  return pdTRUE;

}

 

修改网络配置文件FreeRTOSIPConfig.h,引用头文件stm32f4xx.h、在文件添加宏定义使能发送校验。在文件121、122行修改随机数配置、定义零拷贝发送、接收使能、定义MAC、IP、GATEWAY、DNS_SERVER、ECHO_SERVER地址和NET_MASK。定义USE_STM324xG_EVAL为0。参考如下代码。

// 使能发送校验

#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM   1



// 修改随机数为如下代码

extern uint32_t Random_GetNumber(void);

#define ipconfigRAND32() Random_GetNumber()



// 配置地址、硬件选择、收发模式

#define USE_STM324xG_EVAL    0



#define ipconfigZERO_COPY_RX_DRIVER          ( 1 )

#define ipconfigZERO_COPY_TX_DRIVER          ( 1 )



#define configMAC_ADDR0      0x00

#define configMAC_ADDR1      0x51

#define configMAC_ADDR2      0x52

#define configMAC_ADDR3      0x53

#define configMAC_ADDR4      0x54

#define configMAC_ADDR5      0x55



#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

 

注释FreeRTOSConfig.h文件下的#define xPortSysTickHandler SysTick_Handler。在FreeRTOS\portable\RVDS\ARM_CM4F路径下创建port.h文件,在文件中加入函数声明void xPortSysTickHandler( void );。在clock.c文件中引用port.h头文件并加入如下代码。

void SysTick_Handler()

{

  HAL_IncTick();

  xPortSysTickHandler();

}

 

在main.c文件中引用头文件netInfoConfig.h,在main函数中调用初始化函数。参考如下代码。

#include "bsp_clock.h"

#include "bsp_randomnum.h"

#include "netInfoConfig.h" 



int main(void)

{

  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  CLOCLK_Init();

 

  RNG_init();                                           

  IP_init();

  vTaskStartScheduler();

}

 

工程编译,出现大量错误,报错类型是类型未定义,在FreeRTOS_IP.h文件中引用FreeRTOS.h和list.h头文件。

编译工程,有13个错误。这里是要提供两个编译器内置的命令以取消结构体自动字节对齐,如果用的IDE是keil,那么默认编译器是armcc,需要pack_struct_start.h文件内添加#pragma pack(1),并在pack_struct_end.h文件内添加#pragma pack(),在FreeRTOS-TCP\include路径下创建这两个文件,并将代码添加进去。再次编译。

工程编译,出现大量错误,报错类型是说明符的无效组合,在报错的结构体后面加上英文“;”。再次编译。

工程编译,出现大量警告,警告类型是已弃用声明,在函数定义和声明的地方将参数加入void。

修改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);
  }
}

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32F407是一款32位ARM Cortex-M4内核的微控制器,拥有丰富的周边设备和高性能。FreeRTOS是一款用于嵌入式系统的实时操作系统,提供了任务调度、内存管理、定时器等功能,使得开发者能够创建复杂的多任务应用程序。TCP客户端是指在TCP/IP协议栈的应用层,通过TCP协议与服务器进行通信的客户端程序。 将STM32F407与FreeRTOSTCP客户端结合起来,可以创建一个具有实时性能的TCP客户端应用。首先,我们需要将FreeRTOS移植STM32F407上,这可以通过使用STM32CubeMX和FreeRTOS内核文件进行配置和生成来实现。接下来,我们需要编写TCP客户端代码,使用TCP/IP协议栈的相关API进行连接服务器、发送和接收数据等操作。在编写TCP客户端代码时,我们需要创建一个或多个任务,用于处理与服务器的TCP连接和数据通信。这些任务可以使用FreeRTOS提供的任务调度器进行管理和调度。 在使用STM32F407、FreeRTOSTCP客户端时,还需要注意一些重要的方面。首先,需要根据应用需求进行系统资源的合理配置,包括内存大小、任务优先级等。其次,需要注意任务之间的同步和通信,以避免竞争条件和数据一致性的问题。还需要考虑网络连接的稳定性,处理网络异常和错误情况的方法。最后,还需要进行性能测试和调优,以确保系统在给定约束条件下的稳定工作。 综上所述,将STM32F407、FreeRTOSTCP客户端结合起来,可以实现一个具有实时性能的嵌入式TCP客户端应用程序。这种应用可以广泛应用于物联网、智能家居、远程监测等领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值