diff --git a/sys/analog_util/adc_util.c b/sys/analog_util/adc_util.c
index af4f480237b2d8a42cf01ab7fad41e1129e12260..ba1ca7c9c40b0f65eec3b6073d8431cfab3e4b88 100644
--- a/sys/analog_util/adc_util.c
+++ b/sys/analog_util/adc_util.c
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2018 Eistec AB
  * Copyright (C) 2015 Freie Universität Berlin
  *
  * This file is subject to the terms and conditions of the GNU Lesser
@@ -14,28 +15,58 @@
  * @brief       ADC utility function implementation
  *
  * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ * @author      Joakim Nohlgård <joakim.nohlgard@eistec.se>
  *
  * @}
  */
 
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "periph/adc.h"
 #include "analog_util.h"
+#include "assert.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
 
-/* keep a max value to ADC resolution mapping for quick access in the ROM */
-static const int val_max[] = {
-    [ADC_RES_6BIT] =    0x003f,
-    [ADC_RES_8BIT] =    0x00ff,
-    [ADC_RES_10BIT] =   0x03ff,
-    [ADC_RES_12BIT] =   0x0fff,
-    [ADC_RES_14BIT] =   0x3fff,
-    [ADC_RES_16BIT] =   0xffff
-};
+/**
+ * @brief Convert adc_res_t resolution setting into numeric bit count
+ */
+static unsigned int _adc_res_bits(adc_res_t res)
+{
+    switch (res) {
+        case ADC_RES_6BIT:
+            return 6;
+        case ADC_RES_8BIT:
+            return 8;
+        case ADC_RES_10BIT:
+            return 10;
+        case ADC_RES_12BIT:
+            return 12;
+        case ADC_RES_14BIT:
+            return 14;
+        case ADC_RES_16BIT:
+            return 16;
+        default:
+            /* Unsupported ADC resolution, modify your application to use a
+             * different resolution, or add it above */
+            assert(0 == 1);
+            return 0;
+    }
+}
 
-int adc_util_map(int sample, adc_res_t res, int min, int max)
+int32_t adc_util_map(int sample, adc_res_t res, int32_t min, int32_t max)
 {
-    return ((((max - min) * sample) / val_max[res]) + min);
+    /* Using 64 bit signed int as intermediate to prevent overflow when range
+     * multiplied by sample requires more than 32 bits */
+    int32_t scaled = (((int64_t)(max - min) * sample) >> _adc_res_bits(res));
+    DEBUG("scaled: %" PRId32 "\n", scaled);
+    return (min + scaled);
 }
 
 float adc_util_mapf(int sample, adc_res_t res, float min, float max)
 {
-    return ((((max - min) * sample) / val_max[res]) + min);
+    return ((((max - min) * sample) / ((int32_t)1L << _adc_res_bits(res))) + min);
 }
diff --git a/sys/include/analog_util.h b/sys/include/analog_util.h
index a1cd53fa661913ee754be95fa2b91db36e48bbd0..b3aa408286e11063a18531c38820734f12810f7e 100644
--- a/sys/include/analog_util.h
+++ b/sys/include/analog_util.h
@@ -21,6 +21,8 @@
 #ifndef ANALOG_UTIL_H
 #define ANALOG_UTIL_H
 
+#include <stdint.h>
+
 #include "periph/adc.h"
 
 #ifdef __cplusplus
@@ -33,8 +35,6 @@ extern "C" {
  * This function is useful for converting sampled ADC values into their physical
  * representation.
  *
- * The min value is asserted to be smaller than the max value.
- *
  * @param[in] sample        sampled ADC value
  * @param[in] res           ADC resolution
  * @param[in] min           the lower bound of the target interval
@@ -42,7 +42,7 @@ extern "C" {
  *
  * @return                  the mapped value
  */
-int adc_util_map(int sample, adc_res_t res, int min, int max);
+int32_t adc_util_map(int sample, adc_res_t res, int32_t min, int32_t max);
 
 /**
  * @brief   Map a sampled ADC value to a given range (using floating point
diff --git a/tests/unittests/tests-analog_util/Makefile b/tests/unittests/tests-analog_util/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /dev/null
+++ b/tests/unittests/tests-analog_util/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/tests/unittests/tests-analog_util/Makefile.include b/tests/unittests/tests-analog_util/Makefile.include
new file mode 100644
index 0000000000000000000000000000000000000000..96a45617679176bbbcf9a21eee7af414faa01fb4
--- /dev/null
+++ b/tests/unittests/tests-analog_util/Makefile.include
@@ -0,0 +1 @@
+USEMODULE += analog_util
diff --git a/tests/unittests/tests-analog_util/tests-analog_util.c b/tests/unittests/tests-analog_util/tests-analog_util.c
new file mode 100644
index 0000000000000000000000000000000000000000..a46f1e169fb7dd9d563b9687a11257246717dc4b
--- /dev/null
+++ b/tests/unittests/tests-analog_util/tests-analog_util.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 Eistec AB
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+#include "embUnit.h"
+#include "tests-analog_util.h"
+
+#include "analog_util.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+typedef struct {
+    int32_t expected;
+    int sample;
+    int32_t min;
+    int32_t max;
+    adc_res_t res;
+} test_values_t;
+
+/* Arbitrarily chosen test vectors */
+/* TODO: Choose test vectors in a more qualified manner to catch any edge cases */
+static test_values_t test_data[] = {
+    {     0L,     0,          0L,      10000L, ADC_RES_16BIT},
+    {  1000L,     0,       1000L,          0L, ADC_RES_16BIT},
+    { 65535L, 65535,          0L,      65536L, ADC_RES_16BIT},
+    { 32768L,   128,          0L,      65536L,  ADC_RES_8BIT},
+    {  8192L,   128,          0L,      65536L, ADC_RES_10BIT},
+    {   256L,     1,          0L,      65536L,  ADC_RES_8BIT},
+    { 65280L,   255,          0L,      65536L,  ADC_RES_8BIT},
+    {  1039L,    10,       1000L,       2000L,  ADC_RES_8BIT},
+    { 17324L,  3000,      10000L,      20000L, ADC_RES_12BIT},
+    { 11831L,  3000,      10000L,      20000L, ADC_RES_14BIT},
+    {  2301L,  3000,         13L,      50000L, ADC_RES_16BIT},
+    {  -134L, 56789,      -1000L,          0L, ADC_RES_16BIT},
+    { 16062L, 45671,      30000L,      10000L, ADC_RES_16BIT},
+    { -2535L, 30000,     -30000L,      30000L, ADC_RES_16BIT},
+    {     0L, 65535,      65535L,          0L, ADC_RES_16BIT},
+    { 65534L,     1,      65535L,          0L, ADC_RES_16BIT},
+    {  3972L,  9876,      10000L,          0L, ADC_RES_14BIT},
+};
+
+#define TEST_DATA_NUMOF (sizeof(test_data) / sizeof(test_data[0]))
+
+static void test_adc_util_map(void)
+{
+    for (unsigned int k = 0; k < TEST_DATA_NUMOF; ++k) {
+        test_values_t *testp = &test_data[k];
+        int32_t res = adc_util_map(testp->sample, testp->res, testp->min, testp->max);
+        TEST_ASSERT_EQUAL_INT(testp->expected, res);
+    }
+}
+
+Test *tests_adc_util_tests(void)
+{
+    EMB_UNIT_TESTFIXTURES(fixtures) {
+        new_TestFixture(test_adc_util_map),
+    };
+
+    EMB_UNIT_TESTCALLER(adc_util_tests, NULL, NULL, fixtures);
+
+    return (Test *)&adc_util_tests;
+}
+
+void tests_analog_util(void)
+{
+    TESTS_RUN(tests_adc_util_tests());
+}
diff --git a/tests/unittests/tests-analog_util/tests-analog_util.h b/tests/unittests/tests-analog_util/tests-analog_util.h
new file mode 100644
index 0000000000000000000000000000000000000000..273ac33bbf93d5a9bcb8bb20f9920b92f93e3022
--- /dev/null
+++ b/tests/unittests/tests-analog_util/tests-analog_util.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 Eistec AB
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @addtogroup  unittests
+ * @{
+ *
+ * @file
+ * @brief       Unittests for the ``adc_utils`` header
+ *
+ * @author      Joakim Nohlgård <joakim.nohlgard@eistec.se>
+ */
+#ifndef TESTS_ANALOG_UTIL_H
+#define TESTS_ANALOG_UTIL_H
+#include "embUnit/embUnit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+*  @brief   The entry point of this test suite.
+*/
+void tests_adc_util(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TESTS_ANALOG_UTIL_H */
+/** @} */