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或者PM3,halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;进入PM3的条件为OSAL定时器超时与LL层下次发射事件均为0.
c, halSleepSetTimer()设定睡眠定时器,HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode)设定睡眠模式,而后进入睡眠,重新启动后,继续执行下文代码。
注:常用睡眠模式为PM2/PM3,二者都关闭主时钟,挂起MCU,PM2关闭外部32Mhz晶振和内部16MhzRC震荡,可由定时器、中断唤醒,PM3关闭全部时钟源,仅由外部中断唤醒。