【RL-TCPnet網絡教程】第39章 RL-TCPnet之TFTP服務器

第39章     RL-TCPnet之TFTP服務器

本章節爲大家講解RL-TCPnet的TFTP服務器應用,學習本章節前,務必要優先學習第38章的TFTP基礎知識。有了這些基礎知識之後,再搞本章節會有事半功倍的效果。

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

39.1  初學者重要提示

39.2  TFTP函數

39.3  TFTP配置說明(Net_Config.c)

39.4  TFTP調試說明(Net_Debug.c)

39.5  TFTP客戶端軟件和板子的操作步驟

39.6  實驗例程說明(裸機)

39.7  實驗例程說明(RTX)

39.8  總結

39.1  初學者重要提示

  1.  學習本章節前,務必保證已經學習了第38章的基礎知識。
  2.   本章配套的例子是將開發板作爲TFTP服務器,使用開發板上面的SD卡作爲服務器的存儲介質。所以測試本章節的例子,務必要準備一個SD卡。
  3.  由於配套例子的文件系統是採用的RL-FlashFS,此文件系統的文件名僅支持ASCII字符,不支持中文,特別注意!
  4.   具體TFTP服務器的訪問方法在本章的39.5小節有詳細說明,操作例子前必看!

39.2  TFTP函數

使用如下11個函數可以實現RL-TCPnet的TFTP:

  •   tftp_accept_host
  •   tftp_fclose
  •   tftp_fopen
  •   tftp_fread
  •   tftp_fwrite
  •   tftpc_fclose
  •   tftpc_fopen
  •   tftpc_fread
  •   tftpc_fwrite
  •   tftpc_get
  •   tftpc_put

關於這11個函數的講解及其使用方法可以看教程第 3 章 3.4 小節裏面說的參考資料 rlarm.chm 文件:

這裏我們重點的說以下6個函數,因爲本章節配套的例子使用的是這6個函數:

  •   tftp_accept_host
  •   tftp_fclose
  •   tftp_fopen
  •   tftp_fread
  •   tftp_fwrite
  •   tftpc_fclose

關於這些函數注意以下三點:

  1.   TFTP的所有函數都不支持重入,也就是不支持多任務調用。
  2.   以tftp_開頭的函數是用於TFTP服務器的。
  3.   以tftpc_開頭的函數是用於TFTP客戶端的。

39.2.1    函數tftp_fopen

函數原型:

void* tftp_fopen (

    U8* fname,     /* 文件名地址 */

    U8* mode );    /* 操作模式 */

函數描述:

函數tftp_fopen用於打開文件。此函數在MDK安裝目錄中的TFTP_uif.c文件裏面,屬於底層接口函數,用戶要在此函數裏面添加具體的操作。

  1. 第1個參數是文件名地址。
  2. 第2個參數是操作模式,可以是讀操作或者寫操作,具體支持的形參類型如下:
  1.  返回值,打開文件成功的話,返回指向此文件的指針變量,否則返回NULL。

使用舉例:

void *tftp_fopen (U8 *fname, U8 *mode) {

  /* 打開文件 */

  return (fopen((char *)fname, (char *)mode));

}

39.2.2   函數tftp_fclose

函數原型:

void tftp_fclose (

FILE* file);  /* 文件句柄地址 */

函數描述:

函數tftp_fclose用於關閉文件。此函數在MDK安裝目錄中的TFTP_uif.c文件裏面,屬於底層接口函數,用戶要在此函數裏面添加具體的操作。

  1. 第1個參數是要關閉的文件句柄地址。

使用舉例:

void tftp_fclose (void *file) {

  /* 關閉文件,當TFTP會話將結束的時候會調用此函數 */

  fclose (file);

}

39.2.3   函數tftp_fread

函數原型:

U16 tftp_fread (

    FILE* file,     /* 文件句柄地址 */

    U8*   buf,      /* 數據緩衝地址 */

    U16   len );    /* 要讀取的字節數 */

函數描述:

函數tftp_fread用於從文件中讀出len個字節數據。此函數在MDK安裝目錄中的TFTP_uif.c文件裏面,屬於底層接口函數,用戶要在此函數裏面添加具體的操作。

  1. 第1個參數是要讀取數據的文件句柄地址。
  2. 第2個參數是數據緩衝地址,用於存儲讀取出來的數據。
  3. 第3個參數是要讀取出來的數據大小,單位字節。
  4. 返回值,返回從文件中實際讀出的字節數。

使用這個函數要注意以下問題:

  1. 設置讀取函數時,必須設置指定大小的字節數。如果實際讀出的字節數小於len,將停止讀取並關閉TFTP會話(打開的文件也會關閉),這種情況一般都是文件已經讀取完畢。

使用舉例:

U16 tftp_fread (void *file, U8 *buf, U16 len) {

 /* 讀取len字節到buf中,當此函數的返回值,即實際讀取的字節數小於len的時候,說明文件已經讀取完畢,

文件將被關閉*/

  return (fread (buf, 1, len, file));

}

39.2.4   函數tftp_fwrite

函數原型:

U16 tftp_fwrite (

    FILE* file,     /* 文件句柄地址 */

    U8*   buf,      /* 數據緩衝地址 */

U16   len );    /* 要寫入的字節數 */

函數描述:

函數tftp_fwrite用於往文件中寫入len個字節數據。此函數在MDK安裝目錄中的TFTP_uif.c文件裏面,屬於底層接口函數,用戶要在此函數裏面添加具體的操作。

  1. 第1個參數是要寫入數據的文件句柄地址。
  2. 第2個參數是數據緩衝地址,存儲了要寫入的數據。
  3. 第3個參數是要寫入的數據大小,單位字節。
  4. 返回值,返回實際寫入文件的字節數。

使用舉例:

U16 tftp_fwrite (FILE *file, U8 *buf, U16 len) {

  /* 將buf中的len字節寫入到文件中 */

  return (fwrite (buf, 1, len, file));

}

39.2.5   函數tftp_accept_host

函數原型:

BOOL tftp_accept_host (

    U8* rem_ip,      /* 遠程設備IP地址 */

U16 rem_port );  /* 遠程設備端口號 */

函數描述:

函數tftp_accept_host用於設置是否接受遠程連接,用戶可以通過此函數選擇允許哪些設備可以連接,哪些不可以連接。

  1. 第1個參數是遠程設備的IP地址。
  2. 第2個參數是遠程設備的端口號。
  3. 返回值,返回__TRUE表示允許此遠程連接,返回__FALSE表示不允許此遠程連接。

使用這個函數要注意以下問題:

  1. 此函數是可選的,如果大家在工程中沒有寫這個函數,RL-TCPnet庫會調用默認的函數,允許所有的連接請求,如果在工程中寫了此函數,會執行新寫的這個函數。

使用舉例:

BOOL ftp_accept_host (U8 *rem_ip, U16 rem_port) {

 

  if (rem_ip[0] == 192  &&

      rem_ip[1] == 168  &&

      rem_ip[2] == 1    &&

      rem_ip[3] == 1) {

    /* 接受此連接. */

    return (__TRUE);

  }

  /* 拒絕此連接 */

  return (__FALSE);

}

39.3 TFTP配置說明(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數量,這裏配置了5個。

範圍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接收窗口大小。

TFTP Server

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

(1)  Number of TFTP Sessions

同時可以連接的會話個數,即可以連接的TFTP客戶端個數。

範圍1-10。

(2)  Port Number

TFTP服務器的監聽端口號。

範圍1-65535。

(3) Enable Firewall Support

是否使能使用同一個端口號接收客戶端的請求並回復。

打上對勾表示使能。

(4) Inactive Session Timeout in seconds

交互會話期間的溢出時間,如果會話期間,這段時間內無操作,TFTP服務器將斷開客戶端的連接。

範圍5-120,單位秒。

(5)  Number of Retries

TFTP服務器放棄發送前,可以嘗試的發送次數,超過這個次數還沒有發送成功,將放棄發送。

範圍1-10次。

39.4 TFTP調試說明(Net_Debug.c)

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

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

Print Time Stamp

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

其它所有的選項

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

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

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

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

具體測試,我們這裏就不做了,大家可以按照第11章講解的調試方法進行測試。

39.5 TFTP客戶端軟件和板子的操作步驟

本章節配套的例子是用開發板做TFTP服務器,SD卡做爲服務器的存儲介質,所以務必準備好一個SD卡插到開發板上面。電腦端安裝TFTP客戶端進行訪問,這裏主要測試了文件上傳和下載功能。

另外,特別注意一點,我們使用的是RL-FlashFS文件系統,此文件系統的文件名僅支持ASCII字符,不支持中文,對於中文名的文件夾或者文件是無法操作的。

39.5.1 獲取板子IP地址

首先,強烈推薦將網線接到路由器或者交換機上面測試,因爲已經使能了DHCP,可以自動獲取IP地址,而且在前面的配置嚮導使能了局域網域名NetBIOS,用戶只需在電腦端ping armfly就可以獲得板子的IP地址。測試方法如下:

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

(2)彈出的命令窗口中,輸入ping armfly。

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

獲得IP地址是192.168.1.6。

39.5.2 TFTP客戶端軟件安裝和設置

第1步:下載TFTP客戶端軟件。

TFTP軟件推薦採用TFTPD32,客戶端和服務器都支持,分32bit和64bit兩個版本,大家根據自己電腦系統選擇相應版本進行安裝,另外推薦綠色版,無需安裝,使用起來簡單省事。下載地址:http://bbs.armfly.com/read.php?tid=32486

第2步:下載綠色版後,解壓出來就可以使用,打開軟件的效果如下(我的系統是WIN7 64bit,所以使用的是64位版本):

第3步:關閉不需要的功能,僅留下TFTP Client(不是必須的,僅剩下客戶端功能,看着簡潔些)。

首先點擊settings:

在彈出的窗口裏面僅選擇TFTP Client:

設置後,點擊OK按鍵,彈出如下窗口,繼續點擊OK:

經過這麼設置後,就僅剩下TFTP客戶端功能了,爲了使得設置的功能起作用,務必關閉軟件,然後重新打開。

設置完畢後,就可以測試文件的上傳和下載功能了。

39.5.3 通過TFTP客戶端軟件上傳文件到開發板

第1步:準備一個測試文件:

爲了方便查看下載文件的效果,找一個稍大些的文件,這裏使用我們之前做的DSP教程,並將名字修改爲111.pdf(已經將這個文件放在了本章節配套例子的Doc文件夾,修改名字是因爲我們用的文件系統RL-FlashFS不支持中文名,僅支持ASCII字符)。大家將這個文件放到桌面即可:

第2步:配置TFTP客戶端:

  1. 選擇Tftp Client選項。
  2. 選擇電腦端用於通信的網卡IP。
  3. 這裏填寫開發板的IP地址,因爲已經使能了NetBIOS Name,所以這裏直接填armfly即可,也可以填寫前面39.5.1小節獲取的IP地址192.168.1.6(如果填寫IP地址的話,根據自己實際獲取的填寫)。
  4. 填寫端口號,在例子的Net_Config.c文件中配置TFTP服務器的端口號是69,這裏也填69即可。
  5. 添加放在桌面的111.pdf文件,點擊右側的小按鈕,選擇111.pdf文件。
  1. 這裏可以什麼都不填,這樣下載到開發板SD卡中的文件就叫111.pdf,如果填了文件名,那麼下載到SD卡中的文件就叫這個新配置的文件名,我們這裏是設置成文件名222.pdf(切不要是中文,因爲不支持中文名)。
  2. 這裏塊大小的設置務必選擇默認配置,實際測試發現,只有默認配置才比較穩定。
  3. 最後點擊Put按鈕就可以將111.pdf文件上傳到開發板的SD卡中了,上傳到SD卡中的文件名叫222.pdf。上傳的過程中,會有一個上傳進度:

上傳完畢後,會彈出如下的對話框:

共傳輸了34202塊,文件的大小是16.6MB,耗時18秒上傳完畢,上傳速度是16.6MB/18 = 922KB/S。另外,爲了驗證上傳是否成功,大家可以查看SD卡中的222.pdf文件是否可以正常打開並瀏覽,如果可以,說明上傳成功。

39.5.4 通過TFTP客戶端軟件來下載開發板中的文件

爲了方便測試,我們這裏直接將39.5.3小節中上傳到開發板SD卡中的222.pdf文件下載到電腦端。並換個文件名,叫333.pdf(另外特別注意,這個Local File選項裏面是一定要添加路徑的,不可以空置)。

點擊Get按鈕就可以開始下載了,下載的過程中,下面截圖中紅色小方框的地方會記錄已經傳輸的塊大小:

下載完畢後,彈出如下對話框:

共傳輸了34202塊,跟39.5.3小節中的上傳的塊數是一致的。只是這裏耗時稍長,用了23秒,下載速度是16.6MB/23 = 721KB/S。另外,爲了驗證下載是否成功,需要大家查看電腦端的333.pdf文件是否可以正常瀏覽。

39.6 實驗例程說明(RTX)

39.6.1 STM32F407開發板實驗

配套例子:

V5-1057_RL-TCPnet實驗_TFTP服務器(RTX)

實驗目的:

  1. 學習RL-TCPnet的TFTP服務器實現。

實驗內容:

  1. 強烈推薦將網線接到路由器或者交換機上面測試,因爲已經使能了DHCP,可以自動獲取IP地址。
  2. TFTP服務器的存儲器是採用的SD卡,所以測試本例子前務必準備好一個SD卡並插上。
  3. 文件系統是採用的RL-FlashFS,此文件系統的文件名僅支持ASCII字符,不支持中文,特別注意!!
  4. TFTP服務器的訪問方法在本實例配套教程裏面有詳細講解,測試本例子前必須要看。

實驗操作:

詳見本章節39.5小節。

配置嚮導文件設置(Net_Config.c):

詳見本章節39.3小節。

調試文件設置(Net_Debug.c):

詳見本章節39.4小節。

RTX配置:

RTX配置嚮導詳情如下:

Task Configuration

(1) Number of concurrent running tasks

允許創建6個任務,實際創建瞭如下5個任務:

AppTaskUserIF任務   :按鍵消息處理。

AppTaskLED任務     :LED閃爍。

AppTaskMsgPro任務 :按鍵檢測。

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

AppTaskStart任務  :啓動任務,也是最高優先級任務,這裏實現RL-TCPnet的時間基準更新。

(2) Number of tasks with user-provided stack

創建的5個任務都是採用自定義堆棧方式。

(3) Run in privileged mode

設置任務運行在非特權級模式。

RTX任務調試信息:

程序設計:

任務棧大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任務棧 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任務棧 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任務棧 */

static uint64_t AppTaskTCPMainStk[4096/8]; /* 任務棧 */

static uint64_t AppTaskStartStk[1024/8];     /* 任務棧 */

將任務棧定義成uint64_t類型可以保證任務棧是8字節對齊的,8字節對齊的含義就是數組的首地址對8求餘等於0。如果不做8字節對齊的話,部分C語言庫函數、浮點運算和uint64_t類型數據運算會出問題。

系統棧大小分配:

RTX初始化:

/*

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

*    函 數 名: main

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

*    形    參: 無

*    返 回 值: 無

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

*/

int main (void)

{   

     /* 初始化外設 */

     bsp_Init();

    

     /* 創建啓動任務 */

     os_sys_init_user (AppTaskStart,              /* 任務函數 */

                       5,                         /* 任務優先級 */

                       &AppTaskStartStk,          /* 任務棧 */

                       sizeof(AppTaskStartStk));  /* 任務棧大小,單位字節數 */

     while(1);

}

硬件外設初始化

硬件外設的初始化是在 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_InitDWT();     /* 初始化DWT */

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

     bsp_InitKey();    /* 初始化按鍵變量(必須在 bsp_InitTimer() 之前調用) */

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

 

     MountSD();        /* 掛載SD卡 */

}

RTX任務創建:

/*

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

*    函 數 名: AppTaskCreate

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

*    形    參: 無

*    返 回 值: 無

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

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任務函數 */

                                           1,                         /* 任務優先級 */

                                           &AppTaskUserIFStk,         /* 任務棧 */

                                           sizeof(AppTaskUserIFStk)); /* 任務棧大小,單位字節數 */

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任務函數 */

                                        2,                       /* 任務優先級 */

                                        &AppTaskLEDStk,          /* 任務棧 */

                                        sizeof(AppTaskLEDStk));  /* 任務棧大小,單位字節數 */

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任務函數 */

                                           3,                         /* 任務優先級 */

                                           &AppTaskMsgProStk,         /* 任務棧 */

                                           sizeof(AppTaskMsgProStk)); /* 任務棧大小,單位字節數 */

    

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任務函數 */

                                           4,                         /* 任務優先級 */

                                           &AppTaskTCPMainStk,         /* 任務棧 */

                                           sizeof(AppTaskTCPMainStk)); /* 任務棧大小,單位字節數 */

}

五個RTX任務的實現:

/*

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

*    函 數 名: AppTaskUserIF

*    功能說明: 按鍵消息處理     

*    形    參: 無

*    返 回 值: 無

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

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

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1鍵按下 */

                   case KEY_DOWN_K1:

                       printf("K1鍵按下 \r\n");       

                       break;  

 

                   /* K2鍵按下 */

                  case KEY_DOWN_K2:

                       printf("K2鍵按下\r\n");        

                       break;

                  

                   /* K3鍵按下 */

                   case KEY_DOWN_K3:

                       printf("K3鍵按下 \r\n");

                       break;

 

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

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

/*

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

*    函 數 名: AppTaskLED

*    功能說明: LED閃爍。

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 2 

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

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 500; /* 延遲週期 */

    

     /* 設置延遲週期 */

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

 

         /* os_itv_wait是絕對延遲,os_dly_wait是相對延遲。*/

         os_itv_wait();

    }

}

 

/*

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

*    函 數 名: AppTaskMsgPro

*    功能說明: 按鍵檢測

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 3 

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

*/

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_KeyScan();

         os_dly_wait(10);

    }

}

 

/*

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

*    函 數 名: AppTaskTCPMain

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

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 4 

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

*/

__task void AppTaskTCPMain(void)

{

     while (1)

     {

         TCPnetTest();

     }

}

 

/*

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

*    函 數 名: AppTaskStart

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

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 5 

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

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 創建任務 */

     AppTaskCreate();

    

     os_itv_set (100);

    

    while(1)

    {

         os_itv_wait ();

        

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

         timer_tick ();

         os_evt_set(0x0001, HandleTaskTCPMain);

    }

}

RL-TCPnet功能測試

這裏專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,此文件主要實現網絡主函數main_TcpNet的調用。

#include "includes.h"

 

 

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPent測試函數。

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{

    

     while (1)

     {

         os_evt_wait_or(0x0001, 0xFFFF); 

        

         /* RL-TCPnet主處理函數 */       

         while (main_TcpNet() == __TRUE);

     }

}

TFTP用戶接口文件的實現

KEIL官網有提供TFTP的接口文件,名爲TFTP_uif.c文件。我們就是在這個文件上修改。具體修改後的代碼如下:

#include <Net_Config.h>

#include <File_Config.h>

#include <stdio.h>

 

/*----------------------------------------------------------------------------

 * FTP Server File Access Functions

 *---------------------------------------------------------------------------*/

 

 

#include <stdio.h>

#include <Net_Config.h>

 

/*----------------------------------------------------------------------------

 * TFTP Server File Access Functions

 *---------------------------------------------------------------------------*/

 

/*--------------------------- tftp_fopen ------------------------------------*/

 

void *tftp_fopen (U8 *fname, U8 *mode) {

  /* Open filename fname for reading or writing. */

  return (fopen((char *)fname, (char *)mode));

}

 

 

/*--------------------------- tftp_fclose -----------------------------------*/

 

void tftp_fclose (void *file) {

  /* Close the file, opened for reading or writing. */

  fclose (file);

}

 

 

/*--------------------------- tftp_fread ------------------------------------*/

 

U16 tftp_fread (void *file, U8 *buf, U16 len) {

  /* Read 'len' bytes from file to buffer 'buf'. Return number of bytes */

  /* copied. The file will be closed, when the return value is < 'len'  */

  return (fread (buf, 1, len, file));

}

 

 

/*--------------------------- tftp_fwrite -----------------------------------*/

 

U16 tftp_fwrite (void *file, U8 *buf, U16 len) {

  /* Write data to file. Return number of bytes actually written. */

  return (fwrite (buf, 1, len, file));

}

 

 

/*--------------------------- tftp_accept_host ------------------------------*/

#if 0

BOOL tftp_accept_host (U8 *rem_ip, U16 rem_port) {

  /* This function checks if a connection from remote host is accepted or  */

  /* not. If this function is missing, all remote hosts are accepted.      */

 

   if (rem_ip[0] == 192  &&

       rem_ip[1] == 168  &&

       rem_ip[2] == 1    &&

       rem_ip[3] == 1) {

      /* Accept a connection. */ 

      return (__TRUE);

   }

   /* Deny a connection. */

   return (__FALSE);

}

#endif

 

/*----------------------------------------------------------------------------

 * end of file

 *---------------------------------------------------------------------------*/

39.6.2 STM32F429開發板實驗

配套例子:

V6-1057_RL-TCPnet實驗_TFTP服務器(RTX)

實驗目的:

  1. 學習RL-TCPnet的TFTP服務器實現。

實驗內容:

  1. 強烈推薦將網線接到路由器或者交換機上面測試,因爲已經使能了DHCP,可以自動獲取IP地址。
  2. TFTP服務器的存儲器是採用的SD卡,所以測試本例子前務必準備好一個SD卡並插上。
  3. 文件系統是採用的RL-FlashFS,此文件系統的文件名僅支持ASCII字符,不支持中文,特別注意!!
  4. TFTP服務器的訪問方法在本實例配套教程裏面有詳細講解,測試本例子前必須要看。

實驗操作:

詳見本章節39.5小節。

配置嚮導文件設置(Net_Config.c):

詳見本章節39.3小節。

調試文件設置(Net_Debug.c):

詳見本章節39.4小節。

RTX配置:

RTX配置嚮導詳情如下:

  Task Configuration

(1) Number of concurrent running tasks

允許創建6個任務,實際創建瞭如下5個任務:

AppTaskUserIF任務   :按鍵消息處理。

AppTaskLED任務     :LED閃爍。

AppTaskMsgPro任務 :按鍵檢測。

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

AppTaskStart任務  :啓動任務,也是最高優先級任務,這裏實現RL-TCPnet的時間基準更新。

(2) Number of tasks with user-provided stack

創建的5個任務都是採用自定義堆棧方式。

(3)  Run in privileged mode

設置任務運行在非特權級模式。

RTX任務調試信息:

程序設計:

任務棧大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任務棧 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任務棧 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任務棧 */

static uint64_t AppTaskTCPMainStk[4096/8]; /* 任務棧 */

static uint64_t AppTaskStartStk[1024/8];     /* 任務棧 */

將任務棧定義成uint64_t類型可以保證任務棧是8字節對齊的,8字節對齊的含義就是數組的首地址對8求餘等於0。如果不做8字節對齊的話,部分C語言庫函數、浮點運算和uint64_t類型數據運算會出問題。

系統棧大小分配:

RTX初始化:

/*

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

*    函 數 名: main

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

*    形    參: 無

*    返 回 值: 無

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

*/

int main (void)

{   

     /* 初始化外設 */

     bsp_Init();

    

     /* 創建啓動任務 */

     os_sys_init_user (AppTaskStart,              /* 任務函數 */

                       5,                         /* 任務優先級 */

                       &AppTaskStartStk,          /* 任務棧 */

                       sizeof(AppTaskStartStk));  /* 任務棧大小,單位字節數 */

     while(1);

}

硬件外設初始化

硬件外設的初始化是在 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);

 

     SystemCoreClockUpdate();    /* 根據PLL配置更新系統時鐘頻率變量 SystemCoreClock */

 

     bsp_InitDWT();      /* 初始化DWT */

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

     bsp_InitKey();     /* 初始化按鍵變量(必須在 bsp_InitTimer() 之前調用) */

 

     bsp_InitExtIO();    /* FMC總線上擴展了32位輸出IO, 操作LED等外設必須初始化 */

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

 

     MountSD();          /* 掛載SD卡 */

}

RTX任務創建:

/*

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

*    函 數 名: AppTaskCreate

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

*    形    參: 無

*    返 回 值: 無

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

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任務函數 */

                                           1,                         /* 任務優先級 */

                                           &AppTaskUserIFStk,         /* 任務棧 */

                                           sizeof(AppTaskUserIFStk)); /* 任務棧大小,單位字節數 */

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任務函數 */

                                        2,                       /* 任務優先級 */

                                        &AppTaskLEDStk,          /* 任務棧 */

                                        sizeof(AppTaskLEDStk));  /* 任務棧大小,單位字節數 */

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任務函數 */

                                           3,                         /* 任務優先級 */

                                           &AppTaskMsgProStk,         /* 任務棧 */

                                           sizeof(AppTaskMsgProStk)); /* 任務棧大小,單位字節數 */

    

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任務函數 */

                                           4,                         /* 任務優先級 */

                                           &AppTaskTCPMainStk,         /* 任務棧 */

                                           sizeof(AppTaskTCPMainStk)); /* 任務棧大小,單位字節數 */

}

五個RTX任務的實現:

/*

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

*    函 數 名: AppTaskUserIF

*    功能說明: 按鍵消息處理     

*    形    參: 無

*    返 回 值: 無

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

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

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1鍵按下 */

                   case KEY_DOWN_K1:

                       printf("K1鍵按下 \r\n");       

                       break;  

 

                   /* K2鍵按下 */

                   case KEY_DOWN_K2:

                       printf("K2鍵按下\r\n");        

                       break;

                  

                   /* K3鍵按下 */

                   case KEY_DOWN_K3:

                       printf("K3鍵按下 \r\n");

                       break;

 

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

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

/*

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

*    函 數 名: AppTaskLED

*    功能說明: LED閃爍。

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 2 

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

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 500; /* 延遲週期 */

    

     /* 設置延遲週期 */

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

 

         /* os_itv_wait是絕對延遲,os_dly_wait是相對延遲。*/

         os_itv_wait();

    }

}

 

/*

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

*    函 數 名: AppTaskMsgPro

*    功能說明: 按鍵檢測

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 3 

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

*/

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_KeyScan();

         os_dly_wait(10);

    }

}

 

/*

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

*    函 數 名: AppTaskTCPMain

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

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 4 

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

*/

__task void AppTaskTCPMain(void)

{

     while (1)

     {

         TCPnetTest();

     }

}

 

/*

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

*    函 數 名: AppTaskStart

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

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 5 

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

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 創建任務 */

     AppTaskCreate();

    

     os_itv_set (100);

    

    while(1)

    {

         os_itv_wait ();

        

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

          timer_tick ();

         os_evt_set(0x0001, HandleTaskTCPMain);

    }

}

RL-TCPnet功能測試

這裏專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,此文件主要實現網絡主函數main_TcpNet的調用。

#include "includes.h"

 

 

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPent測試函數。

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{

    

     while (1)

     {

         os_evt_wait_or(0x0001, 0xFFFF); 

        

         /* RL-TCPnet主處理函數 */       

         while (main_TcpNet() == __TRUE);

     }

}

TFTP用戶接口文件的實現

KEIL官網有提供TFTP的接口文件,名爲TFTP_uif.c文件。我們就是在這個文件上修改。具體修改後的代碼如下:

#include <Net_Config.h>

#include <File_Config.h>

#include <stdio.h>

 

/*----------------------------------------------------------------------------

 * FTP Server File Access Functions

 *---------------------------------------------------------------------------*/

 

 

#include <stdio.h>

#include <Net_Config.h>

 

/*----------------------------------------------------------------------------

 * TFTP Server File Access Functions

 *---------------------------------------------------------------------------*/

 

/*--------------------------- tftp_fopen ------------------------------------*/

 

void *tftp_fopen (U8 *fname, U8 *mode) {

  /* Open filename fname for reading or writing. */

  return (fopen((char *)fname, (char *)mode));

}

 

 

/*--------------------------- tftp_fclose -----------------------------------*/

 

void tftp_fclose (void *file) {

  /* Close the file, opened for reading or writing. */

  fclose (file);

}

 

 

/*--------------------------- tftp_fread ------------------------------------*/

 

U16 tftp_fread (void *file, U8 *buf, U16 len) {

  /* Read 'len' bytes from file to buffer 'buf'. Return number of bytes */

  /* copied. The file will be closed, when the return value is < 'len'  */

  return (fread (buf, 1, len, file));

}

 

 

/*--------------------------- tftp_fwrite -----------------------------------*/

 

U16 tftp_fwrite (void *file, U8 *buf, U16 len) {

  /* Write data to file. Return number of bytes actually written. */

  return (fwrite (buf, 1, len, file));

}

 

 

/*--------------------------- tftp_accept_host ------------------------------*/

#if 0

BOOL tftp_accept_host (U8 *rem_ip, U16 rem_port) {

  /* This function checks if a connection from remote host is accepted or  */

  /* not. If this function is missing, all remote hosts are accepted.      */

 

   if (rem_ip[0] == 192  &&

       rem_ip[1] == 168  &&

       rem_ip[2] == 1    &&

       rem_ip[3] == 1) {

      /* Accept a connection. */ 

      return (__TRUE);

   }

   /* Deny a connection. */

   return (__FALSE);

}

#endif

 

/*----------------------------------------------------------------------------

 * end of file

 *---------------------------------------------------------------------------*/

39.7 總結

本章節就爲大家講解這麼多,其中TFTP的測試稍麻煩些,希望大家實際動手操作一遍,並將其熟練掌握。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章