ED设备低功耗代码分析

改代码步骤
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值