所以我有時間在芯片MKL17Z256VFT4的ARM M0 +上做一些測試。使用CMSIS-RTOS RTX(v 4.75)。
它的工作原理是這樣的:
void os_idle_demon(void) { // task with lowest priority - scheduled by
//system when there is no action to do
for (;;) {
timeToSleep = os_suspend(); // stop OS from switching tasks and get maximum allowed sleep time
__disable_irq();
LPTMR_Init(timeToSleep...); // set Low Power sleep timer
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;//set DeepSleep
GPIO(pin=0,val=1); // signalize on GPIO pad that CPU is (almost) in sleep
__enable_irq();
__wfi(); // go to DeepSleep
GPIO(pin=0,val=0); // signalize on GPIO pad that CPU just wakeup
sleptTime = LPTMR_GetCounterValue(); // get sleepTime after wakeup
os_resume(sleptTime); // set system to schedule tasks and give os info about sleep time
}
我確實觀察到,當我刺激中斷在代碼執行的不同的地方發生了什麼。我使用NVIC_SetPendingIRQ(PORTCD_IRQn);
來實施IRQ。我通過觀察GPIO引腳觀察了邏輯分析儀正在運行哪個任務。
情況1)容易的情況是:在調用ISR之前觸發IRQ,並在ISR系統信令中使用osSignalSet(ID_Thread1, SIGNAL_X)
。由於每個線程的優先級都高於os_idle_demon
,因此正在等待event = osSignalWait(ANY_SIGNAL, osWaitForever);
的線程ID_Thread1
被切換到(通過RTOS)並處理信號。之後線程開始再次等待任何信號,並計劃os_idle_demon
任務,ARM進入睡眠狀態。
案例2)另一種情況是:IRQ設置在os_suspend()
和__disable_irq()
之間。我發現當在__disable_irq()
之前調用IRQ時,ARM處理IRQ的速度不夠快,而實際上__disable_irq()
首先被執行。所以IRQ一直等到__enable_irq()
被調用。所有這些都涉及另一個案例。
情況3)在__enable_irq()
之前設置IRQ。在啓用IRQ之後,在CPU執行DeepsSleep(__wfi();
)之前,ISR被執行。信號已設置。但是系統不能切換線程(我們稱之爲os_suspend()
)。但顯然WFI在某種程度上神奇地(我仍在研究規範爲什麼)沒有執行。深度睡眠不是敵人,代碼繼續到os_resume()
。然後OS切換任務和信號被正確處理。
所以,唯一的車的情況是,當你把指令之間的東西:
__enable_irq();
// do not put anything here
__wfi();
如果你把有什麼情況,然後將3這樣的反應:ISR已經__enable_irq()
後立即執行。 ISR設置操作系統信號但沒有安排信號任務(因爲我們之前稱之爲os_suspend()
)。然後通過__wfi()
進入深度睡眠。系統然後永久休眠或直到LPTMR。但這是錯誤的,因爲有信號應該儘快處理,而不是!
所以結論是,看起來序列中描繪的序列是安全的。只要你沒有在__enable_irq();
和__wfi();
之間放置任何指令。您也不得在中間放置任何指示:os_suspend();
和__disable_irq();
。這至少對於MKL17Z256VFT4有效。不知道其他芯片。但是你可以通過執行該IRQ標記功能測試自己NVIC_SetPendingIRQ()
---編輯---
所以,我的朋友給我看了也01其中寫入,即使你關閉中斷CPSID
的ARM喚醒功能WFI。因此,也許更安全的序列是
__wfi(); // go to DeepSleep
// optionally enable peripherals that might been disabled
__enable_irq();
不要忘了最新的你叫os_resume(sleptTime);
之前,否則我的芯片上,我得到HardFault打電話__enable_irq();
。
--- EDIT 2 ---
而且我發現,我們可以使用__WFE();
指令,以確保沒有賽車條件。 WFE正在等待事件。它將CPU置於與WFI相同的睡眠模式。但它也會檢查「事件記錄」。該寄存器在每個ISR(最後)被設置。當WFE之前有IRQ時,WFE將不會進入睡眠狀態。您可以通過調用指令__SEV();
來選擇設置「事件寄存器」。 SW無法訪問「事件寄存器」。如果你想確保清除它,你可以撥打電話
__SEV(); // set event register if it was not set
__WFE(); // clear event register and don't goto sleep because we set event register just before
但請注意,該指令與WFI的行爲略有不同。例如,如果設置了SEVONPEND,WFE也可以在掛起的IRQ上喚醒(請參閱WFE spec)。 (如果中斷的優先級低於Base Priority Mask Register中的配置,則說明中斷處於掛起狀態)另請參閱Entering Sleep Mode。這裏也是非常的nice table about difference WFI vs WFE
謝謝你的評論,但在情況下1)我買不起信號要處理很長一段時間。設定很短的時間意味着我不會睡得太久(我的應用程序從電池運行,所以睡眠是至關重要的)。廣告2)我有RTOS。所以我不能在ISR中只運行代碼,在沒有ISR的情況下睡眠。 –