diff --git a/examples/pmw3389.rs b/examples/pmw3389.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b1d0c3f3bc0d8c08e31329977954fc194f295874
--- /dev/null
+++ b/examples/pmw3389.rs
@@ -0,0 +1,369 @@
+#![deny(unsafe_code)]
+// #![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use cortex_m::{
+    iprintln,
+    peripheral::{itm::Stim, DWT},
+};
+use embedded_hal::spi::MODE_0;
+// use cortex_m_semihosting::hprintln;
+use panic_halt as _;
+use rtic::cyccnt::{Instant, U32Ext as _};
+use stm32f4xx_hal::{
+    // gpio::gpioa::PA0,
+    prelude::*,
+    // rcc::CFGR,
+    spi::Spi,
+    stm32,
+};
+
+//use crate::hal::gpio::{gpioa::PA0, Edge, Input, PullDown};
+//use hal::spi::{Mode, Phase, Polarity};
+
+#[rtic::app(device = stm32f4xx_hal::stm32, monotonic = rtic::cyccnt::CYCCNT, peripherals = true)]
+const APP: () = {
+    struct Resources {
+        // late resources
+        GPIOA: stm32::GPIOA,
+        ITM: stm32::ITM,
+    }
+    #[init(schedule = [toggle])]
+    fn init(cx: init::Context) -> init::LateResources {
+        let mut core = cx.core;
+        let device = cx.device;
+
+        // Configure led:
+        // power on GPIOA, RM0368 6.3.11
+        device.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit());
+        // configure PA5 as output, RM0368 8.4.1
+        device.GPIOA.moder.modify(|_, w| w.moder5().bits(1));
+
+        // setup clocks
+        let rcc = device.RCC.constrain();
+
+        let clocks = rcc.cfgr.freeze();
+
+        let stim = &mut core.ITM.stim[0];
+
+        iprintln!(stim, "pmw3389");
+
+        // iprintln!(stim, "hclk {:?}", clocks.hclk().into::());
+
+        // Initialize (enable) the monotonic timer (CYCCNT)
+        core.DCB.enable_trace();
+        // required on Cortex-M7 devices that software lock the DWT (e.g. STM32F7)
+        DWT::unlock();
+        core.DWT.enable_cycle_counter();
+
+        // Configure SPI
+        // spi2
+        // sck    - pb10, (yellow)
+        // miso   - pc2, (red)
+        // mosi   - pc3, (orange)
+        // ncs    - pb4, (long yellow)
+        // motion - (brown)
+        //
+        // +5, (white)
+        // gnd, (black)
+
+        let gpiob = device.GPIOB.split();
+        let gpioc = device.GPIOC.split();
+
+        let sck = gpiob.pb10.into_alternate_af5();
+        let miso = gpioc.pc2.into_alternate_af5();
+        let mosi = gpioc.pc3.into_alternate_af5();
+        let cs = gpiob.pb4.into_push_pull_output();
+
+        let spi = Spi::spi2(
+            device.SPI2,
+            (sck, miso, mosi),
+            // Mode {
+            //     polarity: Polarity::IdleLow,
+            //     phase: Phase::CaptureOnFirstTransition,
+            // },
+            MODE_0,
+            stm32f4xx_hal::time::KiloHertz(2000).into(),
+            clocks,
+        );
+
+        iprintln!(stim, "clocks:\n hclk {}", clocks.hclk().0);
+
+        let mut pmw3389 = pmw3389::Pmw3389::new(spi, cs).unwrap();
+        let id = pmw3389.product_id().unwrap();
+        iprintln!(stim, "id {}", id);
+
+        // semantically, the monotonic timer is frozen at time "zero" during `init`
+        // NOTE do *not* call `Instant::now` in this context; it will return a nonsense value
+        // let now = cx.start; // the start time of the system
+
+        // Schedule `toggle` to run 8e6 cycles (clock cycles) in the future
+        // cx.schedule.toggle(now + 8_000_000.cycles()).unwrap();
+
+        // pass on late resources
+        init::LateResources {
+            GPIOA: device.GPIOA,
+            ITM: core.ITM,
+        }
+    }
+
+    #[task(resources = [GPIOA, ITM], schedule = [toggle])]
+    fn toggle(cx: toggle::Context) {
+        static mut TOGGLE: bool = false;
+        iprintln!(&mut cx.resources.ITM.stim[0], "foo  @ {:?}", Instant::now());
+
+        if *TOGGLE {
+            cx.resources.GPIOA.bsrr.write(|w| w.bs5().set_bit());
+        } else {
+            cx.resources.GPIOA.bsrr.write(|w| w.br5().set_bit());
+        }
+
+        *TOGGLE = !*TOGGLE;
+        cx.schedule
+            .toggle(cx.scheduled + 8_000_000.cycles())
+            .unwrap();
+    }
+
+    extern "C" {
+        fn EXTI0();
+    }
+};
+
+mod pmw3389 {
+
+    use embedded_hal::blocking::spi::{Transfer, Write};
+    use embedded_hal::digital::v2::OutputPin;
+    use embedded_hal::spi::Mode;
+
+    #[allow(dead_code)]
+    #[derive(Clone, Copy)]
+    pub enum Register {
+        ProductId = 0x00,
+        RevisionId = 0x01,
+        Motion = 0x02,
+        DeltaXL = 0x03,
+        DeltaXH = 0x04,
+        DeltaYL = 0x05,
+        DeltaYH = 0x06,
+        SQUAL = 0x07,
+        RawDataSum = 0x08,
+        MaximumRawdata = 0x09,
+        MinimumRawdata = 0x0A,
+        ShutterLower = 0x0B,
+        ShutterUpper = 0x0C,
+        RippleControl = 0x0D,
+        ResoultionL = 0x0E,
+        ResolutionH = 0x0F,
+        Config2 = 0x10,
+        AngleTune = 0x11,
+        FrameCapture = 0x12,
+        SROMEnable = 0x13,
+        RunDownshift = 0x14,
+        Rest1RateLower = 0x15,
+        Rest1RateUpper = 0x16,
+        Rest1Downshift = 0x17,
+        Rest2RateLower = 0x18,
+        Rest2RateUpper = 0x19,
+        Rest2Downshift = 0x1A,
+        Rest3RateLower = 0x1B,
+        Rest3RateUpper = 0x1C,
+        Observation = 0x24,
+        DataOutLower = 0x25,
+        DataOutUpper = 0x26,
+        RawDataDump = 0x29,
+        SROMId = 0x2A,
+        MinSQRun = 0x2B,
+        RawDataThreshold = 0x2C,
+        Control2 = 0x2D,
+        Config5L = 0x2E,
+        Config5H = 0x2F,
+        PowerUpReset = 0x3A,
+        Shutdown = 0x3B,
+        InverseProductID = 0x3F,
+        LiftCutoffTune3 = 0x41,
+        AngleSnap = 0x42,
+        LiftCutoffTune1 = 0x4A,
+        MotionBurst = 0x50,
+        LiftCutoffTune1Timeout = 0x58,
+        LiftCutoffTune1MinLength = 0x5A,
+        SROMLoadBurst = 0x62,
+        LiftConfig = 0x63,
+        RawDataBurst = 0x64,
+        LiftCutoffTune2 = 0x65,
+        LiftCutoffTune2Timeout = 0x71,
+        LiftCutoffTune2MinLength = 0x72,
+        PWMPeriodCnt = 0x73,
+        PWMWidthCnt = 0x74,
+    }
+
+    impl Register {
+        fn addr(self) -> u8 {
+            self as u8
+        }
+    }
+
+    const READ: u8 = 1 << 7;
+    const WRITE: u8 = 0 << 7;
+    const MULTI: u8 = 1 << 6;
+    const SINGLE: u8 = 0 << 6;
+
+    pub struct Pmw3389<SPI, CS> {
+        spi: SPI,
+        cs: CS,
+    }
+
+    impl<SPI, CS, E> Pmw3389<SPI, CS>
+    where
+        SPI: Transfer<u8, Error = E> + Write<u8, Error = E>,
+        CS: OutputPin,
+    {
+        /// Creates a new driver from a SPI peripheral and a NCS pin
+        pub fn new(spi: SPI, cs: CS) -> Result<Self, E> {
+            let mut pmw3389 = Pmw3389 { spi, cs };
+
+            // power up and enable all the axes
+            // l3gd20.write_register(Register::CTRL_REG1, 0b00_00_1_111)?;
+
+            Ok(pmw3389)
+        }
+
+        /// Reads the ProductId register; should return `0x47`
+        pub fn product_id(&mut self) -> Result<u8, E> {
+            self.read_register(Register::ProductId)
+        }
+
+        // /// Temperature measurement + gyroscope measurements
+        // pub fn all(&mut self) -> Result<Measurements, E> {
+        //     let mut bytes = [0u8; 9];
+        //     self.read_many(Register::OUT_TEMP, &mut bytes)?;
+
+        //     Ok(Measurements {
+        //         gyro: I16x3 {
+        //             x: (bytes[3] as u16 + ((bytes[4] as u16) << 8)) as i16,
+        //             y: (bytes[5] as u16 + ((bytes[6] as u16) << 8)) as i16,
+        //             z: (bytes[7] as u16 + ((bytes[8] as u16) << 8)) as i16,
+        //         },
+        //         temp: bytes[1] as i8,
+        //     })
+        // }
+
+        // /// Gyroscope measurements
+        // pub fn gyro(&mut self) -> Result<I16x3, E> {
+        //     let mut bytes = [0u8; 7];
+        //     self.read_many(Register::OUT_X_L, &mut bytes)?;
+
+        //     Ok(I16x3 {
+        //         x: (bytes[1] as u16 + ((bytes[2] as u16) << 8)) as i16,
+        //         y: (bytes[3] as u16 + ((bytes[4] as u16) << 8)) as i16,
+        //         z: (bytes[5] as u16 + ((bytes[6] as u16) << 8)) as i16,
+        //     })
+        // }
+
+        // /// Temperature sensor measurement
+        // pub fn temp(&mut self) -> Result<i8, E> {
+        //     Ok(self.read_register(Register::OUT_TEMP)? as i8)
+        // }
+
+        // /// Read `STATUS_REG` of sensor
+        // pub fn status(&mut self) -> Result<Status, E> {
+        //     let sts = self.read_register(Register::STATUS_REG)?;
+        //     Ok(Status::from_u8(sts))
+        // }
+
+        // /// Get the current Output Data Rate
+        // pub fn odr(&mut self) -> Result<Odr, E> {
+        //     // Read control register
+        //     let reg1 = self.read_register(Register::CTRL_REG1)?;
+        //     Ok(Odr::from_u8(reg1))
+        // }
+
+        // /// Set the Output Data Rate
+        // pub fn set_odr(&mut self, odr: Odr) -> Result<&mut Self, E> {
+        //     self.change_config(Register::CTRL_REG1, odr)
+        // }
+
+        // /// Get current Bandwidth
+        // pub fn bandwidth(&mut self) -> Result<Bandwidth, E> {
+        //     let reg1 = self.read_register(Register::CTRL_REG1)?;
+        //     Ok(Bandwidth::from_u8(reg1))
+        // }
+
+        // /// Set low-pass cut-off frequency (i.e. bandwidth)
+        // ///
+        // /// See `Bandwidth` for further explanation
+        // pub fn set_bandwidth(&mut self, bw: Bandwidth) -> Result<&mut Self, E> {
+        //     self.change_config(Register::CTRL_REG1, bw)
+        // }
+
+        // /// Get the current Full Scale Selection
+        // ///
+        // /// This is the sensitivity of the sensor, see `Scale` for more information
+        // pub fn scale(&mut self) -> Result<Scale, E> {
+        //     let scl = self.read_register(Register::CTRL_REG4)?;
+        //     Ok(Scale::from_u8(scl))
+        // }
+
+        // /// Set the Full Scale Selection
+        // ///
+        // /// This sets the sensitivity of the sensor, see `Scale` for more
+        // /// information
+        // pub fn set_scale(&mut self, scale: Scale) -> Result<&mut Self, E> {
+        //     self.change_config(Register::CTRL_REG4, scale)
+        // }
+
+        fn read_register(&mut self, reg: Register) -> Result<u8, E> {
+            let _ = self.cs.set_low();
+
+            let mut buffer = [reg.addr() | SINGLE | READ, 0];
+            self.spi.transfer(&mut buffer)?;
+
+            let _ = self.cs.set_high();
+
+            Ok(buffer[1])
+        }
+
+        /// Read multiple bytes starting from the `start_reg` register.
+        /// This function will attempt to fill the provided buffer.
+        fn read_many(&mut self, start_reg: Register, buffer: &mut [u8]) -> Result<(), E> {
+            let _ = self.cs.set_low();
+            buffer[0] = start_reg.addr() | MULTI | READ;
+            self.spi.transfer(buffer)?;
+            let _ = self.cs.set_high();
+
+            Ok(())
+        }
+
+        fn write_register(&mut self, reg: Register, byte: u8) -> Result<(), E> {
+            let _ = self.cs.set_low();
+
+            let buffer = [reg.addr() | SINGLE | WRITE, byte];
+            self.spi.write(&buffer)?;
+
+            let _ = self.cs.set_high();
+
+            Ok(())
+        }
+
+        // /// Change configuration in register
+        // ///
+        // /// Helper function to update a particular part of a register without
+        // /// affecting other parts of the register that might contain desired
+        // /// configuration. This allows the `L3gd20` struct to be used like
+        // /// a builder interface when configuring specific parameters.
+        // fn change_config<B: BitValue>(&mut self, reg: Register, bits: B) -> Result<&mut Self, E> {
+        //     // Create bit mask from width and shift of value
+        //     let mask = B::mask() << B::shift();
+        //     // Extract the value as u8
+        //     let bits = (bits.value() << B::shift()) & mask;
+        //     // Read current value of register
+        //     let current = self.read_register(reg)?;
+        //     // Use supplied mask so we don't affect more than necessary
+        //     let masked = current & !mask;
+        //     // Use `or` to apply the new value without affecting other parts
+        //     let new_reg = masked | bits;
+        //     self.write_register(reg, new_reg)?;
+        //     Ok(self)
+        // }
+    }
+}
diff --git a/examples/rtic_blinky.rs b/examples/rtic_blinky.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9bf9bb2945c580cafd74bb905c80b1251fbfb683
--- /dev/null
+++ b/examples/rtic_blinky.rs
@@ -0,0 +1,67 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use cortex_m::peripheral::DWT;
+use cortex_m_semihosting::hprintln;
+use panic_halt as _;
+use rtic::cyccnt::{Instant, U32Ext as _};
+use stm32f4xx_hal::stm32;
+
+#[rtic::app(device = stm32f4xx_hal::stm32, monotonic = rtic::cyccnt::CYCCNT, peripherals = true)]
+const APP: () = {
+    struct Resources {
+        // late resources
+        GPIOA: stm32::GPIOA,
+    }
+    #[init(schedule = [toggle])]
+    fn init(cx: init::Context) -> init::LateResources {
+        let mut core = cx.core;
+        let device = cx.device;
+
+        // Initialize (enable) the monotonic timer (CYCCNT)
+        core.DCB.enable_trace();
+        // required on Cortex-M7 devices that software lock the DWT (e.g. STM32F7)
+        DWT::unlock();
+        core.DWT.enable_cycle_counter();
+
+        // semantically, the monotonic timer is frozen at time "zero" during `init`
+        // NOTE do *not* call `Instant::now` in this context; it will return a nonsense value
+        let now = cx.start; // the start time of the system
+
+        // Schedule `toggle` to run 8e6 cycles (clock cycles) in the future
+        cx.schedule.toggle(now + 8_000_000.cycles()).unwrap();
+
+        // power on GPIOA, RM0368 6.3.11
+        device.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit());
+        // configure PA5 as output, RM0368 8.4.1
+        device.GPIOA.moder.modify(|_, w| w.moder5().bits(1));
+
+        // pass on late resources
+        init::LateResources {
+            GPIOA: device.GPIOA,
+        }
+    }
+
+    #[task(resources = [GPIOA], schedule = [toggle])]
+    fn toggle(cx: toggle::Context) {
+        static mut TOGGLE: bool = false;
+        hprintln!("foo  @ {:?}", Instant::now()).unwrap();
+
+        if *TOGGLE {
+            cx.resources.GPIOA.bsrr.write(|w| w.bs5().set_bit());
+        } else {
+            cx.resources.GPIOA.bsrr.write(|w| w.br5().set_bit());
+        }
+
+        *TOGGLE = !*TOGGLE;
+        cx.schedule
+            .toggle(cx.scheduled + 8_000_000.cycles())
+            .unwrap();
+    }
+
+    extern "C" {
+        fn EXTI0();
+    }
+};