diff --git a/stmhal/can.c b/stmhal/can.c
index e293b8742577b5b9e5f63cae04d4172c04b9d418..9b8f2a071508429de81e0a3e4492c7d48a92bbb9 100644
--- a/stmhal/can.c
+++ b/stmhal/can.c
@@ -865,6 +865,7 @@ void can_rx_irq_handler(uint can_id, uint fifo_id) {
     }
 
     if (callback != mp_const_none) {
+        mp_sched_lock();
         gc_lock();
         nlr_buf_t nlr;
         if (nlr_push(&nlr) == 0) {
@@ -877,6 +878,7 @@ void can_rx_irq_handler(uint can_id, uint fifo_id) {
             mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
         }
         gc_unlock();
+        mp_sched_unlock();
     }
 }
 
diff --git a/stmhal/extint.c b/stmhal/extint.c
index dacf8dd568d97e0a117717e6b2f3777ec8277adb..59b00cb73cf1c6d37463fc35eee8cad81aaec8f1 100644
--- a/stmhal/extint.c
+++ b/stmhal/extint.c
@@ -28,7 +28,6 @@
 #include <stddef.h>
 #include <string.h>
 
-#include "py/nlr.h"
 #include "py/runtime.h"
 #include "py/gc.h"
 #include "py/mphal.h"
@@ -412,6 +411,7 @@ void Handle_EXTI_Irq(uint32_t line) {
         if (line < EXTI_NUM_VECTORS) {
             mp_obj_t *cb = &MP_STATE_PORT(pyb_extint_callback)[line];
             if (*cb != mp_const_none) {
+                mp_sched_lock();
                 // When executing code within a handler we must lock the GC to prevent
                 // any memory allocations.  We must also catch any exceptions.
                 gc_lock();
@@ -427,6 +427,7 @@ void Handle_EXTI_Irq(uint32_t line) {
                     mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
                 }
                 gc_unlock();
+                mp_sched_unlock();
             }
         }
     }
diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h
index 38a5ac5da9e1f0fc55a514f2ae9c682aa387fb53..339360baefded5455f536334121e0c25824461fd 100644
--- a/stmhal/mpconfigport.h
+++ b/stmhal/mpconfigport.h
@@ -69,6 +69,8 @@
 #define MICROPY_MODULE_WEAK_LINKS   (1)
 #define MICROPY_CAN_OVERRIDE_BUILTINS (1)
 #define MICROPY_USE_INTERNAL_ERRNO  (1)
+#define MICROPY_ENABLE_SCHEDULER    (1)
+#define MICROPY_SCHEDULER_DEPTH     (8)
 #define MICROPY_VFS                 (1)
 #define MICROPY_VFS_FAT             (1)
 
@@ -303,6 +305,8 @@ static inline mp_uint_t disable_irq(void) {
 #if MICROPY_PY_THREAD
 #define MICROPY_EVENT_POLL_HOOK \
     do { \
+        extern void mp_handle_pending(void); \
+        mp_handle_pending(); \
         if (pyb_thread_enabled) { \
             MP_THREAD_GIL_EXIT(); \
             pyb_thread_yield(); \
@@ -312,7 +316,12 @@ static inline mp_uint_t disable_irq(void) {
         } \
     } while (0);
 #else
-#define MICROPY_EVENT_POLL_HOOK            __WFI();
+#define MICROPY_EVENT_POLL_HOOK \
+    do { \
+        extern void mp_handle_pending(void); \
+        mp_handle_pending(); \
+        __WFI(); \
+    } while (0);
 #endif
 
 // There is no classical C heap in bare-metal ports, only Python
diff --git a/stmhal/systick.c b/stmhal/systick.c
index eb11de9b74dd558f3cd31a7d9a85c37964a593e8..71e3d34889610c18a19168e478967ccdc8b4b608 100644
--- a/stmhal/systick.c
+++ b/stmhal/systick.c
@@ -24,6 +24,7 @@
  * THE SOFTWARE.
  */
 
+#include "py/runtime.h"
 #include "py/mphal.h"
 #include "irq.h"
 #include "systick.h"
@@ -58,6 +59,7 @@ void mp_hal_delay_ms(mp_uint_t Delay) {
         // Wraparound of tick is taken care of by 2's complement arithmetic.
         while (uwTick - start < Delay) {
             // Enter sleep mode, waiting for (at least) the SysTick interrupt.
+            mp_handle_pending();
             #if MICROPY_PY_THREAD
             if (pyb_thread_enabled) {
                 pyb_thread_yield();
diff --git a/stmhal/timer.c b/stmhal/timer.c
index 494045c28f702b0dad4a15bf0a88ad009c940eae..7f0a70c5e8ff2c2d0b97e7b9582b8c3e2bb8d815 100644
--- a/stmhal/timer.c
+++ b/stmhal/timer.c
@@ -1363,6 +1363,7 @@ STATIC void timer_handle_irq_channel(pyb_timer_obj_t *tim, uint8_t channel, mp_o
 
             // execute callback if it's set
             if (callback != mp_const_none) {
+                mp_sched_lock();
                 // When executing code within a handler we must lock the GC to prevent
                 // any memory allocations.  We must also catch any exceptions.
                 gc_lock();
@@ -1382,6 +1383,7 @@ STATIC void timer_handle_irq_channel(pyb_timer_obj_t *tim, uint8_t channel, mp_o
                     mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
                 }
                 gc_unlock();
+                mp_sched_unlock();
             }
         }
     }