diff --git a/cpu/stm32f1/periph/timer.c b/cpu/stm32f1/periph/timer.c
index d2ce653bd20c9d830996783dcddb9bf7a25ffc67..0513d0350c6072c4e3a96da33ecbd2f848807600 100644
--- a/cpu/stm32f1/periph/timer.c
+++ b/cpu/stm32f1/periph/timer.c
@@ -220,16 +220,33 @@ int timer_clear(tim_t dev, int channel)
 
 unsigned int timer_read(tim_t dev)
 {
+    unsigned a, b;
     switch (dev) {
 #if TIMER_0_EN
         case TIMER_0:
-            return (((unsigned int)(0xffff & TIMER_0_DEV_0->CNT)) | (TIMER_0_DEV_1->CNT<<16));
-            break;
+            /* do OR'ing two times and only use value if results are equal.
+             * otherwise, the lower 16bit counter could overflow while the
+             * upper counter is read, leading to an incorrect result. */
+            do {
+                a = (((unsigned int)(0xffff & TIMER_0_DEV_0->CNT)) |
+                        (TIMER_0_DEV_1->CNT<<16));
+                b = (((unsigned int)(0xffff & TIMER_0_DEV_0->CNT)) |
+                        (TIMER_0_DEV_1->CNT<<16));
+            } while (a!=b);
+
+            return a;
 #endif
 #if TIMER_1_EN
         case TIMER_1:
-            return (((unsigned int)(0xffff & TIMER_1_DEV_0->CNT)) | (TIMER_1_DEV_1->CNT<<16));
-            break;
+             /* see above about why loop is needed */
+            do {
+                a = (((unsigned int)(0xffff & TIMER_1_DEV_0->CNT)) |
+                        (TIMER_1_DEV_1->CNT<<16));
+                b = (((unsigned int)(0xffff & TIMER_1_DEV_0->CNT)) |
+                        (TIMER_1_DEV_1->CNT<<16));
+            } while (a!=b);
+
+            return a;
 #endif
         case TIMER_UNDEFINED:
         default:
diff --git a/sys/xtimer/xtimer_core.c b/sys/xtimer/xtimer_core.c
index d7a57117fe5fedfdd0481c1fb07e02a158dd7315..0e1ceaf2e1696c369fc59ee6b39577a3d9be276e 100644
--- a/sys/xtimer/xtimer_core.c
+++ b/sys/xtimer/xtimer_core.c
@@ -170,19 +170,17 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target)
     }
 
     timer->target = target;
+    timer->long_target = _long_cnt;
+    if (target < now) {
+        timer->long_target++;
+    }
 
     unsigned state = disableIRQ();
     if ( !_this_high_period(target) ) {
         DEBUG("xtimer_set_absolute(): the timer doesn't fit into the low-level timer's mask.\n");
-        timer->long_target = _long_cnt;
         _add_timer_to_long_list(&long_list_head, timer);
     }
     else {
-        if (!target) {
-            /* set long_target != 0 so _is_set() can work */
-            timer->long_target = 1;
-        }
-
         if (_mask(now) >= target) {
             DEBUG("xtimer_set_absolute(): the timer will expire in the next timer period\n");
             _add_timer_to_list(&overflow_list_head, timer);