diff --git a/examples/lsm9ds1.rs b/examples/lsm9ds1.rs
new file mode 100644
index 0000000000000000000000000000000000000000..933e9edd8a6afa20c19a1a62c6170e138f826050
--- /dev/null
+++ b/examples/lsm9ds1.rs
@@ -0,0 +1,123 @@
+//! Read WHO_AM_I registers from LSM9DS1 3D accelerometer, gyroscope, magnetometer.
+//! 
+//! Using the SparkFun LSM9DS1 breakout board, connect:
+//! PA_8 -> CS_AG            (Chip Select for accelerometer/gyro)
+//! PA_9 -> CS_M             (Chip Select for magnetometer)
+//! PB_3 -> SCL              (SPI clock)
+//! PB_4 -> SDO_M and SDO_AG (Combined SPI MISO)
+//! PB_5 -> SDA              (SPI MOSI)
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_std]
+
+extern crate cortex_m;
+extern crate embedded_hal as hal;
+#[macro_use(block)]
+extern crate nb;
+extern crate stm32f4x_hal as f4;
+
+use hal::spi::{Mode, Phase, Polarity};
+use f4::prelude::*;
+use f4::spi::{Spi, SpiConfig};
+use f4::stm32f4x;
+use f4::gpio::{Input, OpenDrain, Output, OutputSpeed, PushPull};
+use f4::gpio::gpioa::{PA8, PA9};
+use f4::gpio::gpiob::{PB3, PB4, PB5};
+
+fn main() {
+    let p = stm32f4x::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+    let mut gpioa = p.GPIOA.split(&mut rcc.ahb1);
+    let mut gpiob = p.GPIOB.split(&mut rcc.ahb1);
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    // Software CS for accelerometer/gyro
+    let mut cs_ag: PA8<Output<PushPull>> = gpioa
+        .pa8
+        .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper)
+        .into();
+    cs_ag.internal_pull_up(&mut gpioa.pupdr, true);
+    cs_ag.set_speed(&mut gpioa.ospeedr, OutputSpeed::HIGH);
+    cs_ag.set_high();
+
+    // Software CS for magnetometer
+    let mut cs_m: PA9<Output<PushPull>> = gpioa
+        .pa9
+        .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper)
+        .into();
+    cs_m.internal_pull_up(&mut gpioa.pupdr, true);
+    cs_m.set_speed(&mut gpioa.ospeedr, OutputSpeed::HIGH);
+    cs_m.set_high();
+
+    // SPI SCK
+    let mut sck: PB3<Output<PushPull>> = gpiob
+        .pb3
+        .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper)
+        .into();
+    sck.set_speed(&mut gpiob.ospeedr, OutputSpeed::HIGH);
+    let sck = sck.into_af6(&mut gpiob.moder, &mut gpiob.afrl);
+
+    // SPI MISO
+    let mut miso: PB4<Input<OpenDrain>> = gpiob
+        .pb4
+        .into_open_drain_input(&mut gpiob.moder, &mut gpiob.otyper)
+        .into();
+    miso.set_speed(&mut gpiob.ospeedr, OutputSpeed::HIGH);
+    miso.internal_pull_up(&mut gpiob.pupdr, true);
+    let miso = miso.into_af6(&mut gpiob.moder, &mut gpiob.afrl);
+
+    // SPI MOSI
+    let mut mosi: PB5<Output<PushPull>> = gpiob
+        .pb5
+        .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper)
+        .into();
+    mosi.set_speed(&mut gpiob.ospeedr, OutputSpeed::HIGH);
+    let mosi = mosi.into_af6(&mut gpiob.moder, &mut gpiob.afrl);
+
+    // SPI MASTER 
+    let mut spi = Spi::spi3(
+        p.SPI3,
+        (sck, miso, mosi),
+        Mode {
+            polarity: Polarity::IdleHigh,
+            phase: Phase::CaptureOnSecondTransition,
+        },
+        SpiConfig::Master,
+        1.mhz(),
+        clocks,
+        &mut rcc.apb1,
+    );
+
+    // Read mode
+    const R: u8 = 0x80;
+
+    // Registers to read
+    const WHO_AM_I_AG: u8 = 0x0F;
+    const WHO_AM_I_M: u8 = 0x0F;
+
+    // Expected answers
+    const ANS_AG: u8 = 0x68;
+    const ANS_M: u8 = 0x3D;
+
+    loop {
+        cs_ag.set_low();
+        block!(spi.send(WHO_AM_I_AG | R)).ok();
+        block!(spi.read()).unwrap();
+        block!(spi.send(0)).ok();
+        let byte_ag = block!(spi.read()).unwrap();
+        assert_eq!(byte_ag, ANS_AG);
+        cs_ag.set_high();
+
+        cs_m.set_low();
+        block!(spi.send(WHO_AM_I_M | R)).ok();
+        block!(spi.read()).unwrap();
+        block!(spi.send(0)).ok();
+        let byte_m = block!(spi.read()).unwrap();
+        assert_eq!(byte_m, ANS_M);
+        cs_m.set_high();
+    }
+}
diff --git a/src/gpio.rs b/src/gpio.rs
index 3d6c815b40a8c8c9fc3d45f33b61b5cdea30fc0a..8cc8cb8eb529bdbbcb7e5ba8d3b99d951c7e8cfc 100644
--- a/src/gpio.rs
+++ b/src/gpio.rs
@@ -86,6 +86,22 @@ pub struct AF14;
 /// Alternate function 15 (type state)
 pub struct AF15;
 
+pub enum OutputSpeed {
+    LOW,
+    MEDIUM,
+    FAST,
+    HIGH,
+}
+impl OutputSpeed {
+    fn value(&self) -> u32 {
+        match *self {
+            OutputSpeed::LOW => 0b00,
+            OutputSpeed::MEDIUM => 0b01,
+            OutputSpeed::FAST => 0b10,
+            OutputSpeed::HIGH => 0b11,
+        }
+    }
+}
 macro_rules! gpio {
     ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $iopxenr:ident, $iopxrst:ident, $PXx:ident, [
         $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty, $AFR:ident),)+
@@ -115,6 +131,8 @@ macro_rules! gpio {
                 pub otyper: OTYPER,
                 /// Opaque PUPDR register
                 pub pupdr: PUPDR,
+                /// Opaque OSPEEDR register
+                pub ospeedr: OSPEEDR,
                 $(
                     /// Pin
                     pub $pxi: $PXi<$MODE>,
@@ -136,6 +154,8 @@ macro_rules! gpio {
                         moder: MODER { _0: () },
                         otyper: OTYPER { _0: () },
                         pupdr: PUPDR { _0: () },
+                        ospeedr: OSPEEDR { _0: () },
+
                         $(
                             $pxi: $PXi { _mode: PhantomData },
                         )+
@@ -198,6 +218,17 @@ macro_rules! gpio {
                 }
             }
 
+            /// Opaque OSPEEDR register
+            pub struct OSPEEDR {
+                _0: (),
+            }
+
+            impl OSPEEDR {
+                pub(crate) fn ospeedr(&mut self) -> &$gpioy::OSPEEDR {
+                    unsafe { &(*$GPIOX::ptr()).ospeedr }
+                }
+            }
+
             /// Partially erased pin
             pub struct $PXx<MODE> {
                 i: u8,
@@ -223,6 +254,7 @@ macro_rules! gpio {
                     // NOTE(unsafe) atomic write to a stateless register
                     unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + self.i))) }
                 }
+
             }
 
             $(
@@ -410,6 +442,28 @@ macro_rules! gpio {
                         $PXi { _mode: PhantomData }
                     }
 
+                    /// Configures the pin to operate as an open drain input pin
+                    pub fn into_open_drain_input(
+                        self,
+                        moder: &mut MODER,
+                        otyper: &mut OTYPER,
+                    ) -> $PXi<Input<OpenDrain>> {
+                        let offset = 2 * $i;
+
+                        // general purpose input mode
+                        let mode = 0b10;
+                        moder.moder().modify(|r, w| unsafe {
+                            w.bits((r.bits() & !(0b11 << offset)) | (mode << offset))
+                        });
+
+                        // open drain input
+                        otyper
+                            .otyper()
+                            .modify(|r, w| unsafe { w.bits(r.bits() | (0b1 << $i)) });
+
+                        $PXi { _mode: PhantomData }
+                    }
+
                     /// Configures the pin to operate as an push pull output pin
                     pub fn into_push_pull_output(
                         self,
@@ -449,6 +503,38 @@ macro_rules! gpio {
                         });
                     }
                 }
+                impl $PXi<Input<OpenDrain>> {
+                    /// Enables / disables the internal pull up
+                    pub fn internal_pull_up(&mut self, pupdr: &mut PUPDR, on: bool) {
+                        let offset = 2 * $i;
+
+                        pupdr.pupdr().modify(|r, w| unsafe {
+                            w.bits(
+                                (r.bits() & !(0b11 << offset)) | if on {
+                                    0b01 << offset
+                                } else {
+                                    0
+                                },
+                            )
+                        });
+                    }
+                }
+                impl $PXi<Output<PushPull>> {
+                    /// Enables / disables the internal pull up
+                    pub fn internal_pull_up(&mut self, pupdr: &mut PUPDR, on: bool) {
+                        let offset = 2 * $i;
+
+                        pupdr.pupdr().modify(|r, w| unsafe {
+                            w.bits(
+                                (r.bits() & !(0b11 << offset)) | if on {
+                                    0b01 << offset
+                                } else {
+                                    0
+                                },
+                            )
+                        });
+                    }
+                }
 
                 impl<MODE> $PXi<Output<MODE>> {
                     /// Erases the pin number from the type
@@ -461,6 +547,32 @@ macro_rules! gpio {
                             _mode: self._mode,
                         }
                     }
+
+                    /// Set output speed
+                    pub fn set_speed(&mut self, ospeedr: &mut OSPEEDR, speed: ::OutputSpeed) {
+                        let offset = 2 * $i;
+
+                        ospeedr.ospeedr().modify(|r, w| unsafe {
+                            w.bits(
+                                (r.bits() & !(0b11 << offset)) |
+                                    speed.value() << offset
+                            )
+                        });
+                    }
+                }
+
+                impl<MODE> $PXi<Input<MODE>> {
+                    /// Set output speed
+                    pub fn set_speed(&mut self, ospeedr: &mut OSPEEDR, speed: ::OutputSpeed) {
+                        let offset = 2 * $i;
+
+                        ospeedr.ospeedr().modify(|r, w| unsafe {
+                            w.bits(
+                                (r.bits() & !(0b11 << offset)) |
+                                    speed.value() << offset
+                            )
+                        });
+                    }
                 }
 
                 impl<MODE> OutputPin for $PXi<Output<MODE>> {
diff --git a/src/lib.rs b/src/lib.rs
index 8548e4fb98ad1590d2149a0a57825f523ff3d318..b1a569503891c156794193aedd60a1cc91d1ee8f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -42,8 +42,11 @@ pub mod dma;
 pub mod prelude;
 pub mod rcc;
 pub mod serial;
-// pub mod spi;
+pub mod spi;
 pub mod time;
 pub mod timer;
 
 pub mod led;
+
+pub use gpio::OutputSpeed;
+pub use spi::SpiConfig;
\ No newline at end of file
diff --git a/src/spi.rs b/src/spi.rs
index 396334c92e98f675c214b71e1b41efd28780b757..5ca01b0242343af1837db4a3361a73abb818ce90 100644
--- a/src/spi.rs
+++ b/src/spi.rs
@@ -4,15 +4,24 @@ use core::ptr;
 
 use hal::spi::{FullDuplex, Mode, Phase, Polarity};
 use nb;
-use stm32f30x::{SPI1, SPI2, SPI3};
+use stm32f4x::{SPI1, SPI2, SPI3, SPI4, SPI5};
 
-use gpio::gpioa::{PA5, PA6, PA7};
-use gpio::gpiob::{PB13, PB14, PB15, PB5};
-use gpio::gpioc::{PC10, PC11, PC12};
-use gpio::{AF5, AF6};
+use gpio::gpioa::{PA1, PA10, PA11, PA12, PA15, PA4, PA5, PA6, PA7};
+use gpio::gpiob::{PB0, PB1, PB10, PB12, PB13, PB14, PB15, PB3, PB4, PB5, PB8, PB9};
+use gpio::gpioc::{PC10, PC11, PC12, PC2, PC3, PC7};
+use gpio::{AF5, AF6, AF7};
 use rcc::{APB1, APB2, Clocks};
 use time::Hertz;
 
+/// SPI configuraiton
+#[derive(Clone, Copy, PartialEq)]
+pub enum SpiConfig {
+    Master,
+    Slave,
+    #[doc(hidden)]
+    _Extensible,
+}
+
 /// SPI error
 #[derive(Debug)]
 pub enum Error {
@@ -22,7 +31,8 @@ pub enum Error {
     ModeFault,
     /// CRC error
     Crc,
-    #[doc(hidden)] _Extensible,
+    #[doc(hidden)]
+    _Extensible,
 }
 
 // FIXME these should be "closed" traits
@@ -35,28 +45,46 @@ pub unsafe trait MisoPin<SPI> {}
 /// MOSI pin -- DO NOT IMPLEMENT THIS TRAIT
 pub unsafe trait MosiPin<SPI> {}
 
-unsafe impl SckPin<SPI1> for PA5<AF5> {}
-// unsafe impl SckPin<SPI1> for PB3<AF5> {}
-
-unsafe impl SckPin<SPI2> for PB13<AF5> {}
-
-// unsafe impl SckPin<SPI3> for PB3<AF6> {}
-unsafe impl SckPin<SPI3> for PC10<AF6> {}
+/// NSS pin -- DO NOT IMPLEMENT THIS TRAIT
+pub unsafe trait NssPin<SPI> {}
 
+// DM00102166 - Table 9. Alternate function mapping
+unsafe impl MosiPin<SPI4> for PA1<AF5> {}
+unsafe impl NssPin<SPI1> for PA4<AF5> {}
+unsafe impl NssPin<SPI3> for PA4<AF6> {}
+unsafe impl SckPin<SPI1> for PA5<AF5> {}
 unsafe impl MisoPin<SPI1> for PA6<AF5> {}
-// unsafe impl MisoPin<SPI1> for PB4<AF5> {}
-
-unsafe impl MisoPin<SPI2> for PB14<AF5> {}
-
-// unsafe impl MisoPin<SPI3> for PB4<AF6> {}
-unsafe impl MisoPin<SPI3> for PC11<AF6> {}
-
 unsafe impl MosiPin<SPI1> for PA7<AF5> {}
+unsafe impl MosiPin<SPI5> for PA10<AF6> {}
+unsafe impl MisoPin<SPI4> for PA11<AF6> {}
+unsafe impl MisoPin<SPI5> for PA12<AF6> {}
+unsafe impl NssPin<SPI1> for PA15<AF5> {}
+unsafe impl NssPin<SPI3> for PA15<AF6> {}
+
+unsafe impl SckPin<SPI5> for PB0<AF6> {}
+unsafe impl NssPin<SPI5> for PB1<AF6> {}
+unsafe impl SckPin<SPI1> for PB3<AF5> {}
+unsafe impl SckPin<SPI3> for PB3<AF6> {}
+unsafe impl MisoPin<SPI1> for PB4<AF5> {}
+unsafe impl MisoPin<SPI3> for PB4<AF6> {}
 unsafe impl MosiPin<SPI1> for PB5<AF5> {}
-
+unsafe impl MosiPin<SPI3> for PB5<AF6> {}
+unsafe impl MosiPin<SPI5> for PB8<AF6> {}
+unsafe impl NssPin<SPI2> for PB9<AF5> {}
+unsafe impl SckPin<SPI2> for PB10<AF5> {}
+unsafe impl NssPin<SPI2> for PB12<AF5> {}
+unsafe impl NssPin<SPI4> for PB12<AF6> {}
+unsafe impl SckPin<SPI3> for PB12<AF7> {}
+unsafe impl SckPin<SPI2> for PB13<AF5> {}
+unsafe impl SckPin<SPI4> for PB13<AF6> {}
+unsafe impl MisoPin<SPI2> for PB14<AF5> {}
 unsafe impl MosiPin<SPI2> for PB15<AF5> {}
 
-unsafe impl MosiPin<SPI3> for PB5<AF6> {}
+unsafe impl MisoPin<SPI2> for PC2<AF5> {}
+unsafe impl MosiPin<SPI2> for PC3<AF5> {}
+unsafe impl SckPin<SPI2> for PC7<AF5> {}
+unsafe impl SckPin<SPI3> for PC10<AF6> {}
+unsafe impl MisoPin<SPI3> for PC11<AF6> {}
 unsafe impl MosiPin<SPI3> for PC12<AF6> {}
 
 /// SPI peripheral operating in full duplex master mode
@@ -74,6 +102,7 @@ macro_rules! hal {
                     spi: $SPIX,
                     pins: (SCK, MISO, MOSI),
                     mode: Mode,
+                    config: ::SpiConfig,
                     freq: F,
                     clocks: Clocks,
                     apb2: &mut $APBX,
@@ -85,18 +114,12 @@ macro_rules! hal {
                     MOSI: MosiPin<$SPIX>,
                 {
                     // enable or reset $SPIX
-                    apb2.enr().modify(|_, w| w.$spiXen().enabled());
+                    apb2.enr().modify(|_, w| w.$spiXen().set_bit());
                     apb2.rstr().modify(|_, w| w.$spiXrst().set_bit());
                     apb2.rstr().modify(|_, w| w.$spiXrst().clear_bit());
 
-                    // FRXTH: RXNE event is generated if the FIFO level is greater than or equal to
-                    //        8-bit
-                    // DS: 8-bit data size
                     // SSOE: Slave Select output disabled
-                    spi.cr2
-                        .write(|w| unsafe {
-                            w.frxth().set_bit().ds().bits(0b111).ssoe().clear_bit()
-                        });
+                    spi.cr2.write(|w| w.ssoe().clear_bit());
 
                     let br = match clocks.$pclkX().0 / freq.into().0 {
                         0 => unreachable!(),
@@ -114,33 +137,36 @@ macro_rules! hal {
                     // CPOL: polarity
                     // MSTR: master mode
                     // BR: 1 MHz
-                    // SPE: SPI disabled
                     // LSBFIRST: MSB first
                     // SSM: enable software slave management (NSS pin free for other uses)
                     // SSI: set nss high = master mode
                     // CRCEN: hardware CRC calculation disabled
                     // BIDIMODE: 2 line unidirectional (full duplex)
+                    // DFF: 8-bit data size
+                    // SPE: SPI enabled
                     spi.cr1.write(|w| unsafe {
                         w.cpha()
                             .bit(mode.phase == Phase::CaptureOnSecondTransition)
                             .cpol()
                             .bit(mode.polarity == Polarity::IdleHigh)
                             .mstr()
-                            .set_bit()
+                            .bit(config == SpiConfig::Master)
                             .br()
                             .bits(br)
-                            .spe()
-                            .set_bit()
                             .lsbfirst()
                             .clear_bit()
-                            .ssi()
-                            .set_bit()
                             .ssm()
                             .set_bit()
+                            .ssi()
+                            .set_bit()
                             .crcen()
                             .clear_bit()
                             .bidimode()
                             .clear_bit()
+                            .dff()
+                            .set_bit()
+                            .spe()
+                            .set_bit()
                     });
 
                     Spi { spi, pins }