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