Arm CMSIS-RTOS Keil RTX-进入手臂深度睡眠的正确方式

Arm CMSIS-RTOS Keil RTX-进入手臂深度睡眠的正确方式,arm,cortex-m,sleep-mode,Arm,Cortex M,Sleep Mode,你好,我想知道什么是让手臂皮层M0+进入深度睡眠的正确方法。特别是我使用的是CMSIS-RTOS RTX 我的IRQ处理方式是ISR只设置操作系统信号并清除IRQ。例如: void ISR_A(){ osSignalSet(ID_Task_Handling_IRQ_A, IRQ_A_SIGNAL_CODE); DisableIRQ_A(); } 然后在我的空闲循环中 void os_idle_demon(void) { ... timeToSleep = os_suspend(); /

你好,我想知道什么是让手臂皮层M0+进入深度睡眠的正确方法。特别是我使用的是CMSIS-RTOS RTX

我的IRQ处理方式是ISR只设置操作系统信号并清除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\u suspend()
\u wfi()
指令之间时,IRQ被清除,但任务无法调度(因为
os\u 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; 
}

我推荐两种方法中的一种

1) 当我在操作系统上下文切换中使用wfi()时,我会启用SysTick中断,以便在极少数情况下,中断在OS_suspend()和wfi()之间到达,系统将仅在SysTick中断期间休眠,然后唤醒以检查操作系统状态。这种方法在大多数情况下都有效


2) 如果您有硬实时要求,可以使用此处介绍的“退出时睡眠”功能:。这可能会更加复杂,但使用中断优先级可以保证在os_suspend()和进入睡眠之间进行原子操作。

因此我有时间在芯片MKL17Z256VFT4中的ARM M0+上进行一些测试。使用CMSIS-RTOS RTX(V4.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系统信令中使用ISR(ID\U Thread1,SIGNAL\U X)
。由于每个线程的优先级都高于
os\u idle\u demon
线程
ID\u Thread1
,该线程正在
event=osignalwait(任何信号,osWaitForever)中等待切换到(由RTOS)并处理信号。在此之后,线程开始再次等待任何信号,并计划任务,ARM进入睡眠状态

情况2)另一种情况是:IRQ设置在
os\u suspend()
\u disable\u IRQ()
之间。我发现在
\uu disable\u IRQ()
之前调用IRQ时,ARM处理IRQ的速度不够快,实际上是先执行
\uu disable\u IRQ()
。所以IRQ会一直等到调用
\u enable\u IRQ()
。因此,所有这些都进入了另一个案例

案例3)IRQ设置在
\u启用\u IRQ()
之前。在启用IRQ之后,在CPU执行深度睡眠(
\uu wfi();
)之前,执行ISR。信号已设置。但系统不能切换线程(我们称之为
os\u suspend()
)。但很明显,WFI以某种神奇的方式(我仍在研究规范为什么)没有被执行。深度睡眠没有中断,代码继续
os\u resume()
。然后操作系统开关任务和信号被正确处理

因此,唯一的错误情况是在指令之间放置某些内容:

__enable_irq();
// do not put anything here
__wfi();
如果你把任何东西放在那里,那么案例3的反应是这样的:ISR在
\uu enable\u irq()
之后立即执行。ISR设置操作系统信号,但未计划发出信号的任务(因为我们以前调用了
OS\u suspend()
)。然后通过
\uuwfi()
进入深度睡眠。然后系统将永远休眠或直到LPTMR。但这是错误的,因为有信号需要尽快处理,而事实并非如此

所以结论是,反应中描述的序列似乎是安全的。只要不在
\uu enable\u irq()之间插入任何指令
\uuuwfi()。此外,您不应将任何指令置于以下两者之间:
os_suspend()
禁用irq()。这至少对MKL17Z256VFT4有效。不知道还有其他芯片。但是您可以使用函数
NVIC\u setpendingingirq()

---编辑---

因此,我的朋友也告诉我,哪怕你禁用中断
CPSID
时,手臂也会从WFI中唤醒。所以也许更安全的顺序是

__wfi();   // go to DeepSleep
// optionally enable peripherals that might been disabled
__enable_irq();
不要忘记调用
\uuuu enable\u irq()最迟在调用操作系统恢复(SLEPTIME)之前否则在我的芯片上会出现硬故障

---编辑2---

我还发现我们可以使用
\uuwfe()说明,以确保没有赛车条件。WFE正在等待事件。它将CPU置于与WFI相同的睡眠模式。但它也检查“事件寄存器”。在每个ISR上设置此寄存器(在末尾)。当在WFE之前有IRQ时,WFE将不会入睡。您可以通过调用指令
\uu SEV()来选择设置“事件寄存器”。无法从软件访问“事件寄存器”。如果你想确保清除它,你可以打电话

__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上唤醒(请参阅)。(注意,如果中断优先级低于中配置的优先级,则中断处于挂起状态)另请参阅。这里也是非常

感谢您的评论,但在案例1)我负担不起长时间后处理的信号。设置较短的时间意味着我不会睡太久(我的应用程序是用电池运行的,所以睡眠至关重要)。广告2)我有RTO。所以我不能只在ISR中运行代码,而在没有ISR的情况下睡觉。。。