Skip to content

Commit 1d131d9

Browse files
committed
alif: implement wake up sources for machine.lightsleep().
Signed-off-by: Damien George <damien@micropython.org>
1 parent 299930a commit 1d131d9

10 files changed

Lines changed: 186 additions & 37 deletions

File tree

ports/alif/alif.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ SRC_C = \
113113
alif_flash.c \
114114
cyw43_port_spi.c \
115115
fatfs_port.c \
116+
lptimer_ext.c \
116117
machine_pin.c \
117118
machine_i2c.c \
118119
machine_spi.c \

ports/alif/boards/OPENMV_AE3/board.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ const ospi_flash_settings_t ospi_flash_settings[] = {
8888
};
8989
const size_t ospi_flash_settings_len = 3;
9090

91+
static void board_config_user_button_irq(void) {
92+
#if CORE_M55_HP
93+
// Configure the user button to wake on rising edge (button release).
94+
mp_hal_pin_config_irq_rising(pin_SW, true);
95+
NVIC_ClearPendingIRQ(LPGPIO_IRQ7_IRQn);
96+
NVIC_SetPriority(LPGPIO_IRQ7_IRQn, IRQ_PRI_GPIO);
97+
NVIC_EnableIRQ(LPGPIO_IRQ7_IRQn);
98+
#endif
99+
}
100+
91101
void board_startup(void) {
92102
// Switch the USB multiplexer to use the Alif USB port.
93103
mp_hal_pin_output(pin_USB_D_SEL);
@@ -182,6 +192,9 @@ void board_early_init(void) {
182192
}
183193

184194
MP_WEAK void board_enter_stop(void) {
195+
// Allow the user button to wake the device.
196+
board_config_user_button_irq();
197+
185198
// Let USB_D_SEL float, so it doesn't source 30uA through the 110k resistors to GND.
186199
mp_hal_pin_input(pin_USB_D_SEL);
187200

@@ -209,7 +222,8 @@ MP_WEAK void board_enter_stop(void) {
209222
}
210223

211224
MP_WEAK void board_enter_standby(void) {
212-
225+
// Allow the user button to wake the device.
226+
board_config_user_button_irq();
213227
}
214228

215229
MP_WEAK void board_exit_standby(void) {

ports/alif/lptimer_ext.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2026 OpenMV LLC.
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "irq.h"
28+
#include "lptimer.h"
29+
#include "lptimer_ext.h"
30+
#include "sys_ctrl_lptimer.h"
31+
32+
#define LPTIMER ((LPTIMER_Type *)LPTIMER_BASE)
33+
#define LPTIMER_CH_A (0)
34+
35+
void LPTIMER0_IRQHandler(void) {
36+
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_A);
37+
}
38+
39+
void lptimer_init(void) {
40+
// LPTIMER is not reset on CPU reset and may still be active, so reset it now.
41+
lptimer_cancel_wakeup();
42+
}
43+
44+
void lptimer_set_wakeup(uint64_t timeout_us) {
45+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_A);
46+
47+
ANA_REG->MISC_CTRL |= 1U << 0; // SEL_32K, select LXFO
48+
49+
select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_A);
50+
51+
// Maximum 131 second timeout, to not overflow 32-bit ticks when
52+
// LPTIMER is clocked at 32768Hz.
53+
if (timeout_us > 131000000ULL) {
54+
timeout_us = 131000000ULL;
55+
}
56+
uint32_t timeout_ticks = timeout_us * 32768ULL / 1000000ULL;
57+
58+
// Set up the LPTIMER interrupt to fire after the given timeout.
59+
lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_A);
60+
lptimer_load_count(LPTIMER, LPTIMER_CH_A, &timeout_ticks);
61+
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_A);
62+
lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_A);
63+
64+
NVIC_SetPriority(LPTIMER0_IRQ_IRQn, IRQ_PRI_RTC);
65+
NVIC_ClearPendingIRQ(LPTIMER0_IRQ_IRQn);
66+
NVIC_EnableIRQ(LPTIMER0_IRQ_IRQn);
67+
68+
lptimer_enable_counter(LPTIMER, LPTIMER_CH_A);
69+
}
70+
71+
void lptimer_cancel_wakeup(void) {
72+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_A);
73+
lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_A);
74+
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_A);
75+
}

ports/alif/lptimer_ext.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2026 OpenMV LLC.
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
#ifndef MICROPY_INCLUDED_ALIF_LPTIMER_EXT_H
27+
#define MICROPY_INCLUDED_ALIF_LPTIMER_EXT_H
28+
29+
#include <stdint.h>
30+
31+
void lptimer_init(void);
32+
33+
// Configure the LPTIMER to wake the CPU via an IRQ after the given timeout.
34+
void lptimer_set_wakeup(uint64_t timeout_us);
35+
36+
// Cancel any pending wakeup started by lptimer_set_wakeup().
37+
void lptimer_cancel_wakeup(void);
38+
39+
#endif // MICROPY_INCLUDED_ALIF_LPTIMER_EXT_H

ports/alif/machine_pin.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,12 @@ typedef struct _machine_pin_irq_obj_t {
5252
// Defines a single GPIO IRQ handler
5353
#define DEFINE_GPIO_IRQ_HANDLER(pname, port, pin) \
5454
void pname##_IRQ##pin##Handler(void) { \
55+
gpio_interrupt_eoi((GPIO_Type *)pname##_BASE, pin); \
5556
machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(port, pin); \
56-
machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent); \
57-
gpio_interrupt_eoi(self->gpio, pin); \
58-
irq->flags = irq->trigger; \
59-
mp_irq_handler(&irq->base); \
57+
if (irq != NULL) { \
58+
irq->flags = irq->trigger; \
59+
mp_irq_handler(&irq->base); \
60+
} \
6061
}
6162

6263
// Defines all 8 pin IRQ handlers for a port

ports/alif/machine_rtc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ void machine_rtc_set_wakeup(uint32_t seconds) {
120120
MICROPY_END_ATOMIC_SECTION(atomic_state);
121121
}
122122

123+
void machine_rtc_cancel_wakeup(void) {
124+
lprtc_interrupt_disable(machine_rtc.rtc);
125+
}
126+
123127
static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) {
124128
if (n_args == 1) {
125129
// Get datetime.

ports/alif/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "shared/runtime/softtimer.h"
3939
#include "shared/tinyusb/mp_usbd.h"
4040
#include "tusb.h"
41+
#include "lptimer_ext.h"
4142
#include "modmachine.h"
4243
#include "mpbthciport.h"
4344
#include "mpuart.h"
@@ -79,6 +80,7 @@ int main(void) {
7980

8081
MICROPY_BOARD_EARLY_INIT();
8182

83+
lptimer_init();
8284
machine_rtc_init();
8385

8486
#if MICROPY_HW_ENABLE_UART_REPL

ports/alif/modmachine.c

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
// This file is never compiled standalone, it's included directly from
2828
// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE.
2929

30+
#include "lptimer_ext.h"
3031
#include "modmachine.h"
3132
#include "se_services.h"
3233
#include "tusb.h"
@@ -91,7 +92,31 @@ static void mp_machine_enable_usb(bool enable) {
9192
}
9293
#endif
9394

95+
static void mp_machine_config_wakeup(mp_int_t sleep_ms, bool enable) {
96+
if (sleep_ms >= 0) {
97+
// RTC has a resolution of 1 second so use LPTIMER for small sleep duration.
98+
if (sleep_ms < 10000) {
99+
if (enable) {
100+
lptimer_set_wakeup(sleep_ms * 1000);
101+
} else {
102+
lptimer_cancel_wakeup();
103+
}
104+
} else {
105+
if (enable) {
106+
machine_rtc_set_wakeup(sleep_ms / 1000);
107+
} else {
108+
machine_rtc_cancel_wakeup();
109+
}
110+
}
111+
}
112+
}
113+
94114
static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
115+
mp_int_t sleep_ms = -1;
116+
if (n_args != 0) {
117+
sleep_ms = mp_obj_get_int(args[0]);
118+
}
119+
95120
#if MICROPY_HW_ENABLE_USBDEV
96121
mp_machine_enable_usb(false);
97122
#endif
@@ -102,10 +127,14 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
102127

103128
__disable_irq();
104129

130+
mp_machine_config_wakeup(sleep_ms, true);
131+
105132
// This enters the deepest possible CPU sleep state, without
106133
// losing CPU state. CPU and subsystem power will remain on.
107134
pm_core_enter_deep_sleep();
108135

136+
mp_machine_config_wakeup(sleep_ms, false);
137+
109138
__enable_irq();
110139

111140
#ifdef MICROPY_BOARD_EXIT_STANDBY
@@ -117,31 +146,6 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
117146
#endif
118147
}
119148

120-
#include "lptimer.h"
121-
#include "sys_ctrl_lptimer.h"
122-
123-
#define LPTIMER ((LPTIMER_Type *)LPTIMER_BASE)
124-
#define LPTIMER_CH_A (0)
125-
126-
static void lptimer_set_wakeup(uint64_t timeout_us) {
127-
lptimer_disable_counter(LPTIMER, LPTIMER_CH_A);
128-
129-
ANA_REG->MISC_CTRL |= 1 << 0; // SEL_32K, select LXFO
130-
131-
select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_A);
132-
133-
// Maximum 131 second timeout, to not overflow 32-bit ticks when
134-
// LPTIMER is clocked at 32768Hz.
135-
uint32_t timeout_ticks = (uint64_t)MIN(timeout_us, 131000000) * 32768 / 1000000;
136-
137-
// Set up the LPTIMER interrupt to fire after the given timeout.
138-
lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_A);
139-
lptimer_load_count(LPTIMER, LPTIMER_CH_A, &timeout_ticks);
140-
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_A);
141-
lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_A);
142-
lptimer_enable_counter(LPTIMER, LPTIMER_CH_A);
143-
}
144-
145149
MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
146150
mp_int_t sleep_ms = -1;
147151
if (n_args != 0) {
@@ -158,13 +162,7 @@ MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args
158162

159163
__disable_irq();
160164

161-
if (sleep_ms >= 0) {
162-
if (sleep_ms < 10000) {
163-
lptimer_set_wakeup(sleep_ms * 1000);
164-
} else {
165-
machine_rtc_set_wakeup(sleep_ms / 1000);
166-
}
167-
}
165+
mp_machine_config_wakeup(sleep_ms, true);
168166

169167
// If power is removed from the subsystem, the function does
170168
// not return, and the CPU will reboot when/if the subsystem

ports/alif/modmachine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@
2828

2929
void machine_rtc_init(void);
3030
void machine_rtc_set_wakeup(uint32_t seconds);
31+
void machine_rtc_cancel_wakeup(void);
3132

3233
#endif // MICROPY_INCLUDED_ALIF_MODMACHINE_H

ports/alif/mphalport.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,25 @@ static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) {
312312
gpio_set_direction_output(pin->gpio, pin->pin);
313313
}
314314

315+
static inline void mp_hal_pin_config_irq_rising(mp_hal_pin_obj_t pin, bool enable) {
316+
if (enable) {
317+
gpio_enable_interrupt(pin->gpio, pin->pin);
318+
gpio_unmask_interrupt(pin->gpio, pin->pin);
319+
gpio_interrupt_set_edge_trigger(pin->gpio, pin->pin);
320+
gpio_interrupt_set_polarity_high(pin->gpio, pin->pin);
321+
gpio_interrupt_eoi(pin->gpio, pin->pin);
322+
} else {
323+
gpio_disable_interrupt(pin->gpio, pin->pin);
324+
}
325+
}
326+
315327
static inline void mp_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) {
316328
if (enable) {
317329
gpio_enable_interrupt(pin->gpio, pin->pin);
330+
gpio_unmask_interrupt(pin->gpio, pin->pin);
318331
gpio_interrupt_set_edge_trigger(pin->gpio, pin->pin);
319332
gpio_interrupt_set_polarity_low(pin->gpio, pin->pin);
333+
gpio_interrupt_eoi(pin->gpio, pin->pin);
320334
} else {
321335
gpio_disable_interrupt(pin->gpio, pin->pin);
322336
}

0 commit comments

Comments
 (0)