灵动微电子 MM32
直播中

zhu

8年用户 1640经验值
擅长:控制/MCU
私信 关注
[原创]

灵动微课堂 (第136讲) 基于MM32 MCU的OS移植与应用——RT-Thread 电源管理

`简介
随着物联网(IoT)的兴起,产品对功耗的需求越来越强烈。作为数据采集的传感器节点通常需要在电池供电时长期工作,而作为联网的SOC也需要有快速的响应功能和较低的功耗。IoT 产品的功耗来源可以分成2部分,一部分是 MCU 内部的功耗,一部分是板载其他外设的功耗。而在 MCU 内的功耗又可以分成CPU 的功耗和片内外设的功耗。为了实现低功耗的功能,我们需要对它们进行合适的管理。

在该文档中,基于MM32 MCU 特性,适配RT-Thread 强大的应用调度功能,对功耗做优良的管理。
MM32 Series MCU 低功耗有三种模式,分别为:Sleep/Stop/Standby,其中Sleep可以被任意中断唤醒,故在进入Sleep前需要关闭Systick。Stop/Standby则不需要考虑这种问题。程序的移植 Low-power modes有三种,Sleep mode、Stop mode、Standby mode,实现如下 :void pwr_control(rt_uint8_t NewState)
{
    /* Check the parameters */
    assert_param(IS_POWERMODE_STATE(NewState));
    if(PM_SLEEP_MODE_STANDBY == NewState)
    {
        // PWR CLOCK
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
        //  RCC->APB2RSTR|=0X01FC;//REAST IO
        //Enable to wake up pin function
        PWR_WakeUpPinCmd(ENABLE);
        //Enter standby mode
        PWR_EnterSTANDBYMode();
    }
    else if(PM_SLEEP_MODE_DEEP == NewState)
    {
        /* enter Stop mode */
        sysTick_Cmd(DISABLE);
        RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWREN, ENABLE);
        PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
        /* exit Sleep mode */
        /* HSE和HSI已关闭需重启开启 */
        sysTick_init();
        SYSCLKConfig_START();
    }
    else
    {        
        /* enter Sleep mode */
        /* SysTick_Handler() 会自增rt_tick,需要DISABLE。否则无法进入睡眠 */
        sysTick_Cmd(DISABLE);
        __WFI();
        /* exit Sleep mode */
        /* 单纯使用sysTick_Cmd(ENABLE)不能正常,rt_thread_delay()也会有影响 */
        sysTick_init();
    }
}

在RT-Thread任务运行中使MCU进入低功耗状态,由于系统有个默认的工作模式,是IDLE空闲模式,这个模式下会一直调用sleep这个函数,导致无法进入低功耗模式,故需要修改成NONE模式,同时在进入前使系统进入临界状态。进入睡眠后使用K1(外部中断PA0)唤醒,在EXTI0中断中使任务退出临界状态,执行正常的任务。因为RT-Thread使用到systick,在Sleep mode模式时,是无法进入低功耗状态的,因此需要临时关闭Systick,唤醒后再次启用。在实验中我们使用外部中断模式唤醒,故进行如下配置:
//   This function is EXTI0 1 Handler
void EXTI0_1_IRQHandler(void)
{
    EXTI_ClearFlag(EXTI_Line0);
    rt_exit_critical();
}

//    NVIC EXTI Config
static void EXTI_NVIC_Init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    //ENABLE  External interrupt

    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
//   EXTI Config
static void EXTI_Config(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    //PA.0 use EXTI line 0
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
    EXTI_StructInit(&EXTI_InitStructure);
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

//   EXTI Config
static void EXTI_GPIO_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA , ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;                    //PA0
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                 //set as pull down input
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

//   External interrupt wake-up standby initialization
void WKUP_STOP_Init(void)
{
    // EXTI  SYSTEM CLOLK ENABLE
    EXTI_NVIC_Init();
    EXTI_Config();
    EXTI_GPIO_Config();
}

为了便于观察,我们建立两个线程,线程中进行LED闪烁,用来判断任务是否正常运行,代码如下:/* 线程1入口函数 */
static void thread1_entry(void *param)
{
    while (1)
    {
        LED2_TOGGLE();   
        LED1_TOGGLE();
        rt_thread_delay(1000);
    }
}
unsigned char flash_buff[40];
/* 线程2入口函数 */
static void thread2_entry(void *param)
{
    while (1)
    {
        LED4_TOGGLE();
        LED3_TOGGLE();  
        rt_thread_delay(1000);
    }
}
int event_simple_init()
{

    /* 创建线程1 */
    tid1 = rt_thread_create("t1",
                            thread1_entry, RT_NULL, /* 线程入口是thread1_entry, 入口参数是RT_NULL */
                            THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);
    else
        tc_stat(TC_STAT_END | TC_STAT_FAILED);

    /* 创建线程2 */
    tid2 = rt_thread_create("t2",
                            thread2_entry, RT_NULL, /* 线程入口是thread2_entry, 入口参数是RT_NULL */
                            THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid2 != RT_NULL)
        rt_thread_startup(tid2);
    else
        tc_stat(TC_STAT_END | TC_STAT_FAILED);
    return 0;
}
int rt_application_init()
{
    u16 i;
    LED_Init();
    for(i = 0; i < 10; i++) {
        LED1_TOGGLE();
        LED2_TOGGLE();
        LED3_TOGGLE();
        LED4_TOGGLE();
        deleyNop(1000);
    }
    rt_enter_critical();   
    WKUP_STOP_Init();  
    pwr_control(PM_SLEEP_MODE_STANDBY);
    event_simple_init();
}

考虑到芯片进入低功耗模式时,有些时候不易擦除,故在进入低功耗之前最好做一个比较大的延时。同时在进入之前,先使系统进入临界区。进入临界区后调度器将被上锁。在系统锁住调度器的期间,系统依然响应中断,如果中断唤醒了的更高优先级线程,调度器并不会立刻执行它,直到调用解锁调度器函数才尝试进行下一次调度。需要注意的是在唤醒后需要先执行退出临界区操作,当系统退出临界区的时候,系统会计算当前是否有更高优先级的线程就绪,如果有比当前线程更高优先级的线程就绪,将切换到这个高优先级线程中执行;如果无更高优先级线程就绪,将继续执行当前任务。

实验
按下Miniboard的K1键后工程开始运行,可以看到LED闪烁,表示唤醒开始工作。


` 1.jpg

更多回帖

发帖
×
20
完善资料,
赚取积分