2021-09-29低功耗-展锐

指令名

功能描述

DMB

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

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

DSB

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

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

ISB

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

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

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

2021-09-29低功耗-展锐

当某时刻所有优先级高于 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 )

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[];

    uint32_t source = osiCallWithStack(__svc_stack_end, prv32KSleepSram, 0, 0);

       //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;//返回唤醒原因

}

 

上一篇:8行Python代码轻松绘制新冠疫情地图


下一篇:Python绘制16省支援湖北地图