gpt4 book ai didi

arm - CMSIS-RTOS Keil RTX - 进入 ARM 深度 sleep 的正确方法

转载 作者:行者123 更新时间:2023-12-03 03:37:54 34 4
gpt4 key购买 nike

您好,我想知道让 ARM Cortex M0+ 进入深度 sleep 的正确方法是什么。特别是我正在使用 CMSIS-RTOS RTX。

我的 IRQ 处理方式是 ISR 只是设置 OS Signal 并清除 IRQ。例如。:

void ISR_A(){
osSignalSet(ID_Task_Handling_IRQ_A, IRQ_A_SIGNAL_CODE);
DisableIRQ_A();
}

然后在我的空闲循环中
void os_idle_demon(void) {
...
timeToSleep = os_suspend(); // get from OS how long I can sleep and also stop OS scheduling
LPTMR_Init(timeToSleep,...) // set wakeup timer
POWER_EnterLLS(void) // enter deep sleep. Set registers and calls WFI instruction
// after wakup compute actual slpetTime
os_resume(sleptTime); // enable OS scheduling
}

问题是我的 ISR 没有完全处理 IRQ(它只是在操作系统中设置信号,一些线程会根据优先级和调度来处理它——我想保持这种方式)。但是当 IRQ 介于 os_suspend() 之间时和 __wfi()指令然后 IRQ 被清除但任务不能被调度(因为 os_suspend() )。当 CPU 进入 WFI 时,它会进入休眠状态,因此应处理来自 ISR 的信号的 OS 线程永远不会执行。但是 CPU 也不会被(pad)IRQ 唤醒,因为它已经被处理了。

问题是如何自动检查没有待处理的任务并启动 WFI。

就像是
if( ! OS_Signal_Is_rised) {  
// only do it atomically because what if IRQ would come here?
wfi;
}

最佳答案

所以我有时间对芯片 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) 简单案例是:IRQ 在 os_suspend() 之前触发ISR 被调用,我在 ISR 系统信令中使用 osSignalSet(ID_Thread1, SIGNAL_X) .由于每个线程的优先级都高于 os_idle_demon线程 ID_Thread1正在等待 event = osSignalWait(ANY_SIGNAL, osWaitForever);切换到(通过 RTOS)并处理信号。在该线程再次开始等待任何信号和 os_idle_demon 之后任务已安排,ARM 进入休眠状态。

案例2)另一种情况是:IRQ设置在 os_suspend()之间和 __disable_irq() .我发现在 __disable_irq() 之前调用 IRQ 时ARM 处理 IRQ 的速度不够快,实际上 __disable_irq()首先执行。所以 IRQ 等到 __enable_irq()叫做。所以一切都进入另一个案例。

案例 3) IRQ 设置在 __enable_irq() 之前.在启用 IRQ 之后和 CPU 执行 DeepsSleep ( __wfi();) 之前,将执行 ISR。信号已设置。但是系统不能切换线程(我们称之为 os_suspend() )。但显然 WFI 以某种方式神奇地(我仍在研究规范为什么)没有执行。没有进入深度 sleep ,代码继续 os_resume() .然后操作系统切换任务和信号被正确处理。

所以唯一有问题的情况是当你在指令之间放一些东西时:
__enable_irq();
// do not put anything here
__wfi();

如果你把任何东西放在那里,那么案例 3 的 react 是这样的:ISR 在 __enable_irq() 之后立即执行。 . ISR 设置 OS 信号,但信号任务未安排(因为我们之前调用了 os_suspend())。然后 __wfi()进入深度 sleep .然后系统永远休眠或直到 LPTMR。但这是错误的,因为有信号应该尽快处理,但没有!

所以结论是,响应中描述的序列似乎是安全的。只要您不在 __enable_irq(); 之间添加任何指令和 __wfi(); .此外,您不得在其间添加任何指令: os_suspend();__disable_irq(); .这至少对 MKL17Z256VFT4 有效。不知道其他芯片。但是您可以通过使用函数 NVIC_SetPendingIRQ() 强制执行 IRQ 标志来测试自己。

- - 编辑 - -

所以我的 friend 也给我看了 documentation哪里写到即使你禁用中断 CPSID ARM 从 WFI 中唤醒。所以也许更安全的顺序是
__wfi();   // go to DeepSleep
// optionally enable peripherals that might been disabled
__enable_irq();

不要忘记调用 __enable_irq();最迟在您调用 os_resume(sleptTime); 之前否则在我的芯片上我会得到 HardFault。

--- 编辑 2 ---

我还发现我们可以使用 __WFE();指示以确保没有比赛条件。 WFE 是等待事件。它将 CPU 置于与 WFI 相同的 sleep 模式。但它也会检查“事件寄存器”。该寄存器在每个 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

关于arm - CMSIS-RTOS Keil RTX - 进入 ARM 深度 sleep 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35875753/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com