首页 > 技术文章 > OSAL睡眠浅析

zzmx 2015-11-18 15:55 原文

 

BLE低功耗需要IAR中定义POWER_SAVING宏,OSAL系统在主循环中调用电源管理函数osal_pwrmgr_powerconserve(),该函数中获取下一次osal定时器超时时间,然后通过宏OSAL_SET_CPU_INTO_SLEEP调用void halSleep( uint32 osal_timeout )

 

  1 /**************************************************************************
  2 
  3  * @fn          halSleep
  4 
  5  *
  6 
  7  * @brief       This function is called from the OSAL task loop using and
  8 
  9  *              existing OSAL interface.  It sets the low power mode of the LL
 10 
 11  *              and the CC2540.
 12 
 13  * input parameters
 14 
 15  *
 16 
 17  * @param       osal_timeout - Next OSAL timer timeout, in msec.
 18 
 19  *
 20 
 21  * output parameters
 22 
 23  *
 24 
 25  * @param       None.
 26 
 27  *
 28 
 29  * @return      None.
 30 
 31  */
 32 
 33 void halSleep( uint32 osal_timeout )
 34 {
 35 
 36   uint32 timeout;
 37 
 38   uint32 llTimeout;
 39 
 40   uint32 sleepTimer;
 41 
 42  
 43 #ifdef DEBUG_GPIO
 44 
 45   // TEMP
 46 
 47   P1_0 = 1;
 48 
 49 #endif // DEBUG_GPIO
 50 
 51  
 52   // max allowed sleep time in ms
 53 
 54   if (osal_timeout > MAX_SLEEP_TIMEOUT)
 55   {
 56     osal_timeout = MAX_SLEEP_TIMEOUT;
 57   }
 58 
 59  
 60   // get LL timeout value already converted to 32kHz ticks
 61 
 62   LL_TimeToNextRfEvent( &sleepTimer, &llTimeout );
 63 
 64 
 65   // check if no OSAL timeout
 66 
 67   // Note: If the next wake event is due to an OSAL timeout, then wakeForRF
 68 
 69   //       will already be FALSE, and the call to LL_TimeToNExtRfEvent will
 70 
 71   //       already have taken a snapshot of the Sleep Timer.
 72 
 73   if (osal_timeout == 0)
 74   {
 75 
 76     // use common variable
 77 
 78     timeout = llTimeout;
 79 
 80     // check if there's time before the next radio event
 81 
 82     // Note: Since the OSAL timeout is zero, then if the radio timeout is
 83 
 84     //       not zero, the next wake (if one) will be due to the radio event.
 85 
 86     wakeForRF = (timeout != 0) ? TRUE : FALSE;
 87 
 88   }
 89   else // OSAL timeout is non-zero
 90   {
 91 
 92     // convet OSAL timeout to sleep time
 93 
 94     // Note: Could be early by one 32kHz timer tick due to rounding.
 95 
 96     timeout = HAL_SLEEP_MS_TO_32KHZ( osal_timeout );
 97 
 98     // so check time to radio event is non-zero, and if so, use shorter value
 99 
100     if ((llTimeout != 0) && (llTimeout < timeout))
101     {
102 
103       // use common variable
104 
105       timeout = llTimeout;
106 
107  
108       // the next ST wake time is due to radio
109 
110       wakeForRF = TRUE;
111 
112     }
113     else // OSAL timeout will be used to wake
114     {
115 
116       // so take a snapshot of the sleep timer for sleep based on OSAL timeout
117 
118       sleepTimer = halSleepReadTimer();
119 
120  
121 
122       // the next ST wake time is not due to radio
123 
124       wakeForRF = FALSE;
125     }
126 
127   }
128 
129  
130 
131   // HAL_SLEEP_PM3 is entered only if the timeout is zero
132 
133   halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;
134 
135 
136 #ifdef DEBUG_GPIO
137   // TEMP
138   P1_0 = 0;
139 #endif // DEBUG_GPIO
140 
141   // check if sleep should be entered
142 
143   if ( (timeout > PM_MIN_SLEEP_TIME) || (timeout == 0) )
144   {
145     halIntState_t ien0, ien1, ien2;
146 
147 #ifdef DEBUG_GPIO
148     // TEMP
149     P1_0 = 1;
150 #endif // DEBUG_GPIO
151 
152     HAL_ASSERT( HAL_INTERRUPTS_ARE_ENABLED() );
153     HAL_DISABLE_INTERRUPTS();
154 
155     // check if radio allows sleep, and if so, preps system for shutdown
156     if ( LL_PowerOffReq(halPwrMgtMode) == LL_SLEEP_REQUEST_ALLOWED )
157     {
158 #if ((defined HAL_KEY) && (HAL_KEY == TRUE))
159       // get peripherals ready for sleep
160       HalKeyEnterSleep();
161 #endif // ((defined HAL_KEY) && (HAL_KEY == TRUE))
162 
163 #ifdef HAL_SLEEP_DEBUG_LED
164       HAL_TURN_OFF_LED3();
165 #else
166 
167       // use this to turn LEDs off during sleep
168       HalLedEnterSleep();
169 #endif // HAL_SLEEP_DEBUG_LED
170 
171       // enable sleep timer interrupt
172       if (timeout != 0)
173       {
174         // check if the time to next wake event is greater than max sleep time
175         if (timeout > MAX_SLEEP_TIME )
176         {
177           // it is, so limit to max allowed sleep time (~510s)
178           halSleepSetTimer( sleepTimer, MAX_SLEEP_TIME );
179         }
180         else // not more than allowed sleep time
181         {
182           // so set sleep time to actual amount
183           halSleepSetTimer( sleepTimer, timeout );
184         }
185       }
186 
187       // prep CC254x power mode
188       HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode);
189 
190       // save interrupt enable registers and disable all interrupts
191       HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2);
192 
193       HAL_ENABLE_INTERRUPTS();
194 
195  
196 #ifdef DEBUG_GPIO
197       // TEMP
198       P1_0 = 0;
199 #endif // DEBUG_GPIO
200 
201       // set CC254x power mode; interrupts are disabled after this function
202       // Note: Any ISR that could wake the device from sleep needs to use
203       //       CLEAR_SLEEP_MODE(), which will clear the halSleepPconValue flag
204       //       used to enter sleep mode, thereby preventing the device from
205       //       missing this interrupt.
206       HAL_SLEEP_SET_POWER_MODE();
207 
208  
209 
210 #ifdef DEBUG_GPIO
211       // TEMP
212       P1_0 = 1;
213 #endif // DEBUG_GPIO
214 
215        // check if ST interrupt pending, and if not, clear wakeForRF flag
216        // Note: This is needed in case we are not woken by the sleep timer but
217       //       by for example a key press. In this case, the flag has to be
218       //       cleared as we are not just before a radio event.
219       // Note: There is the possiblity that we may wake from an interrupt just
220       //       before the sleep timer would have woken us just before a radio
221       //       event, in which case power will be wasted as we will probably
222       //       enter this routine one or more times before the radio event.
223       //       However, this is presumably unusual, and isn't expected to have
224       //       much impact on average power consumption.
225 
226       if ( (wakeForRF == TRUE) && !(IRCON & 0x80) )
227       {
228         wakeForRF = FALSE;
229       }
230 
231        // restore interrupt enable registers
232       HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2);
233 
234       // power on the LL; blocks until completion
235       // Note: This is done here to ensure the 32MHz XOSC has stablized, in
236       //       case it is needed (e.g. the ADC is used by the joystick).
237       LL_PowerOnReq( (halPwrMgtMode == CC2540_PM3), wakeForRF );
238 
239  #ifdef HAL_SLEEP_DEBUG_LED
240       HAL_TURN_ON_LED3();
241 #else //!HAL_SLEEP_DEBUG_LED
242       // use this to turn LEDs back on after sleep
243       HalLedExitSleep();
244 #endif // HAL_SLEEP_DEBUG_LED
245 
246  
247 #if ((defined HAL_KEY) && (HAL_KEY == TRUE))
248       // handle peripherals
249       (void)HalKeyExitSleep();
250 #endif // ((defined HAL_KEY) && (HAL_KEY == TRUE))
251     }
252 
253      HAL_ENABLE_INTERRUPTS();
254   }
255 
256  
257 #ifdef DEBUG_GPIO
258       // TEMP
259       P1_0 = 0;
260 #endif // DEBUG_GPIO
261 
262  
263   return;
264 
265 }

 

 

 

a,  helSleep()中,获取LL层下一次射频发射时间ll_timeout,与入参OSAL的定时器超时时间osal_timeout进行比较,取较小值作为sleep timer的定时时间;

b, 根据比较结果的值是否为0来决定休眠模式为PM2或者PM3halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;进入PM3的条件为OSAL定时器超时与LL层下次发射事件均为0.

c, halSleepSetTimer()设定睡眠定时器,HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode)设定睡眠模式,而后进入睡眠,重新启动后,继续执行下文代码。

 

注:常用睡眠模式为PM2/PM3,二者都关闭主时钟,挂起MCUPM2关闭外部32Mhz晶振和内部16MhzRC震荡,可由定时器、中断唤醒,PM3关闭全部时钟源,仅由外部中断唤醒。

推荐阅读