2021-09-29低功耗-展锐

指令名

功能描述

DMB

数据存储器隔离。DMB 指令保证: 仅当所有在它前面的存储器访问操作

都执行完毕后,才提交(commit)在它后面的存储器访问操作。回写ram

DSB

数据同步隔离。比 DMB 严格: 仅当所有在它前面的存储器访问操作   回写ram和清空cache

都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访 问操作——译者注)

ISB

指令同步隔离。最严格:它会清洗流水线,以保证所有它前面的指令都执

行完毕之后,才执行它后面的指令。回写ram和清空cache,清洗流水线

注意:dma的使用,要加唤醒锁。

当某时刻所有优先级高于 Idle 任务的任务处于被阻塞或者被挂起的状态,此刻调度器会调度 Idle 任务运行,它的执行函数为:

static portTASK_FUNCTION( prvIdleTask, pvParameters )

{

       /* Stop warnings. */

       ( void ) pvParameters;

       /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE

       SCHEDULER IS STARTED. **/

       /* In case a task that has a secure context deletes itself, in which case

       the idle task is responsible for deleting the task's secure context, if

       any. */

       portTASK_CALLS_SECURE_FUNCTIONS();

       for( ;; )

       {

              /* See if any tasks have deleted themselves - if so then the idle task

              is responsible for freeing the deleted task's TCB and stack. */

              prvCheckTasksWaitingTermination(); //判断是否有需要 Task 自己删除自己,如果有,那么在 Idle 任务中来回收这种类型的场景

              #if ( configUSE_PREEMPTION == 0 )

              {

                     /* If we are not using preemption we keep forcing a task switch to

                     see if any other task has become available.  If we are using

                     preemption we don't need to do this as any task becoming available

                     will automatically get the processor anyway. */

                     taskYIELD();

              }

              #endif /* configUSE_PREEMPTION */

              #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )

              {

                     /* When using preemption tasks of equal priority will be

                     timesliced.  If a task that is sharing the idle priority is ready

                     to run then the idle task should yield before the end of the

                     timeslice.

                     A critical region is not required here as we are just reading from

                     the list, and an occasional incorrect value will not matter.  If

                     the ready list at the idle priority contains more than one task

                     then a task other than the idle task is ready to execute. */

                     if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )

                     {

                            taskYIELD();

                     }

                     else

                     {

                            mtCOVERAGE_TEST_MARKER();

                     }

              }

              #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */

              #if ( configUSE_IDLE_HOOK == 1 )

              {

                     extern void vApplicationIdleHook( void );

                     /* Call the user defined function from within the idle task.  This

                     allows the application designer to add background functionality

                     without the overhead of a separate task.

                     NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,

                     CALL A FUNCTION THAT MIGHT BLOCK. */

                     vApplicationIdleHook();

              }

              #endif /* configUSE_IDLE_HOOK */

              /* This conditional compilation should use inequality to 0, not equality

              to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when

              user defined low power mode    implementations require

              configUSE_TICKLESS_IDLE to be set to a value other than 1. */

              #if ( configUSE_TICKLESS_IDLE != 0 )

              {

              TickType_t xExpectedIdleTime;

                     /* It is not desirable to suspend then resume the scheduler on

                     each iteration of the idle task.  Therefore, a preliminary

                     test of the expected idle time is performed without the

                     scheduler suspended.  The result here is not necessarily

                     valid. */

                     xExpectedIdleTime = prvGetExpectedIdleTime();

                     if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )

                     {

                            vTaskSuspendAll();

                            {

                                   /* Now the scheduler is suspended, the expected idle

                                   time can be sampled again, and this time its value can

                                   be used. */

                                   configASSERT( xNextTaskUnblockTime >= xTickCount );

                                   xExpectedIdleTime = prvGetExpectedIdleTime();

                                   /* Define the following macro to set xExpectedIdleTime to 0

                                   if the application does not want

                                   portSUPPRESS_TICKS_AND_SLEEP() to be called. */

                                   configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );

                                   if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )

                                   {

                                          traceLOW_POWER_IDLE_BEGIN();

                                          portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); //执行休眠

                                          traceLOW_POWER_IDLE_END();

                                   }

                                   else

                                   {

                                          mtCOVERAGE_TEST_MARKER();

                                   }

                            }

                            ( void ) xTaskResumeAll();

                     }

                     else

                     {

                            mtCOVERAGE_TEST_MARKER();

                     }

              }

              #endif /* configUSE_TICKLESS_IDLE */

       }

}

extern void osiIdleSleep( TickType_t xExpectedIdleTime );

       #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) osiIdleSleep( xExpectedIdleTime )

prvSleepLocked(idle_tick);

static void prvSleepLocked(uint32_t idle_tick)

{

    osiPmContext_t *d = &gOsiPmCtx;

    osiSuspendMode_t mode = DEBUG_SUSPEND_MODE;

    if (osiIsSleepAbort())

        return;

      

    if (gOsiPmCtx.sleep32k_flags != 0)

    {

        if (prv32KSleepPermitted(d))

        {

            int64_t deep_sleep_ms = osiTimerDeepSleepTime(idle_tick);

            if (deep_sleep_ms > SUSPEND_MIN_TIME)

            {

                osiTraceSetPushEnable(false);

                prv32KSleep(d, deep_sleep_ms); //usb连接,32k睡眠

                osiTraceSetPushEnable(true);

                return;

            }

        }

    }

    else

    {

        if (prvSuspendPermitted(d))

        {

            int64_t deep_sleep_ms = osiTimerDeepSleepTime(idle_tick);

            if (deep_sleep_ms > SUSPEND_MIN_TIME)

            {

                osiTraceSetPushEnable(false);

                prvSuspend(d, mode, deep_sleep_ms); //usb未连接,深睡眠

                osiTraceSetPushEnable(true);

                return;

            }

        }

}

       //展锐平台usbcameralcdsdmmcpwtpwl在使用时,会锁定最小时钟频率26M,会导致系统在idle下不能进入轻睡眠模式,功耗增加6mA以上

    prvLightSleep(d, idle_tick); //idel下进入轻度睡眠,有26M就进不了轻度睡眠

}

//一、调用prv32KSleep()           //usb连接,32k睡眠

static void prv32KSleep(osiPmContext_t *d, int64_t sleep_ms)

{

    OSI_LOGI(0, "32K sleep sleep/%u", (unsigned)sleep_ms);

#ifdef CONFIG_QUEC_PROJECT_FEATURE_SLEEP

       osiPmSource_t *p;

    TAILQ_FOREACH(p, &d->resume_list, resume_iter)

    {

        if ( p->ops.suspend && \

                  (p->tag == DRV_NAME_PWT || p->tag == DRV_NAME_LPG))

        {

            OSI_LOGD(0, "suspend cb %4c", p->tag);

            p->ops.suspend(p->cb_ctx, 0);

        }

    }

       quec_enter_sleep_cb();   //1.关硬件外设(led、流控、gnss);2.客户自定义关闭

#endif

    osiProfileEnter(PROFCODE_DEEP_SLEEP);

    halSysWdtStop();   //关系统定时器

    WDT_ENTER_DEEPSLEEP(OSI_MIN(int64_t, osiCpDeepSleepTime(), sleep_ms) + SUSPEND_WDT_MARGIN_TIME);

    uint32_t source = osiChip32KSleep(sleep_ms);

    OSI_LOGI(0, "suspend resume source 0x%08x", source);

#ifdef CONFIG_QUEC_PROJECT_FEATURE_SLEEP

    TAILQ_FOREACH(p, &d->resume_list, resume_iter)

    {

        if (p->ops.resume != NULL && \

                  (p->tag == DRV_NAME_PWT || p->tag == DRV_NAME_LPG))

        {

            OSI_LOGD(0, "resume cb %4c", p->tag);

            p->ops.resume(p->cb_ctx, 0, source);

        }

    }

       quec_exit_sleep_cb(source); //1.回复硬件外设(led、流控、gnss);2.客户自定义的打开

#endif

    halSysWdtStart();   //开系统定时器

    WDT_EXIT_DEEPSLEEP();  //开启看门狗

    osiProfileExit(PROFCODE_DEEP_SLEEP);

}

uint32_t osiChip32KSleep(int64_t sleep_ms)

{     

       //1.关闭硬件定时器

    osiChipTimerDisable();

       //2.开启idel的定时器

prvSetIdleTimerForSleep(sleep_ms);

//3.保存tick

    prvSaveTickRef32K();

       //得到栈的地址

    extern char __svc_stack_end[]; //注意要8字节对齐

    uint32_t source = osiCallWithStack(__svc_stack_end, prv32KSleepSram, 0, 0); //在sram中运行

       //4.记录tick

prvRestoreTickRef32K();

//5.定时器唤醒处理

    osiTimerWakeupProcess();

    return source;  //返回唤醒原因

}

栈 从psram移到sram中运行

prv32KSleepSram()

{     

       //1.关闭cp(协处理器,流媒体加速器)的时钟

       REG_SYS_CTRL_CLK_OTHERS_DISABLE_T clk_others_disable =  {.b.disable_oc_id_cp_a5 = 1};

       hwp_sysCtrl->clk_others_disable = clk_others_disable.v;

       //2.usb检测使能

       REGT_FIELD_CHANGE(hwp_analogReg->usb_reg3,

                      REG_ANALOG_REG_USB_REG3_T,

                      usb_det_en, 1);

hwp_analogReg->usb_reserved |= 0x20;

//3.保持ap和aon_lp在深度睡眠继续打开电源

REGT_FIELD_CHANGE(hwp_pwrctrl->pwr_hwen,

                      REG_CP_PWRCTRL_PWR_HWEN_T,

                      ap_pwr_en, 0,

                      aon_lp_pon_en, 0);

//4.禁止分支预测

__set_SCTLR(__get_SCTLR() & (~SCTLR_Z_Msk));

__ISB(); //清洗流水线

//5.关闭MMU,因为页表(TTB)在psram

__set_SCTLR(__get_SCTLR() & ~1);

__ISB();

//6.psram进入低电压模式(psram不能被访问了)

REGT_FIELD_CHANGE(hwp_sysCtrl->cfg_force_lp_mode_lp,

                      REG_SYS_CTRL_CFG_FORCE_LP_MODE_LP_T,

                      cfg_force_lp_psram, 1);

//7.psram进入了自刷新模式

REGT_FIELD_CHANGE(hwp_pwrctrl->ddr_slp_req_hwen,

                      REG_CP_PWRCTRL_DDR_SLP_REQ_HWEN_T,

                      ddrslpreq_hwen, 0);

REGT_FIELD_CHANGE(hwp_pwrctrl->ddr_slp_req_sw,

                      REG_CP_PWRCTRL_DDR_SLP_REQ_SW_T,

                      ddrslpreq, 1);

REGT_WAIT_FIELD_NEZ(hwp_pwrctrl->ddr_slp_ack,

                        REG_CP_PWRCTRL_DDR_SLP_ACK_T,

                        ddrslpack);

//8.设置gpio9输入、高电平唤醒

REG_IOMUX_PAD_GPIO_9_CFG_REG_T gpio_9_cfg = {};

gpio_9_cfg.b.pad_gpio_9_sel = 0;

hwp_iomux->pad_gpio_9_cfg_reg = gpio_9_cfg.v;

//9.设置gpio1

hwp_gpio1->gpio_oen_set_in = (1 << CONFIG_SLEEP32K_GPIO);

hwp_gpio1->gpint_mode_set_reg = (1 << CONFIG_SLEEP32K_GPIO);

hwp_gpio1->gpint_ctrl_r_clr_reg = (1 << CONFIG_SLEEP32K_GPIO);

hwp_gpio1->gpint_ctrl_f_clr_reg = (1 << CONFIG_SLEEP32K_GPIO);

hwp_gpio1->gpint_ctrl_r_set_reg = (1 << CONFIG_SLEEP32K_GPIO);

//10.刷cache

__DSB();

__ISB();

       //11.读取usb的状态

    gusbcfg.v = hwp_usbc->gusbcfg;

              //12.时间查看

    unsigned tick16k = hwp_timer2->hwtimer_curval;

       //13.wfi:等待usb接入,中断,定时器唤醒

    for (int count = 0;;)

    {

        // 获取IRQ

        uint32_t isr = __get_ISR();

        if ((isr & (CPSR_I_Msk | CPSR_F_Msk | CPSR_A_Msk)) != 0)

            break;

        // 610us

        if ((unsigned)(hwp_timer2->hwtimer_curval - tick16k) > SLEEP32K_POLL_PLL_START)

        {

            REG_SYS_CTRL_SEL_CLOCK_T sel_clock = {hwp_sysCtrl->sel_clock};

            if (sel_clock.b.apll_locked_h)

                break;

        }

        // usb接入

        if (gusbcfg.b.physel)

        {

            ggpio.v = hwp_usbc->ggpio;

            count = (ggpio.b.gpi == 2) ? count + 1 : 0;

        }

        else

        {

            usb_mon.v = hwp_analogReg->usb_mon;

            count = (usb_mon.b.usb_dm_chr == 0 && usb_mon.b.usb_dp_chr == 1) ? count + 1 : 0;

        }

        if (count >= USB_MON_DET_COUNT)

        {

            source = HAL_RESUME_SRC_USB_MON;

            break;

        }

    }

       //14.gpio9输出高

       gpio_9_cfg.b.pad_gpio_9_oen_frc = 1;

       gpio_9_cfg.b.pad_gpio_9_out_frc = 1;

       gpio_9_cfg.b.pad_gpio_9_out_reg = 1;

       hwp_iomux->pad_gpio_9_cfg_reg = gpio_9_cfg.v;

//15.gpio1设置

hwp_gpio1->gpint_ctrl_r_clr_reg = (1 << CONFIG_SLEEP32K_GPIO);

hwp_gpio1->gpint_ctrl_f_clr_reg = (1 << CONFIG_SLEEP32K_GPIO);

//16.usb使能关闭

REGT_FIELD_CHANGE(hwp_analogReg->usb_reg3,

                      REG_ANALOG_REG_USB_REG3_T,

                      usb_det_en, 0);

    hwp_analogReg->usb_reserved &= ~0x20;

//17.退出自刷新

REGT_FIELD_CHANGE(hwp_pwrctrl->ddr_slp_req_sw,

                      REG_CP_PWRCTRL_DDR_SLP_REQ_SW_T,

                      ddrslpreq, 0);

    REGT_FIELD_CHANGE(hwp_pwrctrl->ddr_slp_req_hwen,

                      REG_CP_PWRCTRL_DDR_SLP_REQ_HWEN_T,

                      ddrslpreq_hwen, 1);

//18.恢复电压

REGT_FIELD_CHANGE(hwp_sysCtrl->cfg_force_lp_mode_lp,

                      REG_SYS_CTRL_CFG_FORCE_LP_MODE_LP_T,

                      cfg_force_lp_psram, 0);

//19.使能mmu

__set_SCTLR((__get_SCTLR() & ~(1 << 28) & ~(1 << 1)) | 1 | (1 << 29));

    __ISB();

//20.清除唤醒的记录

REG_CP_IDLE_IDL_AWK_ST_T awk_st = {hwp_idle->idl_awk_st};

    REG_CP_IDLE_IDL_AWK_ST_T awk_st_clr = {

        .b.awk0_awk_stat = 1, // pmic

        .b.awk1_awk_stat = 0, // vad_int

        .b.awk2_awk_stat = 1, // key

        .b.awk3_awk_stat = 1, // gpio1

        .b.awk4_awk_stat = 1, // uart1

        .b.awk5_awk_stat = 1, // pad uart1_rxd

        .b.awk6_awk_stat = 1, // wcn2sys

        .b.awk7_awk_stat = 1, // pad wcn_osc_en

        .b.awk_osw2_stat = 1};

    hwp_idle->idl_awk_st = awk_st_clr.v;

//21.记录唤醒

    if (awk_st.b.awk0_awk_stat)

        source |= HAL_RESUME_SRC_PMIC;

    if (awk_st.b.awk1_awk_stat)

        source |= HAL_RESUME_SRC_VAD;

    if (awk_st.b.awk2_awk_stat)

        source |= HAL_RESUME_SRC_KEY;

    if (awk_st.b.awk3_awk_stat)

        source |= HAL_RESUME_SRC_GPIO1;

    if (awk_st.b.awk4_awk_stat)

        source |= HAL_RESUME_SRC_UART1;

    if (awk_st.b.awk5_awk_stat)

        source |= HAL_RESUME_SRC_UART1_RXD;

    if (awk_st.b.awk6_awk_stat)

        source |= HAL_RESUME_SRC_WCN2SYS;

    if (awk_st.b.awk7_awk_stat)

        source |= HAL_RESUME_SRC_WCN_OSC;

    if (awk_st.b.awk_osw1_stat)

        source |= HAL_RESUME_SRC_IDLE_TIMER1;

    if (awk_st.b.awk_osw2_stat)

        source |= HAL_RESUME_SRC_IDLE_TIMER2;

    if (awk_st.b.awk_self_stat)

        source |= HAL_RESUME_SRC_SELF;

//22.打开协处理器的时钟

REG_SYS_CTRL_CLK_OTHERS_ENABLE_T clk_others_enable = {.b.enable_oc_id_cp_a5 = 1};

    hwp_sysCtrl->clk_others_enable = clk_others_enable.v;

//23.打开分支预测

__set_SCTLR(__get_SCTLR() | SCTLR_Z_Msk);

__ISB();

//24. ap和aon_lp在深度睡眠关闭电源

REGT_FIELD_CHANGE(hwp_pwrctrl->pwr_hwen,

                      REG_CP_PWRCTRL_PWR_HWEN_T,

                      ap_pwr_en, 1,

                      aon_lp_pon_en, 1);

return source;//返回唤醒原因

}

//二、调用prvSuspend()            //usb未连接,深睡眠

static void prvSuspend(osiPmContext_t *d, osiSuspendMode_t mode, int64_t sleep_ms)

{

    // when PSM sleep condition isn't satisfied, it will return harmless

    osiShutdown(OSI_SHUTDOWN_PSM_SLEEP);

    OSI_LOGD(0, "deep sleep mode/%d sleep/%u", mode, (unsigned)sleep_ms);

       //quectel睡眠函数

#ifdef CONFIG_QUEC_PROJECT_FEATURE_SLEEP

       quec_enter_sleep_cb();

#endif

    osiPmSource_t *p;

    TAILQ_FOREACH_REVERSE(p, &d->resume_list, osiPmSourceHead, resume_iter)

    {

        if (p->ops.suspend != NULL)

        {

            OSI_LOGD(0, "suspend cb %4c", p->tag);

            p->ops.suspend(p->cb_ctx, mode); //执行pm source suspend函数

        }

    }

    osiProfileEnter(PROFCODE_DEEP_SLEEP);

    WDT_ENTER_DEEPSLEEP(OSI_MIN(int64_t, osiCpDeepSleepTime(), sleep_ms) + SUSPEND_WDT_MARGIN_TIME);

    halSysWdtStop();

    osiChipSuspend(mode);

    uint32_t source = osiPmCpuSuspend(mode, sleep_ms);  //睡眠停留函数

    OSI_LOGI(0, "suspend resume source 0x%08x", source);

    osiChipResume(mode, source);

    halSysWdtStart();

    WDT_EXIT_DEEPSLEEP();

    osiProfileExit(PROFCODE_DEEP_SLEEP);

    TAILQ_FOREACH(p, &d->resume_list, resume_iter)

    {

        if (p->ops.resume != NULL)

        {

            OSI_LOGD(0, "resume cb %4c", p->tag);

            p->ops.resume(p->cb_ctx, mode, source);  //执行pm source resume函数

        }

    }

    osiChipResumePost(mode, source);

#ifdef CONFIG_QUEC_PROJECT_FEATURE_SLEEP

       quec_exit_sleep_cb(source);  //quectel 唤醒函数

#endif

    osiTimerWakeupProcess();

}

Pm source

//三、轻睡眠模式

static void prvLightSleep(osiPmContext_t *d, uint32_t idle_tick)

{

    bool timer_moved = osiTimerLightSleep(idle_tick);

    osiProfileEnter(PROFCODE_LIGHT_SLEEP);

    prvCpuSleep();

    osiProfileExit(PROFCODE_LIGHT_SLEEP);

    if (timer_moved)

        osiTimerWakeupProcess();

}

static inline void prvCpuSleep(void)

{

#ifdef CONFIG_CPU_ARM

    osiChipLightSleepEnter();

    __DSB();

    __ISB();

    __WFI();

    osiChipLightSleepExit();

#else

    asm volatile(".align 4\n"

                 ".set noreorder\n"

                 "li $6, 1\n"

                 "sw $6, (%0)\n"

                 "lw $6, (%0)\n"

                 "nop\n"

                 ".set reorder\n"

                 :

                 : "r"(&(hwp_sysIrq->cpu_sleep))

                 : "$6");

#endif

}

void osiChipLightSleepEnter(void)

{

    halSysWdtStop();

    if (osiIsSlowSysClkAllowed())

    {

        REG_CP_IDLE_SLOW_SYS_CLK_SEL_HWEN_T clk_sel_hwen = {hwp_idle->slow_sys_clk_sel_hwen};

        clk_sel_hwen.b.hwen = 0;

        hwp_idle->slow_sys_clk_sel_hwen = clk_sel_hwen.v;   //设置进入睡眠

    }

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值