一、FreeRTOS的內核控制接口分析
1.1 函數taskYIELD
????????此函數用于進行任務切換,此函數本質上是一個宏。它允許當前任務主動放棄CPU使用權,將控制權轉移給調度器,以便調度器可以選擇另一個就緒任務運行。taskYIELD通常用于協作式多任務系統中,任務在沒有更多工作可做或需要等待某些事件時調用此函數。通過調用taskYIELD,任務可以顯式地讓出CPU資源,從而提高系統的響應性和效率。此外,taskYIELD的實現通常依賴于特定的硬件平臺和操作系統,以確保任務切換的原子性和正確性。
1.2 函數taskENTER_CRITICAL
????????進入臨界區(即共享資源區),用于任務函數中,此函數本質上是一個宏,它通過禁用中斷來確保任務在臨界區內執行時不會被其他任務或中斷打斷,從而保護共享資源的完整性和一致性。在進入臨界區后,任務可以安全地訪問和修改共享數據,而不會受到并發訪問的干擾。完成臨界區操作后,應調用taskEXIT_CRITICAL函數來恢復中斷并退出臨界區,以允許其他任務和中斷繼續執行。這種機制在多任務系統中尤為重要,尤其是在實時操作系統中,用于確保任務之間的同步和資源的正確管理。
1.3 函數taskEXIT_CRITICAL
????????退出臨界區,用于任務函數中,此函數本質上是一個宏,用于在任務執行過程中安全地退出臨界區,確保在退出臨界區時不會發生任務切換或中斷,從而保護共享資源的完整性。該宏通常會taskENTER_CRITICAL配對使用,以確保在進入和退出臨界區時的一致性。taskEXIT_CRITICAL的實現通常依賴于特定的操作系統或實時內核的底層機制,以提供高效的臨界區管理。
1.4函數taskENTER_CRITICAL_FROM_ISR
進入臨界區,用于中斷服務函數中,此函數本質上是一個宏。
1.5函數taskEXIT_CRITICAL_FROM_ISR
退出臨界區,用于中斷服務函數中,此函數本質上是一個宏。
1.6函數taskDISABLE_INTERRUPTS
關閉可屏蔽的中斷,此函數本質上是一個宏。
1.7函數taskENABLE_INTERRUPTS
打開可屏蔽的中斷,此函數本質上是一個宏。
1.8函數vTaskStartScheduler
啟動任務調度器,且會創建空閑任務。
1.9函數vTaskEndScheduler
關閉任務調度器。
1.10函數vTaskSuspendAll
掛起任務調度器,調用此函數不需要關閉可屏蔽中斷即可掛起任務調度器。
1.11?函數xTaskResumeAll
此函數用于將任務調度器從掛起狀態恢復。
臨界區指的是一個訪問共用資源(例如:共用設備或是共用存儲器)的程序片段,而這些共用資源又無法同時被多個線程訪問的特性。當有線程進入臨界區段時,其他線程或是進程必須等待(例如:bounded waiting 等待法),有一些同步的機制必須在臨界區段的進入點與離開點實現,以確保這些共用資源是被互斥獲得使用
二、睡眠延時函數
1.1 vTaskDelay(常用)
????????在UCOSIII 中延時函數OSTimeDly()可以設置為三種模式:相對模式、周期模式和絕對模式。在FreeRTOS中延時函數只有相對模式和絕對模式,在FreeRTOS中不同的模式用的函數不同,其中函數 vTaskDelay()是相對模式(相對延時函數),函數 vTaskDelayUntil()是絕對模式(絕對延時函數)。函數vTaskDelay()在文件 tasks.c中有定義,要使用此函數的話宏INCLUDE_vTaskDelay必須為1,函數代碼如下:
//xTicksToDelay:要延時的時間節拍數,該數值須大于0。void vTaskDelay( const TickType_t xTicksToDelay )
注意延時的時間為:節拍數*系統節拍時間
系統默認時間節拍:1ms(正常情況1ms請求一次任務切換)
系統已經使用配置Systick 1ms中斷一次,用于系統節拍時間,任務是基于系統節拍時間進行任務調度。
SysTick->LOAD,下圖是如何配置自己的時間節拍
系統節拍修改宏為configTICK_RATE_HZ, 系統節拍時間為:1/configTICK_RATE_HZ。系統節拍時間最好在1~10ms之間。過短,就是出現不是在切換任務就在切換任務的路上,過長,系統的實時性差。
1.2 vTaskDelayUntil(絕對延時)-了解
????????函數 vTaskDelayUntil()會阻塞任務,阻塞時間是一個絕對時間,那些需要按照一定的頻率運行的任務可以使用函數vTaskDelayUntil(),即使任務中執行的代碼逐漸添加,也能讓任務按照固定的頻率運行(要求:任務總的執行時間要小于延時時間)。
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,const TickType_t xTimeIncrement )
代碼示例:
static void app_task1(void* pvParameters)
{for(;;){printf("app_task1 is running ...,tick count = %u\r\n",xTaskGetTickCount());/* 相對延時:任務延時2000個節拍,每個節拍為1ms,所以延時2000ms */vTaskDelay(2000);}
} static void app_task2(void* pvParameters)
{uint32_t i=0,j=1;TickType_t xLastWakeTime;/* 初始化上次喚醒時間為當前時間 */xLastWakeTime = xTaskGetTickCount();//一次循環時間為2Sfor(;;){for(i=0; i<j*10000; i++);j+=10;printf("app_task2 is running ...,tick count = %u\r\n",xTaskGetTickCount());/* 絕對延時:任務延時2000個節拍,每個節拍為1ms,所以延時2000ms */ vTaskDelayUntil(&xLastWakeTime, 2000);}
}
實驗效果:可知,每次延時2s(誤差可以省略)
app_task2 is running ...,tick count = 0app_task1 is running ...,tick count = 47app_task2 is running ...,tick count = 2002app_task1 is running ...,tick count = 2096app_task2 is running ...,tick count = 4005app_task1 is running ...,tick count = 4150
總結:
任務2使用絕對延時能夠給按照逼近2000個節拍頻率固定運行(當前計數值:0-2002-4005-6007-8009-10012),任務1使用相對延時每次運行相隔時間不保證固定(當前計數值:47-2096-4150-6206-8264-10326)。
1.3 相對延時與絕對延時對比
相對延時:只會延時其任務的相對時間
絕對延時:會把其他突發事件的時間也計算進來
三、自定義延時函數
2.1 微秒延時
void delay_us(uint32_t nus)
{ uint32_t ticks;uint32_t told,tnow,tcnt=0;uint32_t reload=SysTick->LOAD; //系統定時器的重載值 ticks=nus*(SystemCoreClock/1000000);//需要的節拍數 told=SysTick->VAL; //剛進入時的計數器值/* 掛起調度器[可選,會導致高優先級任務無法搶占當前任務,但能夠提高當前任務時間的精確性] */vTaskSuspendAll(); while(1){tnow=SysTick->VAL;if(tnow!=told){ /* SYSTICK是一個遞減的計數器 */if(tnow<told)tcnt+=told-tnow; else tcnt+=reload-tnow+told+1; told=tnow;/* 時間超過/等于要延遲的時間,則退出。*/if(tcnt>=ticks)break; } }/* 恢復調度器[可選] */xTaskResumeAll();
}
使用us級延時,不要超1000us,過長時間的微秒延時會導致系統實時性。
2.2 毫秒延時
void delay_ms(uint32_t nms)
{vTaskDelay(nms);
}
延時整個項目的代碼示例:
https://download.csdn.net/download/m0_63622771/90887340
四、FreeRTOS共享資源的基本概述
1.1 基本概念
????????共享資源典型的共享資源有:全局變量、I/O設備中的寄存器、多個任務訪問的函數、緩沖區等。共享資源的可靠訪問,任務必須對數據具有獨享權變得極其重要,否則將可能導致任務間的競爭與數據損壞,進而引發系統崩潰、數據不一致、死鎖、資源泄露等問題。
????????為確保共享資源的安全訪問,通常需要采用互斥鎖、信號量、條件變量、原子操作等同步機制,以及設計合理的資源管理策略,如優先級繼承、資源分配協議等,以避免任務間的沖突并保證系統的穩定性和可靠性。
1.2 種類說明
- 關中斷(就可以不會發送中斷了。所以獨占資源)
- 禁止任務調度(給調度器上鎖,調度器停擺)
- 使用信號量(計數型的型號量、二值信號量)
- 使用互斥型信號量(互斥鎖)
? ? ? ?獲得獨占共享資源的方法,取決于代碼訪問共享資源的速度,即占用共享資源的時間(重點),具體使用說明如下表:
選擇題
1.以下請問使用( A )方式保護最為合適。
void T1(void *parg)
{while(1){ ...........喂狗 ........... }
}A. 關中斷、開中斷 B.給調度器上鎖、解鎖 C.信號量 D.互斥鎖
2.以下請問使用( D )方式保護最為合適。
uint32 g=0;
void T1(void *parg)
{while(1){ ...........g++; ........... }
}void T2(void *parg)
{uint32_t a=0;while(1){ ...........a = g; ........... }
}A. 關中斷、開中斷 B.給調度器上鎖、解鎖 C.信號量 D.互斥鎖
3.以下請問使用( A )方式保護最為合適。
uint32 g=0;void T1(void *parg)
{while(1){ ...........g++; ........... }
}void T2(void *parg)
{uint32_t a=0;while(1){ ...........a = g; ........... }
}void USART1_IRQHandler(void)
{...........g = USART_ReceiveData(USART1); ........... }A. 關中斷、開中斷 B.給調度器上鎖、解鎖 C.信號量 D.互斥鎖
4.以下請問使用( D )方式保護最為合適。
void T1(void *parg)
{while(1){ ...........printf("hello teacher.chen\r\n"); ........... }
}void T2(void *parg)
{while(1){ ...........printf("good bye teacher.chen\r\n"); ........... }
}A. 關中斷、開中斷 B.給調度器上鎖、解鎖 C.信號量 D.互斥鎖
5.以下請問使用( D )方式保護最為合適。
void T1(void *parg)
{while(1){ ...........printf("hello teacher.chen\r\n"); ........... }
}void T2(void *parg)
{while(1){ ...........printf("good bye teacher.chen\r\n"); ........... }
}void T3(void *parg)
{while(1){ ...........printf("he's teacher.chen\r\n"); ........... }
}A. 關中斷、開中斷 B.給調度器上鎖、解鎖 C.信號量 D.互斥鎖
辨析題
1.請分析以下代碼。
//高優先級
void T1(void *parg)
{while(1){ 調度器上鎖printf("hello teacher.chen\r\n");delay_ms(1000) 調度器解鎖 }
}
//低優先級
void T2(void *parg)
{while(1){ 調度器上鎖printf("good bye teacher.chen\r\n");delay_ms(1000) 調度器解鎖 }
}調度器上鎖,T1任務打印hello teacher.chen,執行delay_ms后導致停止了任務調度器,
導致兩個任務都無法執行。
2.請分析以下代碼。
void T1(void *parg)
{while(1){ 等待互斥鎖printf("hello teacher.chen\r\n");delay_ms(1000);釋放互斥鎖 }
}void T2(void *parg)
{while(1){ 等待互斥鎖printf("good bye teacher.chen\r\n");delay_ms(1000);釋放互斥鎖 }
}只有得到鎖的任務才能執行
3.請分析以下代碼。
void app_task_init(void *parg)
{創建信號量,初值為0
}void T1(void *parg)
{while(1){ 等待信號量printf("hello teacher.chen\r\n");釋放信號量 }
}void T2(void *parg)
{while(1){ 等待信號量printf("good bye teacher.chen\r\n");釋放信號量 }
}只有得到信號量的任務才能執行