diff --git a/Cargo.toml b/Cargo.toml
index 9f5555bdc277b5d0ffc8662f49e93fc6a24aacaf..b959bb83852325dfdee390cac59616a5fe22a9e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,7 @@ version = "0.1.0"
 [dependencies]
 static-ref = "0.1.0"
 stm32f103xx = "0.6.1"
+volatile-register = "0.2.0"
 
 [dependencies.cast]
 default-features = false
diff --git a/src/adc.rs b/src/adc.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0e16b658e24e79fc6fde3018994270911945e15e
--- /dev/null
+++ b/src/adc.rs
@@ -0,0 +1,149 @@
+//! Analog to Digital Converter
+
+use core::marker::Unsize;
+
+use cast::u16;
+use hal::prelude::*;
+use static_ref::Ref;
+
+use dma::{self, CircBuffer, Dma1Channel1};
+use stm32f103xx::{ADC1, DMA1, GPIOA, RCC, TIM2};
+use {Channel, Pwm};
+
+/// ADC Channel 1 (PA1)
+pub struct Adc1<'a>(pub &'a ADC1);
+
+impl<'a> Adc1<'a> {
+    /// Initializes the ADC
+    ///
+    /// NOTE `Pwm<TIM2>.init` must be called before this method because both
+    /// methods configure the PA1 pin (one as input and the other as output :-/)
+    pub fn init(&self, dma1: &DMA1, gpioa: &GPIOA, rcc: &RCC) {
+        let adc1 = self.0;
+
+        // enable ADC1, DMA1, GPIOA, TIM2
+        rcc.ahbenr.modify(|_, w| w.dma1en().enabled());
+        rcc.apb1enr.modify(|_, w| w.tim2en().enabled());
+        rcc.apb2enr
+            .modify(|_, w| w.adc1en().enabled().iopaen().enabled());
+
+        // Set PA1 as analog input
+        gpioa.crl.modify(|_, w| w.cnf1().bits(0b00).mode1().input());
+
+        // Sample only the channel 1
+        adc1.sqr1.modify(|_, w| unsafe { w.l().bits(1) });
+        adc1.sqr3.modify(|_, w| unsafe { w.sq1().bits(1) });
+
+        // Sample time: 55.5 + 12.5 = 68 cycles
+        adc1.smpr2.modify(|_, w| unsafe { w.smp1().bits(0b101) });
+
+        // ADC1
+        // mem2mem: Memory to memory mode disabled
+        // pl: Medium priority
+        // msize: Memory size = 16 bits
+        // psize: Peripheral size = 16 bits
+        // minc: Memory increment mode enabled
+        // pinc: Peripheral increment mode disabled
+        // circ: Circular mode enabled
+        // dir: Transfer from peripheral to memory
+        // htie: Half transfer interrupt enabled
+        // tceie: Transfer complete interrupt enabled
+        // en: Disabled
+        dma1.ccr1.write(|w| unsafe {
+            w.mem2mem()
+                .clear()
+                .pl()
+                .bits(0b01)
+                .msize()
+                .bits(0b01)
+                .psize()
+                .bits(0b01)
+                .minc()
+                .set()
+                .pinc()
+                .clear()
+                .circ()
+                .set()
+                .dir()
+                .clear()
+                .htie()
+                .set()
+                .tcie()
+                .set()
+                .en()
+                .clear()
+        });
+
+        // exttrig: Conversion on external event enabled
+        // extsel: Timer 2 CC2 event
+        // align: Right alignment
+        // dma: DMA mode enabled
+        // cont: Single conversion mode
+        // adon: Disable ADC conversion
+        adc1.cr2.write(|w| unsafe {
+            w.exttrig()
+                .set()
+                .extsel()
+                .bits(0b011) // T2C2
+                // .bits(0b111) // swstart
+                .align()
+                .clear()
+                .dma()
+                .set()
+                .cont()
+                .clear()
+                .adon()
+                .clear()
+        });
+    }
+
+    /// Disables the ADC
+    pub fn disable(&self) {
+        self.0.cr2.modify(|_, w| w.adon().clear());
+    }
+
+    /// Enables the ADC
+    pub fn enable(&self) {
+        self.0.cr2.modify(|_, w| w.adon().set());
+    }
+
+    /// Starts an analog to digital conversion that will be periodically
+    /// triggered by the channel 2 of TIM2
+    ///
+    /// The conversions will be stored in the circular `buffer`
+    pub fn start<B>(
+        &self,
+        buffer: Ref<CircBuffer<u16, B, Dma1Channel1>>,
+        dma1: &DMA1,
+        pwm: Pwm<TIM2>,
+    ) -> Result<(), dma::Error>
+    where
+        B: Unsize<[u16]>,
+    {
+        let adc1 = self.0;
+
+
+        if dma1.ccr1.read().en().is_set() {
+            return Err(dma::Error::InUse);
+        }
+
+        pwm.disable(Channel::_2);
+        pwm.set_duty(Channel::_2, 1);
+
+        let buffer: &[u16] = &buffer.lock()[0];
+
+        dma1.cndtr1
+            .write(|w| unsafe { w.ndt().bits(u16(buffer.len() * 2).unwrap()) });
+
+        dma1.cpar1
+            .write(|w| unsafe { w.bits(&adc1.dr as *const _ as u32) });
+
+        dma1.cmar1
+            .write(|w| unsafe { w.bits(buffer.as_ptr() as u32) });
+
+        dma1.ccr1.modify(|_, w| w.en().set());
+        pwm.enable(Channel::_2);
+
+        Ok(())
+    }
+}
diff --git a/src/dma.rs b/src/dma.rs
index c47fbcf8a8d4fa75b06db14b027aa1310830b1a1..d4f6f31773c6a186c804bb11372f378024bee0b3 100644
--- a/src/dma.rs
+++ b/src/dma.rs
@@ -1,21 +1,30 @@
 //! Direct Memory Access (DMA)
 
 use core::cell::{Cell, UnsafeCell};
-use core::marker::PhantomData;
-use core::ops;
+use core::marker::{PhantomData, Unsize};
+use core::{ops, slice};
 
 use nb;
 use stm32f103xx::DMA1;
+use volatile_register::RO;
 
 /// DMA error
 #[derive(Debug)]
 pub enum Error {
     /// DMA channel in use
     InUse,
+    /// Previous data got overwritten before it could be read because it was
+    /// not accessed in a timely fashion
+    Overrun,
     /// Transfer error
     Transfer,
 }
 
+/// Channel 1 of DMA1
+pub struct Dma1Channel1 {
+    _0: (),
+}
+
 /// Channel 2 of DMA1
 pub struct Dma1Channel2 {
     _0: (),
@@ -249,3 +258,116 @@ impl<T> Buffer<T, Dma1Channel5> {
         }
     }
 }
+
+/// A circular buffer associated to a DMA `CHANNEL`
+pub struct CircBuffer<T, B, CHANNEL>
+where
+    B: Unsize<[T]>,
+{
+    _marker: PhantomData<CHANNEL>,
+    _t: PhantomData<[T]>,
+    buffer: UnsafeCell<[B; 2]>,
+    status: Cell<CircStatus>,
+}
+
+impl<T, B, CHANNEL> CircBuffer<T, B, CHANNEL>
+where
+    B: Unsize<[T]>,
+{
+    pub(crate) fn lock(&self) -> &[B; 2] {
+        assert_eq!(self.status.get(), CircStatus::Free);
+
+        self.status.set(CircStatus::MutatingFirstHalf);
+
+        unsafe { &*self.buffer.get() }
+    }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum CircStatus {
+    /// Not in use by the DMA
+    Free,
+    /// The DMA is mutating the first half of the buffer
+    MutatingFirstHalf,
+    /// The DMA is mutating the second half of the buffer
+    MutatingSecondHalf,
+}
+
+impl<T, B> CircBuffer<T, B, Dma1Channel1>
+where
+    B: Unsize<[T]>,
+    T: Atomic,
+{
+    /// Constructs a circular buffer from two halves
+    pub const fn new(buffer: [B; 2]) -> Self {
+        CircBuffer {
+            _t: PhantomData,
+            _marker: PhantomData,
+            buffer: UnsafeCell::new(buffer),
+            status: Cell::new(CircStatus::Free),
+        }
+    }
+
+    /// Yields read access to the half of the circular buffer that's not
+    /// currently being mutated by the DMA
+    pub fn read(&self, dma1: &DMA1) -> nb::Result<&[RO<T>], Error> {
+        let status = self.status.get();
+
+        assert_ne!(status, CircStatus::Free);
+
+        let isr = dma1.isr.read();
+
+        if isr.teif1().is_set() {
+            Err(nb::Error::Other(Error::Transfer))
+        } else {
+            match status {
+                CircStatus::MutatingFirstHalf => {
+                    if isr.tcif1().is_set() {
+                        Err(nb::Error::Other(Error::Overrun))
+                    } else if isr.htif1().is_set() {
+                        dma1.ifcr.write(|w| w.chtif1().set());
+
+                        self.status.set(CircStatus::MutatingSecondHalf);
+
+                        unsafe {
+                            let half: &[T] = &(*self.buffer.get())[0];
+                            Ok(slice::from_raw_parts(
+                                half.as_ptr() as *const _,
+                                half.len(),
+                            ))
+                        }
+                    } else {
+                        Err(nb::Error::WouldBlock)
+                    }
+                }
+                CircStatus::MutatingSecondHalf => {
+                    if isr.htif1().is_set() {
+                        Err(nb::Error::Other(Error::Overrun))
+                    } else if isr.tcif1().is_set() {
+                        dma1.ifcr.write(|w| w.ctcif1().set());
+
+                        self.status.set(CircStatus::MutatingFirstHalf);
+
+                        unsafe {
+                            let half: &[T] = &(*self.buffer.get())[1];
+                            Ok(slice::from_raw_parts(
+                                half.as_ptr() as *const _,
+                                half.len(),
+                            ))
+                        }
+                    } else {
+                        Err(nb::Error::WouldBlock)
+                    }
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+}
+
+/// Values that can be atomically read
+pub trait Atomic: Copy {}
+
+impl Atomic for u8 {}
+impl Atomic for u16 {}
+impl Atomic for u32 {}
diff --git a/src/lib.rs b/src/lib.rs
index ccf869a365d9261f8a1a056cdc7dd79fb7cd3b00..fd8c0a7aafe38ce6d4aa513c2090840b77bcc376 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -23,9 +23,11 @@ extern crate either;
 extern crate embedded_hal as hal;
 extern crate nb;
 extern crate static_ref;
+extern crate volatile_register;
 
 pub extern crate stm32f103xx;
 
+pub mod adc;
 pub mod capture;
 pub mod dma;
 pub mod gpio;
diff --git a/src/pwm.rs b/src/pwm.rs
index e9cdcc02b84e61741d92475681f114d999bf4022..4d48d7eb92b1092576c4397c139ee631b4ef97f8 100644
--- a/src/pwm.rs
+++ b/src/pwm.rs
@@ -392,7 +392,7 @@ where
                 // pinc: Peripheral increment mode disabled
                 // circ: Circular mode disabled
                 // dir: Transfer from memory to peripheral
-                // tceie: Transfer complete interrupt disabled
+                // tceie: Transfer complete interrupt enabled
                 // en: Disabled
                 dma1.ccr2.write(|w| unsafe {
                     w.mem2mem()