diff --git a/lib/utils/mpirq.h b/lib/utils/mpirq.h
index 186c9e1b0b92..dd423c010113 100644
--- a/lib/utils/mpirq.h
+++ b/lib/utils/mpirq.h
@@ -26,7 +26,7 @@
 #ifndef MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H
 #define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H
 
-#include "py/obj.h"
+#include "py/runtime.h"
 
 /******************************************************************************
  DEFINE CONSTANTS
diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c
index 631f46e81f6e..2862f4c0e7b1 100644
--- a/ports/stm32/machine_uart.c
+++ b/ports/stm32/machine_uart.c
@@ -73,76 +73,6 @@
 ///
 ///     uart.any()               # returns True if any characters waiting
 
-typedef struct _pyb_uart_irq_map_t {
-    uint16_t irq_en;
-    uint16_t flag;
-} pyb_uart_irq_map_t;
-
-STATIC const pyb_uart_irq_map_t mp_irq_map[] = {
-    { USART_CR1_IDLEIE, UART_FLAG_IDLE}, // RX idle
-    { USART_CR1_PEIE,   UART_FLAG_PE},   // parity error
-    { USART_CR1_TXEIE,  UART_FLAG_TXE},  // TX register empty
-    { USART_CR1_TCIE,   UART_FLAG_TC},   // TX complete
-    { USART_CR1_RXNEIE, UART_FLAG_RXNE}, // RX register not empty
-    #if 0
-    // For now only IRQs selected by CR1 are supported
-    #if defined(STM32F4)
-    { USART_CR2_LBDIE,  UART_FLAG_LBD},  // LIN break detection
-    #else
-    { USART_CR2_LBDIE,  UART_FLAG_LBDF}, // LIN break detection
-    #endif
-    { USART_CR3_CTSIE,  UART_FLAG_CTS},  // CTS
-    #endif
-};
-
-// OR-ed IRQ flags which should not be touched by the user
-STATIC const uint32_t mp_irq_reserved = UART_FLAG_RXNE;
-
-// OR-ed IRQ flags which are allowed to be used by the user
-STATIC const uint32_t mp_irq_allowed = UART_FLAG_IDLE;
-
-STATIC mp_obj_t pyb_uart_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
-
-STATIC void pyb_uart_irq_config(pyb_uart_obj_t *self, bool enable) {
-    if (self->mp_irq_trigger) {
-        for (size_t entry = 0; entry < MP_ARRAY_SIZE(mp_irq_map); ++entry) {
-            if (mp_irq_map[entry].flag & mp_irq_reserved) {
-                continue;
-            }
-            if (mp_irq_map[entry].flag & self->mp_irq_trigger) {
-                if (enable) {
-                    self->uartx->CR1 |= mp_irq_map[entry].irq_en;
-                } else {
-                    self->uartx->CR1 &= ~mp_irq_map[entry].irq_en;
-                }
-            }
-        }
-    }
-}
-
-STATIC mp_uint_t pyb_uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
-    pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
-    pyb_uart_irq_config(self, false);
-    self->mp_irq_trigger = new_trigger;
-    pyb_uart_irq_config(self, true);
-    return 0;
-}
-
-STATIC mp_uint_t pyb_uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
-    pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
-    if (info_type == MP_IRQ_INFO_FLAGS) {
-        return self->mp_irq_flags;
-    } else if (info_type == MP_IRQ_INFO_TRIGGERS) {
-        return self->mp_irq_trigger;
-    }
-    return 0;
-}
-
-STATIC const mp_irq_methods_t pyb_uart_irq_methods = {
-    .trigger = pyb_uart_irq_trigger,
-    .info = pyb_uart_irq_info,
-};
-
 STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
     pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
     if (!self->is_enabled) {
@@ -518,7 +448,7 @@ STATIC mp_obj_t pyb_uart_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
 
     if (self->mp_irq_obj == NULL) {
         self->mp_irq_trigger = 0;
-        self->mp_irq_obj = mp_irq_new(&pyb_uart_irq_methods, MP_OBJ_FROM_PTR(self));
+        self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self));
     }
 
     if (n_args > 1 || kw_args->used != 0) {
@@ -530,17 +460,17 @@ STATIC mp_obj_t pyb_uart_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
 
         // Check the trigger
         mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int;
-        mp_uint_t not_supported = trigger & ~mp_irq_allowed;
+        mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS;
         if (trigger != 0 && not_supported) {
             mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%08x unsupported"), not_supported);
         }
 
         // Reconfigure user IRQs
-        pyb_uart_irq_config(self, false);
+        uart_irq_config(self, false);
         self->mp_irq_obj->handler = handler;
         self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool;
         self->mp_irq_trigger = trigger;
-        pyb_uart_irq_config(self, true);
+        uart_irq_config(self, true);
     }
 
     return MP_OBJ_FROM_PTR(self->mp_irq_obj);
diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c
index 8693813fa441..b14a18c6ddfa 100644
--- a/ports/stm32/uart.c
+++ b/ports/stm32/uart.c
@@ -93,6 +93,28 @@
 
 extern void NORETURN __fatal_error(const char *msg);
 
+typedef struct _pyb_uart_irq_map_t {
+    uint16_t irq_en;
+    uint16_t flag;
+} pyb_uart_irq_map_t;
+
+STATIC const pyb_uart_irq_map_t mp_uart_irq_map[] = {
+    { USART_CR1_IDLEIE, UART_FLAG_IDLE}, // RX idle
+    { USART_CR1_PEIE,   UART_FLAG_PE},   // parity error
+    { USART_CR1_TXEIE,  UART_FLAG_TXE},  // TX register empty
+    { USART_CR1_TCIE,   UART_FLAG_TC},   // TX complete
+    { USART_CR1_RXNEIE, UART_FLAG_RXNE}, // RX register not empty
+    #if 0
+    // For now only IRQs selected by CR1 are supported
+    #if defined(STM32F4)
+    { USART_CR2_LBDIE,  UART_FLAG_LBD},  // LIN break detection
+    #else
+    { USART_CR2_LBDIE,  UART_FLAG_LBDF}, // LIN break detection
+    #endif
+    { USART_CR3_CTSIE,  UART_FLAG_CTS},  // CTS
+    #endif
+};
+
 void uart_init0(void) {
     #if defined(STM32H7)
     RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit = {0};
@@ -444,6 +466,23 @@ bool uart_init(pyb_uart_obj_t *uart_obj,
     return true;
 }
 
+void uart_irq_config(pyb_uart_obj_t *self, bool enable) {
+    if (self->mp_irq_trigger) {
+        for (size_t entry = 0; entry < MP_ARRAY_SIZE(mp_uart_irq_map); ++entry) {
+            if (mp_uart_irq_map[entry].flag & MP_UART_RESERVED_FLAGS) {
+                continue;
+            }
+            if (mp_uart_irq_map[entry].flag & self->mp_irq_trigger) {
+                if (enable) {
+                    self->uartx->CR1 |= mp_uart_irq_map[entry].irq_en;
+                } else {
+                    self->uartx->CR1 &= ~mp_uart_irq_map[entry].irq_en;
+                }
+            }
+        }
+    }
+}
+
 void uart_set_rxbuf(pyb_uart_obj_t *self, size_t len, void *buf) {
     self->read_buf_head = 0;
     self->read_buf_tail = 0;
@@ -864,3 +903,26 @@ void uart_irq_handler(mp_uint_t uart_id) {
         mp_irq_handler(self->mp_irq_obj);
     }
 }
+
+STATIC mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
+    pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    uart_irq_config(self, false);
+    self->mp_irq_trigger = new_trigger;
+    uart_irq_config(self, true);
+    return 0;
+}
+
+STATIC mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
+    pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    if (info_type == MP_IRQ_INFO_FLAGS) {
+        return self->mp_irq_flags;
+    } else if (info_type == MP_IRQ_INFO_TRIGGERS) {
+        return self->mp_irq_trigger;
+    }
+    return 0;
+}
+
+const mp_irq_methods_t uart_irq_methods = {
+    .trigger = uart_irq_trigger,
+    .info = uart_irq_info,
+};
diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h
index 62676fb91be5..9a38db593d4f 100644
--- a/ports/stm32/uart.h
+++ b/ports/stm32/uart.h
@@ -26,7 +26,7 @@
 #ifndef MICROPY_INCLUDED_STM32_UART_H
 #define MICROPY_INCLUDED_STM32_UART_H
 
-struct _mp_irq_obj_t;
+#include "lib/utils/mpirq.h"
 
 typedef enum {
     PYB_UART_NONE = 0,
@@ -45,6 +45,12 @@ typedef enum {
 #define CHAR_WIDTH_8BIT (0)
 #define CHAR_WIDTH_9BIT (1)
 
+// OR-ed IRQ flags which are allowed to be used by the user
+#define MP_UART_ALLOWED_FLAGS UART_FLAG_IDLE
+
+// OR-ed IRQ flags which should not be touched by the user
+#define MP_UART_RESERVED_FLAGS UART_FLAG_RXNE
+
 typedef struct _pyb_uart_obj_t {
     mp_obj_base_t base;
     USART_TypeDef *uartx;
@@ -62,16 +68,18 @@ typedef struct _pyb_uart_obj_t {
     byte *read_buf;                     // byte or uint16_t, depending on char size
     uint16_t mp_irq_trigger;            // user IRQ trigger mask
     uint16_t mp_irq_flags;              // user IRQ active IRQ flags
-    struct _mp_irq_obj_t *mp_irq_obj;   // user IRQ object
+    mp_irq_obj_t *mp_irq_obj;           // user IRQ object
 } pyb_uart_obj_t;
 
 extern const mp_obj_type_t pyb_uart_type;
+extern const mp_irq_methods_t uart_irq_methods;
 
 void uart_init0(void);
 void uart_deinit_all(void);
 bool uart_exists(int uart_id);
 bool uart_init(pyb_uart_obj_t *uart_obj,
     uint32_t baudrate, uint32_t bits, uint32_t parity, uint32_t stop, uint32_t flow);
+void uart_irq_config(pyb_uart_obj_t *self, bool enable);
 void uart_set_rxbuf(pyb_uart_obj_t *self, size_t len, void *buf);
 void uart_deinit(pyb_uart_obj_t *uart_obj);
 void uart_irq_handler(mp_uint_t uart_id);