From d0c5a8047abdb86f32f1d40a993345fd15a44395 Mon Sep 17 00:00:00 2001
From: Luca Erbetta <luca.erbetta@skywarder.eu>
Date: Sat, 7 Mar 2020 16:49:54 +0100
Subject: [PATCH] [L3GD20] better timestamp measurement

---
 src/shared/sensors/L3GD20.h            | 230 +++++++++++++++++------
 src/tests/drivers/test-l3gd20-fifo.cpp | 244 +++++++++++++++++++++++++
 src/tests/drivers/test-l3gd20.cpp      | 188 ++++++++-----------
 3 files changed, 493 insertions(+), 169 deletions(-)
 create mode 100644 src/tests/drivers/test-l3gd20-fifo.cpp

diff --git a/src/shared/sensors/L3GD20.h b/src/shared/sensors/L3GD20.h
index 9133d5b50..65e47100a 100644
--- a/src/shared/sensors/L3GD20.h
+++ b/src/shared/sensors/L3GD20.h
@@ -1,3 +1,4 @@
+
 /**
  * Copyright (c) 2019 Skyward Experimental Rocketry
  * Authors: Luca Erbetta
@@ -32,6 +33,12 @@
 using miosix::GpioPin;
 using std::array;
 
+struct L3GD20Data
+{
+    uint64_t timestamp;
+    Vec3 gyro;
+};
+
 class L3GD20 : public GyroSensor
 {
 public:
@@ -44,11 +51,12 @@ public:
 
     enum class OutPutDataRate
     {
-        ODR_95  = 0x00,
-        ODR_190 = 0x01,
-        ODR_380 = 0x02,
-        ODR_760 = 0x03
+        ODR_95  = 95,
+        ODR_190 = 190,
+        ODR_380 = 380,
+        ODR_760 = 760
     };
+
     /**
      * @brief Creates an instance of an L3GD20 sensor
      *
@@ -57,21 +65,57 @@ public:
      * @param    range Full Scale Range (See datasheet)
      * @param    odr Output Data Rate (See datasheet)
      * @param    cutoff_freq Low pass filter cutoff frequency (See datasheet)
-     * @param    fifo_enabled Fifo enabled
-     * @param    fifo_watermark FIFO watermark level in range [1,32] (used for
-     * interrupt generation, see datasheet).
      */
     L3GD20(SPIBusInterface& bus, GpioPin cs,
            FullScaleRange range = FullScaleRange::FS_250,
            OutPutDataRate odr   = OutPutDataRate::ODR_95,
-           uint8_t cutoff_freq = 0x03, bool fifo_enabled = false,
-           unsigned int fifo_watermark = 24)
-        : fifo_enabled(fifo_enabled), fifo_watermark(fifo_watermark),
-          spislave(bus, cs), fs(range), odr(odr), cutoff_freq(cutoff_freq)
+           uint8_t cutoff_freq  = 0x03)
+        : L3GD20(bus, cs, {}, range, odr, cutoff_freq)
     {
         // Configure SPI
-        spislave.config.br = SPIBaudRate::DIV_64;
-        // memset(last_fifo, 0, sizeof(Vec3) * 32);
+        spislave.config.br = SPIBaudRate::DIV_32;
+    }
+
+    /**
+     * @brief Creates an instance of an L3GD20 sensor
+     *
+     * @param    bus SPI bus the sensor is connected to
+     * @param    cs Chip Select GPIO
+     * @param    cfg Custom SPI bus configuration
+     * @param    range Full Scale Range (See datasheet)
+     * @param    odr Output Data Rate (See datasheet)
+     * @param    cutoff_freq Low pass filter cutoff selector (See datasheet)
+     */
+    L3GD20(SPIBusInterface& bus, GpioPin cs, SPIBusConfig cfg,
+           FullScaleRange range = FullScaleRange::FS_250,
+           OutPutDataRate odr   = OutPutDataRate::ODR_95,
+           uint8_t cutoff_freq  = 0x03)
+        : spislave(bus, cs, cfg), fs(range), odr(odr), cutoff_freq(cutoff_freq)
+    {
+        switch (fs)
+        {
+            case FullScaleRange::FS_250:
+                sensitivity = SENSITIVITY_250;
+                break;
+            case FullScaleRange::FS_500:
+                sensitivity = SENSITIVITY_500;
+                break;
+            case FullScaleRange::FS_2000:
+                sensitivity = SENSITIVITY_2000;
+                break;
+        }
+    }
+
+    /**
+     * @brief Enables storing samples in a FIFO, must be called before init().
+     *
+     * @param fifo_watermark How many samples in the FIFO when the fifo
+     * watermark input is generated on INT2.
+     */
+    void enableFifo(unsigned int fifo_watermark)
+    {
+        fifo_enabled         = true;
+        this->fifo_watermark = fifo_watermark;
     }
 
     bool init()
@@ -87,8 +131,6 @@ public:
             return false;
         }
 
-        // uint8_t ctrl4 = spi.read(REG_CTRL4);
-
         switch (fs)
         {
             case FullScaleRange::FS_250:
@@ -103,6 +145,7 @@ public:
             default:
                 break;
         }
+
         if (fifo_enabled)
         {
             // Enable fifo
@@ -111,23 +154,42 @@ public:
             // Set watermark level to fifo_watermark samples
             uint8_t fifo_ctrl = fifo_watermark;
 
-            // Set fifo to FIFO mode
+            // Set fifo to STREAM mode
             fifo_ctrl |= 0x02 << 5;
 
             spi.write(REG_FIFO_CTRL, fifo_ctrl);
 
-            // Enable FIFO watermark interrupt
+            // Enable FIFO watermark interrupt on INT2
             spi.write(REG_CTRL3, 0x04);
         }
+        else
+        {
+            // Enable DRDY interrupt on INT2
+            spi.write(REG_CTRL3, 0x08);
+        }
+
         // Enter normal mode, enable output
         uint8_t ctrl1 = 0x0F;
 
-        // Configure ODR
-        ctrl1 |= static_cast<uint8_t>(odr) << 6;
-
         // Configure cutoff frequency
         ctrl1 |= (cutoff_freq & 0x03) << 4;
 
+        // Configure ODR
+        switch (odr)
+        {
+            case OutPutDataRate::ODR_95:
+                break;
+            case OutPutDataRate::ODR_190:
+                ctrl1 |= 1 << 6;
+                break;
+            case OutPutDataRate::ODR_380:
+                ctrl1 |= 2 << 6;
+                break;
+            case OutPutDataRate::ODR_760:
+                ctrl1 |= 3 << 6;
+                break;
+        }
+
         spi.write(REG_CTRL1, ctrl1);
 
         return true;
@@ -137,75 +199,123 @@ public:
 
     bool onSimpleUpdate()
     {
-        float scale = static_cast<int>(fs);
-
-        if (!fifo_enabled)
+        if (!fifo_enabled)  // FIFO not enabled
         {
-            uint8_t data[6];
             // Read output data registers (X, Y, Z)
             {
                 SPITransaction spi(spislave);
-                spi.read(REG_OUT_X_L | 0x40, data, 6);
+                spi.read(REG_OUT_X_L | 0x40, buf, 6);
             }
 
-            int16_t x = data[0] | data[1] << 8;
-            int16_t y = data[2] | data[3] << 8;
-            int16_t z = data[4] | data[5] << 8;
+            int16_t x = buf[0] | buf[1] << 8;
+            int16_t y = buf[2] | buf[3] << 8;
+            int16_t z = buf[4] | buf[5] << 8;
             // printf("%02X,%02X,%02X\n", x, y, z);
 
-            mLastGyro =
-                Vec3(x * scale / 65535, y * scale / 65535, z * scale / 65535);
+            last_fifo[0] = {interrupt_us, toRadiansPerSecond(x, y, z)};
         }
+
         else  // FIFO is enabled
         {
-            uint8_t buf[192];
-
             SPITransaction spi(spislave);
             // Read last fifo level
-            uint8_t fifo_src = spi.read(REG_FIFO_SRC);
-            uint8_t ovr      = (fifo_src & 0x40);
-            last_fifo_level  = fifo_src & 0x1F;
-
-            // if ovr --> fifo is full --> level = level + 1
-            if (ovr)
-            {
-                ++last_fifo_level;
-            }
+            uint8_t fifo_src   = spi.read(REG_FIFO_SRC);
+            uint8_t ovr        = (fifo_src & 0x40) >> 7;  // Overrun bit
+            uint8_t fifo_level = (fifo_src & 0x1F) + ovr;
 
             // Read fifo
-            spi.read(REG_OUT_X_L | 0x40, buf, last_fifo_level * 6);
-
-            // Convert units & store the FIFO content
-            for (uint8_t i = 0; i < last_fifo_level; i++)
-            {
-                int16_t x = buf[i * 6] | buf[i * 6 + 1] << 8;
-                int16_t y = buf[i * 6 + 2] | buf[i * 6 + 3] << 8;
-                int16_t z = buf[i * 6 + 4] | buf[i * 6 + 5] << 8;
+            spi.read(REG_OUT_X_L | 0x40, buf, fifo_level * 6);
 
-                Vec3 t = Vec3(x * scale / 65535, y * scale / 65535,
-                              z * scale / 65535);
+            uint64_t dt = dt_interrupt / last_fifo_level;
 
-                last_fifo[i] = t;
+            uint8_t duplicates = 0;
+            for (uint8_t i = 0; i < fifo_level; ++i)
+            {
+                bool rmv;
+                // Check for duplicates: there seems to be a bug where the
+                // sensor occasionaly shifts out the same sample two times in a
+                // row: discard one
+                if (i < fifo_level - 1)
+                {
+                    rmv = true;
+                    for (uint8_t j = 0; j < 6; ++j)
+                    {
+                        if (buf[i * 6 + j] != buf[(i + 1) * 6 + j])
+                        {
+                            rmv = false;
+                            break;
+                        }
+                    }
+                }
+                else
+                {
+                    rmv = false;
+                }
+
+                if (rmv)
+                {
+                    // Skip this sample;
+                    ++duplicates;
+                    continue;
+                }
+
+                // Assign a timestamp to each sample in the FIFO
+                // Samples before the watermark are older, after the
+                // watermark are younger.
+                last_fifo[i - duplicates].timestamp =
+                    interrupt_us +
+                    ((int)i - (int)fifo_watermark - (int)duplicates) * dt;
+                last_fifo[i - duplicates].gyro =
+                    toRadiansPerSecond(buf[i * 6] | buf[i * 6 + 1] << 8,
+                                       buf[i * 6 + 2] | buf[i * 6 + 3] << 8,
+                                       buf[i * 6 + 4] | buf[i * 6 + 5] << 8);
             }
+
+            last_fifo_level = fifo_level - duplicates;
         }
 
         return true;
     }
 
-    const array<Vec3, 32>& getLastFifo() const { return last_fifo; }
+    L3GD20Data getLastSample() { return last_fifo[last_fifo_level - 1]; }
+
+    void IRQupdateTimestamp(uint64_t ts)
+    {
+        dt_interrupt = ts - interrupt_us;
+        interrupt_us = ts;
+    }
+
+    const array<L3GD20Data, 32>& getLastFifo() const { return last_fifo; }
     uint8_t getLastFifoSize() const { return last_fifo_level; }
 
 private:
-    bool fifo_enabled = false;
-    unsigned int fifo_watermark;
+    Vec3 toRadiansPerSecond(int16_t x, int16_t y, int16_t z)
+    {
+        return Vec3(x * sensitivity, y * sensitivity, z * sensitivity);
+    }
 
-    array<Vec3, 32> last_fifo;
-    uint8_t last_fifo_level = 0;
+    static constexpr float SENSITIVITY_250  = 0.00875f;
+    static constexpr float SENSITIVITY_500  = 0.0175f;
+    static constexpr float SENSITIVITY_2000 = 0.070f;
 
     SPISlave spislave;
-    FullScaleRange fs;
-    OutPutDataRate odr;
-    uint8_t cutoff_freq;
+
+    FullScaleRange fs   = FullScaleRange::FS_250;
+    OutPutDataRate odr  = OutPutDataRate::ODR_760;
+    uint8_t cutoff_freq = 0x03;
+
+    bool fifo_enabled           = false;
+    unsigned int fifo_watermark = 24;
+
+    float sensitivity = SENSITIVITY_250;
+
+    uint64_t interrupt_us = 0;
+    uint64_t dt_interrupt = 0;
+
+    array<L3GD20Data, 32> last_fifo;
+    uint8_t last_fifo_level = 1;
+
+    uint8_t buf[192];
 
     constexpr static uint8_t WHO_AM_I_VAL = 212;
 
diff --git a/src/tests/drivers/test-l3gd20-fifo.cpp b/src/tests/drivers/test-l3gd20-fifo.cpp
new file mode 100644
index 000000000..aa3149c1a
--- /dev/null
+++ b/src/tests/drivers/test-l3gd20-fifo.cpp
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2019 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <array>
+
+#include "diagnostic/CpuMeter.h"
+#include "drivers/HardwareTimer.h"
+#include "drivers/spi/SPIDriver.h"
+#include "sensors/L3GD20-newt.h"
+
+using namespace miosix;
+using std::array;
+
+typedef Gpio<GPIOF_BASE, 7> GpioSck;
+typedef Gpio<GPIOF_BASE, 8> GpioMiso;
+typedef Gpio<GPIOF_BASE, 9> GpioMosi;
+
+typedef Gpio<GPIOA_BASE, 2> GpioINT2;
+
+static constexpr unsigned int FIFO_WATERMARK = 12;
+
+// Expected frequency from the datasheet is 760 Hz, but due to clock
+// misalignment / temperature errors and other factors, the observed clock (and
+// output data rate) is the following:
+static constexpr float SAMPLE_FREQUENCY = 782.3f;
+static constexpr int NUM_SAMPLES        = SAMPLE_FREQUENCY * 120;
+
+// FD
+void enableInterrupt();
+void configure();
+
+struct GyroSample
+{
+    int fifo_num;
+    L3GD20Data gyro;
+    int level;
+    uint64_t wtm_delta;
+    float cpu;
+    uint64_t update;
+};
+
+// GyroSample data[NUM_SAMPLES];
+GyroSample data[NUM_SAMPLES];
+int data_counter = 0;
+
+// High resolution hardware timer using TIM5
+HardwareTimer<uint32_t> hrclock{
+    TIM5, TimerUtils::getPrescalerInputFrequency(TimerUtils::InputClock::APB1)};
+
+// Last interrupt received timer tick
+volatile uint32_t last_watermark_tick;  // Stores the high-res tick of the last
+                                        // interrupt (L3GD20 watermark event)
+volatile uint32_t watermark_delta;  // Tick delta between the last 2 watermark
+                                    // events
+
+// L3GD20 SPI
+SPIBus bus(SPI5);
+GpioPin cs(GPIOC_BASE, 1);
+
+L3GD20* gyro = nullptr;
+
+// Interrupt handlers
+void __attribute__((naked)) EXTI2_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z20EXTI2_IRQHandlerImplv");
+    restoreContext();
+}
+
+void __attribute__((used)) EXTI2_IRQHandlerImpl()
+{
+    // Current high resolution tick
+    uint32_t tick       = hrclock.tick();
+    watermark_delta     = tick - last_watermark_tick;
+    last_watermark_tick = tick;
+    // Pass tick microseconds to sensor
+    if(gyro != nullptr)
+        gyro->IRQupdateTimestamp(hrclock.toIntMicroSeconds(tick));
+
+    EXTI->PR |= EXTI_PR_PR2;  // Reset pending register
+}
+
+int main()
+{
+    Thread::sleep(5000);
+
+    configure();
+
+    // Setup sensor
+    gyro = new L3GD20(bus, cs, L3GD20::FullScaleRange::FS_250,
+                      L3GD20::OutPutDataRate::ODR_760, 0x03);
+
+    gyro->enableFifo(FIFO_WATERMARK);
+
+    // Init
+    while (!gyro->init())
+    {
+    }
+
+    // Sample NUM_SAMPLES data
+    int fifo_num = 0;
+    while (data_counter < NUM_SAMPLES)
+    {
+        long last_tick = miosix::getTick();
+
+        // Read the fifo
+        uint32_t update = hrclock.tick();
+        gyro->onSimpleUpdate();
+        update = hrclock.tick() - update;
+
+        uint8_t level =
+            gyro->getLastFifoSize();  // Number of samples in the FIFO
+
+        // Get the FIFO
+        const array<L3GD20Data, 32>& fifo = gyro->getLastFifo();
+
+        // Store everything in the data buffer
+        for (int i = 0; i < level; i++)
+        {
+            // data[data_counter++] = fifo[i];
+            data[data_counter++] = {fifo_num,
+                                    fifo[i],
+                                    level,
+                                    hrclock.toIntMicroSeconds(watermark_delta),
+                                    averageCpuUtilization(), 
+                                    hrclock.toIntMicroSeconds(update)};
+
+            // Stop if we have enough data
+            if (data_counter >= NUM_SAMPLES)
+            {
+                break;
+            }
+        }
+        ++fifo_num;
+
+        // Wait until fifo has about 25 samples
+        Thread::sleepUntil(last_tick + 25.5 * 1000 / SAMPLE_FREQUENCY);
+    }
+
+    // Dump buffer content as CSV on the serial (might take a while)
+    // printf("t,x,y,z\n");
+    printf("FIFO_num,timestamp,int_delta,read_time,sample_delta,x,y,z,cpu\n");
+
+    for (int i = 1; i < data_counter; i++)
+    {
+        // clang-format off
+        printf("%d,%llu,%llu,%llu,%llu,%f,%f,%f,%.2f\n", 
+                data[i].fifo_num,
+                data[i].gyro.timestamp, 
+                data[i].wtm_delta,
+                data[i].update,
+                (data[i].gyro.timestamp - data[i - 1].gyro.timestamp),
+                data[i].gyro.gyro.getX(), 
+                data[i].gyro.gyro.getY(), 
+                data[i].gyro.gyro.getZ(),
+                data[i].cpu);
+        // clang-format on
+    }
+
+    printf("\n\n\nend.\n");
+    for (;;)
+    {
+        Thread::sleep(1000);
+    }
+}
+
+void configure()
+{
+    {
+        FastInterruptDisableLock dLock;
+
+        RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
+        RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
+
+        GpioSck::mode(Mode::ALTERNATE);
+        GpioMiso::mode(Mode::ALTERNATE);
+        GpioMosi::mode(Mode::ALTERNATE);
+
+        GpioSck::alternateFunction(5);
+        GpioMiso::alternateFunction(5);
+        GpioMosi::alternateFunction(5);
+
+        // Interrupt
+        GpioINT2::mode(Mode::INPUT_PULL_DOWN);
+
+        cs.mode(Mode::OUTPUT);
+    }
+
+    cs.high();
+
+    enableInterrupt();
+
+    // High resolution clock configuration
+    hrclock.setPrescaler(382);
+    hrclock.start();
+}
+
+void enableInterrupt()
+{
+    {
+        FastInterruptDisableLock l;
+        RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
+    }
+    // Refer to the datasheet for a detailed description on the procedure and
+    // interrupt registers
+
+    // Clear the mask on the wanted line
+    EXTI->IMR |= EXTI_IMR_MR2;
+
+    // Trigger the interrupt on a falling edge
+    // EXTI->FTSR |= EXTI_FTSR_TR2;
+
+    // Trigger the interrupt on a rising edge
+    EXTI->RTSR |= EXTI_RTSR_TR2;
+
+    EXTI->PR |= EXTI_PR_PR2;  // Reset pending register
+
+    // Enable interrupt on PA2 in SYSCFG
+    SYSCFG->EXTICR[0] &= 0xFFFFF0FF;
+
+    // // Enable the interrput in the interrupt controller
+    NVIC_EnableIRQ(EXTI2_IRQn);
+    NVIC_SetPriority(EXTI2_IRQn, 15);
+}
\ No newline at end of file
diff --git a/src/tests/drivers/test-l3gd20.cpp b/src/tests/drivers/test-l3gd20.cpp
index f5a63718c..e37d13c76 100644
--- a/src/tests/drivers/test-l3gd20.cpp
+++ b/src/tests/drivers/test-l3gd20.cpp
@@ -37,28 +37,20 @@ typedef Gpio<GPIOF_BASE, 9> GpioMosi;
 
 typedef Gpio<GPIOA_BASE, 2> GpioINT2;
 
-static constexpr bool FIFO_ENABLED           = true;
-static constexpr unsigned int FIFO_WATERMARK = 24;
-// Expected frequency from the datasheet is 760 Hz, but due to clock
-// misalignment / temperature errors and other factors, the observed clock (and
-// output data rate) is the following:
-static constexpr float SAMPLE_FREQUENCY = 782.3f;
-static constexpr int NUM_SAMPLES        = 10000;
+static constexpr int NUM_SAMPLES = 500 * 5;  // 120 seconds of data
 
 void enableInterrupt();
+void configure();
 
 struct GyroSample
 {
-    int fifo_num;
-    float timestamp;
+    uint64_t timestamp;
+    uint64_t sample_delta;
     Vec3 data;
-    int level;
-    float wtm_delta;
     float cpu;
 };
 
-// 364 KB buffer to store up to 30 seconds of data @ 760 Hz
-GyroSample data[22800];
+GyroSample data[NUM_SAMPLES];
 int data_counter = 0;
 
 // High resolution hardware timer using TIM5
@@ -66,16 +58,17 @@ HardwareTimer<uint32_t> hrclock{
     TIM5, TimerUtils::getPrescalerInputFrequency(TimerUtils::InputClock::APB1)};
 
 // Last interrupt received timer tick
-volatile uint32_t last_watermark_tick;  // Stores the high-res tick of the last
-                                        // interrupt (L3GD20 watermark event)
-volatile uint32_t watermark_delta;  // Tick delta between the last 2 watermark
-                                    // events
+volatile uint32_t last_sample_tick;  // Stores the high-res tick of the last
+                                     // interrupt (L3GD20 watermark event)
+volatile uint32_t sample_delta;      // Tick delta between the last 2 watermark
+                                     // events
 
 // L3GD20 SPI
 SPIBus bus(SPI5);
 GpioPin cs(GPIOC_BASE, 1);
 SPIBusConfig cfg;
-uint8_t buf[192];
+
+L3GD20* gyro;
 
 // Interrupt handlers
 void __attribute__((naked)) EXTI2_IRQHandler()
@@ -87,121 +80,66 @@ void __attribute__((naked)) EXTI2_IRQHandler()
 
 void __attribute__((used)) EXTI2_IRQHandlerImpl()
 {
-    uint32_t tick       = hrclock.tick();
-    watermark_delta     = tick - last_watermark_tick;
-    last_watermark_tick = tick;
+    // Current high resolution tick
+    uint32_t tick    = hrclock.tick();
+    sample_delta     = tick - last_sample_tick;
+    last_sample_tick = tick;
+
+    // Pass tick microseconds to sensor for timestamp estimation
+    if (gyro != nullptr)
+        gyro->IRQupdateTimestamp(hrclock.toIntMicroSeconds(tick));
 
     EXTI->PR |= EXTI_PR_PR2;  // Reset pending register
 }
 
 int main()
 {
-    cfg.br = SPIBaudRate::DIV_64;
-
-    {
-        FastInterruptDisableLock dLock;
-
-        RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
-        RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
-
-        GpioSck::mode(Mode::ALTERNATE);
-        GpioMiso::mode(Mode::ALTERNATE);
-        GpioMosi::mode(Mode::ALTERNATE);
+    Thread::sleep(5000);
 
-        GpioSck::alternateFunction(5);
-        GpioMiso::alternateFunction(5);
-        GpioMosi::alternateFunction(5);
+    configure();
 
-        // Interrupt
-        GpioINT2::mode(Mode::INPUT_PULL_DOWN);
-
-        cs.mode(Mode::OUTPUT);
-    }
-    // High resolution clock configuration
-    // 1.8 hours run time, 1.5 us resolution
-    hrclock.setPrescaler(127);
-    hrclock.start();
+    cfg.br = SPIBaudRate::DIV_64;
 
-    enableInterrupt();
-    cs.high();
+    gyro = new L3GD20(bus, cs, L3GD20::FullScaleRange::FS_250,
+                      L3GD20::OutPutDataRate::ODR_760, 0x03);
 
-    L3GD20 gyro(bus, cs, L3GD20::FullScaleRange::FS_250,
-                L3GD20::OutPutDataRate::ODR_760, 0x03, FIFO_ENABLED,
-                FIFO_WATERMARK);
+    // gyro->setRealSampleInterval(
+    //     static_cast<uint64_t>(1000000 / SAMPLE_FREQUENCY));
 
-    while (!gyro.init())
+    while (!gyro->init())
     {
     }
 
     Thread::sleep(500);
 
-    long long first_tick = miosix::getTick();
-
-    // Sample NUM_SAMPLES data
-    int fifo_num = 0;
     while (data_counter < NUM_SAMPLES)
     {
         long last_tick = miosix::getTick();
 
-        if (FIFO_ENABLED)
-        {
-            // Precise timestamp of the last sample in the FIFO
-            float wtm_timestamp = hrclock.toSeconds(last_watermark_tick);
-            float wtm_delta     = hrclock.toMilliSeconds(watermark_delta);
-
-            // Read the fifo
-            gyro.onSimpleUpdate();
-
-            uint8_t level =
-                gyro.getLastFifoSize();  // Number of samples in the FIFO
-
-            const array<Vec3, 32>& fifo = gyro.getLastFifo();
-
-            for (int i = 0; i < level; i++)
-            {
-                // Assign a timestamp to each sample in the FIFO
-                // Samples before the watermark are older, after the watermark
-                // are younger. Time delta between samples is
-                // (1 / SAMPLE_FREQUENCY) seconds
-                float ts = wtm_timestamp +
-                           (i - (int)FIFO_WATERMARK) / SAMPLE_FREQUENCY;
-
-                data[data_counter++] = {fifo_num,  ts,
-                                        fifo[i],   level,
-                                        wtm_delta, averageCpuUtilization()};
-                if (data_counter >= NUM_SAMPLES)
-                {
-                    break;
-                }
-            }
-            ++fifo_num;
-
-            // Wait until fifo has about 25 samples
-            Thread::sleepUntil(last_tick + 34);
-        }
-        else
-        {
-            // Sample sensor @ 500 Hz
-            gyro.onSimpleUpdate();
-
-            data[data_counter++] = {0,
-                                    (last_tick - first_tick) / 1000.0f,
-                                    *(gyro.gyroDataPtr()),
-                                    0,
-                                    0,
-                                    averageCpuUtilization()};
-            Thread::sleepUntil(last_tick + 2);
-        }
+        // Sample sensor @ 500 Hz
+        gyro->onSimpleUpdate();
+        L3GD20Data d = gyro->getLastSample();
+
+        data[data_counter++] = {d.timestamp, sample_delta, d.gyro,
+                                averageCpuUtilization()};
+
+        Thread::sleepUntil(last_tick + 1);
     }
     // Dump buffer content as CSV on the serial (might take a while)
-    printf("FIFO_num,timestamp,watermark_delta,sample_delta,x,y,z,level,cpu");
+    printf("FIFO_num,timestamp,int_delta,sample_delta,x,y,z,cpu\n");
     for (int i = 1; i < data_counter; i++)
     {
-        printf("%d,%.3f,%.3f,%.3f,%f,%f,%f,%d,%.2f\n", data[i].fifo_num,
-               data[i].timestamp * 1000, data[i].wtm_delta,
-               (data[i].timestamp - data[i - 1].timestamp) * 1000,
-               data[i].data.getX(), data[i].data.getY(), data[i].data.getZ(),
-               data[i].level, data[i].cpu);
+        // clang-format off
+         printf("%d,%llu,%llu,%llu,%f,%f,%f,%.2f\n", 
+                0,
+                data[i].timestamp, 
+                hrclock.toIntMicroSeconds(data[i].sample_delta),
+                (data[i].timestamp - data[i - 1].timestamp),
+                data[i].data.getX(), 
+                data[i].data.getY(), 
+                data[i].data.getZ(),
+                data[i].cpu);
+        // clang-format on
     }
 
     printf("\n\n\nend.\n");
@@ -211,6 +149,38 @@ int main()
     }
 }
 
+void configure()
+{
+    {
+        FastInterruptDisableLock dLock;
+
+        RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
+        RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
+
+        GpioSck::mode(Mode::ALTERNATE);
+        GpioMiso::mode(Mode::ALTERNATE);
+        GpioMosi::mode(Mode::ALTERNATE);
+
+        GpioSck::alternateFunction(5);
+        GpioMiso::alternateFunction(5);
+        GpioMosi::alternateFunction(5);
+
+        // Interrupt
+        GpioINT2::mode(Mode::INPUT_PULL_DOWN);
+
+        cs.mode(Mode::OUTPUT);
+    }
+
+    cs.high();
+
+    enableInterrupt();
+
+    // High resolution clock configuration
+    // 1.8 hours run time, 1.5 us resolution
+    hrclock.setPrescaler(127);
+    hrclock.start();
+}
+
 void enableInterrupt()
 {
     {
-- 
GitLab