【安富萊】【RL-TCPnet網絡教程】第10章 RL-TCPnet網絡協議棧移植(FreeRTOS)


第10章     RL-TCPnet網絡協議棧移植(FreeRTOS)

本章教程為大家講解RL-TCPnet網絡協議棧的FreeRTOS操作系統移植方式,學習了第6章講解的底層驅動接口函數之后,移植就比較容易了,主要是添加庫文件、配置文件和驅動文件即可。另外,RL-TCPnet移植到FreeRTOS要重新配置RL-TCPnet的接口函數,以此來支持RL-TCPnet多任務運行。使用RTX無需重新配置,因為默認情況下就是采用RTX的API函數配置的。

本章教程含STM32F407開發板和STM32F429開發板的移植。

10.1  移植前准備工作說明

10.2  STM32F407移植RL-TCPnet協議棧

10.3  STM32F429移植RL-TCPnet協議棧

10.4  總結

 

 

10.1  移植前准備工作說明

1、學習本章節前,務必要優先學習第6章的底層驅動講解。

2、RL-TCPnet只有庫,沒有源碼。庫分為兩個版本,一個用於調試的版本TCPD_CM3.lib和一個正式版本TCP_CM3.lib,當前的例子統一使用調試版本。另外注意,雖然是CM3版本的,但可同時用於CM3和CM4內核的MCU,因為官方沒有專門的CM4內核庫。

3、測試時,請將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址。

     而且使能了NetBIOS局域網域名,用戶只需在電腦端ping armfly,就可以獲得板子的IP地址。

4、如果要使用固定IP進行測試,請看附件C。

5、網口使用的是DM9161/9162(挨着9幀串口座的網口),而不是DM9000。

6、找一個簡單的工程,最好是跑馬燈之類的,越簡單越好,我們就在這個簡單的工程上面移植即可。

 

10.2  STM32F407移植RL-TCPnet協議棧

10.2.1  RL-TCPnet網絡協議棧移植

首先准備好一個簡單的FreeRTOS工程模板,工程模板的制作就不做講解了,這里的重點是教大家移植RL-TCPnet協議棧。准備好的工程模板如下圖所示(大家也可以制作其它任意的工程模板,不限制):

 

准備好工程模板后,就可以開始移植了。首先要做的就是將所有需要的文件放到工程模板里面。下面分4步跟大家進行說明,當然,不限制必須使用下面的方法添加源碼到工程,只要將需要的文件添加到工程模板即可。

第1步:將我們FreeRTOS模板中制作好的RL-ARM文件夾復制粘貼到大家准備好的工程模板中。

 

RL-ARM文件夾中有如下七個文件夾:

 

Config文件夾用於存放RTX及其中間件的配置文件。

Driver文件夾用於存放中間件的驅動文件,也就是底層移植文件。

RL-CAN文件夾用於存放CAN總線的源碼文件。

RL-FlashFS文件夾用於存放文件系統RL-FlashFS的庫文件。

RL-RTX文件夾用於存放RTX的源碼文件。

RL-TCPnet文件夾用於存放網絡協議棧RL-TCPnet的庫文件。

RL-USB文件夾用於存放USB協議棧RL-USB的庫文件。

也許有用戶會問:我們不是僅僅需要移植RL-TCPnet的相關文件就行了嗎,為什么把RTX及其所有中間件都添加進來了?這樣做的目的是為了以后升級的方便,如果需要添加USB、文件系統、CAN等組件,直接添加到工程即可。

這些文件全部來自MDK4.74的安裝目錄,庫文件位於路徑:C:\Keil_v474\ARM\RV31下,而驅動和配置文件位於路徑:C:\Keil_v474\ARM\RL下。

第2步:添加RL-TCPnet的庫文件、配置文件和驅動文件到工程,添加完畢后的效果如下:

 

Net_lib.c,Net_Config.c和NET_Debug.c在RL-ARM文件夾的Config文件里面。

TCPD_CM3.lib在RL-ARM文件夾的RL-TCPnet文件里面。

ETH_STM32F4xx.c和ETH_STM32F4xx.h在RL-ARM文件夾的Driver文件里面。

第3步:添加相應的頭文件路徑,在原來工程模板的基礎上新添加的幾個路徑:

 

第4步:也是最后一步,添加預定義宏,點擊MDK的option -> c/c++選項,添加上__RTX(注意,字母RTX前面有兩個下划線的),添加這個宏定義才可以使能RL-TCPnet的多任務支持。

 

至此,RL-TCPnet的移植工作就完成了,剩下就是系統配置和應用了。

 

10.2.2 RL-TCPnet配置說明(Net_Config.c)

RL-TCPnet的配置工作是通過配置文件Net_Config.c實現。在MDK工程中打開文件Net_Config.c,可以看到下圖所示的工程配置向導:

 

RL-TCPnet要配置的選項非常多,我們這里把幾個主要的配置選項簡單介紹下。

 

System Definitions

(1)Local Host Name

局域網域名。

這里起名為armfly,使用局域網域名限制為15個字符。

(2)Memory Pool size

參數范圍1536-262144字節。

內存池大小配置,單位字節。另外注意一點,配置向導這里顯示的單位是字節,如果看原始定義,MDK會做一個自動的4字節倍數轉換,比如我們這里配置的是8192字節,那么原始定義是#define MEM_SIZE  2048,也就是8192/4 = 2048。

(3)Tick Timer interval

可取10,20,25,40,50,100,200,單位ms。

系統滴答時鍾間隔,也就是網絡協議棧的系統時間基准,默認情況下,取值100ms。

 

Ethernet Network Interface

以太網接口配置,勾選了此選項就可以配置了,如果沒有使能DHCP的話,將使用這里配置的固定IP

(1)MAC Address

局域網內可以隨意配置,只要不跟局域網內其它設備的MAC地址沖突即可。

(2)IP Address

IP地址。

(3)Subnet mask

子網掩碼。

(4)Default Gateway

默認網關。

 

Ethernet Network Interface

以太網接口配置,這個配置里面還有如下兩項比較重要的配置需要說明。

(1)NetBIOS Name Service

NetBIOS局域網域名服務,這里打上對勾就使能了。這樣我們就可以通過前面配置的Local Host Name局域網域名進行訪問,而不需要通過IP地址訪問了。

(2)Dynaminc Host Configuration

即DHCP,這里打上對勾就使能了。使能了DHCP后,RL-TCPnet就可以從外接的路由器上獲得動態IP地址。

 

UDP Sockets

UDP Sockets配置,打上對勾就使能了此項功能

(1)Number of UDP Sockets

用於配置可創建的UDP Sockets數量。

范圍1 – 20。

 

TCP Sockets

TCP Sockets配置,打上對勾就使能了此項功能

(1)Number of TCP Sockets

用於配置可創建的TCP Sockets數量。

(2)Number of Retries

范圍0-20。

用於配置重試次數,TCP數據傳輸時,如果在設置的重試時間內得不到應答,算一次重試失敗,這里就是配置的最大重試次數。

(3)Retry Timeout in seconds

范圍1-10,單位秒。

重試時間。學習了第6章講解的底層驅動接口函數之后,移植就比較容易了。

(4)Default Connect Timeout in seconds

范圍1-600,單位秒。

用於配置默認的保持連接時間,即我們常說的Keep Alive時間,如果時間到了將斷開連接。常用於HTTP Server,Telnet Server等。

(5)Maximum Segment Size

范圍536-1460,單位字節。

MSS定義了TCP數據包能夠傳輸的最大數據分段。

(6)Receive Window Size

范圍536-65535,單位字節。

TCP接收窗口大小。

 

10.2.3 RL-TCPnet調試說明(Net_Debug.c)

重要說明,RL-TCPnet的調試是通過串口打印出來的

RL-TCPnet的調試功能是通過配置文件Net_Debug.c實現。在MDK工程中打開文件Net_Debug.c,可以看到下圖所示的工程配置向導:

 

Print Time Stamp

勾選了此選項的話,打印消息時,前面會附帶時間信息。

其它所有的選項

默認情況下,所有的調試選項都是關閉的,每個選項有三個調試級別可選擇,這里我們以Memory Management Debug為例,點擊下拉列表,可以看到里面有Off,Errors only和Full debug三個調試級別可供選擇,每個調試選項里面都是這三個級別。

 

Off:表示關閉此選項的調試功能。

Errors only:表示僅在此選項出錯時,將其錯誤打印出來。

Full debug:表示此選項的全功能調試。

關於調試功能的使用會在第11章詳細為大家講解,移植階段將其全部關閉即可。

 

10.2.4 RL-TCPnet的多任務驅動接口函數

要讓RL-TCPnet支持多任務,就需要修改Net_lib.c文件。默認情況下,Net_lib.c文件是支持RTX操作系統的,現在要將其修改為支持FreeRTOS,需要修改的幾個地方如下:

添加FreeRTOS的頭文件。

#if (__RTX)

  #include "FreeRTOS.h"

  #include "task.h"

  #include "queue.h"

  #include "semphr.h"

#endif

定義信號量和互斥信號量。

#if (BSD_ENABLE)

 static BSD_INFO bsd_scb[BSD_NUMSOCKS + BSD_SRVSOCKS];

 #ifdef __RTX

   SemaphoreHandle_t  bsd_mutex = NULL;

   SemaphoreHandle_t  bsd_sem = NULL;

  #define BSD_INRTX  __TRUE

 #else

  #define BSD_INRTX  __FALSE

 #endif

 BSD_CFG bsd_config = {

   bsd_scb,

   BSD_NUMSOCKS + BSD_SRVSOCKS,

   BSD_INRTX,

   BSD_RCVTOUT * TICK_RATE

 };

#endif

創建互斥信號量和信號量。

/*--------------------------- init_system -----------------------------------*/

void init_system (void) {

  /* Initialize configured interfaces and applications. */

    

#if (ETH_ENABLE)

  eth_init_link ();

#endif

#if (PPP_ENABLE)

  ppp_init_link ();

#endif

#if (SLIP_ENABLE)

  slip_init_link ();

#endif

  ip_init ();

  icmp_init ();

#if (ETH_ENABLE && IGMP_ENABLE)

  igmp_init ();

#endif

#if (UDP_ENABLE)

  udp_init ();

#endif

#if (TCP_ENABLE)

  tcp_init ();

#endif

#if (BSD_ENABLE)

  bsd_init ();

 #if (BSD_GETHOSTEN)

  bsd_init_host ();

 #endif

#endif

#if (HTTP_ENABLE)

  http_init ();

#endif

#if (TNET_ENABLE)

  tnet_init ();

#endif

#if (TFTP_ENABLE)

  tftp_init ();

#endif

#if (TFTPC_ENABLE)

  tftpc_init ();

#endif

#if (FTP_ENABLE)

  ftp_init ();

#endif

#if (FTPC_ENABLE)

  ftpc_init ();

#endif

#if (ETH_ENABLE && NBNS_ENABLE)

  nbns_init ();

#endif

#if (ETH_ENABLE && DHCP_ENABLE)

  dhcp_init ();

#elif (ETH_ENABLE)

  arp_notify ();

#endif

#if (DNS_ENABLE)

  dns_init ();

#endif

#if (SMTP_ENABLE)

  smtp_init ();

#endif

#if (SNMP_ENABLE)

  snmp_init ();

#endif

#if (SNTP_ENABLE)

  sntp_init ();

#endif

 

#if (BSD_ENABLE && __RTX)

 {

     bsd_mutex = xSemaphoreCreateMutex();

     if(bsd_mutex == NULL)

    {

        /* ERR */

    }   

    

     bsd_sem = xSemaphoreCreateBinary();

     if(bsd_sem == NULL)

    {

        /* ERR */

    }

 }

#endif

}

系統運行時,使用互斥信號量。

/*--------------------------- run_system ------------------------------------*/

void run_system (void) {

  /* Run configured interfaces and applications. */

 

#if (BSD_ENABLE && __RTX)

     xSemaphoreTake(bsd_mutex, portMAX_DELAY);

#endif

 

#if (ETH_ENABLE)

  eth_run_link ();

#endif

#if (PPP_ENABLE)

  ppp_run_link ();

#endif

#if (SLIP_ENABLE)

  slip_run_link ();

#endif

  ip_run_local ();

  icmp_run_engine ();

#if (ETH_ENABLE && IGMP_ENABLE)

  igmp_run_host ();

#endif

#if (TCP_ENABLE)

  tcp_poll_sockets ();

#endif

#if (BSD_ENABLE)

  bsd_poll_sockets ();

#endif

#if (HTTP_ENABLE)

  http_run_server ();

#endif

#if (TNET_ENABLE)

  tnet_run_server ();

#endif

#if (TFTP_ENABLE)

  tftp_run_server ();

#endif

#if (TFTPC_ENABLE)

  tftpc_run_client ();

#endif

#if (FTP_ENABLE)

  ftp_run_server ();

#endif

#if (FTPC_ENABLE)

  ftpc_run_client ();

#endif

#if (ETH_ENABLE && DHCP_ENABLE)

  dhcp_run_client ();

#endif

#if (DNS_ENABLE)

  dns_run_client ();

#endif

#if (SMTP_ENABLE)

  smtp_run_client ();

#endif

#if (SNMP_ENABLE)

  snmp_run_agent ();

#endif

#if (SNTP_ENABLE)

  sntp_run_client ();

#endif

 

#if (BSD_ENABLE && __RTX)

  xSemaphoreGive(bsd_mutex);

#endif

}

使能BSD Socket的話,Socket掛起和恢復的實現。

/*--------------------------- bsd_suspend/resume ----------------------------*/

#if (BSD_ENABLE && __RTX)

__used void bsd_suspend (U8 *tsk_id) {

  /* Suspend a socket owner task. */

  xSemaphoreGive(bsd_mutex);

  xSemaphoreTake(bsd_sem, portMAX_DELAY);

  xSemaphoreTake(bsd_mutex, portMAX_DELAY);   

}

 

__used void bsd_resume (U8 tsk_id) {

  /* Resume a task waiting for a socket event. */

  xSemaphoreGive(bsd_sem);

}

#endif

使能BSD Socket的話,鎖機制的實現。

/*--------------------------- bsd_lock/unlock -------------------------------*/

#if (BSD_ENABLE && __RTX)

__used void bsd_lock (void) {

  /* Acquire mutex - Lock TCPnet functions. */

  xSemaphoreTake(bsd_mutex, portMAX_DELAY);   

}

 

__used void bsd_unlock (void) {

  /* Release mutex - Unlock TCPnet functions. */

  xSemaphoreGive(bsd_mutex);

}

#endif

 

10.2.5 RL-TCPnet應用實例

為了驗證移植的RL-TCPnet是否可以使用,需要添加測試代碼。下面是編寫的測試代碼,配套的測試例子完整版是:V5-1003_RL-TCPnet實驗_工程移植模板(FreeRTOS)。

 

FreeRTOS操作系統創建的任務

經過上面的移植和配置之后,在 main.c 文件中添加如下代碼,代碼中創建了5個用戶任務:

vTaskTaskUserIF 任務: 按鍵消息處理。

vTaskLED        任務: LED閃爍。

vTaskMsgPro    任務: 消息處理,這里用作按鍵檢測。

vTaskTCPnet    任務: RL-TCPnet測試任務。

vTaskStart      任務: 啟動任務,實現RL-TCPnet的時間基准更新。

具體代碼如下:

#include "includes.h"

 

 

 

/*

**********************************************************************************************************

                                                   函數聲明

**********************************************************************************************************

*/

static void vTaskTaskUserIF(void *pvParameters);

static void vTaskLED(void *pvParameters);

static void vTaskMsgPro(void *pvParameters);

static void vTaskTCPnet(void *pvParameters);

static void vTaskStart(void *pvParameters);

static void AppTaskCreate (void);

static void AppObjCreate (void);

static void App_Printf(char *format, ...);

 

 

/*

**********************************************************************************************************

                                                   變量聲明

**********************************************************************************************************

*/

static TaskHandle_t xHandleTaskUserIF = NULL;

static TaskHandle_t xHandleTaskLED = NULL;

static TaskHandle_t xHandleTaskMsgPro = NULL;

static TaskHandle_t xHandleTaskTCPnet = NULL;

static TaskHandle_t xHandleTaskStart = NULL;

static SemaphoreHandle_t  xMutex = NULL;

EventGroupHandle_t xCreatedEventGroup = NULL;

 

 

/*

*********************************************************************************************************

*    函 數 名: main

*    功能說明: 標准c程序入口。

*    形    參:無

*    返 回 值: 無

*********************************************************************************************************

*/

int main(void)

{

     /*

       在啟動調度前,為了防止初始化STM32外設時有中斷服務程序執行,這里禁止全局中斷(除了NMI和HardFault)。

       這樣做的好處是:

       1. 防止執行的中斷服務程序中有FreeRTOS的API函數。

       2. 保證系統正常啟動,不受別的中斷影響。

       3. 關於是否關閉全局中斷,大家根據自己的實際情況設置即可。

       在移植文件port.c中的函數prvStartFirstTask中會重新開啟全局中斷。通過指令cpsie i開啟,__set_PRIMASK(1)

       和cpsie i是等效的。

     */

     __set_PRIMASK(1); 

    

     /* 硬件初始化 */

     bsp_Init();

    

     /* 1. 初始化一個定時器中斷,精度高於滴答定時器中斷,這樣才可以獲得准確的系統信息 僅供調試目的,實際項

           目中不要使用,因為這個功能比較影響系統實時性。

        2. 為了正確獲取FreeRTOS的調試信息,可以考慮將上面的關閉中斷指令__set_PRIMASK(1); 注釋掉。

     */

     vSetupSysInfoTest();

    

     /* 創建任務 */

     AppTaskCreate();

 

     /* 創建任務通信機制 */

     AppObjCreate();

    

    /* 啟動調度,開始執行任務 */

    vTaskStartScheduler();

 

     /*

       如果系統正常啟動是不會運行到這里的,運行到這里極有可能是用於定時器任務或者空閑任務的

       heap空間不足造成創建失敗,此要加大FreeRTOSConfig.h文件中定義的heap大小:

       #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )

     */

     while(1);

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskTaskUserIF

*    功能說明: 接口消息處理。

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 1  (數值越小優先級越低,這個跟uCOS相反)

*********************************************************************************************************

*/

static void vTaskTaskUserIF(void *pvParameters)

{

     uint8_t ucKeyCode;

     uint8_t pcWriteBuffer[500];

 

    

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1鍵按下,直接發送事件標志給任務vTaskTCPnet,設置bit0 */

                   case KEY_DOWN_K1:

                       App_Printf("K1鍵按下,直接發送事件標志給任務AppTaskTCPMain,bit0被設置\r\n");

                       xEventGroupSetBits(xCreatedEventGroup, KEY1_BIT0);          

                       break;  

 

                   /* K2鍵按下,直接發送事件標志給任務vTaskTCPnet,設置bit1 */

                   case KEY_DOWN_K2:

                       App_Printf("K2鍵按下,直接發送事件標志給任務AppTaskTCPMain,bit1被設置\r\n");

                       xEventGroupSetBits(xCreatedEventGroup, KEY2_BIT1);

                       break;

                  

                   /* K3鍵按下,直接發送事件標志給任務vTaskTCPnet,設置bit2 */

                   case KEY_DOWN_K3:

                       App_Printf("K3鍵按下,直接發送事件標志給任務AppTaskTCPMain,bit2被設置\r\n");

                       xEventGroupSetBits(xCreatedEventGroup, KEY3_BIT2);

                       break;

                  

                   /* 搖桿的OK鍵按下,打印任務執行情況 */

                   case JOY_DOWN_OK:          

                       App_Printf("=================================================\r\n");

                       App_Printf("任務名      任務狀態 優先級   剩余棧 任務序號\r\n");

                       vTaskList((char *)&pcWriteBuffer);

                       App_Printf("%s\r\n", pcWriteBuffer);

                  

                       App_Printf("\r\n任務名       運行計數         使用率\r\n");

                       vTaskGetRunTimeStats((char *)&pcWriteBuffer);

                       App_Printf("%s\r\n", pcWriteBuffer);

                       App_Printf("當前動態內存剩余大小 = %d字節\r\n", xPortGetFreeHeapSize());

                       break;

                  

                   /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

        

         vTaskDelay(20);

     }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskLED

*    功能說明: LED閃爍

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 2 

*********************************************************************************************************

*/

static void vTaskLED(void *pvParameters)

{

     TickType_t xLastWakeTime;

     const TickType_t xFrequency = 500;

 

     /* 獲取當前的系統時間 */

    xLastWakeTime = xTaskGetTickCount();

    

    while(1)

    {

         bsp_LedToggle(2);

        

          /* vTaskDelayUntil是絕對延遲,vTaskDelay是相對延遲。*/

        vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskMsgPro

*    功能說明: 消息處理,這里用作按鍵檢測

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 3 

*********************************************************************************************************

*/

static void vTaskMsgPro(void *pvParameters)

{

    while(1)

    {

          /* 按鍵掃描 */

         bsp_KeyScan();

         vTaskDelay(10);

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskTCPnet

*    功能說明: RL-TCPnet測試任務

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 4 

*********************************************************************************************************

*/

static void vTaskTCPnet(void *pvParameters)

{

    while(1)

    {

         TCPnetTest();

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskStart

*    功能說明: 啟動任務,也是最高優先級任務,這里實現RL-TCPnet的時間基准更新

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 5 

*********************************************************************************************************

*/

static void vTaskStart(void *pvParameters)

{

     TickType_t xLastWakeTime;

     const TickType_t xFrequency = 100;

    

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 獲取當前的系統時間 */

    xLastWakeTime = xTaskGetTickCount();

    

    while(1)

    {   

          /* RL-TCPnet時間基准更新函數 */

         timer_tick ();

        

         /* vTaskDelayUntil是絕對延遲,vTaskDelay是相對延遲。*/

        vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

}

                  

/*

*********************************************************************************************************

*    函 數 名: AppTaskCreate

*    功能說明: 創建應用任務

*    形    參:無

*    返 回 值: 無

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

    xTaskCreate( vTaskTaskUserIF,   /* 任務函數  */

                 "vTaskUserIF",      /* 任務名    */

                 512,               /* 任務棧大小,單位word,也就是4字節 */

                 NULL,              /* 任務參數  */

                 1,                  /* 任務優先級*/

                 &xHandleTaskUserIF );  /* 任務句柄  */

    

    

     xTaskCreate( vTaskLED,           /* 任務函數  */

                 "vTaskLED",         /* 任務名    */

                 512,                /* stack大小,單位word,也就是4字節 */

                 NULL,               /* 任務參數  */

                 2,                   /* 任務優先級*/

                 &xHandleTaskLED ); /* 任務句柄  */

    

     xTaskCreate( vTaskMsgPro,            /* 任務函數  */

                 "vTaskMsgPro",           /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 3,                       /* 任務優先級*/

                 &xHandleTaskMsgPro );  /* 任務句柄  */

 

    xTaskCreate( vTaskTCPnet,             /* 任務函數  */

                 "vTaskTCPnet",           /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 4,                       /* 任務優先級*/

                 &xHandleTaskTCPnet );  /* 任務句柄  */

    

    

     xTaskCreate( vTaskStart,             /* 任務函數  */

                 "vTaskStart",            /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 5,                       /* 任務優先級*/

                 &xHandleTaskStart );   /* 任務句柄  */

}

 

/*

*********************************************************************************************************

*    函 數 名: AppObjCreate

*    功能說明: 創建任務通信機制

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

static void AppObjCreate (void)

{

     /* 創建互斥信號量 */

    xMutex = xSemaphoreCreateMutex();

    

     if(xMutex == NULL)

    {

        /* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */

    }

    

     /* 創建事件標志組 */

     xCreatedEventGroup = xEventGroupCreate();

    

     if(xCreatedEventGroup == NULL)

    {

        /* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: App_Printf

*    功能說明: 線程安全的printf方式                      

*    形    參: 同printf的參數。

*             在C中,當無法列出傳遞函數的所有實參的類型和數目時,可以用省略號指定參數表

*    返 回 值: 無

*********************************************************************************************************

*/

static void  App_Printf(char *format, ...)

{

    char  buf_str[512 + 1];

    va_list   v_args;

 

 

    va_start(v_args, format);

   (void)vsnprintf((char       *)&buf_str[0],

                   (size_t      ) sizeof(buf_str),

                   (char const *) format,

                                  v_args);

    va_end(v_args);

 

     /* 互斥信號量 */

     xSemaphoreTake(xMutex, portMAX_DELAY);

 

    printf("%s", buf_str);

 

     xSemaphoreGive(xMutex);

}

硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*

*********************************************************************************************************

*    函 數 名: bsp_Init

*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次

*    形    參:無

*    返 回 值: 無

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由於ST固件庫的啟動文件已經執行了CPU系統時鍾的初始化,所以不必再次重復配置系統時鍾。

         啟動文件配置了CPU主時鍾頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。

 

         系統時鍾缺省配置為168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 優先級分組設置為4,可配置0-15級搶占式優先級,0級子優先級,即不存在子優先級。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

 

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按鍵變量 */

     bsp_InitLed();     /* 初始LED指示燈端口 */

}

RL-TCPnet功能測試

這里專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,主要功能是創建了一個TCP Server。

#include "includes.h" 

 

 

 

/*

*********************************************************************************************************

*                                      用於本文件的調試

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

*********************************************************************************************************

*                                      用於本文件的調試

*********************************************************************************************************

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

*********************************************************************************************************

*                                         變量

*********************************************************************************************************

*/

uint8_t socket_tcp;

 

 

/*

*********************************************************************************************************

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 數 名: TCP_StatusCheck

*    功能說明: 檢測TCP的連接狀態,主要用於網線插拔的判斷

*    形    參: 無

*    返 回 值: __TRUE  連接

*             __FALSE 斷開

*********************************************************************************************************

*/

uint8_t TCP_StatusCheck(void)

{

     uint8_t res;

    

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              res = tcp_listen (socket_tcp, PORT_NUM);

              printf_debug("tcp listen res = %d\r\n", res);

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

*********************************************************************************************************

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     const TickType_t xTicksToWait = 2; /* 延遲2ms */

     EventBits_t uxBits;

    

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

 

         /* 等待所有任務發來事件標志 */

         uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件標志組句柄 */

                                      0xFFFF,        /* 等待0xFFFF某一位被設置 */

                                      pdTRUE,/* 退出前0xFFFF位被清除,這里是任意0xFFFF位被設置就“退出”*/

                                      pdFALSE,       /* 設置為pdTRUE表示等待0xFFFF任意位被設置*/

                                       xTicksToWait);/* 等待延遲時間 */

 

         if((uxBits != 0)&&(tcp_status == __TRUE))

         {

              switch (uxBits)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

至此,FreeRTOS方式移植的RL-TCPnet就可以運行了。

 

10.2.6 RL-TCPnet實驗測試和實驗現象

測試前,先將開發板上面的DM9161/9162網口通過網線接到路由器或者交換機上面。

RJ45網絡變壓器插座上綠燈和黃燈現象

各種網卡、交換機等網絡設備都不一樣,一般來講:綠燈分為亮或不亮(代表網絡速度),黃燈分為閃爍或不閃爍(代表是否有數據收發)。

綠燈:長亮代表100M; 不亮代表10M。

黃燈:長亮代表無數據收發; 閃爍代表有數據收發。

也有些千兆網卡的燈以顏色區分,不亮代表10M / 綠色代表100M / 黃色代表1000M。現在10M的網絡基本看不到了,如果一個燈長亮,基本可以說明100M網絡或更高,而另一個燈時而閃爍,那代表有數據收發,具體要看你的網絡設備了。甚至有些低等網卡如TP-LINK,只有一個燈,亮代表連通,閃爍代表數據收發。

對於STM32F407開發板上面的RJ45網絡變壓器插座上面的燈而言,綠燈代表數據收發,長亮的話表示無數據收發,閃爍代表有數據收發。黃燈代表網絡速度,長亮代表100M,不亮代表10M。

底層驅動執行情況

為了驗證RL-TCPnet底層驅動接口函數是否有問題,專門在ETH_STM32F4xx.c文件中配置了串口調試打印函數:

/*

*********************************************************************************************************

*                                      用於本文件的調試

*********************************************************************************************************

*/

#if 1

     #define printf_eth printf

#else

     #define printf_eth(...)

#endif

如果底層驅動正常執行了,打印的效果如下:

 

ping是否正確

ping命令的主要作用是通過發送數據包並接收應答信息來檢測兩台設備之間的網絡是否連通。ping命令成功說明當前主機與目的主機之間存在連通的路徑。如果不成功,需要查看網線是否連通、網卡設置是否正確、IP地址是否可用等。測試方法如下:

(1)WIN+R組合鍵打開“運行”窗口,輸入cmd。

 

(2)彈出的命令窗口中,輸入ping armfly,因為在前面的配置中我們使能了NetBIOS局域網域名,並將名字設置為armfly,而且使能了DHCP,通過ping命令還可以獲得板子自動獲取的IP地址。

 

(3)輸入ping armfly后,回車。

 

收發相同,沒有數據丟失,說明ping命令也是成功的。

電腦端創建一個TCP Client與板子上面的TCP Server通信

具體測試方法,查看第13章的13.6小節即可,因為配套例子實現的功能是一樣的。

 

10.3 STM32F429移植RL-TCPnet協議棧

10.3.1 RL-TCPnet網絡協議棧移植

首先准備好一個簡單的FreeRTOS工程模板,工程模板的制作就不做講解了,這里的重點是教大家移植RL-TCPnet協議棧。准備好的工程模板如下圖所示(大家也可以制作其它任意的工程模板,不限制):

 

准備好工程模板后,就可以開始移植了。首先要做的就是將所有需要的文件放到工程模板里面。下面分4步跟大家進行說明,當然,不限制必須使用下面的方法添加源碼到工程,只要將需要的文件添加到工程模板即可。

第1步:將我們FreeRTOS模板中制作好的RL-ARM文件夾復制粘貼到大家准備好的工程模板中。

 

RL-ARM文件夾中有如下七個文件夾:

 

Config文件夾用於存放RTX及其中間件的配置文件。

Driver文件夾用於存放中間件的驅動文件,也就是底層移植文件。

RL-CAN文件夾用於存放CAN總線的源碼文件。

RL-FlashFS文件夾用於存放文件系統RL-FlashFS的庫文件。

RL-RTX文件夾用於存放RTX的源碼文件。

RL-TCPnet文件夾用於存放網絡協議棧RL-TCPnet的庫文件。

RL-USB文件夾用於存放USB協議棧RL-USB的庫文件。

也許有用戶會問:我們不是僅僅需要移植RL-TCPnet的相關文件就行了嗎,為什么把RTX及其所有中間件都添加進來了?這樣做的目的是為了以后升級的方便,如果需要添加USB、文件系統、CAN等組件,直接添加到工程即可。

這些文件全部來自MDK4.74的安裝目錄,庫文件位於路徑:C:\Keil_v474\ARM\RV31下,而驅動和配置文件位於路徑:C:\Keil_v474\ARM\RL下。

第2步:添加RL-TCPnet的庫文件、配置文件和驅動文件到工程,添加完畢后的效果如下:

 

Net_lib.c,Net_Config.c和NET_Debug.c在RL-ARM文件夾的Config文件里面。

TCPD_CM3.lib在RL-ARM文件夾的RL-TCPnet文件里面。

ETH_STM32F4xx.c和ETH_STM32F4xx.h在RL-ARM文件夾的Driver文件里面。

第3步:添加相應的頭文件路徑,在原來工程模板的基礎上新添加的幾個路徑:

 

第4步:也是最后一步,添加預定義宏,點擊MDK的option -> c/c++選項,添加上__RTX(注意,字母RTX前面有兩個下划線的),添加這個宏定義才可以使能RL-TCPnet的多任務支持。

 

至此,RL-TCPnet的移植工作就完成了,剩下就是系統配置和應用了。

 

10.3.2 RL-TCPnet配置說明(Net_Config.c)

RL-TCPnet的配置工作是通過配置文件Net_Config.c實現。在MDK工程中打開文件Net_Config.c,可以看到下圖所示的工程配置向導:

 

RL-TCPnet要配置的選項非常多,我們這里把幾個主要的配置選項簡單介紹下。

 

System Definitions

(1)Local Host Name

局域網域名。

這里起名為armfly,使用局域網域名限制為15個字符。

(2)Memory Pool size

參數范圍1536-262144字節。

內存池大小配置,單位字節。另外注意一點,配置向導這里顯示的單位是字節,如果看原始定義,MDK會做一個自動的4字節倍數轉換,比如我們這里配置的是8192字節,那么原始定義是#define MEM_SIZE  2048,也就是8192/4 = 2048。

(3)Tick Timer interval

可取10,20,25,40,50,100,200,單位ms。

系統滴答時鍾間隔,也就是網絡協議棧的系統時間基准,默認情況下,取值100ms。

 

Ethernet Network Interface

以太網接口配置,勾選了此選項就可以配置了,如果沒有使能DHCP的話,將使用這里配置的固定IP

(1)MAC Address

局域網內可以隨意配置,只要不跟局域網內其它設備的MAC地址沖突即可。

(2)IP Address

IP地址。

(3)Subnet mask

子網掩碼。

(4)Default Gateway

默認網關。

 

Ethernet Network Interface

以太網接口配置,這個配置里面還有如下兩項比較重要的配置需要說明。

(1)NetBIOS Name Service

NetBIOS局域網域名服務,這里打上對勾就使能了。這樣我們就可以通過前面配置的Local Host Name局域網域名進行訪問,而不需要通過IP地址訪問了。

(2)Dynaminc Host Configuration

即DHCP,這里打上對勾就使能了。使能了DHCP后,RL-TCPnet就可以從外接的路由器上獲得動態IP地址。

 

UDP Sockets

UDP Sockets配置,打上對勾就使能了此項功能

(1)Number of UDP Sockets

用於配置可創建的UDP Sockets數量。

范圍1 – 20。

 

TCP Sockets

TCP Sockets配置,打上對勾就使能了此項功能

(1)Number of TCP Sockets

用於配置可創建的TCP Sockets數量。

(2)Number of Retries

范圍0-20。

用於配置重試次數,TCP數據傳輸時,如果在設置的重試時間內得不到應答,算一次重試失敗,這里就是配置的最大重試次數。

(3)Retry Timeout in seconds

范圍1-10,單位秒。

重試時間。如果發送的數據在重試時間內得不到應答,將重新發送數據。

(4)Default Connect Timeout in seconds

范圍1-600,單位秒。

用於配置默認的保持連接時間,即我們常說的Keep Alive時間,如果時間到了將斷開連接。常用於HTTP Server,Telnet Server等。

(5)Maximum Segment Size

范圍536-1460,單位字節。

MSS定義了TCP數據包能夠傳輸的最大數據分段。

(6)Receive Window Size

范圍536-65535,單位字節。

TCP接收窗口大小。

 

10.3.3 RL-TCPnet調試說明(Net_Debug.c)

重要說明,RL-TCPnet的調試是通過串口打印出來的

RL-TCPnet的調試功能是通過配置文件Net_Debug.c實現。在MDK工程中打開文件Net_Debug.c,可以看到下圖所示的工程配置向導:

 

Print Time Stamp

勾選了此選項的話,打印消息時,前面會附帶時間信息。

其它所有的選項

默認情況下,所有的調試選項都是關閉的,每個選項有三個調試級別可選擇,這里我們以Memory Management Debug為例,點擊下拉列表,可以看到里面有Off,Errors only和Full debug三個調試級別可供選擇,每個調試選項里面都是這三個級別。

 

Off:表示關閉此選項的調試功能。

Errors only:表示僅在此選項出錯時,將其錯誤打印出來。

Full debug:表示此選項的全功能調試。

關於調試功能的使用會在第11章詳細為大家講解,移植階段將其全部關閉即可。

 

10.3.4 RL-TCPnet的多任務驅動接口函數

要讓RL-TCPnet支持多任務,就需要修改Net_lib.c文件。默認情況下,Net_lib.c文件是支持RTX操作系統的,現在要將其修改為支持FreeRTOS,需要修改的幾個地方如下:

添加FreeRTOS的頭文件。

#if (__RTX)

  #include "FreeRTOS.h"

  #include "task.h"

  #include "queue.h"

  #include "semphr.h"

#endif

定義信號量和互斥信號量。

#if (BSD_ENABLE)

 static BSD_INFO bsd_scb[BSD_NUMSOCKS + BSD_SRVSOCKS];

 #ifdef __RTX

   SemaphoreHandle_t  bsd_mutex = NULL;

   SemaphoreHandle_t  bsd_sem = NULL;

  #define BSD_INRTX  __TRUE

 #else

  #define BSD_INRTX  __FALSE

 #endif

 BSD_CFG bsd_config = {

   bsd_scb,

   BSD_NUMSOCKS + BSD_SRVSOCKS,

   BSD_INRTX,

   BSD_RCVTOUT * TICK_RATE

 };

#endif

創建互斥信號量和信號量。

/*--------------------------- init_system -----------------------------------*/

void init_system (void) {

  /* Initialize configured interfaces and applications. */

    

#if (ETH_ENABLE)

  eth_init_link ();

#endif

#if (PPP_ENABLE)

  ppp_init_link ();

#endif

#if (SLIP_ENABLE)

  slip_init_link ();

#endif

  ip_init ();

  icmp_init ();

#if (ETH_ENABLE && IGMP_ENABLE)

  igmp_init ();

#endif

#if (UDP_ENABLE)

  udp_init ();

#endif

#if (TCP_ENABLE)

  tcp_init ();

#endif

#if (BSD_ENABLE)

  bsd_init ();

 #if (BSD_GETHOSTEN)

  bsd_init_host ();

 #endif

#endif

#if (HTTP_ENABLE)

  http_init ();

#endif

#if (TNET_ENABLE)

  tnet_init ();

#endif

#if (TFTP_ENABLE)

  tftp_init ();

#endif

#if (TFTPC_ENABLE)

  tftpc_init ();

#endif

#if (FTP_ENABLE)

  ftp_init ();

#endif

#if (FTPC_ENABLE)

  ftpc_init ();

#endif

#if (ETH_ENABLE && NBNS_ENABLE)

  nbns_init ();

#endif

#if (ETH_ENABLE && DHCP_ENABLE)

  dhcp_init ();

#elif (ETH_ENABLE)

  arp_notify ();

#endif

#if (DNS_ENABLE)

  dns_init ();

#endif

#if (SMTP_ENABLE)

  smtp_init ();

#endif

#if (SNMP_ENABLE)

  snmp_init ();

#endif

#if (SNTP_ENABLE)

  sntp_init ();

#endif

 

#if (BSD_ENABLE && __RTX)

 {

     bsd_mutex = xSemaphoreCreateMutex();

     if(bsd_mutex == NULL)

    {

        /* ERR */

    }   

    

     bsd_sem = xSemaphoreCreateBinary();

     if(bsd_sem == NULL)

    {

        /* ERR */

    }

 }

#endif

}

系統運行時,使用互斥信號量。

/*--------------------------- run_system ------------------------------------*/

void run_system (void) {

  /* Run configured interfaces and applications. */

 

#if (BSD_ENABLE && __RTX)

     xSemaphoreTake(bsd_mutex, portMAX_DELAY);

#endif

 

#if (ETH_ENABLE)

  eth_run_link ();

#endif

#if (PPP_ENABLE)

  ppp_run_link ();

#endif

#if (SLIP_ENABLE)

  slip_run_link ();

#endif

  ip_run_local ();

  icmp_run_engine ();

#if (ETH_ENABLE && IGMP_ENABLE)

  igmp_run_host ();

#endif

#if (TCP_ENABLE)

  tcp_poll_sockets ();

#endif

#if (BSD_ENABLE)

  bsd_poll_sockets ();

#endif

#if (HTTP_ENABLE)

  http_run_server ();

#endif

#if (TNET_ENABLE)

  tnet_run_server ();

#endif

#if (TFTP_ENABLE)

  tftp_run_server ();

#endif

#if (TFTPC_ENABLE)

  tftpc_run_client ();

#endif

#if (FTP_ENABLE)

  ftp_run_server ();

#endif

#if (FTPC_ENABLE)

  ftpc_run_client ();

#endif

#if (ETH_ENABLE && DHCP_ENABLE)

  dhcp_run_client ();

#endif

#if (DNS_ENABLE)

  dns_run_client ();

#endif

#if (SMTP_ENABLE)

  smtp_run_client ();

#endif

#if (SNMP_ENABLE)

  snmp_run_agent ();

#endif

#if (SNTP_ENABLE)

  sntp_run_client ();

#endif

 

#if (BSD_ENABLE && __RTX)

  xSemaphoreGive(bsd_mutex);

#endif

}

使能BSD Socket的話,Socket掛起和恢復的實現。

/*--------------------------- bsd_suspend/resume ----------------------------*/

#if (BSD_ENABLE && __RTX)

__used void bsd_suspend (U8 *tsk_id) {

  /* Suspend a socket owner task. */

  xSemaphoreGive(bsd_mutex);

  xSemaphoreTake(bsd_sem, portMAX_DELAY);

  xSemaphoreTake(bsd_mutex, portMAX_DELAY);   

}

 

__used void bsd_resume (U8 tsk_id) {

  /* Resume a task waiting for a socket event. */

  xSemaphoreGive(bsd_sem);

}

#endif

使能BSD Socket的話,鎖機制的實現。

/*--------------------------- bsd_lock/unlock -------------------------------*/

#if (BSD_ENABLE && __RTX)

__used void bsd_lock (void) {

  /* Acquire mutex - Lock TCPnet functions. */

  xSemaphoreTake(bsd_mutex, portMAX_DELAY);   

}

 

__used void bsd_unlock (void) {

  /* Release mutex - Unlock TCPnet functions. */

  xSemaphoreGive(bsd_mutex);

}

#endif

 

10.3.5 RL-TCPnet應用實例

為了驗證移植的RL-TCPnet是否可以使用,需要添加測試代碼。下面是編寫的測試代碼,配套的測試例子完整版是:V6-1003_RL-TCPnet實驗_工程移植模板(FreeRTOS)。

 

FreeRTOS操作系統創建的任務

經過上面的移植和配置之后,在 main.c 文件中添加如下代碼,代碼中創建了5個用戶任務:

vTaskTaskUserIF 任務: 按鍵消息處理。

vTaskLED        任務: LED閃爍。

vTaskMsgPro    任務: 消息處理,這里用作按鍵檢測。

vTaskTCPnet    任務: RL-TCPnet測試任務。

vTaskStart      任務: 啟動任務,實現RL-TCPnet的時間基准更新。

具體代碼如下:

#include "includes.h"

 

 

 

/*

**********************************************************************************************************

                                                   函數聲明

**********************************************************************************************************

*/

static void vTaskTaskUserIF(void *pvParameters);

static void vTaskLED(void *pvParameters);

static void vTaskMsgPro(void *pvParameters);

static void vTaskTCPnet(void *pvParameters);

static void vTaskStart(void *pvParameters);

static void AppTaskCreate (void);

static void AppObjCreate (void);

static void App_Printf(char *format, ...);

 

 

/*

**********************************************************************************************************

                                                   變量聲明

**********************************************************************************************************

*/

static TaskHandle_t xHandleTaskUserIF = NULL;

static TaskHandle_t xHandleTaskLED = NULL;

static TaskHandle_t xHandleTaskMsgPro = NULL;

static TaskHandle_t xHandleTaskTCPnet = NULL;

static TaskHandle_t xHandleTaskStart = NULL;

static SemaphoreHandle_t  xMutex = NULL;

EventGroupHandle_t xCreatedEventGroup = NULL;

 

 

/*

*********************************************************************************************************

*    函 數 名: main

*    功能說明: 標准c程序入口。

*    形    參:無

*    返 回 值: 無

*********************************************************************************************************

*/

int main(void)

{

     /*

       在啟動調度前,為了防止初始化STM32外設時有中斷服務程序執行,這里禁止全局中斷(除了NMI和HardFault)。

       這樣做的好處是:

       1. 防止執行的中斷服務程序中有FreeRTOS的API函數。

       2. 保證系統正常啟動,不受別的中斷影響。

       3. 關於是否關閉全局中斷,大家根據自己的實際情況設置即可。

       在移植文件port.c中的函數prvStartFirstTask中會重新開啟全局中斷。通過指令cpsie i開啟,__set_PRIMASK(1)

       和cpsie i是等效的。

     */

     __set_PRIMASK(1); 

    

     /* 硬件初始化 */

     bsp_Init();

    

     /* 1. 初始化一個定時器中斷,精度高於滴答定時器中斷,這樣才可以獲得准確的系統信息 僅供調試目的,實際項

           目中不要使用,因為這個功能比較影響系統實時性。

        2. 為了正確獲取FreeRTOS的調試信息,可以考慮將上面的關閉中斷指令__set_PRIMASK(1); 注釋掉。

     */

     vSetupSysInfoTest();

    

     /* 創建任務 */

     AppTaskCreate();

 

     /* 創建任務通信機制 */

     AppObjCreate();

    

    /* 啟動調度,開始執行任務 */

    vTaskStartScheduler();

 

     /*

       如果系統正常啟動是不會運行到這里的,運行到這里極有可能是用於定時器任務或者空閑任務的

       heap空間不足造成創建失敗,此要加大FreeRTOSConfig.h文件中定義的heap大小:

       #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )

     */

     while(1);

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskTaskUserIF

*    功能說明: 接口消息處理。

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 1  (數值越小優先級越低,這個跟uCOS相反)

*********************************************************************************************************

*/

static void vTaskTaskUserIF(void *pvParameters)

{

     uint8_t ucKeyCode;

     uint8_t pcWriteBuffer[500];

 

    

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1鍵按下,直接發送事件標志給任務vTaskTCPnet,設置bit0 */

                   case KEY_DOWN_K1:

                       App_Printf("K1鍵按下,直接發送事件標志給任務AppTaskTCPMain,bit0被設置\r\n");

                       xEventGroupSetBits(xCreatedEventGroup, KEY1_BIT0);          

                       break;  

 

                   /* K2鍵按下,直接發送事件標志給任務vTaskTCPnet,設置bit1 */

                   case KEY_DOWN_K2:

                       App_Printf("K2鍵按下,直接發送事件標志給任務AppTaskTCPMain,bit1被設置\r\n");

                       xEventGroupSetBits(xCreatedEventGroup, KEY2_BIT1);

                       break;

                  

                   /* K3鍵按下,直接發送事件標志給任務vTaskTCPnet,設置bit2 */

                   case KEY_DOWN_K3:

                       App_Printf("K3鍵按下,直接發送事件標志給任務AppTaskTCPMain,bit2被設置\r\n");

                       xEventGroupSetBits(xCreatedEventGroup, KEY3_BIT2);

                       break;

                  

                   /* 搖桿的OK鍵按下,打印任務執行情況 */

                   case JOY_DOWN_OK:          

                        App_Printf("=================================================\r\n");

                       App_Printf("任務名      任務狀態 優先級   剩余棧 任務序號\r\n");

                       vTaskList((char *)&pcWriteBuffer);

                       App_Printf("%s\r\n", pcWriteBuffer);

                  

                       App_Printf("\r\n任務名       運行計數         使用率\r\n");

                       vTaskGetRunTimeStats((char *)&pcWriteBuffer);

                       App_Printf("%s\r\n", pcWriteBuffer);

                       App_Printf("當前動態內存剩余大小 = %d字節\r\n", xPortGetFreeHeapSize());

                       break;

                  

                   /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

        

         vTaskDelay(20);

     }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskLED

*    功能說明: LED閃爍

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 2 

*********************************************************************************************************

*/

static void vTaskLED(void *pvParameters)

{

     TickType_t xLastWakeTime;

     const TickType_t xFrequency = 500;

 

     /* 獲取當前的系統時間 */

    xLastWakeTime = xTaskGetTickCount();

    

    while(1)

    {

         bsp_LedToggle(2);

        

          /* vTaskDelayUntil是絕對延遲,vTaskDelay是相對延遲。*/

        vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskMsgPro

*    功能說明: 消息處理,這里用作按鍵檢測

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 3 

*********************************************************************************************************

*/

static void vTaskMsgPro(void *pvParameters)

{

    while(1)

    {

          /* 按鍵掃描 */

         bsp_KeyScan();

         vTaskDelay(10);

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskTCPnet

*    功能說明: RL-TCPnet測試任務

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 4 

*********************************************************************************************************

*/

static void vTaskTCPnet(void *pvParameters)

{

    while(1)

    {

         TCPnetTest();

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: vTaskStart

*    功能說明: 啟動任務,也是最高優先級任務,這里實現RL-TCPnet的時間基准更新

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 5 

*********************************************************************************************************

*/

static void vTaskStart(void *pvParameters)

{

     TickType_t xLastWakeTime;

     const TickType_t xFrequency = 100;

    

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 獲取當前的系統時間 */

    xLastWakeTime = xTaskGetTickCount();

    

    while(1)

    {   

          /* RL-TCPnet時間基准更新函數 */

         timer_tick ();

        

         /* vTaskDelayUntil是絕對延遲,vTaskDelay是相對延遲。*/

        vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

}

                  

/*

*********************************************************************************************************

*    函 數 名: AppTaskCreate

*    功能說明: 創建應用任務

*    形    參:無

*    返 回 值: 無

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

    xTaskCreate( vTaskTaskUserIF,   /* 任務函數  */

                 "vTaskUserIF",      /* 任務名    */

                 512,               /* 任務棧大小,單位word,也就是4字節 */

                 NULL,              /* 任務參數  */

                 1,                  /* 任務優先級*/

                 &xHandleTaskUserIF );  /* 任務句柄  */

    

    

     xTaskCreate( vTaskLED,           /* 任務函數  */

                 "vTaskLED",         /* 任務名    */

                 512,                /* stack大小,單位word,也就是4字節 */

                 NULL,               /* 任務參數  */

                 2,                   /* 任務優先級*/

                 &xHandleTaskLED ); /* 任務句柄  */

    

     xTaskCreate( vTaskMsgPro,            /* 任務函數  */

                 "vTaskMsgPro",           /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 3,                       /* 任務優先級*/

                 &xHandleTaskMsgPro );  /* 任務句柄  */

 

    xTaskCreate( vTaskTCPnet,             /* 任務函數  */

                 "vTaskTCPnet",           /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 4,                       /* 任務優先級*/

                 &xHandleTaskTCPnet );  /* 任務句柄  */

    

    

     xTaskCreate( vTaskStart,             /* 任務函數  */

                 "vTaskStart",            /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 5,                       /* 任務優先級*/

                 &xHandleTaskStart );   /* 任務句柄  */

}

 

/*

*********************************************************************************************************

*    函 數 名: AppObjCreate

*    功能說明: 創建任務通信機制

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

static void AppObjCreate (void)

{

     /* 創建互斥信號量 */

    xMutex = xSemaphoreCreateMutex();

    

     if(xMutex == NULL)

    {

        /* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */

    }

    

     /* 創建事件標志組 */

     xCreatedEventGroup = xEventGroupCreate();

    

     if(xCreatedEventGroup == NULL)

    {

        /* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */

    }

}

 

/*

*********************************************************************************************************

*    函 數 名: App_Printf

*    功能說明: 線程安全的printf方式                      

*    形    參: 同printf的參數。

*             在C中,當無法列出傳遞函數的所有實參的類型和數目時,可以用省略號指定參數表

*    返 回 值: 無

*********************************************************************************************************

*/

static void  App_Printf(char *format, ...)

{

    char  buf_str[512 + 1];

    va_list   v_args;

 

 

    va_start(v_args, format);

   (void)vsnprintf((char       *)&buf_str[0],

                   (size_t      ) sizeof(buf_str),

                   (char const *) format,

                                  v_args);

    va_end(v_args);

 

     /* 互斥信號量 */

     xSemaphoreTake(xMutex, portMAX_DELAY);

 

    printf("%s", buf_str);

 

     xSemaphoreGive(xMutex);

}

硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*

*********************************************************************************************************

*    函 數 名: bsp_Init

*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次

*    形    參:無

*    返 回 值: 無

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由於ST固件庫的啟動文件已經執行了CPU系統時鍾的初始化,所以不必再次重復配置系統時鍾。

         啟動文件配置了CPU主時鍾頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。

 

         系統時鍾缺省配置為168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 優先級分組設置為4,可配置0-15級搶占式優先級,0級子優先級,即不存在子優先級。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

 

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按鍵變量 */

     bsp_InitLed();     /* 初始LED指示燈端口 */

}

RL-TCPnet功能測試

這里專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,主要功能是創建了一個TCP Server。

#include "includes.h" 

 

 

 

/*

*********************************************************************************************************

*                                      用於本文件的調試

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

*********************************************************************************************************

*                                      用於本文件的調試

*********************************************************************************************************

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

*********************************************************************************************************

*                                         變量

*********************************************************************************************************

*/

uint8_t socket_tcp;

 

 

/*

*********************************************************************************************************

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 數 名: TCP_StatusCheck

*    功能說明: 檢測TCP的連接狀態,主要用於網線插拔的判斷

*    形    參: 無

*    返 回 值: __TRUE  連接

*             __FALSE 斷開

*********************************************************************************************************

*/

uint8_t TCP_StatusCheck(void)

{

     uint8_t res;

    

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              res = tcp_listen (socket_tcp, PORT_NUM);

              printf_debug("tcp listen res = %d\r\n", res);

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

*********************************************************************************************************

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     const TickType_t xTicksToWait = 2; /* 延遲2ms */

     EventBits_t uxBits;

    

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

 

         /* 等待所有任務發來事件標志 */

         uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件標志組句柄 */

                                      0xFFFF,        /* 等待0xFFFF某一位被設置 */

                                      pdTRUE,/* 退出前0xFFFF位被清除,這里是任意0xFFFF位被設置就“退出”*/

                                      pdFALSE,       /* 設置為pdTRUE表示等待0xFFFF任意位被設置*/

                                       xTicksToWait);/* 等待延遲時間 */

 

         if((uxBits != 0)&&(tcp_status == __TRUE))

         {

              switch (uxBits)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

至此,FreeRTOS方式移植的RL-TCPnet就可以運行了。

 

10.3.6 RL-TCPnet實驗測試和實驗現象

測試前,先將開發板上面的DM9161/9162網口通過網線接到路由器或者交換機上面。

RJ45網絡變壓器插座上綠燈和黃燈現象

各種網卡、交換機等網絡設備都不一樣,一般來講:綠燈分為亮或不亮(代表網絡速度),黃燈分為閃爍或不閃爍(代表是否有數據收發)。

綠燈:長亮代表100M; 不亮代表10M。

黃燈:長亮代表無數據收發; 閃爍代表有數據收發。

也有些千兆網卡的燈以顏色區分,不亮代表10M / 綠色代表100M / 黃色代表1000M。現在10M的網絡基本看不到了,如果一個燈長亮,基本可以說明100M網絡或更高,而另一個燈時而閃爍,那代表有數據收發,具體要看你的網絡設備了。甚至有些低等網卡如TP-LINK,只有一個燈,亮代表連通,閃爍代表數據收發。

對於STM32F429開發板上面的RJ45網絡變壓器插座上面的燈而言,綠燈代表數據收發,長亮的話表示無數據收發,閃爍代表有數據收發。黃燈代表網絡速度,長亮代表100M,不亮代表10M。

底層驅動執行情況

為了驗證RL-TCPnet底層驅動接口函數是否有問題,專門在ETH_STM32F4xx.c文件中配置了串口調試打印函數:

/*

*********************************************************************************************************

*                                      用於本文件的調試

*********************************************************************************************************

*/

#if 1

     #define printf_eth printf

#else

     #define printf_eth(...)

#endif

如果底層驅動正常執行了,打印的效果如下:

 

ping是否正確

ping命令的主要作用是通過發送數據包並接收應答信息來檢測兩台設備之間的網絡是否連通。ping命令成功說明當前主機與目的主機之間存在連通的路徑。如果不成功,需要查看網線是否連通、網卡設置是否正確、IP地址是否可用等。測試方法如下:

(1)WIN+R組合鍵打開“運行”窗口,輸入cmd。

 

(2)彈出的命令窗口中,輸入ping armfly,因為在前面的配置中我們使能了NetBIOS局域網域名,並將名字設置為armfly,而且使能了DHCP,通過ping命令還可以獲得板子自動獲取的IP地址。

 

(3)輸入ping armfly后,回車。

 

收發相當,沒有數據丟失,說明ping命令也是成功的。

電腦端創建一個TCP Client與板子上面的TCP Server通信

具體測試方法,查看第13章的13.6小節即可,因為配套例子實現的功能是一樣的。

 

10.4 總結

本章節為大家講解了RL-TCPnet網絡協議棧的FreeRTOS操作系統移植方法,移植相對比較簡單。另一個重要內容是Net_Config.c配置向導文件的說明,這個比較重要,初學者要好好熟悉下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM