1 /*
2  * Copyright (c) 2022-2024, Xiaohua Semiconductor Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-12-30     CDT          first version
9  */
10 
11 /*
12 * 功能
13 * 程序清单:这是一个 PM 设备使用例程
14 * 例程导出了 pm_sample_init 命令到控制终端
15 * 命令调用格式:pm_sample_init
16 *
17 *   展示RTT休眠模式的进入和退出
18 *   hc32 drv_pm 支持的RTT休眠模式包括: idle、deep、standby、shutdown
19 *   每种休眠模式与芯片低功耗模式的对应关系是:
20 *       RTT                    |   HC32
21 *       -----------------------|----------
22 *       PM_SLEEP_MODE_IDLE     | 睡眠模式
23 *       PM_SLEEP_MODE_DEEP     | 停止模式
24 *       PM_SLEEP_MODE_STANDBY  | 掉电模式1或2(可配,默认配置是模式1)
25 *       PM_SLEEP_MODE_SHUTDOWN | 掉电模式3或4(可配,默认配置是模式3)
26 *
27 * 操作步骤1:
28 *   1)按下WKUP按键:  MCU进入休眠模式
29 *   2)再按下WKUP按键:MCU退出休眠模式
30 *   3)重复上述按键操作,MCU循环进入休眠模式(deep、standby、shutdown、idle)和退出对应的休眠模式。
31 *   每次进入休眠模式前,MCU打印 "sleep:" + 休眠模式名称
32 *   每次退出休眠模式后,MCU打印 "wake from sleep:" + 休眠模式名称
33 * 操作步骤2:
34 *   1)支持运行模式切换的芯片循环切换 低速->高速->低速 运行模式,对应时钟输出口输出对应模式下的时钟信号
35 */
36 
37 #include <rtthread.h>
38 #include <rtdevice.h>
39 #include <board.h>
40 #include <drivers/lptimer.h>
41 
42 #if defined(BSP_USING_PM)
43 
44 #if defined (HC32F4A0) || defined (HC32F4A8)
45     #define PLL_SRC                             ((CM_CMU->PLLHCFGR & CMU_PLLHCFGR_PLLSRC) >> CMU_PLLHCFGR_PLLSRC_POS)
46     #define BSP_KEY_PORT                        (GPIO_PORT_A)
47     #define BSP_KEY_PIN                         (GPIO_PIN_00)
48     #define BSP_KEY_EXTINT                      (EXTINT_CH00)
49     #define BSP_KEY_INT_SRC                     (INT_SRC_PORT_EIRQ0)
50     #define BSP_KEY_IRQn                        (INT001_IRQn)
51     #define BSP_KEY_INTC_STOP_WKUP_EXTINT       (INTC_STOP_WKUP_EXTINT_CH0)
52     #define BSP_KEY_EVT                         (EVT_SRC_PORT_EIRQ0)
53     #define BSP_KEY_PWC_PD_WKUP_TRIG_WKUP       (PWC_PD_WKUP_TRIG_WKUP0)
54     #define BSP_KEY_PWC_PD_WKUP_WKUP            (PWC_PD_WKUP_WKUP00)
55 
56     #define LED_GREEN_PORT                      (GPIO_PORT_C)
57     #define LED_GREEN_PIN                       (GPIO_PIN_09)
58 
59     #define MCO_PORT                            (GPIO_PORT_A)
60     #define MCO_PIN                             (GPIO_PIN_08)
61     #define MCO_GPIO_FUNC                       (GPIO_FUNC_1)
62 
63 #elif defined (HC32F460)
64     #define PLL_SRC                             ((CM_CMU->PLLCFGR & CMU_PLLCFGR_PLLSRC) >> CMU_PLLCFGR_PLLSRC_POS)
65     #define BSP_KEY_PORT                        (GPIO_PORT_B)   /* Key10 */
66     #define BSP_KEY_PIN                         (GPIO_PIN_01)
67     #define BSP_KEY_EXTINT                      (EXTINT_CH01)
68     #define BSP_KEY_INT_SRC                     (INT_SRC_PORT_EIRQ1)
69     #define BSP_KEY_IRQn                        (INT001_IRQn)
70     #define BSP_KEY_INTC_STOP_WKUP_EXTINT       (INTC_STOP_WKUP_EXTINT_CH1)
71     #define BSP_KEY_EVT                         (EVT_SRC_PORT_EIRQ1)
72     #define BSP_KEY_PWC_PD_WKUP_TRIG_WKUP       (PWC_PD_WKUP_TRIG_WKUP1)
73     #define BSP_KEY_PWC_PD_WKUP_WKUP            (PWC_PD_WKUP_WKUP01)
74 
75     #define LED_GREEN_PORT                      (GPIO_PORT_D)
76     #define LED_GREEN_PIN                       (GPIO_PIN_04)
77 
78     #define MCO_PORT                            (GPIO_PORT_A)
79     #define MCO_PIN                             (GPIO_PIN_08)
80     #define MCO_GPIO_FUNC                       (GPIO_FUNC_1)
81 
82 #elif defined (HC32F448)
83     #define PLL_SRC                             ((CM_CMU->PLLHCFGR & CMU_PLLHCFGR_PLLSRC) >> CMU_PLLHCFGR_PLLSRC_POS)
84     #define BSP_KEY_PORT                        (GPIO_PORT_B)   /* Key5 */
85     #define BSP_KEY_PIN                         (GPIO_PIN_06)
86     #define BSP_KEY_EXTINT                      (EXTINT_CH06)
87     #define BSP_KEY_INT_SRC                     (INT_SRC_PORT_EIRQ6)
88     #define BSP_KEY_IRQn                        (INT001_IRQn)
89     #define BSP_KEY_INTC_STOP_WKUP_EXTINT       (INTC_STOP_WKUP_EXTINT_CH6)
90     #define BSP_KEY_EVT                         (EVT_SRC_PORT_EIRQ6)
91     #define BSP_KEY_PWC_PD_WKUP_TRIG_WKUP       (PWC_PD_WKUP_TRIG_WKUP1)
92     #define BSP_KEY_PWC_PD_WKUP_WKUP            (PWC_PD_WKUP_WKUP12)
93 
94     #define LED_GREEN_PORT                      (GPIO_PORT_A)
95     #define LED_GREEN_PIN                       (GPIO_PIN_02)
96 
97     #define MCO_PORT                            (GPIO_PORT_A)
98     #define MCO_PIN                             (GPIO_PIN_08)
99     #define MCO_GPIO_FUNC                       (GPIO_FUNC_1)
100 
101 #elif defined (HC32F472)
102     #define PLL_SRC                             ((CM_CMU->PLLHCFGR & CMU_PLLHCFGR_PLLSRC) >> CMU_PLLHCFGR_PLLSRC_POS)
103     #define BSP_KEY_PORT                        (GPIO_PORT_B)   /* Key5 */
104     #define BSP_KEY_PIN                         (GPIO_PIN_05)
105     #define BSP_KEY_EXTINT                      (EXTINT_CH05)
106     #define BSP_KEY_INT_SRC                     (INT_SRC_PORT_EIRQ5)
107     #define BSP_KEY_IRQn                        (INT001_IRQn)
108     #define BSP_KEY_INTC_STOP_WKUP_EXTINT       (INTC_STOP_WKUP_EXTINT_CH5)
109     #define BSP_KEY_EVT                         (EVT_SRC_PORT_EIRQ5)
110     #define BSP_KEY_PWC_PD_WKUP_TRIG_WKUP       (PWC_PD_WKUP_TRIG_WKUP1)
111     #define BSP_KEY_PWC_PD_WKUP_WKUP            (PWC_PD_WKUP_WKUP11)
112 
113     #define LED_GREEN_PORT                      (GPIO_PORT_C)
114     #define LED_GREEN_PIN                       (GPIO_PIN_09)
115 
116 #endif
117 
118 #define KEYCNT_BACKUP_ADDR                      (uint32_t *)(0x200F0010)
119 #define KEYCNT_CMD_SLEEP_NONE                   (0)
120 #define KEYCNT_CMD_SLEEP_IDLE                   (1)
121 #define KEYCNT_CMD_SLEEP_DEEP                   (3)
122 #define KEYCNT_CMD_SLEEP_STANDBY                (5)
123 #define KEYCNT_CMD_SLEEP_SHUTDOWN               (7)
124 
125 #define PM_DBG
126 #if defined PM_DBG
127     #define pm_dbg  rt_kprintf
128 #else
129     #define pm_dbg
130 #endif
131 
132 static volatile uint32_t g_keycnt_cmd;
133 static volatile rt_bool_t g_wkup_flag = RT_FALSE;
134 
KEY_IrqHandler(void)135 static void KEY_IrqHandler(void)
136 {
137     if (SET == EXTINT_GetExtIntStatus(BSP_KEY_EXTINT))
138     {
139         EXTINT_ClearExtIntStatus(BSP_KEY_EXTINT);
140         __DSB();
141         __ISB();
142     }
143 
144     if (g_wkup_flag)
145     {
146         g_wkup_flag = RT_FALSE;
147         return;
148     }
149 
150     g_keycnt_cmd++;
151     pm_dbg("g_keycnt_cmd =%d, ", g_keycnt_cmd);
152     pm_dbg("recv sleep request\n");
153     NVIC_DisableIRQ(BSP_KEY_IRQn);
154     NVIC_ClearPendingIRQ(BSP_KEY_IRQn);
155 }
156 
_key_int_init(void)157 static void _key_int_init(void)
158 {
159     stc_extint_init_t stcExtIntInit;
160     stc_irq_signin_config_t stcIrqSignConfig;
161     stc_gpio_init_t stcGpioInit;
162 
163     /* configuration structure initialization */
164     (void)GPIO_StructInit(&stcGpioInit);
165     stcGpioInit.u16ExtInt = PIN_EXTINT_ON;
166     stcGpioInit.u16PullUp = PIN_PU_ON;
167     /* GPIO config */
168     (void)GPIO_Init(BSP_KEY_PORT, BSP_KEY_PIN, &stcGpioInit);
169 
170     /* Extint config */
171     (void)EXTINT_StructInit(&stcExtIntInit);
172     stcExtIntInit.u32Edge = EXTINT_TRIG_FALLING;
173     (void)EXTINT_Init(BSP_KEY_EXTINT, &stcExtIntInit);
174 
175     /* IRQ sign-in */
176     stcIrqSignConfig.enIntSrc = BSP_KEY_INT_SRC;
177     stcIrqSignConfig.enIRQn   = BSP_KEY_IRQn;
178     stcIrqSignConfig.pfnCallback = KEY_IrqHandler;
179     (void)INTC_IrqSignIn(&stcIrqSignConfig);
180 
181     /* NVIC config */
182     NVIC_ClearPendingIRQ(stcIrqSignConfig.enIRQn);
183     NVIC_SetPriority(stcIrqSignConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT);
184     NVIC_EnableIRQ(stcIrqSignConfig.enIRQn);
185 }
186 
187 
_wkup_cfg_sleep_deep()188 static void _wkup_cfg_sleep_deep()
189 {
190     INTC_WakeupSrcCmd(BSP_KEY_INTC_STOP_WKUP_EXTINT, ENABLE);
191 }
192 
_wkup_cfg_sleep_standby(void)193 static void _wkup_cfg_sleep_standby(void)
194 {
195     PWC_PD_SetWakeupTriggerEdge(BSP_KEY_PWC_PD_WKUP_TRIG_WKUP, PWC_PD_WKUP_TRIG_FALLING);
196     PWC_PD_WakeupCmd(BSP_KEY_PWC_PD_WKUP_WKUP, ENABLE);
197 
198     PWC_PD_ClearWakeupStatus(PWC_PD_WKUP_FLAG_ALL);
199 }
_wkup_cfg_sleep_shutdown(void)200 static void _wkup_cfg_sleep_shutdown(void)
201 {
202     PWC_PD_SetWakeupTriggerEdge(BSP_KEY_PWC_PD_WKUP_TRIG_WKUP, PWC_PD_WKUP_TRIG_FALLING);
203     PWC_PD_WakeupCmd(BSP_KEY_PWC_PD_WKUP_WKUP, ENABLE);
204 }
205 
_sleep_enter_event_idle(void)206 static void _sleep_enter_event_idle(void)
207 {
208     rt_kprintf("sleep: idle\n");
209 }
210 
_sleep_enter_event_deep(void)211 static void _sleep_enter_event_deep(void)
212 {
213     _wkup_cfg_sleep_deep();
214     rt_kprintf("sleep: deep\n");
215     DDL_DelayMS(50);
216 }
217 
_sleep_enter_event_standby(void)218 static void _sleep_enter_event_standby(void)
219 {
220     _wkup_cfg_sleep_standby();
221 #if defined (HC32F4A0) || defined (HC32F4A8)
222     PWC_BKR_Write(0, g_keycnt_cmd & 0xFF);
223 #endif
224     *KEYCNT_BACKUP_ADDR = g_keycnt_cmd;
225     rt_kprintf("sleep: standby\n");
226     DDL_DelayMS(50);
227 }
228 
_sleep_enter_event_shutdown(void)229 static void _sleep_enter_event_shutdown(void)
230 {
231     _wkup_cfg_sleep_shutdown();
232     *KEYCNT_BACKUP_ADDR = g_keycnt_cmd;
233     rt_kprintf("sleep: shutdown\n");
234     DDL_DelayMS(50);
235 }
236 
_sleep_exit_event_idle(void)237 static void _sleep_exit_event_idle(void)
238 {
239     rt_pm_release(PM_SLEEP_MODE_IDLE);
240     rt_pm_request(PM_SLEEP_MODE_NONE);
241     rt_kprintf("wakeup from sleep: idle\n");
242 }
243 
_sleep_exit_event_deep(void)244 static void _sleep_exit_event_deep(void)
245 {
246 #if defined (HC32F460)
247     PWC_STOP_ClockRecover();
248 #endif
249     rt_pm_release(PM_SLEEP_MODE_DEEP);
250     rt_pm_request(PM_SLEEP_MODE_NONE);
251     rt_kprintf("wakeup from sleep: deep\n");
252 }
253 
254 typedef void (*notify)(void);
255 static notify sleep_enter_func[PM_SLEEP_MODE_MAX] =
256 {
257     RT_NULL,
258     _sleep_enter_event_idle,
259     RT_NULL,
260     _sleep_enter_event_deep,
261     _sleep_enter_event_standby,
262     _sleep_enter_event_shutdown,
263 };
264 
265 static notify sleep_exit_func[PM_SLEEP_MODE_MAX] =
266 {
267     RT_NULL,
268     _sleep_exit_event_idle,
269     RT_NULL,
270     _sleep_exit_event_deep,
271     RT_NULL,
272     RT_NULL,
273 };
274 
_notify_func(uint8_t event,uint8_t mode,void * data)275 static void  _notify_func(uint8_t event, uint8_t mode, void *data)
276 {
277     if (event == RT_PM_ENTER_SLEEP)
278     {
279         SysTick_Suspend();
280         if (sleep_enter_func[mode] == RT_NULL)
281         {
282             return;
283         }
284         GPIO_ResetPins(LED_GREEN_PORT, LED_GREEN_PIN);
285         sleep_enter_func[mode]();
286     }
287     else
288     {
289         SysTick_Resume();
290         if (sleep_exit_func[mode] != RT_NULL)
291         {
292             sleep_exit_func[mode]();
293         }
294         g_keycnt_cmd++;
295         g_wkup_flag = RT_TRUE;
296         pm_dbg("g_keycnt_cmd =%d, ", g_keycnt_cmd);
297 
298         NVIC_EnableIRQ(BSP_KEY_IRQn);
299     }
300 }
301 
pm_cmd_handler(void * parameter)302 static void pm_cmd_handler(void *parameter)
303 {
304     rt_uint8_t sleep_mode = PM_SLEEP_MODE_NONE;
305 
306     while (1)
307     {
308         if ((KEYCNT_CMD_SLEEP_IDLE == g_keycnt_cmd) || (KEYCNT_CMD_SLEEP_DEEP == g_keycnt_cmd) || \
309                 (KEYCNT_CMD_SLEEP_STANDBY == g_keycnt_cmd) || (KEYCNT_CMD_SLEEP_SHUTDOWN == g_keycnt_cmd))
310         {
311             switch (g_keycnt_cmd)
312             {
313             case KEYCNT_CMD_SLEEP_IDLE:
314                 sleep_mode = PM_SLEEP_MODE_IDLE;
315                 break;
316             case KEYCNT_CMD_SLEEP_DEEP:
317                 sleep_mode = PM_SLEEP_MODE_DEEP;
318                 break;
319             case KEYCNT_CMD_SLEEP_STANDBY:
320                 sleep_mode = PM_SLEEP_MODE_STANDBY;
321                 break;
322             case KEYCNT_CMD_SLEEP_SHUTDOWN:
323                 sleep_mode = PM_SLEEP_MODE_SHUTDOWN;
324                 break;
325             default:
326                 break;
327             }
328             rt_pm_request(sleep_mode);
329             rt_pm_release(PM_SLEEP_MODE_NONE);
330             rt_thread_mdelay(500);
331         }
332         else
333         {
334             rt_thread_mdelay(50);
335         }
336     }
337 }
338 
339 #if defined(HC32F4A0) || defined(HC32F460) || defined(HC32F448) || defined(HC32F4A8)
pm_run_main(void * parameter)340 static void pm_run_main(void *parameter)
341 {
342     static rt_uint8_t run_index = 0;
343     char *speed[] = {"low", "high"};
344     const rt_uint8_t run_mode[] = {PM_RUN_MODE_LOW_SPEED,  PM_RUN_MODE_HIGH_SPEED};
345 
346     GPIO_SetFunc(MCO_PORT, MCO_PIN, MCO_GPIO_FUNC);
347     /* Configure clock output system clock */
348     CLK_MCOConfig(CLK_MCO1, CLK_MCO_SRC_HCLK, CLK_MCO_DIV8);
349     /* MCO1 output enable */
350     CLK_MCOCmd(CLK_MCO1, ENABLE);
351 
352     while (1)
353     {
354         rt_pm_run_enter(run_mode[run_index]);
355 
356         rt_thread_mdelay(100);
357 
358         rt_kprintf("system clock switch to %s speed\n\n", speed[run_index]);
359         if (++run_index >= ARRAY_SZ(run_mode))
360         {
361             run_index = 0;
362         }
363 
364         rt_thread_mdelay(3000);
365     }
366 }
367 #endif
368 
_keycnt_cmd_init_after_power_on(void)369 static void _keycnt_cmd_init_after_power_on(void)
370 {
371     en_flag_status_t wkup_from_ptwk = PWC_PD_GetWakeupStatus(PWC_PD_WKUP_FLAG_WKUP0);
372 #if defined (HC32F4A0) || defined (HC32F4A8)
373     en_flag_status_t bakram_pd = PWC_BKR_GetStatus(PWC_BACKUP_RAM_FLAG_RAMPDF);
374     uint8_t bkr0 = PWC_BKR_Read(0);
375 
376     if (bakram_pd == RT_TRUE)
377     {
378         g_keycnt_cmd = KEYCNT_CMD_SLEEP_NONE;
379     }
380     else
381 #endif
382     {
383         g_keycnt_cmd = *KEYCNT_BACKUP_ADDR;
384         if (g_keycnt_cmd == KEYCNT_CMD_SLEEP_STANDBY)
385         {
386             if (wkup_from_ptwk)
387             {
388                 g_keycnt_cmd++;
389                 pm_dbg("g_keycnt_cmd =%d, ", g_keycnt_cmd);
390                 rt_kprintf("wakeup from sleep: standby\n\n");
391             }
392             else
393             {
394                 g_keycnt_cmd = KEYCNT_CMD_SLEEP_NONE;
395             }
396         }
397         else if (g_keycnt_cmd >= KEYCNT_CMD_SLEEP_SHUTDOWN)
398         {
399             if ((g_keycnt_cmd == KEYCNT_CMD_SLEEP_SHUTDOWN) && wkup_from_ptwk)
400             {
401                 pm_dbg("g_keycnt_cmd =%d \n", KEYCNT_CMD_SLEEP_NONE);
402                 rt_kprintf("wakeup from sleep: shutdown\n\n");
403             }
404             g_keycnt_cmd = KEYCNT_CMD_SLEEP_NONE;
405         }
406     }
407 
408     pm_dbg("KEYCNT_BACKUP_ADDR addr =0x%p,value = %d\n", KEYCNT_BACKUP_ADDR, *KEYCNT_BACKUP_ADDR);
409     pm_dbg("wkup_from_ptwk = %d\n", wkup_from_ptwk);
410 #if defined (HC32F4A0) || defined (HC32F4A8)
411     pm_dbg("bakram_pd = %d\n", bakram_pd);
412     pm_dbg("bkr0 = %d\n", bkr0);
413 #endif
414 }
415 
_vbat_init(void)416 static void _vbat_init(void)
417 {
418 #if defined (HC32F4A0) || defined (HC32F4A8)
419     while (PWC_BKR_GetStatus(PWC_BACKUP_RAM_FLAG_RAMVALID) == RESET)
420     {
421         rt_thread_delay(10);
422     }
423     FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_SRAMB, ENABLE);
424 #elif defined (HC32F448)
425     FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_SRAMB, ENABLE);
426 #elif defined (HC32F460)
427     FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_SRAMRET, ENABLE);
428 #elif defined (HC32F472)
429     FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_SRAMRET, ENABLE);
430 #endif
431     pm_dbg("vbat init success\n");
432 }
433 
pm_sample_init(void)434 int pm_sample_init(void)
435 {
436     pm_dbg("pm_sample_init\n\n");
437 
438     _keycnt_cmd_init_after_power_on();
439     _vbat_init();
440     _key_int_init();
441 
442     rt_pm_notify_set(_notify_func, NULL);
443 
444     rt_thread_t  thread = rt_thread_create("pm_cmd_handler", pm_cmd_handler, RT_NULL, 1024, 25, 10);
445     if (thread != RT_NULL)
446     {
447         rt_thread_startup(thread);
448     }
449     else
450     {
451         rt_kprintf("create pm sample thread failed!\n");
452     }
453 
454 #if defined(HC32F4A0) || defined(HC32F460) || defined(HC32F448) || defined(HC32F4A8)
455     thread = rt_thread_create("pm_run_main", pm_run_main, RT_NULL, 1024, 25, 10);
456     if (thread != RT_NULL)
457     {
458         rt_thread_startup(thread);
459     }
460     else
461     {
462         rt_kprintf("create pm run thread failed!\n");
463     }
464 #endif
465 
466     return RT_EOK;
467 }
468 MSH_CMD_EXPORT(pm_sample_init, pm sample init);
469 
470 #endif /* end of BSP_USING_PM */
471