1113049_zh-CN

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

1113049_zh-CN

1113049_zh-CN

连接软件:在 FreeRTOS 中实现无滴答模式<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

FreeRTOS 通过计数滴答来跟踪系统中经过的时间。主机 MCU 中可用的定时器之一生成的周期性中断例程内的滴答计数会增加。当 FreeRTOS 运行空闲任务挂钩时,微控制器可以进入低功耗模式。根据低功耗模式,可以禁用一个或多个外围设备,以节省尽可能多的能源。FreeRTOS 无滴答空闲模式允许在空闲期间停止滴答中断。停止滴答中断可使微控制器保持深度省电状态,直到唤醒事件发生。

应用程序需要配置模块(计时器、ADC 等),以便在执行下一个 FreeRTOS 任务之前唤醒微控制器。为此,在执行 vPortSuppressTicksAndSleep(启用无滴答空闲时由 FreeRTOS 调用的函数)期间,MCU 可以保持休眠状态的最大时间量作为输入参数传递,以便正确配置唤醒模块。一旦 MCU 唤醒并且 FreeRTOS 滴答中断重新启动,就必须恢复 MCU 睡眠期间丢失的滴答计数。

在连接软件 FreeRTOS 演示中,无滴答模式默认未启用。在这篇文章中,我们将 展示如何启用它。对于此示例,我们将使用 QN9080x 来演示实现。

低功耗

Freertos 无滴答

无滴答

以下文件中实施了更改:

\框架\低功耗\源\QN908XC\PWR.c

\框架\低功耗\接口\QN908XC\PWR_Interface.h

\freertos\fsl_tickless_generic.h

\source\common\ApplMain.c

以下文件已从项目中删除

fsl_tickless_qn_rtc.c

PWR.C 和 PWR_Interface.h

此文件中的更改旨在让 QN9080 准备好使用 RTC 计时器唤醒。其他部件,如 MKW41Z,可能会为此目的启用其他模块(如 LPTMR),并且可能不需要更改这些文件。

*** PWR.c ***

添加 RTC 的驱动程序。这是我们将用来唤醒 QN908x 的计时器

/*Tickless: Add RTC driver for tickless support */
#include "fsl_rtc.h"‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

添加局部变量

uint64_t mLpmTotalSleepDuration;        //Tickless
uint8_t mPWR_DeepSleepTimeUpdated = 0;  //Tickless: Coexistence with TMR manager‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

添加私有函数

 uint32_t PWR_RTCGetMsTimeUntilNextTick (void);         //Tickless
 void PWR_RTCSetWakeupTimeMs (uint32_t wakeupTimeMs);   //Tickless
 void PWR_RTCWakeupStart (void);                        //Tickless‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

在 PWR.C 中进行以下更改。所有需要的更改都标记为注释,其中“Start”表示更改的开始位置,“End”表示更改的结束位置

#if (cPWR_UsePowerDownMode && (cPWR_EnableDeepSleepMode_1 || cPWR_EnableDeepSleepMode_2 || cPWR_EnableDeepSleepMode_3 || cPWR_EnableDeepSleepMode_4))
static void PWR_HandleDeepSleepMode_1_2_3_4(void)
{
#if cPWR_BLE_LL_Enable
    uint8_t   power_down_mode = 0xff;
    bool_t    enterLowPower = TRUE;

    __disable_irq();

/****************START***********************************/
    /*Tickless: Configure wakeup timer */
    if(mPWR_DeepSleepTimeUpdated){
      PWR_RTCSetWakeupTimeMs(mPWR_DeepSleepTimeMs);
      mPWR_DeepSleepTimeUpdated = FALSE;        // Coexistence with TMR Manager
    }
    
    PWR_RTCWakeupStart();
/*****************END**************************************/

    PWRLib_ClearWakeupReason();

    //Try to put BLE in deep sleep mode
    power_down_mode = BLE_sleep();
    if (power_down_mode < kPmPowerDown0)
    {
        enterLowPower = false; // BLE doesn't allow deep sleep
    }
    //no else - enterLowPower is already true

    if(enterLowPower)
    {

/****************START**************************/
        uint32_t freeRunningRtcPriority;
/****************END****************************/

        NVIC_ClearPendingIRQ(OSC_INT_LOW_IRQn);
        NVIC_EnableIRQ(OSC_INT_LOW_IRQn);
        while (SYSCON_SYS_STAT_OSC_EN_MASK & SYSCON->SYS_STAT) //wait for BLE to enter sleep
        {
            POWER_EnterSleep();
        }
        NVIC_DisableIRQ(OSC_INT_LOW_IRQn);

        if(gpfPWR_LowPowerEnterCb != NULL)
        {
            gpfPWR_LowPowerEnterCb();
        }

/* Disable SysTick counter and interrupt */
        sysTickCtrl = SysTick->CTRL & (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);
        SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);
        ICSR |= (1 << 25); // clear PendSysTick bit in ICSR, if set

/************************START***********************************/
        NVIC_ClearPendingIRQ(RTC_FR_IRQn);
        freeRunningRtcPriority = NVIC_GetPriority(RTC_FR_IRQn);
        NVIC_SetPriority(RTC_FR_IRQn,0);
/***********************END***************************************/

        POWER_EnterPowerDown(0); //Nighty night!

/************************START**********************************/
        NVIC_SetPriority(RTC_FR_IRQn,freeRunningRtcPriority);
/************************END************************************/

        if(gpfPWR_LowPowerExitCb != NULL)
        {
            gpfPWR_LowPowerExitCb();
        }

        /* Restore the state of SysTick */
        SysTick->CTRL |= sysTickCtrl;

        PWRLib_UpdateWakeupReason();
    }

    __enable_irq();

#else
    PWRLib_ClearWakeupReason();
#endif /* cPWR_BLE_LL_Enable */
}
#endif /* (cPWR_UsePowerDownMode && cPWR_EnableDeepSleepMode_1) */

‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

void PWR_SetDeepSleepTimeInMs(uint32_t deepSleepTimeMs)
{
#if (cPWR_UsePowerDownMode)
    if(deepSleepTimeMs == 0)
    {
        return;
    }

    mPWR_DeepSleepTimeMs = deepSleepTimeMs;
/****************START******************/
    mPWR_DeepSleepTimeUpdated = TRUE;
/****************END*********************/
#else
    (void) deepSleepTimeMs;
#endif /* (cPWR_UsePowerDownMode) */
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

在文件末尾添加/替换以下函数定义

/*---------------------------------------------------------------------------
* Name: PWR_GetTotalSleepDurationMS
* Description: -
* Parameters: -
* Return: -
*---------------------------------------------------------------------------*/
uint32_t PWR_GetTotalSleepDurationMS(void)
{
    uint32_t time;
    uint32_t currentSleepTime;

    OSA_InterruptDisable();
    currentSleepTime = RTC_GetFreeRunningInterruptThreshold(RTC);
    if(currentSleepTime >= mLpmTotalSleepDuration){
    time = (currentSleepTime-mLpmTotalSleepDuration)*1000/CLOCK_GetFreq(kCLOCK_32KClk);
    }
    else{
    time = ((0x100000000-mLpmTotalSleepDuration)+currentSleepTime)*1000/CLOCK_GetFreq(kCLOCK_32KClk);
    }
    OSA_InterruptEnable();

    return time;
}

/*---------------------------------------------------------------------------
* Name: PWR_ResetTotalSleepDuration
* Description: -
* Parameters: -
* Return: -
*---------------------------------------------------------------------------*/
void PWR_ResetTotalSleepDuration(void)
{
    OSA_InterruptDisable();
    mLpmTotalSleepDuration = RTC_GetFreeRunningCount(RTC);
    OSA_InterruptEnable();
}

/*---------------------------------------------------------------------------
* Name: PWR_RTCGetMsTimeUntilNextTick
* Description: -
* Parameters: -
* Return: Time until next tick in mS
*---------------------------------------------------------------------------*/
uint32_t PWR_RTCGetMsTimeUntilNextTick (void)
{
    uint32_t time;
    uint32_t currentRtcCounts, thresholdRtcCounts;

    OSA_InterruptDisable();
    currentRtcCounts = RTC_GetFreeRunningCount(RTC);
    thresholdRtcCounts = RTC_GetFreeRunningResetThreshold(RTC);
    if(thresholdRtcCounts > currentRtcCounts){
    time = (thresholdRtcCounts-currentRtcCounts)*1000/CLOCK_GetFreq(kCLOCK_32KClk);
    }
    else{
    time = ((0x100000000-currentRtcCounts)+thresholdRtcCounts)*1000/CLOCK_GetFreq(kCLOCK_32KClk);
    }
    OSA_InterruptEnable();

    return time;
}

/*---------------------------------------------------------------------------
* Name: PWR_RTCSetWakeupTimeMs
* Description: -
* Parameters: wakeupTimeMs: New wakeup time in milliseconds
* Return: -
*---------------------------------------------------------------------------*/
void PWR_RTCSetWakeupTimeMs (uint32_t wakeupTimeMs){
    uint32_t wakeupTimeTicks;
    uint32_t thresholdValue;

    wakeupTimeTicks = (wakeupTimeMs*CLOCK_GetFreq(kCLOCK_32KClk))/1000;
    thresholdValue = RTC_GetFreeRunningCount(RTC);
    thresholdValue += wakeupTimeTicks;
    RTC_SetFreeRunningInterruptThreshold(RTC, thresholdValue);
}

/*---------------------------------------------------------------------------
* Name: PWR_RTCWakeupStart
* Description: -
* Parameters: -
* Return: -
*---------------------------------------------------------------------------*/
void PWR_RTCWakeupStart (void){
  if(!(RTC->CNT2_CTRL & RTC_CNT2_CTRL_CNT2_EN_MASK)){
    RTC->CNT2_CTRL |= 0x52850000 | RTC_CNT2_CTRL_CNT2_EN_MASK | RTC_CNT2_CTRL_CNT2_WAKEUP_MASK | RTC_CNT2_CTRL_CNT2_INT_EN_MASK;
  }
  else{
    RTC->CNT2_CTRL |= 0x52850000 | RTC_CNT2_CTRL_CNT2_WAKEUP_MASK | RTC_CNT2_CTRL_CNT2_INT_EN_MASK;
  }
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

*** PWR_接口.h ***

在文件末尾添加以下函数声明

/*---------------------------------------------------------------------------
* Name: PWR_GetTotalSleepDurationMS
* Description: -
* Parameters: -
* Return: -
*---------------------------------------------------------------------------*/
uint32_t PWR_GetTotalSleepDurationMS(void);

/*---------------------------------------------------------------------------
* Name: PWR_ResetTotalSleepDuration
* Description: -
* Parameters: -
* Return: -
*---------------------------------------------------------------------------*/
void PWR_ResetTotalSleepDuration(void);

#ifdef __cplusplus
}
#endif

#endif /* _PWR_INTERFACE_H_ */
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

FSL_TICKLESS_GENERIC

以下更改的目的是让系统做好准备,以恢复低功耗期间错过的滴答声。

在 fsl_tickless_generic.h 中进行以下更改。所有需要的更改都标记为注释,其中“Start”表示更改的开始位置,“End”表示更改的结束位置

/* QN_RTC: The RTC free running is a 32-bit counter. */
#define portMAX_32_BIT_NUMBER (0xffffffffUL)
#define portRTC_CLK_HZ (0x8000UL)

/* A fiddle factor to estimate the number of SysTick counts that would have
occurred while the SysTick counter is stopped during tickless idle
calculations. */
#define portMISSED_COUNTS_FACTOR (45UL)

/*
 * The number of SysTick increments that make up one tick period.
 */
/****************************START**************************/
#if configUSE_TICKLESS_IDLE == 1
    static uint32_t ulTimerCountsForOneTick;
#endif /* configUSE_TICKLESS_IDLE */
/************************END*********************************/

/*
 * Setup the timer to generate the tick interrupts.
 */
void vPortSetupTimerInterrupt(void);

#ifdef __cplusplus
}
#endif

#endif /* FSL_TICKLESS_GENERIC_H */
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

应用程序主程序

这是主要的应用程序文件。在这里我们将调用适当的 API 来进入低功耗模式的 MCU 并执行滴答恢复序列。

包含所需的 RTC 和 FreeRTOS 头文件

/*Tickless: Include RTC and FreeRTOS header files */
#include "fsl_rtc.h"
#include "fsl_tickless_generic.h"
#include "FreeRTOS.h"
#include "task.h"‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

QN9080 包含几种低功耗模式。睡眠模式可使大多数模块保持活动状态。断电模式会关闭大多数模块,但允许用户配置一些模块保持活动状态,以便在必要时唤醒 MCU。使用无滴答 FreeRTOS 需要在执行下一个就绪任务之前通过某个计时器唤醒。对于 QN908x,此计时器将是 RTC,它需要 32.768kHz 振荡器保持活动状态。我们将更改连接软件电源库以使用深度睡眠模式 3(QN908x 的断电模式 0),以保持 32.768kHz 振荡器开启。这个改变是在main_task函数中实现的。

#if !defined(MULTICORE_BLACKBOX)
        /* BLE Host Stack Init */
        if (Ble_Initialize(App_GenericCallback) != gBleSuccess_c)
        {
            panic(0,0,0,0);
            return;
        }
#endif /* MULTICORE_BLACKBOX */

/*************** Start ****************/
#if (cPWR_UsePowerDownMode)
    PWR_ChangeDeepSleepMode(3);
#endif
/*************** End ****************/
    }
    
    /* Call application task */
    App_Thread( param );
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

此外,无滴答 FreeRTOS 需要一个特殊的空闲函数,该函数将 MCU 在需要执行下一个任务之前可以保持休眠状态的 RTOS 滴答数作为输入参数。当启用无滴答模式时,以下更改将禁用连接软件演示中提供的默认空闲功能。

/************************************************************************************
*************************************************************************************
* Private prototypes
*************************************************************************************
************************************************************************************/
#if (cPWR_UsePowerDownMode || gAppUseNvm_d)
#if (mAppIdleHook_c)
    #define AppIdle_TaskInit()
    #define App_Idle_Task()
#else
#if (!configUSE_TICKLESS_IDLE)
    static osaStatus_t AppIdle_TaskInit(void);
    static void App_Idle_Task(osaTaskParam_t argument);
#endif // configUSE_TICKLESS_IDLE
#endif
#endif
/************************************************************************************
*************************************************************************************
* Private memory declarations
*************************************************************************************
************************************************************************************/

/******************************** Start ******************************/
#if ((cPWR_UsePowerDownMode || gAppUseNvm_d) && !configUSE_TICKLESS_IDLE)
/******************************** End ******************************/
#if (!mAppIdleHook_c)
OSA_TASK_DEFINE( App_Idle_Task, gAppIdleTaskPriority_c, 1, gAppIdleTaskStackSize_c, FALSE );
osaTaskId_t gAppIdleTaskId = 0;
#endif
#endif  /* cPWR_UsePowerDownMode */‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
#if !gUseHciTransportDownward_d
        pfBLE_SignalFromISR = BLE_SignalFromISRCallback;
#endif /* !gUseHciTransportDownward_d */

/**************************** Start ************************/
#if ((cPWR_UsePowerDownMode || gAppUseNvm_d) && !configUSE_TICKLESS_IDLE)
/**************************** End ************************/
#if (!mAppIdleHook_c)
        AppIdle_TaskInit();
#endif
#endif‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
/***************************START**************************/
#if (cPWR_UsePowerDownMode && !configUSE_TICKLESS_IDLE)
/******************************END***************************/
static void App_Idle(void)
{
    PWRLib_WakeupReason_t wakeupReason;

    if( PWR_CheckIfDeviceCanGoToSleep() )
    {
        /* Enter Low Power */
        wakeupReason = PWR_EnterLowPower();

#if gFSCI_IncludeLpmCommands_c
        /* Send Wake Up indication to FSCI */
        FSCI_SendWakeUpIndication();
#endif

#if gKBD_KeysCount_c > 0
        /* Woke up on Keyboard Press */
        if(wakeupReason.Bits.FromKeyBoard)
        {
            KBD_SwitchPressedOnWakeUp();
            PWR_DisallowDeviceToSleep();
        }
#endif
    }
    else
    {
        /* Enter MCU Sleep */
        PWR_EnterSleep();
    }
}
#endif /* cPWR_UsePowerDownMode */

#if (mAppIdleHook_c)

void vApplicationIdleHook(void)
{
#if (gAppUseNvm_d)
    NvIdle();
#endif

/*******************************START****************************/
#if (cPWR_UsePowerDownMode && !configUSE_TICKLESS_IDLE)
/*********************************END*******************************/
    App_Idle();
#endif
}

#else /* mAppIdleHook_c */

/******************************* START ****************************/
#if ((cPWR_UsePowerDownMode || gAppUseNvm_d) && !configUSE_TICKLESS_IDLE)
/******************************* END ****************************/
static void App_Idle_Task(osaTaskParam_t argument)
{
    while(1)
    {   
#if gAppUseNvm_d
        NvIdle();
#endif
        
#if (cPWR_UsePowerDownMode)
        App_Idle();
#endif

        /* For BareMetal break the while(1) after 1 run */
        if (gUseRtos_c == 0)
        {
            break;
        }
    }
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

一旦默认的空闲功能被禁用,就必须实现特殊的空闲功能。在ApplMain.c的末尾添加以下代码文件。

/*Tickless: Implement Tickless Idle */
#if (cPWR_UsePowerDownMode && configUSE_TICKLESS_IDLE)
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{


    uint32_t time_ms = xExpectedIdleTime * portTICK_PERIOD_MS;
    uint32_t tmrMgrExpiryTimeMs;

    ulTimerCountsForOneTick = 160000;//VALUE OF THE SYSTICK 10 ms

#if (cPWR_UsePowerDownMode)
    PWRLib_WakeupReason_t wakeupReason;
    
    //TMR_MGR: Get next timer manager expiry time
    tmrMgrExpiryTimeMs = TMR_GetFirstExpireTime(gTmrAllTypes_c);

    // TMR_MGR: Update RTC Threshold only if RTOS needs to wakeup earlier
    if(time_ms<tmrMgrExpiryTimeMs){
      PWR_SetDeepSleepTimeInMs(time_ms);
    }
    
    PWR_ResetTotalSleepDuration();

    if( PWR_CheckIfDeviceCanGoToSleep() )
    {
        wakeupReason = PWR_EnterLowPower();
        
        //Fix: All the tick recovery stuff should only happen if device entered in DSM
        xExpectedIdleTime = PWR_GetTotalSleepDurationMS() / portTICK_PERIOD_MS;     // Fix: ticks = time in mS asleep / mS per each tick (portTICK_PERIOD_MS)

        /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
        again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
        value. The critical section is used to ensure the tick interrupt
        can only execute once in the case that the reload register is near
        zero. */
        portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
        portENTER_CRITICAL();

        portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; 
        vTaskStepTick( xExpectedIdleTime );
        portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; 

        portEXIT_CRITICAL();

#if gKBD_KeysCount_c > 0
        /* Woke up on Keyboard Press */
        if(wakeupReason.Bits.FromKeyBoard)
        { 
          KBD_SwitchPressedOnWakeUp();
          PWR_DisallowDeviceToSleep(); 
        }
#endif
    }
    else
    { 
      /* Enter MCU Sleep */ 
      PWR_EnterSleep(); 
    }


#endif /* cPWR_UsePowerDownMode */

}

#endif  //cPWR_UsePowerDownMode && configUSE_TICKLESS_IDLE
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

从前面的函数中可知,ulTimerCountsForOneTick的值用于在唤醒后恢复RTOS滴答定时器的计数。该值取决于 FreeRTOSConfig.h 中定义的 RTOS Tick 间隔并使用以下公式计算:

SYST_RNR = F_Systick_CLK(赫兹) * T_FreeRTOS_Ticks(毫秒)

其中:

  • F_Systick_CLK(Hz) = AHB 或 SYST_CSR 选择的 32KHz
  • T_FreeRTOS_Ticks(ms) = 滴答计数值。

FreeRTOS配置.h

最后,在 FreeRTOSConfig.h文件中,确保 configUSE_TICKLESS_IDLE 设置为 1

 * See http://www.freertos.org/a00110.html.
 *----------------------------------------------------------*/

#define configUSE_PREEMPTION                    1
#define configUSE_TICKLESS_IDLE                 1 //<--- /***** Start *****/
#define configCPU_CLOCK_HZ                      (SystemCoreClock)
#define configTICK_RATE_HZ                      ((TickType_t)100)
#define configMAX_PRIORITIES                    (18)
#define configMINIMAL_STACK_SIZE                ((unsigned short)90)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

测试无滴答 RTOS

为了测试是否成功添加了无滴答支持,我们实现了一个切换 LED 的示例应用程序。此应用程序配置一个 RTOS 定时器,每 500 毫秒切换一次 LED,并在空闲时间进入 DSM3 中的 MCU。电源分析演示就是为此目的而使用的。

电源分析.c

确保已包含以下头文件

#include "FreeRTOS.h"
#include "task.h"‍‍

创建一个 RTOS 任务,使 LED 每 500 毫秒闪烁一次。首先声明任务函数、任务ID和任务本身。

void vfnTaskLedBlinkTest(void* param); //New Task Definition
OSA_TASK_DEFINE(vfnTaskLedBlinkTest, 1, 1, 500, FALSE );
osaTaskId_t gAppTestTask1Id = 0; // TestTask1 Id‍‍‍

在BleApp_Init函数中创建新任务

void BleApp_Init(void)

{
    PWR_AllowDeviceToSleep();

    mPowerState = 0;   // Board starts with PD1 enabled
    /******************* Start *****************/
    gAppTestTask1Id = OSA_TaskCreate(OSA_TASK(vfnTaskLedBlinkTest), NULL); //Task Creation 
    /*******************  End  *****************/

}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

最后在文件末尾添加任务函数定义。

void vfnTaskLedBlinkTest(void* param)
{
    uint16_t wTimeValue = 500; //500ms

    while(1)
    {
        LED_BLUE_TOGGLE();
        vTaskDelay(pdMS_TO_TICKS(wTimeValue));
    } 
}‍‍‍‍‍‍‍‍‍‍

我们可以监控 MCUXpresso IDE ,带有 功率测量工具。有了它,我们可以看到消耗的电流并证明实施情况符合预期。

配置功率测量工具

pastedImage_84.png

消耗电流

pastedImage_85.png

BLE软件回复:连接软件:在 FreeRTOS 中实现无滴答模式<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

即使在无滴答模式或裸板模式下,电源配置文件也可以达到合理的功耗。

然而,有一个陷阱,需要小心。

如果从电源模式 PD1 恢复,在无滴答空闲任务中,在睡眠前已关闭 32K OSC, PWRLib_LPTMR_Stop将崩溃以操作断电外设。

如果电源模式为 PD0,则 OSC 工作时不会出现该问题,并且在睡眠期间保持通电。

1.PNG

如何修复:

判断从PD1还是PD0恢复。

Capture.PNG

回复:连接软件:在 FreeRTOS 中实现无滴答模式<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

温度传感器示例似乎在无滴答模式下工作。

与电源配置文件示例相比,睡眠消耗略高

回复:连接软件:在 FreeRTOS 中实现无滴答模式<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

哪个示例最适合用于测试无滴答 freertos 和低功耗操作?

回复:连接软件:在 FreeRTOS 中实现无滴答模式<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Hi:

最新的 SDK v2.2 已经支持 freertos 的 tickless 功能,需要在app_preinclude.h中启用以下宏

configUSE_TICKLESS_IDLE :为1
以下定义用于计算无滴消逝时间
/* RTC 在 PD1 中不运行,因此禁用 PWR 模块中的 RTC 相关代码 */
#定义 cPWR_EnableLpTmrRunning 1
#定义cPWR_EnablePD0RtcInterrupt 1

回复:连接软件:在 FreeRTOS 中实现无滴答模式<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

本指南是否可以更新为最新的 SDK?QN9080 的 SDK 中似乎实现了对无滴答模式的部分支持?

Tags (1)
No ratings
Version history
Last update:
‎01-05-2026 05:56 AM
Updated by: