一、問題描述
在一個使用FreeRTOS的工程中,只做了SD卡的驅動,由於RTOS使用了Systick,故非系統延時函數使用的是 DWT中的時鍾周期(CYCCNT)計數功能,但是在SD卡驅動中使用了這個非系統延時導致,燒寫程序后板子工作正常,而下電再上電后板子無反應,分析排查去掉了這個非系統延時后工作正常。
二、使用環境
1)開發環境使用的是MDK5.20,下載器為JLINK;
2)軟件工程是V6的FreeRTOS模板工程,SD卡驅動也是V6的,非系統延時函數所在文件為V6的 bsp_dwt.c;
3)硬件板子是自己做的,MCU是STM32F429ZGT6;
三、問題分析
1)硬件板子已使用了一段時間,工作都正常包括下電再上電的情況,故該問題排出了硬件電路的問題;
2)由於之前也遇見過這樣的現象,再加之網絡查找,和代碼分析實驗,最后將問題定在了延時 bsp_DelayMS(100);
3)工程代碼
1 int main(void) 2 { 3 /* 4 在啟動調度前,為了防止初始化STM32外設時有中斷服務程序執行,這里禁止全局中斷(除了NMI和HardFault)。 5 這樣做的好處是: 6 1. 防止執行的中斷服務程序中有FreeRTOS的API函數。 7 2. 保證系統正常啟動,不受別的中斷影響。 8 3. 關於是否關閉全局中斷,大家根據自己的實際情況設置即可。 9 在移植文件port.c中的函數prvStartFirstTask中會重新開啟全局中斷。通過指令cpsie i開啟,__set_PRIMASK(1) 10 和cpsie i是等效的。 11 */ 12 __set_PRIMASK(1); 13 14 /* 硬件初始化 */ 15 bsp_Init(); 16 17 /* 創建任務 */ 18 AppTaskCreate(); 19 20 /* 啟動調度,開始執行任務 */ 21 vTaskStartScheduler(); 22 23 /* 24 如果系統正常啟動是不會運行到這里的,運行到這里極有可能是用於定時器任務或者空閑任務的 25 heap空間不足造成創建失敗,此要加大FreeRTOSConfig.h文件中定義的heap大小: 26 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) ) 27 */ 28 while(1); 29 } 30 31 /* 32 ********************************************************************************************************* 33 * 函 數 名: vTaskTaskUserIF 34 * 功能說明: 接口消息處理,這里用作LED閃爍 35 * 形 參: pvParameters 是在創建該任務時傳遞的形參 36 * 返 回 值: 無 37 * 優 先 級: 1 (數值越小優先級越低,這個跟uCOS相反) 38 ********************************************************************************************************* 39 */ 40 static void vTaskTaskUserIF(void *pvParameters) 41 { 42 while(1) 43 { 44 bsp_LedToggle(1); 45 vTaskDelay(500); 46 } 47 } 48 49 /* 50 ********************************************************************************************************* 51 * 函 數 名: vTaskLED 52 * 功能說明: LED閃爍 53 * 形 參: pvParameters 是在創建該任務時傳遞的形參 54 * 返 回 值: 無 55 * 優 先 級: 2 56 ********************************************************************************************************* 57 */ 58 static void vTaskLED(void *pvParameters) 59 { 60 while(1) 61 { 62 bsp_LedToggle(2); 63 vTaskDelay(1000); 64 } 65 } 66 67 /* 68 ********************************************************************************************************* 69 * 函 數 名: vTaskMsgPro 70 * 功能說明: 信息處理,這里是用作LED閃爍 71 * 形 參: pvParameters 是在創建該任務時傳遞的形參 72 * 返 回 值: 無 73 * 優 先 級: 3 74 ********************************************************************************************************* 75 */ 76 static void vTaskMsgPro(void *pvParameters) 77 { 78 while(1) 79 { 80 DemoFatFS(); 81 vTaskDelay(300); 82 } 83 } 84 85 /* 86 ********************************************************************************************************* 87 * 函 數 名: vTaskStart 88 * 功能說明: 啟動任務,也就是最高優先級任務,這里用作LED閃爍 89 * 形 參: pvParameters 是在創建該任務時傳遞的形參 90 * 返 回 值: 無 91 * 優 先 級: 4 92 ********************************************************************************************************* 93 */ 94 static void vTaskStart(void *pvParameters) 95 { 96 while(1) 97 { 98 /* 按鍵掃描 */ 99 bsp_LedToggle(4); 100 vTaskDelay(400); 101 } 102 } 103 104 /* 105 ********************************************************************************************************* 106 * 函 數 名: AppTaskCreate 107 * 功能說明: 創建應用任務 108 * 形 參:無 109 * 返 回 值: 無 110 ********************************************************************************************************* 111 */ 112 static void AppTaskCreate (void) 113 { 114 xTaskCreate( vTaskTaskUserIF, /* 任務函數 */ 115 "vTaskUserIF", /* 任務名 */ 116 512, /* 任務棧大小,單位word,也就是4字節 */ 117 NULL, /* 任務參數 */ 118 1, /* 任務優先級*/ 119 &xHandleTaskUserIF ); /* 任務句柄 */ 120 121 122 xTaskCreate( vTaskLED, /* 任務函數 */ 123 "vTaskLED", /* 任務名 */ 124 512, /* 任務棧大小,單位word,也就是4字節 */ 125 NULL, /* 任務參數 */ 126 2, /* 任務優先級*/ 127 &xHandleTaskLED ); /* 任務句柄 */ 128 129 xTaskCreate( vTaskMsgPro, /* 任務函數 */ 130 "vTaskMsgPro", /* 任務名 */ 131 512, /* 任務棧大小,單位word,也就是4字節 */ 132 NULL, /* 任務參數 */ 133 3, /* 任務優先級*/ 134 &xHandleTaskMsgPro ); /* 任務句柄 */ 135 136 137 xTaskCreate( vTaskStart, /* 任務函數 */ 138 "vTaskStart", /* 任務名 */ 139 512, /* 任務棧大小,單位word,也就是4字節 */ 140 NULL, /* 任務參數 */ 141 4, /* 任務優先級*/ 142 &xHandleTaskStart ); /* 任務句柄 */ 143 }
1 /* 2 ********************************************************************************************************* 3 * 函 數 名: DemoFatFS 4 * 功能說明: FatFS文件系統演示主程序 5 * 形 參:無 6 * 返 回 值: 無 7 ********************************************************************************************************* 8 */ 9 void DemoFatFS(void) 10 { 11 uint8_t cmd; 12 13 /* 打印命令列表,用戶可以通過串口操作指令 */ 14 DispMenu(); 15 // while(1) 16 { 17 bsp_Idle(); /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */ 18 19 if (comGetChar(COM1, &cmd)) /* 從串口讀入一個字符(非阻塞方式) */ 20 { 21 printf("\r\n"); 22 switch (cmd) 23 { 24 case '1': 25 printf("【1 - ViewRootDir】\r\n"); 26 ViewRootDir(); /* 顯示SD卡根目錄下的文件名 */ 27 break; 28 29 case '2': 30 printf("【2 - CreateNewFile】\r\n"); 31 CreateNewFile(); /* 創建一個新文件,寫入一個字符串 */ 32 break; 33 34 case '3': 35 printf("【3 - ReadFileData】\r\n"); 36 ReadFileData(); /* 讀取根目錄下armfly.txt的內容 */ 37 break; 38 39 case '4': 40 printf("【4 - CreateDir】\r\n"); 41 CreateDir(); /* 創建目錄 */ 42 break; 43 44 case '5': 45 printf("【5 - DeleteDirFile】\r\n"); 46 DeleteDirFile(); /* 刪除目錄和文件 */ 47 break; 48 49 case '6': 50 printf("【6 - TestSpeed】\r\n"); 51 WriteFileTest(); /* 速度測試 */ 52 break; 53 54 default: 55 DispMenu(); 56 break; 57 } 58 } 59 60 // bsp_DelayMS(100); /* 此延時將導致板子重新上電不工作 */ 61 62 /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */ 63 switch (bsp_GetKey()) /* bsp_GetKey()讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ 64 { 65 case KEY_DOWN_K1: /* K1鍵按下 */ 66 break; 67 68 case KEY_UP_K1: /* K1鍵彈起 */ 69 break; 70 71 case KEY_DOWN_K2: /* K2鍵按下 */ 72 break; 73 74 case KEY_UP_K2: /* K2鍵彈起 */ 75 break; 76 77 case KEY_DOWN_K3: /* K3鍵按下 */ 78 break; 79 80 case KEY_UP_K3: /* K3鍵彈起 */ 81 break; 82 83 case JOY_DOWN_U: /* 搖桿UP鍵按下 */ 84 break; 85 86 case JOY_DOWN_D: /* 搖桿DOWN鍵按下 */ 87 break; 88 89 case JOY_DOWN_L: /* 搖桿LEFT鍵按下 */ 90 break; 91 92 case JOY_DOWN_R: /* 搖桿RIGHT鍵按下 */ 93 break; 94 95 case JOY_DOWN_OK: /* 搖桿OK鍵按下 */ 96 break; 97 98 case JOY_UP_OK: /* 搖桿OK鍵彈起 */ 99 break; 100 101 case KEY_NONE: /* 無鍵按下 */ 102 default: 103 /* 其它的鍵值不處理 */ 104 break; 105 } 106 } 107 }
問題就在上段代碼第60行的延時函數 bsp_DelayMS(100);
四、解決過程
把你函數void DemoFatFS(void)里面的while大循環加上,不要注釋,而函數static void vTaskMsgPro(void *pvParameters)里面的 vTaskDelay(300);注釋掉。
並將你的 bsp_DelayMS(100); 函數所在位置修改為 vTaskDelay(100);就行。
me
1. 將函數void DemoFatFS(void)里面的while大循環加上,將導致系統其他3個任務不執行,一直執行 DemoFatFS任務,我之所以注釋是因為任務本身已有while循環;
2. 將非系統延時函數bsp_DelayMS(100)替換為系統延時函數vTaskDelay(100),經測試工作正常,我想問的是非系統延時函數bsp_DelayMS(100)為什么會導致這樣的問題產生,難道FreeRTOS系統中不支持非系統延時函數,但我在學習V6-349-FreeRTOS實驗_FreeRTOS+STemWin+FatFS+USB Devicet綜合例程時,看到在觸摸屏驅動和外部SDRAM驅動文件中多次使用了非系統延時函數bsp_DelayMS(),這也是我在本工程中加入bsp_dwt.c文件使用非系統延時函數bsp_DelayMS()的緣由,該綜合例程經我在V6開發板上運行正常,所以原因不是不能使用,應該是我的使用方式有問題,或是有些地方未注意到,懇請幫忙分析教導
1. 這個是阻塞式的延遲,這個任務阻塞后,低於此優先級的任務將得不到執行。
2. 那個是驅動,驅動僅調用一次,在bsp_Init初始化的時候僅調用一次,任務執行的時候不會再使用。
me
1. 可以理解為任務中不可以使用非系統延時函數?
2. 我做了如下實驗
上表中包括我的測試條件及個人一些理解,和最終的疑問。
3.為了規避這樣的事情發生,在任務中只使用系統延時函數即可,但有些情況使用非系統延時函數較為方便,比如某些設備的Demo,因為這些設備Demo程序會單獨存在於文件中,或是在不考慮操作系統時而寫的一些應用程序等。為了研究本質刨根問底,個人覺得還是有必要知曉其中機理,在此感謝大家了。
1. 可以加,比如DS18B20這種,是要加的微妙延遲的。
2. 以系統斷電后重新上電為准。關於這個第2個問題,后面還是需要深入學習下RTOS工作原理。通過本質理解現象的效率更高些。
3. 這種大延遲,死等的程序一定要修改,最好改成事件觸發的方式,后面你改的多了就熟練了。需要慢慢從裸機的編程思想轉換到RTOS上面來。
me
好的 多謝
暫且如此吧 待深入后續貼