【STM32F407】第8章 RL-TCPnet V7.X網路協議棧移植(FreeRTOS)
最新教程下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=95243
第8章 RL-TCPnet網路協議棧移植(FreeRTOS)
本章教程為大家講解RL-TCPnet網路協議棧的FreeRTOS版本移植方式。
8.1 初學者重要提示
8.2 移植RL-TCPnet協議棧整體說明(必讀)
8.3 第1步,安裝指定的MDK軟體包版本
8.4 第2步,準備一個工程模板
8.5 第3步,新增FreeRTOS原始碼
8.6 第4步,新增RT-TCPnet並配置
8.7 第5步,獨立新增MAC和PHY驅動檔案
8.8 第6步,初始化ETH_INIT.c檔案
8.9 第7步,MPU和Cache配置檔案bsp.c
8.10 第8步,更新bsp_timer.c和bsp.h檔案
8.12 第10步,建立應用任務
8.13 常見移植錯誤總結
8.14 網路除錯助手和板子的除錯操作步驟
8.15 總結
8.1 初學者重要提示
- 學習本章節前,務必要優先學習第6章的底層驅動講解。
- 測試時,請將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址。而且使能了NetBIOS區域網域名,使用者只需在電腦端ping armfly,就可以獲得板子的IP地址。
- 如果要使用固定IP進行測試,請看附件章節A。
- 網口使用的是DM9161/9162(緊挨著9幀串列埠座的網口),而不是DM9000。
- 測試例子,務必看本章7.14小節的操作步驟。
8.2 移植RL-TCPnet協議棧整體說明(必讀)
移植之前,有必要對移植過程有個整體的認識:
- 第1步,準備一個工程模板。
- 第2步,移植FreeRTOS。
- 移植FreeRTOS是採用MDK的RTE環境直接新增。
- 特別注意幾個網路任務的優先順序安排。
AppTaskMsgPro任務 : osPriorityNormal2
AppTaskEthCheck : osPriorityNormal3。
netCore_Thread任務 : osPriorityNormal3。
netEth0_Thread任務 : osPriorityNormal3。
osRtxTimerThread任務: osPriorityRealtime。
注意這個定時器任務osRtxTimerThread的優先順序一定要最高,因為這個是RL-TCPnet的時間基準執行任務。
- 第3步,移植RL-TCPnet。對於配套的驅動,可以使用經典移植方式,也可以使用STM32CubeMX提供的建立,本教程是採用的經典移植方式,因為STM32CubeMX的配置太繁瑣了,而經典方式就方便太多了。
- 第4步,處理HAL庫時間基準。
- 第5步,建立應用。
總的來說,這5步就可以完成移植,這裡還有一點比較重要的,需要放在開頭說明,為了保證工程的獨立性,教程中將RTE環境新增的HAL庫檔案,MAC驅動和PHY驅動獨立了出來,並且單獨製作了一個移植檔案ETH_INIT.c,將GPIO設定,網線插拔訊息,乙太網中斷等都彙總到這個檔案裡面,方便大家移植工程到自己的板子上。
下面將STM32F4的移植步驟和注意事項為大家做個說明。
8.3 第1步,安裝指定的MDK軟體包版本
移植新版RL-TCPnet網路協議棧需要大家下載指定的MDK軟體包版本:
- CMSIS 軟體包使用當前最新的:V5.6.0
- STM32F4使用當前最新的:V2.14
- CMSIS-Driver使用當前最新的:V10.2.0
- MDK中介軟體使用當前最新的:V7.12.0
- STM32CubeMX使用當前最新的:V5.4
- ARM_Compiler使用當前最新的:V1.6.1
- 這些軟體包的安裝在STM32F4使用者手冊的第2章2.3小節有詳細說明。
http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 。
- 所有這些軟體包彙總下載地址:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=95609 。
不管以後MDK的軟體包版本如何升級,當前的軟體包版本和以後的新版是可以同時安裝的,也就是說可以安裝多個不同版本,在這裡可以選擇指定版本:
8.4 第2步,準備一個工程模板
首先準備好一個簡單的裸機工程模板,工程模板的製作就不做講解了。從這個帖子裡面下載一個例子即可:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255。
這裡的重點是教大家移植RL-TCPnet協議棧。準備好的工程模板如下圖所示(大家也可以製作其它任意的工程模板,不限制):
8.5 第3步,新增FreeRTOS並配置
FreeRTOS可以方便的通過MDK的RTE環境新增進來。對於F4版本,會新增多個F4版的HAL庫檔案,這些庫檔案我們可以使用,也可以不使用。教程配套的工程檔案是不使用這些的,因為前面的工程模板裡面已經添加了。所以要將這些檔案全部隔離出來。
8.5.1 新增FreeRTOS原始碼
點選OK按鈕後,可以看到FreeRTOS原始碼已經新增到工程裡面了:
8.5.2 將自動新增的庫檔案隔離出來
新增的所有檔案中,startup_stm32f407xx.s和system_stm32f4xx.c要隔離出來,隔離方法也比較簡單,比如隔離startup_stm32f407xx.s文庫,滑鼠右擊此檔案選擇Options for file ‘startup_stm32f407xx.s’
取消下面的對勾:
隔離這一個檔案後,另一個檔案system_stm32f4xx.c也被自動隔離了,隔離後的效果如下:
8.5.3 FreeRTOS配置
剩下就是配置FreeRTOS,設定RTX_Config.h檔案即可,移植階段先按照如下設定配置好,後面章節會專門為大家講解每個引數的配置含義:
8.6 第4步,新增RL-TCPnet並配置
像新增FreeRTOS一樣,也可以通過RTE環境新增RL-TCPnet相關配置。
8.6.1 RL-TCPnet相關檔案和驅動
CMSIS-Driver分組中新增MAC驅動和PHY驅動,其中PHY驅動隨便選擇一個即可,因為這個裡面沒有開發板上使用的DM9161/9162:
新增Event Recoder的支援,因為RL-TCPnet庫的除錯版本需要Event Recoder的支援:
選擇經典驅動方式(這個裡面已經包含乙太網部分):
選擇需要的HAL驅動:
新增網路配置:
我們需要的都已經新增好,效果如下:
8.6.2 乙太網引腳配置檔案RTE_Device.h
根據開發板的乙太網介面實際使用的引腳,可以通過檔案RTE_Device.h非常方便的配置出來:
8.6.3 將自動新增的庫檔案隔離出來
紅色方框裡面的這幾個檔案要隔離出來:
特別注意檔案stm32f4xx_hal_conf.h不要隔離,否則會編譯出錯,對應的選項如下:
最終隔離後的效果如下:
8.6.4 RL-TCPnet配置
新增完畢RL-TCPnet所需的檔案後,就是配置RL-TCPnet,具體每個配置所代表的含義,會在後面章節專為大家講解。
Net_Config.c檔案配置:
注意這個檔案裡面還有一個RL-TCPnet核心執行緒的優先順序配置,當前是將其配置為:
osPriorityNormal:
Net_Config_ETH_0.h檔案的配置如下:
這個檔案裡面有一個乙太網介面任務的優先順序配置,當前是將其配置為:osPriorityAboveNormal1。
其它檔案Net_Config_TCP.h,Net_Config_UDP.h和Net_Debug.c使用預設配置即可。
8.7 第5步,獨立新增MAC和PHY驅動檔案
將我們裸機模板中製作好的RL-ARM資料夾複製貼上到大家準備好的工程模板中。
RL-ARM資料夾中有如下七個資料夾,其中只有Driver資料夾裡面有檔案
然後將其也新增到工程檔案裡面:
檔案PHY_DM916x和EMAC_STM32F4xx在第6章已經做了專門說明,這裡重點把檔案ETH_INIT.c檔案做個說明。
8.8 第6步,初始化檔案ETH_INIT.c
這個檔案主要是網線插拔狀態標識:
/* 乙太網連線狀態,0和1都表示初始臨時狀態,2表示連線上,3表示斷開 */ __IO uint8_t g_ucEthLinkStatus = 0; /* ********************************************************************************************************* * 函 數 名: netETH_Notify * 功能說明: 乙太網狀態訊息 * 形 參: --- * 返 回 值: 無 ********************************************************************************************************* */ void netETH_Notify (uint32_t if_num, netETH_Event event, uint32_t val) { NET_ETH_LINK_INFO *info; switch (event) { case netETH_LinkDown: if(g_ucEthLinkStatus == 2) { g_ucEthLinkStatus = 3; } else { g_ucEthLinkStatus = 1; } printf_eth ("Link is down\r\n"); break; case netETH_LinkUp: g_ucEthLinkStatus = 2; printf_eth ("Link is up\r\n"); info = (NET_ETH_LINK_INFO *)&val; switch (info->speed) { case 0: printf_eth ("10 MBit\r\n"); break; case 1: printf_eth ("100 MBit\r\n"); break; case 2: printf_eth ("1 GBit\r\n"); break; } switch (info->duplex) { case 0: printf_eth ("Half duplex\r\n"); break; case 1: printf_eth ("Full duplex\r\n"); break; } break; case netETH_Wakeup: printf_eth ("Wakeup frame received\r\n"); break; case netETH_TimerAlarm: printf_eth ("Timer alarm\r\n"); break; } }
這裡要注意變數g_ucEthLinkStatus = 1的情況。因為上電後,不管板子有沒有插入網線,都會進入一次訊息netETH_LinkDown,我們把這種情況用數值1來表示。
8.9 第7步,初始化配置檔案bsp.c
這個bsp.c檔案也比較重要,移植階段,直接將我們移植好的模板內容複製過去即可,這裡把相關的內容為大家做個說明。
8.9.1 函式System_Init
系統初始化,主要是系統時鐘配置,需要在FreeRTOS初始化之前呼叫。
/* ********************************************************************************************************* * 函 數 名: System_Init * 功能說明: 系統初始化,主要是系統時鐘配置 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void System_Init(void) { /* STM32F407 HAL 庫初始化,此時系統用的還是F407自帶的16MHz,HSI時鐘: - 呼叫函式HAL_InitTick,初始化滴答時鐘中斷1ms。 - 設定NVIV優先順序分組為4。 */ HAL_Init(); /* 配置系統時鐘到168MHz - 切換使用HSE。 - 此函式會更新全域性變數SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。 - 預設不開啟,如果要使能此選項,務必看V5開發板使用者手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif }
8.9.2 函式bsp_Init
硬體外設的初始化,這個函式在FreeRTOS的啟動任務裡面呼叫。
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬體裝置。該函式配置CPU暫存器和外設的暫存器並初始化一些全域性變數。只需要呼叫一次 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串列埠 */ bsp_InitLed(); /* 初始化LED */ }
8.9.3 函式SystemClock_Config
這個函式主要是完成系統時鐘配置。
/* ********************************************************************************************************* * 函 數 名: SystemClock_Config * 功能說明: 初始化系統時鐘 * System Clock source = PLL (HSE) * SYSCLK(Hz) = 168000000 (CPU Clock) * HCLK = SYSCLK / 1 = 168000000 (AHB1Periph) * PCLK2 = HCLK / 2 = 84000000 (APB2Periph) * PCLK1 = HCLK / 4 = 42000000 (APB1Periph) * HSE Frequency(Hz) = 25000000 * PLL_M = 25 * PLL_N = 336 * PLL_P = 2 * PLL_Q = 4 * VDD(V) = 3.3 * Flash Latency(WS) = 5 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitTypeDef RCC_OscInitStruct = {0}; /* 晶片內部的LDO穩壓器輸出的電壓範圍,選用的PWR_REGULATOR_VOLTAGE_SCALE1 */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /* 使能HSE,並選擇HSE作為PLL時鐘源 */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } /* 選擇PLL的輸出作為系統時鐘 HCLK = SYSCLK / 1 (AHB1Periph) PCLK2 = HCLK / 2 (APB2Periph) PCLK1 = HCLK / 4 (APB1Periph) */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; /* 此函式會更新SystemCoreClock,並重新配置HAL_InitTick */ if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } /* 使能SYS時鐘和IO補償 */ __HAL_RCC_SYSCFG_CLK_ENABLE() ; HAL_EnableCompensationCell(); }
8.9.4 函式bsp_RunPer10ms
這個函式裡面預設有個按鍵掃描,如果大家移植的程式裡面沒有按鍵初始化,務必要把這個按鍵掃描函式註釋掉。
/* ********************************************************************************************************* * 函 數 名: bsp_RunPer10ms * 功能說明: 該函式每隔10ms被Systick中斷呼叫1次。詳見 bsp_timer.c的定時中斷服務程式。一些處理時間要求 * 不嚴格的任務可以放在此函式。比如:按鍵掃描、蜂鳴器鳴叫控制等。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_RunPer10ms(void) { bsp_KeyScan10ms(); }
8.10 第8步,更新bsp_timer.c和bsp.h檔案
更新bsp_timer.c檔案是因為此檔案跟FreeRTOS都要使用滴答定時器,有衝突。所以大家直接將我們工程模板裡面此檔案覆蓋移植的這個檔案即可。
bsp.h檔案裡面要新增一個巨集定義,因為bsp_timer.c檔案裡面做了些條件編譯:
#define USE_FreeRTOS 1 #if USE_FreeRTOS == 1 #include "FreeRTOS.h" #include "task.h" #define DISABLE_INT() taskENTER_CRITICAL() #define ENABLE_INT() taskEXIT_CRITICAL() #else /* 開關全域性中斷的巨集 */ #define ENABLE_INT() __set_PRIMASK(0) /* 使能全域性中斷 */ #define DISABLE_INT() __set_PRIMASK(1) /* 禁止全域性中斷 */ #endif
8.11 第9步,HAL庫時間基準stm32f4xx_hal_timbase_tim.c
由於FreeRTOS和HAL庫需要一個時間基準,而且預設都是用的滴答定時器,所有要有一個選用其它的時間基準。當前的處理方案是為HAL庫提供一個時間基準檔案stm32f4xx_hal_timbase_tim.c。此檔案裡面做了兩套方案,一個是使用TIM7做時間基準,另一個是使用FreeRTOS的API做時間基準,通過條件編譯做選擇。預設是採用FreeRTOS的API做時間基準。
/* ********************************************************************************************************* * 函 數 名: HAL_Delay * 功能說明: 重定向毫秒延遲函式。替換HAL中的函式。因為HAL中的預設函式依賴於Systick中斷,如果在USB、SD * 卡中斷中有延遲函式,則會鎖死。也可以通過函式HAL_NVIC_SetPriority提升Systick中斷 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void HAL_Delay(uint32_t Delay) { bsp_DelayMS(Delay); } HAL_StatusTypeDef HAL_InitTick (uint32_t TickPriority) { return HAL_OK; } uint32_t HAL_GetTick (void) { static uint32_t ticks = 0U; uint32_t i; if (osKernelGetState () == osKernelRunning) { return ((uint32_t)osKernelGetTickCount ()); } /* 如果FreeRTOS還沒有執行,採用下面方式 */ for (i = (SystemCoreClock >> 14U); i > 0U; i--) { __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); } return ++ticks; }
函式bsp_DelayMS在bsp_dwt.c檔案裡面,需要大家從我們配套例子中複製到我們的工程裡面,DWT是時鐘週期計數器,在CM3,CM4和CM7核心裡面是通用的。
8.12 第10步,建立應用任務
FreeRTOS和RL-TCPnet都移植完畢後,就可以新增應用任務驗證是否移植成功了。
有三個檔案需要設計,移植階段,直接新增到自己移植的工程裡面即可。
- 檔案main.c
主要功能是FreeRTOS任務的建立和執行。
- 檔案includes.h
主要功能是各種標頭檔案的彙總。
- 檔案app_tcpnet_lib.c
主要功能是TCPnet的應用任務設計。
8.13 常見移植錯誤總結
常見的移植錯誤主要有下面幾種情況:
- 編譯沒有錯誤,下載進去後ping不通過。
解決辦法:重新上電即可。
- 編譯後,提示錯誤cannot open source input file "Driver_ETH_PHY.h"
出現這個錯誤的解決辦法是將新版CMSIS軟體包裡面Driver資料夾全部複製到工程裡面的CMSIS資料夾下,並新增路徑:
- 編譯後提示如下兩種錯誤:
Error: L6200E: Symbol PendSV_Handler multiply defined (by irq_cm4f.o and stm32h7xx_it.o).
Error: L6200E: Symbol SVC_Handler multiply defined (by irq_cm4f.o and stm32h7xx_it.o).
解決辦法:這是函式重定義了,直接將stm32h7xx_it.c檔案裡面的PendSV_Handler和SVC_Handler刪掉。
- 提示如下錯誤
Error: L6218E: Undefined symbol bsp_DelayMS (referred from bsp_fmc_io.o).
解決辦法:開啟bsp_dwt.C檔案中的條件編譯。
8.14 網路除錯助手和板子的除錯操作步驟
我們這裡使用下面這款除錯助手,當然,任何其它網路除錯助手均可,不限制:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=1568 。
8.14.1 測試使用的DM916X網口並注意跳線帽
測試時,網線要插到DM916X網口上:
最後,強烈推薦將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址,而且在前面的配置中使能了局域網域名NetBIOS,使用者只需在電腦端ping armfly就可以獲取板子的IP地址。
如果使用固定IP方式,看附件章節A。
8.14.2 RJ45網路變壓器插座上綠燈和黃燈現象
各種網絡卡、交換機等網路裝置都不一樣,一般來講:綠燈分為亮或不亮(代表網路速度),黃燈分為閃爍或不閃爍(代表是否有資料收發)。
綠燈:長亮代表100M; 不亮代表10M。
黃燈:長亮代表無資料收發; 閃爍代表有資料收發。
也有些千兆網絡卡的燈以顏色區分,不亮代表10M / 綠色代表100M / 黃色代表1000M。現在10M的網路基本看不到了,如果一個燈長亮,基本可以說明100M網路或更高,而另一個燈時而閃爍,那代表有資料收發,具體要看網路裝置了。甚至有些低等網絡卡如TP-LINK,只有一個燈,亮代表連通,閃爍代表資料收發。
對於開發板上面的RJ45網路變壓器插座上面的燈而言,綠燈代表資料收發,長亮的話表示無資料收發,閃爍代表有資料收發。黃燈代表網路速度,長亮代表100M,不亮代表10M。
8.14.3 網線插拔的各種情況
此貼對各種網線插拔情況進行了總結,並且當前配套模板程式也做了支援:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=95386 。
8.14.4 第1步,獲取板子IP地址
首先,強烈推薦將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址,而且在前面的配置中使能了局域網域名NetBIOS,使用者只需在電腦端ping armfly就可以獲取板子的IP地址。
ping命令的主要作用是通過傳送資料包並接收應答資訊來檢測兩臺裝置之間的網路是否連通。ping命令成功說明當前主機與目的主機之間存在連通的路徑。如果不成功,需要檢視網線是否連通、網絡卡設定是否正確、IP地址是否可用等。測試方法如下:
- WIN+R組合鍵開啟“執行”視窗,輸入cmd。
- 彈出的命令視窗中,輸入ping armfly。
- 輸入ping armfly後,回車。
收發相同,沒有資料丟失,說明ping命令也是成功的。獲得IP地址是192.168.1.5。
8.14.5 第2步,網路除錯助手建立TCP客戶端
- 開啟除錯助手,點選左上角建立連線:
- 彈出如下介面,型別選擇TCP,目標IP設定為192.168.1.5,埠號1001,最後點選建立:
特別說明,我們這裡直接填區域網域名armfly也是沒有問題的,即下面這樣:
- 建立後的介面效果如下:
- 點選連線,連線後的介面效果如下:
連線上後,串列埠軟體也會打印出如下資訊(波特率115200,資料位8,奇偶校驗位無,停止位1):
8.14.6 第3步,TCP伺服器傳送資料
板子和網路除錯助手建立連線後就可以相互收發資料了。對於傳送資料。程式中建立了三種大小的資料傳送測試。
- K1按鍵按下,傳送了8個字元,從1到8。
- K2按鍵按下,傳送1024位元組,每次傳送資料包的前8個位元組設定了字元a到字元h,後面未做設定。
- K3按鍵按下,傳送5*1024*1024 = 5242880位元組,即5MB。每次傳送資料包的前8個位元組設定了字元a到字元h,後面都未做設定。
8.14.7 第4步,TCP伺服器接收資料
TCP伺服器接收資料的測試也比較方便,我們這裡通過網路除錯助手給板子傳送0到9,共10個字元:
點擊發送後,可以看到串列埠軟體打印出接收到的10個字元:
字元0對應的ASCII值就是48,其它字元數值依次增加。測試也是沒問題的。
8.15 總結
本章節為大家講解了RL-TCPnet網路協議棧的FreeRTOS版本移植方法,移植涉及到的知識點比較多,初學的話,建議實際動手操作一遍。