diff --git a/examples/pwm-control.rs b/examples/pwm-control.rs
index 3d8b3f2e377c3ae7b702c4e5e5fef3fe610357d1..6c3228d673c48247f9eff1702c491aaa0d190587 100644
--- a/examples/pwm-control.rs
+++ b/examples/pwm-control.rs
@@ -65,7 +65,7 @@ fn init(ref prio: P0, thr: &TMax) {
 
     serial.init(BAUD_RATE.invert(), afio, None, gpioa, rcc);
 
-    pwm.init(FREQUENCY.invert(), afio, gpioa, rcc);
+    pwm.init(FREQUENCY.invert(), afio, None, gpioa, rcc);
     pwm.set_duty(Channel::_1, 0);
 
     pwm.enable(Channel::_1);
diff --git a/examples/pwm2.rs b/examples/pwm2.rs
index bb89489065b399288a53a82264a9e08e3db0a9a6..5d1388dc52e39bbf2c376ee997e44a0b05ecc9e0 100644
--- a/examples/pwm2.rs
+++ b/examples/pwm2.rs
@@ -49,7 +49,7 @@ fn init(ref prio: P0, thr: &TMax) {
 
     let pwm = Pwm(&*tim2);
 
-    pwm.init(FREQUENCY.invert(), afio, gpioa, rcc);
+    pwm.init(FREQUENCY.invert(), afio, None, gpioa, rcc);
     let duty = pwm.get_max_duty() / 16;
 
     const CHANNELS: [Channel; 4] =
diff --git a/examples/pwm3.rs b/examples/pwm3.rs
index 7ba90922091c73d88a63837487a257cc3c9424fb..d2e112625e69401e9c620e9383560090b76995bb 100644
--- a/examples/pwm3.rs
+++ b/examples/pwm3.rs
@@ -49,7 +49,7 @@ fn init(ref prio: P0, thr: &TMax) {
 
     let pwm = Pwm(&*tim3);
 
-    pwm.init(FREQUENCY.invert(), afio, gpioa, rcc);
+    pwm.init(FREQUENCY.invert(), afio, None, gpioa, rcc);
     let duty = pwm.get_max_duty() / 16;
 
     const CHANNELS: [Channel; 2] = [Channel::_1, Channel::_2];
diff --git a/examples/pwm4.rs b/examples/pwm4.rs
index 1f130d4edaad38e6b125f0447990c7b452004e9f..4a83b861d588a4a0dec3986eb81a1237e738ae3d 100644
--- a/examples/pwm4.rs
+++ b/examples/pwm4.rs
@@ -49,7 +49,7 @@ fn init(ref prio: P0, thr: &TMax) {
 
     let pwm = Pwm(&*tim4);
 
-    pwm.init(FREQUENCY.invert(), afio, gpiob, rcc);
+    pwm.init(FREQUENCY.invert(), afio, None, gpiob, rcc);
     let duty = pwm.get_max_duty() / 16;
 
     const CHANNELS: [Channel; 4] =
diff --git a/examples/ws2812.rs b/examples/ws2812.rs
new file mode 100644
index 0000000000000000000000000000000000000000..dfd439d189575c319af29783d6ce68a1846112d2
--- /dev/null
+++ b/examples/ws2812.rs
@@ -0,0 +1,95 @@
+//! Drive a ring of 24 WS2812 LEDs
+//!
+//! To test this demo connect the data-in pin of the LED ring to pin PA0
+
+#![deny(warnings)]
+#![feature(const_fn)]
+#![feature(used)]
+#![no_std]
+
+extern crate blue_pill;
+
+// version = "0.2.3"
+extern crate cortex_m_rt;
+
+// version = "0.1.0"
+#[macro_use]
+extern crate cortex_m_rtfm as rtfm;
+
+extern crate embedded_hal as hal;
+
+#[macro_use]
+extern crate nb;
+
+use blue_pill::dma::{Buffer, Dma1Channel2};
+use blue_pill::time::Hertz;
+use blue_pill::{Channel, Pwm, stm32f103xx};
+use hal::prelude::*;
+use rtfm::{C1, P0, Resource, T0, TMax};
+
+// CONFIGURATION
+const FREQUENCY: Hertz = Hertz(200_000);
+const _0: u8 = 3;
+const _1: u8 = 5;
+
+// RESOURCES
+peripherals!(stm32f103xx, {
+    AFIO: Peripheral {
+        ceiling: C0,
+    },
+    DMA1: Peripheral {
+        ceiling: C0,
+    },
+    GPIOA: Peripheral {
+        ceiling: C0,
+    },
+    RCC: Peripheral {
+        ceiling: C0,
+    },
+    TIM2: Peripheral {
+        ceiling: C0,
+    },
+});
+
+static BUFFER: Resource<Buffer<[u8; (24 * 24) + 1], Dma1Channel2>, C1> =
+    Resource::new(Buffer::new([_0; (24 * 24) + 1]));
+
+// INITIALIZATION PHASE
+fn init(ref prio: P0, thr: &TMax) {
+    let afio = &AFIO.access(prio, thr);
+    let buffer = BUFFER.access(prio, thr);
+    let dma1 = &DMA1.access(prio, thr);
+    let gpioa = &GPIOA.access(prio, thr);
+    let rcc = &RCC.access(prio, thr);
+    let tim2 = TIM2.access(prio, thr);
+
+    let pwm = Pwm(&*tim2);
+
+    pwm.init(FREQUENCY.invert(), afio, Some(dma1), gpioa, rcc);
+    pwm.enable(Channel::_1);
+
+    // end of frame
+    *buffer.borrow_mut().last_mut().unwrap() = 0;
+
+    // set each RGB value to 0x0A0A0A
+    for byte in buffer.borrow_mut()[..(24 * 24)].chunks_mut(8) {
+        byte.copy_from_slice(&[_0, _0, _0, _0, _1, _1, _1, _1]);
+    }
+
+    pwm.set_duties(dma1, Channel::_1, buffer).unwrap();
+
+    block!(buffer.release(dma1)).unwrap();
+
+    rtfm::bkpt();
+}
+
+// IDLE LOOP
+fn idle(_prio: P0, _thr: T0) -> ! {
+    // Sleep
+    loop {
+        rtfm::wfi();
+    }
+}
+
+// TASKS
+tasks!(stm32f103xx, {});
diff --git a/src/dma.rs b/src/dma.rs
index 2df6712bc3b51bfafe412a9129b9f4346b64fb25..c47fbcf8a8d4fa75b06db14b027aa1310830b1a1 100644
--- a/src/dma.rs
+++ b/src/dma.rs
@@ -16,6 +16,11 @@ pub enum Error {
     Transfer,
 }
 
+/// Channel 2 of DMA1
+pub struct Dma1Channel2 {
+    _0: (),
+}
+
 /// Channel 4 of DMA1
 pub struct Dma1Channel4 {
     _0: (),
@@ -179,6 +184,28 @@ impl<T, CHANNEL> Buffer<T, CHANNEL> {
 }
 
 // FIXME these `release` methods probably want some of sort of barrier
+impl<T> Buffer<T, Dma1Channel2> {
+    /// Waits until the DMA releases this buffer
+    pub fn release(&self, dma1: &DMA1) -> nb::Result<(), Error> {
+        let status = self.status.get();
+
+        if status == Status::Unlocked {
+            return Ok(());
+        }
+
+        if dma1.isr.read().teif2().is_set() {
+            Err(nb::Error::Other(Error::Transfer))
+        } else if dma1.isr.read().tcif2().is_set() {
+            unsafe { self.unlock(status) }
+            dma1.ifcr.write(|w| w.ctcif2().set());
+            dma1.ccr2.modify(|_, w| w.en().clear());
+            Ok(())
+        } else {
+            Err(nb::Error::WouldBlock)
+        }
+    }
+}
+
 impl<T> Buffer<T, Dma1Channel4> {
     /// Waits until the DMA releases this buffer
     pub fn release(&self, dma1: &DMA1) -> nb::Result<(), Error> {
diff --git a/src/lib.rs b/src/lib.rs
index 19da932bc90507f589669f469c13c74b8298eced..ccf869a365d9261f8a1a056cdc7dd79fb7cd3b00 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,6 +15,7 @@
 #![feature(const_fn)]
 #![feature(get_type_id)]
 #![feature(never_type)]
+#![feature(unsize)]
 #![no_std]
 
 extern crate cast;
diff --git a/src/pwm.rs b/src/pwm.rs
index 8c3ed4204c881b36b0d213318a92707faa81fc83..67632b7222ff443a2d1e616f433f0a202476dd25 100644
--- a/src/pwm.rs
+++ b/src/pwm.rs
@@ -31,11 +31,14 @@
 //! - CH4 = PB9
 
 use core::any::{Any, TypeId};
+use core::marker::Unsize;
 
 use cast::{u16, u32};
 use hal;
-use stm32f103xx::{AFIO, GPIOA, RCC, TIM1, TIM2, TIM3, TIM4};
+use static_ref::Ref;
+use stm32f103xx::{AFIO, DMA1, GPIOA, RCC, TIM1, TIM2, TIM3, TIM4};
 
+use dma::{self, Buffer, Dma1Channel2};
 use timer::{Channel, TIM};
 
 /// PWM driver
@@ -199,22 +202,33 @@ where
     T: Any + TIM,
 {
     /// Initializes the PWM module
-    pub fn init<P>(&self, period: P, afio: &AFIO, gpio: &T::GPIO, rcc: &RCC)
-    where
+    pub fn init<P>(
+        &self,
+        period: P,
+        afio: &AFIO,
+        dma1: Option<&DMA1>,
+        gpio: &T::GPIO,
+        rcc: &RCC,
+    ) where
         P: Into<::apb1::Ticks>,
     {
-        self._init(period.into(), afio, gpio, rcc)
+        self._init(period.into(), afio, dma1, gpio, rcc)
     }
 
     fn _init(
         &self,
         period: ::apb1::Ticks,
         afio: &AFIO,
+        dma1: Option<&DMA1>,
         gpio: &T::GPIO,
         rcc: &RCC,
     ) {
         let tim2 = self.0;
 
+        // enable AFIO, (DMA1), TIMx and GPIOx
+        if dma1.is_some() {
+            rcc.ahbenr.modify(|_, w| w.dma1en().enabled());
+        }
         if tim2.get_type_id() == TypeId::of::<TIM2>() {
             rcc.apb1enr.modify(|_, w| w.tim2en().enabled());
         } else if tim2.get_type_id() == TypeId::of::<TIM3>() {
@@ -365,6 +379,48 @@ where
 
         self._set_period(period);
 
+        if let Some(dma1) = dma1 {
+            tim2.dier.modify(|_, w| w.ude().set());
+
+            if tim2.get_type_id() == TypeId::of::<TIM2>() {
+                // TIM2_UP
+                // mem2mem: Memory to memory mode disabled
+                // pl: Medium priority
+                // msize: Memory size = 8 bits
+                // psize: Peripheral size = 16 bits
+                // minc: Memory increment mode enabled
+                // pinc: Peripheral increment mode disabled
+                // circ: Circular mode disabled
+                // dir: Transfer from memory to peripheral
+                // tceie: Transfer complete interrupt disabled
+                // en: Disabled
+                dma1.ccr2.write(|w| unsafe {
+                    w.mem2mem()
+                        .clear()
+                        .pl()
+                        .bits(0b01)
+                        .msize()
+                        .bits(0b00)
+                        .psize()
+                        .bits(0b01)
+                        .minc()
+                        .set()
+                        .pinc()
+                        .clear()
+                        .circ()
+                        .clear()
+                        .dir()
+                        .set()
+                        .tcie()
+                        .set()
+                        .en()
+                        .clear()
+                });
+            } else {
+                unimplemented!()
+            }
+        }
+
         tim2.cr1.write(|w| unsafe {
             w.cms()
                 .bits(0b00)
@@ -386,6 +442,48 @@ where
         let arr = u16(period / u32(psc + 1)).unwrap();
         self.0.arr.write(|w| w.arr().bits(arr));
     }
+
+    /// Uses `buffer` to continuously change the duty cycle on every period
+    pub fn set_duties<B>(
+        &self,
+        dma1: &DMA1,
+        channel: Channel,
+        buffer: Ref<Buffer<B, Dma1Channel2>>,
+    ) -> ::core::result::Result<(), dma::Error>
+    where
+        B: Unsize<[u8]>,
+    {
+        let tim2 = self.0;
+
+        if tim2.get_type_id() == TypeId::of::<TIM2>() {
+            if dma1.ccr2.read().en().is_set() {
+                return Err(dma::Error::InUse);
+            }
+
+            let buffer: &[u8] = buffer.lock();
+
+            dma1.cndtr2.write(|w| unsafe {
+                w.ndt().bits(u16(buffer.len()).unwrap())
+            });
+            dma1.cpar2.write(|w| unsafe {
+                match channel {
+                    Channel::_1 => w.bits(&tim2.ccr1 as *const _ as u32),
+                    Channel::_2 => w.bits(&tim2.ccr2 as *const _ as u32),
+                    Channel::_3 => w.bits(&tim2.ccr3 as *const _ as u32),
+                    Channel::_4 => w.bits(&tim2.ccr4 as *const _ as u32),
+                }
+            });
+            dma1.cmar2.write(
+                |w| unsafe { w.bits(buffer.as_ptr() as u32) },
+            );
+            dma1.ccr2.modify(|_, w| w.en().set());
+
+            Ok(())
+
+        } else {
+            unimplemented!()
+        }
+    }
 }
 
 impl<'a, T> hal::Pwm for Pwm<'a, T>