diff --git a/pycardium/mphalport.c b/pycardium/mphalport.c
index 49fbf9232d5c93527cdfe21f7948b7e7f9fa8419..c4ba6bdb1874a890350ccc8b7df57f6167d55f83 100644
--- a/pycardium/mphalport.c
+++ b/pycardium/mphalport.c
@@ -20,6 +20,30 @@
 #include <stdio.h>
 #include <string.h>
 
+// Smallest interval which can be reached exactly
+#define SYSTICK_INTERVAL_US 15625ULL
+#define SYSTICK_FREQ_HZ (1000000 / SYSTICK_INTERVAL_US)
+
+/*
+ * Copied from core_cm4.h and modified to select the
+ * 32768 Hz RTC crystal as the clock source */
+static uint32_t systick_config(uint32_t ticks)
+{
+	if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)
+		return (1); /* Reload value impossible */
+
+	SysTick->LOAD = ticks - 1; /* set reload register */
+	NVIC_SetPriority(
+		SysTick_IRQn,
+		(1 << __NVIC_PRIO_BITS) -
+			1); /* set Priority for Systick Interrupt */
+	SysTick->VAL = 0;   /* Load the SysTick Counter Value */
+	SysTick->CTRL =
+		SysTick_CTRL_TICKINT_Msk |
+		SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
+	return (0);                      /* Function successful */
+}
+
 /* Initialize everything for MicroPython */
 void pycardium_hal_init(void)
 {
@@ -33,9 +57,9 @@ void pycardium_hal_init(void)
 	epic_interrupt_enable(EPIC_INT_UART_RX);
 
 	/*
-	 * Configure SysTick timer for 1ms period.
+	 * Configure SysTick timer for SYSTICK_INTERVAL_US period.
 	 */
-	SysTick_Config(SystemCoreClock / 1000);
+	systick_config(32768 / SYSTICK_FREQ_HZ);
 }
 
 /******************************************************************************
@@ -141,14 +165,18 @@ void SysTick_Handler(void)
  */
 static uint64_t systick_get_us()
 {
-	uint32_t irqsaved = __get_PRIMASK();
-	__set_PRIMASK(0);
+	uint32_t val, count;
 
-	uint64_t counts_per_us = SystemCoreClock / 1000000;
-	uint64_t us            = systick_count * 1000 +
-		      (SysTick->LOAD - SysTick->VAL) / counts_per_us;
+	/* The asynchronous/slow clocking of the systick means that
+	 * its value can jump to 0 before the interrupt is triggered.
+	 * Simply wait until it is not 0 and then read the count. */
+	do {
+		val   = SysTick->VAL;
+		count = systick_count;
+	} while (val == 0);
 
-	__set_PRIMASK(irqsaved);
+	uint64_t us = count * SYSTICK_INTERVAL_US +
+		      (SysTick->LOAD - val) * 1000000ULL / 32768;
 
 	return us;
 }
@@ -160,14 +188,12 @@ static void systick_delay_precise(uint32_t us)
 	 * instruction, read the current timer value to ensure as little skew as
 	 * possible.
 	 *
-	 * Subtract 0.3us (constant_offset) to account for the duration of the
-	 * calculations.
+	 * Accuracy is about 30 us (due to the 32 kHz systick)
 	 */
 	uint32_t count_to_overflow = SysTick->VAL;
 	uint32_t count_reload      = SysTick->LOAD;
-	uint32_t clocks_per_us     = SystemCoreClock / 1000000;
-	uint32_t constant_offset   = clocks_per_us * 3 / 10;
-	uint32_t delay_count       = us * clocks_per_us - constant_offset;
+
+	uint32_t delay_count = us * 32768 / 1000000;
 
 	/*
 	 * Calculate the final count for both paths.  Marked as volatile so the
@@ -203,14 +229,14 @@ static void systick_delay_sleep(uint32_t us)
 	while (1) {
 		uint64_t now = systick_get_us();
 
-		if (now >= final_time) {
+		if ((now + SYSTICK_INTERVAL_US) > final_time) {
 			break;
 		}
 
 		/*
-		 * Sleep with WFI if more than 1ms of delay is remaining.  The
-		 * SysTick interrupt is guaranteed to happen within any timespan
-		 * of 1ms.
+		 * Sleep with WFI if more than SYSTICK_INTERVAL_US of delay
+		 * is remaining.  The SysTick interrupt is guaranteed to
+		 * happen within any timespan of SYSTICK_INTERVAL_US.
 		 *
 		 * Use a critical section encompassing both the check and the
 		 * WFI to prevent a race-condition where the interrupt happens
@@ -218,7 +244,7 @@ static void systick_delay_sleep(uint32_t us)
 		 */
 		uint32_t irqsaved = __get_PRIMASK();
 		__set_PRIMASK(0);
-		if ((now + 1000) < final_time) {
+		if ((now + SYSTICK_INTERVAL_US) < final_time) {
 			__WFI();
 		}
 		__set_PRIMASK(irqsaved);
@@ -234,6 +260,11 @@ static void systick_delay_sleep(uint32_t us)
 		 */
 		mp_handle_pending(true);
 	}
+
+	uint64_t now = systick_get_us();
+	if (now < final_time) {
+		systick_delay_precise(final_time - now);
+	}
 }
 
 static void systick_delay(uint32_t us)
@@ -243,12 +274,12 @@ static void systick_delay(uint32_t us)
 
 	/*
 	 * For very short delays, use the systick_delay_precise() function which
-	 * delays with a microsecond accuracy.  For anything >1ms, use
+	 * delays with a microsecond accuracy.  For anything >SYSTICK_INTERVAL_US, use
 	 * systick_delay_sleep() which puts the CPU to sleep when nothing is
 	 * happening and also checks for MicroPython interrupts every now and
 	 * then.
 	 */
-	if (us < 1000) {
+	if (us < SYSTICK_INTERVAL_US) {
 		systick_delay_precise(us);
 	} else {
 		systick_delay_sleep(us);