应用示例:此程序主要实现开机后经过10秒后进入 STOP 模式,然后每经过5秒 SLEEP 模式和 STOP 模式互相切换,如此循环往复,同时经过一个循环后,切换 MCU 的运行频率,验证运行的稳定性。并且打开了回调和中断唤醒,在进入睡眠和唤醒后会分别熄灭和点亮LED灯,在睡眠时间可以通过外部中断唤醒:
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-11-06 SummerGift first version
*/
#include
#include
#include
#include
#define LOG_TAG 'PM.test'
#define LOG_LVL LOG_LVL_DBG
#include
/* defined the LED0 pin: PB13 */
#define LED0_PIN GET_PIN(C, 6)
#define MCU_IRQ_WAKE_PIN GET_PIN(C, 9)
static RTC_HandleTypeDef hrtc;
static RTC_TimeTypeDef curtime = {0};
static RTC_TimeTypeDef alarmtime = {0};
static RTC_AlarmTypeDef alarm = {0};
static struct rt_semaphore wake_sem;
static rt_uint8_t sleep_mode = PM_SLEEP_MODE_DEEP; /* STOP 模式 */
static rt_uint8_t run_mode = PM_RUN_MODE_NORMAL_SPEED;
static rt_uint32_t get_interval(void);
static void get_rtc_time(RTC_TimeTypeDef *time);
static rt_uint8_t mode_loop(void);
static rt_uint8_t issleep = 0;
/* 中断回调函数 */
static void MCU_IRQ_WAKE(void *args)
{
if(issleep)
{
rt_kprintf('MCU_IRQ_WAKE!\n');
rt_sem_release(&wake_sem);
issleep = 0;
}
}
static void pm_botify(uint8_t event, uint8_t mode, void *data)
{
if(event == RT_PM_ENTER_SLEEP && mode == PM_SLEEP_MODE_DEEP)
{
rt_pin_write(LED0_PIN, PIN_HIGH);
issleep = 1;
}
else if (event == RT_PM_EXIT_SLEEP && mode == PM_SLEEP_MODE_DEEP )
{
rt_pin_write(LED0_PIN, PIN_LOW);
}
}
int pm_test(void)
{
hrtc.Instance = RTC;
rt_sem_init(&wake_sem, 'wake_sem', 0, RT_IPC_FLAG_FIFO);
rt_pm_notify_set(pm_botify,0);
/* 按键0引脚为输入模式 */
rt_pin_mode(MCU_IRQ_WAKE_PIN, PIN_MODE_INPUT_PULLDOWN);
/* 绑定中断,上升沿模式,回调函数名为beep_on */
rt_pin_attach_irq(MCU_IRQ_WAKE_PIN, PIN_IRQ_MODE_RISING, MCU_IRQ_WAKE, RT_NULL);
/* 使能中断 */
rt_pin_irq_enable(MCU_IRQ_WAKE_PIN, PIN_IRQ_ENABLE);
rt_thread_mdelay(10000);
#ifdef RT_USING_PM
/* 申请低功耗模式 */
rt_pm_request(sleep_mode);
#endif
get_rtc_time(&curtime);
if (sleep_mode == PM_SLEEP_MODE_STANDBY)
{
/* 设置休眠,闹钟 20 秒后唤醒,简化版闹钟,只支持 1分钟内有效 */
alarmtime.Hours = curtime.Hours;
alarmtime.Minutes = curtime.Minutes;
alarmtime.SubSeconds = curtime.SubSeconds;
alarmtime.Seconds = curtime.Seconds + 20;
if (alarmtime.Seconds >= 60)
{
alarmtime.Seconds -= 60;
alarmtime.Minutes ++;
if (alarmtime.Minutes >= 60)
alarmtime.Minutes -= 60;
}
alarm.Alarm = RTC_ALARM_A;
alarm.AlarmTime = alarmtime;
alarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
alarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
alarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY | RTC_ALARMMASK_HOURS;
alarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
alarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
alarm.AlarmDateWeekDay = 0x1;
/* 开启闹钟 */
HAL_RTC_SetAlarm_IT(&hrtc, &alarm, RTC_FORMAT_BIN);
}
while (1)
{
/* 开始进入低功耗模式 */
rt_sem_take(&wake_sem, rt_tick_from_millisecond(5000));
/* 退出低功耗模式 */
rt_kprintf('Sleep %d ms\n', get_interval());
#ifdef RT_USING_PM
/* 申请正常模式 */
rt_pm_request(PM_SLEEP_MODE_NONE);
#endif
rt_thread_mdelay(5000);
rt_kprintf('Wakeup %d ms\n', get_interval());
/* 运行模式切换 */
rt_pm_run_enter(mode_loop());
#ifdef RT_USING_PM
rt_pm_release(PM_SLEEP_MODE_NONE);
#endif
}
return RT_EOK;
}
//MSH_CMD_EXPORT(pm_test, PM TEST);
static rt_uint32_t get_interval(void)
{
rt_uint32_t seconds;
rt_uint32_t last_seconds = curtime.Seconds;
rt_uint32_t last_subseconds = curtime.SubSeconds;
get_rtc_time(&curtime);
if (curtime.Seconds < last_seconds)
seconds = 60 + curtime.Seconds - last_seconds;
else
seconds = curtime.Seconds - last_seconds;
return (rt_uint32_t)(seconds * 1000 + ((int32_t)last_subseconds - (int32_t)curtime.SubSeconds) * 1000 \
/ (int32_t)(((RTC->PRER & RTC_PRER_PREDIV_S) >> RTC_PRER_PREDIV_S_Pos) + 1U));
}
static void get_rtc_time(RTC_TimeTypeDef *time)
{
rt_uint32_t st, datetmpreg;
HAL_RTC_GetTime(&hrtc, time, RTC_FORMAT_BIN);
datetmpreg = RTC->DR;
if (HAL_RCC_GetPCLK1Freq() < 32000U * 7U)
{
st = time->SubSeconds;
HAL_RTC_GetTime(&hrtc, time, RTC_FORMAT_BIN);
datetmpreg = RTC->DR;
if (st != time->SubSeconds)
{
HAL_RTC_GetTime(&hrtc, time, RTC_FORMAT_BIN);
datetmpreg = RTC->DR;
}
}
(void)datetmpreg;
}
rt_uint8_t mode_loop(void)
{
rt_uint8_t mode = 1;
run_mode++;
switch (run_mode)
{
case 0:
case 1:
case 2:
case 3:
mode = run_mode;
break;
case 4:
mode = 2;
break;
case 5:
mode = 1;
break;
case 6:
mode = run_mode = 0;
break;
}
return mode;
}
|