改代码步骤
1、工程改为EndDeviceEB
2、编译选项修改:
POWER_SAVING(支持低功耗模式)
xHOLD_AUTO_START(自启动)
ISR_KEYINTERRUPT(按键采用中断,不是POLL的方式)
3、void HalDriverInit (void)中,未用到的驱动不使能。
4、所有IO口设置为输入模式
CC2530有五种工作模式。分别为Active mode、idle mode、PM1、PM2、PM3,PM2模式比较省功耗而且可以被定时唤醒;PM3模式最省电但是只能被外部中断唤醒。
开启睡眠功能很简单:
1、首先确认f8wConfig.cfg文件中DRFD_RCVC_ALWAYS_ON定义为FALSE;
2、然后在IAR的Options->C/C++Compiler->Preprocessor->Defined symbols中添加“POWER_SAVING”;
1、在主循环osal_run_system( void )中,在没有任务事件时执行时调用osal_pwrmgr_powerconserve()函数。
#if defined( POWER_SAVING )
void osal_pwrmgr_powerconserve( void )
{
uint16 next;
halIntState_t intState;
// 首先检查是否支持低功耗
if ( pwrmgr_attribute.pwrmgr_device != PWRMGR_ALWAYS_ON )
{
//是否所有任务支持低功耗
if ( pwrmgr_attribute.pwrmgr_task_state == 0 )
{
//关中断
HAL_ENTER_CRITICAL_SECTION( intState );
//查询软件定时器链表得到最近一次溢出时间
next = osal_next_timeout();
//开中断
HAL_EXIT_CRITICAL_SECTION( intState );
//将系统进入睡眠模式
OSAL_SET_CPU_INTO_SLEEP( next );
}
}
}
#endif
2、进入halSleep(timeout)函数,具体代码分析
void halSleep( uint32 osal_timeout )
{
uint32 timeout;
uint32 macTimeout = 0;
/* get next OSAL timer expiration converted to 320 usec units 把时间换算成320us单位*/
timeout = HAL_SLEEP_MS_TO_320US(osal_timeout);
if (timeout == 0)
{
timeout = MAC_PwrNextTimeout();
}
else
{
/* get next MAC timer expiration */
macTimeout = MAC_PwrNextTimeout();
/* get lesser of two timeouts */
if ((macTimeout != 0) && (macTimeout < timeout))
{
timeout = macTimeout;
}
}
/* HAL_SLEEP_PM2 is entered only if the timeout is zero and
* the device is a stimulated device.
* 注意,这里获取任务到期时间是要获取两个的,因为MAC任务和用户设定的周期任务是分开的。并且比较哪个比较小,就按哪个时间,举个例子,比如你再过5分钟要接个电话,再过10分钟要吃饭,你取10分钟作为任务到期时间,那么也就是说你认为可以睡10分钟,但是在你睡的这10分钟里,电话漏掉了。就是这个意思。
*/
halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;
/* DEEP sleep can only be entered when zgPollRate == 0.
* This is to eliminate any possibility of entering PM3 between
* two network timers.
* 接下来halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;
实际上是综合一下MAC到期时间和用户任务到期时间,把其中小的值给timeout
然后判断它是不是0.如果是说明有任务到期,不能进入深度睡眠,halPwrMgtMode=HAL_SLEEP_TIMER,如果timeout不等于0说明CPU空闲,可以深度睡眠。
*/
#if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL)
if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
(timeout == 0 && zgPollRate == 0))
#else
if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
(timeout == 0))
#endif
上面这几行程序,实际上是判断timeout是不是大于系统设定的最小睡眠时间,你想,如果马上就到期了,系统才睡一小会,没有意义,所以这里做一个这样的判断。举个例子,比如你下一分钟就要去上班了,再睡1分钟也没什么意义,不如直接现在就去上班。就是这个意思。
{
halIntState_t ien0, ien1, ien2;
HAL_ASSERT(HAL_INTERRUPTS_ARE_ENABLED());
HAL_DISABLE_INTERRUPTS();
/* always use "deep sleep" to turn off radio VREG on CC2530 */
if (halSleepPconValue != 0 && MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS)
{
/* The PCON value is not zero. There is no interrupt overriding the
* sleep decision. Also, the radio granted the sleep request.
*/
#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
/* get peripherals ready for sleep */
HalKeyEnterSleep();
#endif
#ifdef HAL_SLEEP_DEBUG_LED
HAL_TURN_OFF_LED3();
#else
/* use this to turn LEDs off during sleep */
HalLedEnterSleep();
#endif
if(timeout > maxSleepLoopTime)
{
timeout = maxSleepLoopTime;
}
do
{
/* enable sleep timer interrupt */
if(timeout != 0)
{
if (timeout > HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ))
{
timeout -= HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME );
halSleepSetTimer(HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ));
}
else
{
/* set sleep timer */
halSleepSetTimer(timeout);
timeout = 0;
}
/* set up sleep timer interrupt 使能睡眠定时*/
HAL_SLEEP_TIMER_CLEAR_INT();
HAL_SLEEP_TIMER_ENABLE_INT();
}
#ifdef HAL_SLEEP_DEBUG_LED
if (halPwrMgtMode == CC2530_PM1)
{
HAL_TURN_ON_LED1();
}
else
{
HAL_TURN_OFF_LED1();
}
#endif
/* Prep CC2530 power mode */
HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode);
/* save interrupt enable registers and disable all interrupts 保存中断*/
HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2);
HAL_ENABLE_INTERRUPTS();
/* set CC2530 power mode, interrupt is disabled after this function
* Note that an ISR (that could wake up from power mode) which runs
* between the previous instruction enabling interrupts and before
* power mode is set would switch the halSleepPconValue so that
* power mode shall not be entered in such a case.
*/
//正式进入睡眠,
HAL_SLEEP_SET_POWER_MODE();
//睡眠定时器时间到了,从这里起来
/* the interrupt is disabled - see halSetSleepMode() */
/* restore interrupt enable registers */
HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2);
/* disable sleep timer interrupt */
HAL_SLEEP_TIMER_DISABLE_INT();
#ifdef HAL_SLEEP_DEBUG_LED
HAL_TURN_ON_LED3();
#else
/* use this to turn LEDs back on after sleep */
HalLedExitSleep();
#endif
#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
/* handle peripherals */
if(HalKeyExitSleep())
{
break;
}
#endif
} while(timeout != 0);
/* power on the MAC; blocks until completion */
MAC_PwrOnReq();
HAL_ENABLE_INTERRUPTS();
/* For CC2530, T2 interrupt won抰 be generated when the current count is greater than
* the comparator. The interrupt is only generated when the current count is equal to
* the comparator. When the CC2530 is waking up from sleep, there is a small window
* that the count may be grater than the comparator, therefore, missing the interrupt.
* This workaround will call the T2 ISR when the current T2 count is greater than the
* comparator. The problem only occurs when POWER_SAVING is turned on, i.e. the 32KHz
* drives the chip in sleep and SYNC start is used.
*/
macMcuTimer2OverflowWorkaround();
}
else
{
/* An interrupt may have changed the sleep decision. Do not sleep at all. Turn on
* the interrupt, exit normally, and the next sleep will be allowed.
*/
HAL_ENABLE_INTERRUPTS();
}
}
}
参考文献
1、低功耗设置工作总结
https://wenku.baidu.com/view/1dbcfaba5fbfc77da269b1ca.html
2、关于CC2530休眠进入PM2设置问题https://e2echina.ti.com/question_answer/wireless_connectivity/zigbee/f/104/t/138668