diff --git a/Cargo.toml b/Cargo.toml
index 736149cdad35f5482777c9b851eecd1b2d8daddc..95785019253a45c2894ed5984ecc2aedf22e5dc9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,7 @@ panic-halt = "0.2.0"
 #panic-itm = "0.4.2"
 
 # Uncomment for the rtt-timing example.
-panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
+# panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
 
 # Uncomment for the panic example.
 #panic-semihosting = "0.5.6"
diff --git a/README.md b/README.md
index 4b61f4501b43919c65719d57940efbf8fe1ed1ab..a6655f0780d165f43dde4c5b8ebf55744222841a 100644
--- a/README.md
+++ b/README.md
@@ -26,24 +26,33 @@
 
 - [USB Cable]
 
-| Signl | Color | Pin  |
-| ----- | ----- | ---- |
-| V+    | Red   | ---- |
-| D-    | White | PA11 |
-| D+    | Green | PA12 |
-| Gnd   | Black | ---- |
+| Signal | Color | Pin  |
+| ------ | ----- | ---- |
+| V+     | Red   | ---- |
+| D-     | White | PA11 |
+| D+     | Green | PA12 |
+| Gnd    | Black | ---- |
 
 D+ used for re-enumeration
 
+---
+
 ## Debug interface
 
 - Serial Wire debugging uses pins PA13 and PA14. So refrain from using those unless absolutely necessary.
 
+---
+
+## Examples
+
+
+---
+
 ## Troubleshooting
 
 ### Fail to connect with openocd
 
-First check that your stilnk nucleo programmer is found by the host.
+First check that your stlink nucleo programmer is found by the host.
 
 ```shell
 > lsusb
diff --git a/build.rs b/build.rs
index d534cc3df609de30befec7eda32c4b44ab7ed63e..1b9fd2cfc35a12ebc1460e4b7e2ee3218c3c7b84 100644
--- a/build.rs
+++ b/build.rs
@@ -8,12 +8,15 @@
 //! updating `memory.x` ensures a rebuild of the application with the
 //! new memory settings.
 
+use core::f64::consts::PI;
 use std::env;
 use std::fs::File;
-use std::io::Write;
-use std::path::PathBuf;
+use std::{
+    io::{Result, Write},
+    path::{Path, PathBuf},
+};
 
-fn main() {
+fn main() -> Result<()> {
     // Put `memory.x` in our output directory and ensure it's
     // on the linker search path.
     let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
@@ -28,4 +31,24 @@ fn main() {
     // here, we ensure the build script is only re-run when
     // `memory.x` is changed.
     println!("cargo:rerun-if-changed=memory.x");
+
+    // generate a sine table
+    println!("cargo:rerun-if-changed=build.rs");
+    let out_dir = env::var("OUT_DIR").unwrap();
+    let dest_path = Path::new(&out_dir).join("sin_abs_const.rs");
+    let mut f = File::create(&dest_path).unwrap();
+
+    const SINE_BUF_SIZE: usize = 256;
+    write!(f, "const SINE_BUF_SIZE: usize = {};\n", SINE_BUF_SIZE)?;
+    write!(f, "const SINE_BUF: [u8; SINE_BUF_SIZE] = [")?;
+
+    for i in 0..SINE_BUF_SIZE {
+        let s = ((i as f64) * 2.0 * PI / SINE_BUF_SIZE as f64).sin();
+        let v = (128.0 + 128.0 * s) as u8;
+
+        write!(f, " {},", v)?;
+    }
+    write!(f, "];\n")?;
+
+    Ok(())
 }
diff --git a/examples/rtt-pwm-dma.rs b/examples/rtt-pwm-dma.rs
index 1c169a947d2c76af5f81741befc345ba60b0c0b6..de6512e444de3e9b401715d2dd30d392d6dd5b1c 100644
--- a/examples/rtt-pwm-dma.rs
+++ b/examples/rtt-pwm-dma.rs
@@ -103,6 +103,60 @@ const APP: () = {
                 .set_bit()
         });
 
+        // Setup TIMER2
+        let tim2 = dp.TIM2;
+        // Here we need unsafe as we are "stealing" the RCC peripheral
+        // At this point it has been contrained into SysConf and used to set clocks
+        let rcc = unsafe { &(*stm32::RCC::ptr()) };
+
+        rcc.apb1enr.modify(|_, w| w.tim2en().set_bit());
+        rcc.apb1rstr.modify(|_, w| w.tim2rst().set_bit());
+        rcc.apb1rstr.modify(|_, w| w.tim2rst().clear_bit());
+
+        // auto-reload, preload
+        tim1.cr1.modify(|_, w| w.arpe().set_bit());
+
+        let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
+        // check that its actually 48_000_000
+        rprintln!("clk {}", clk);
+
+        // assume we have a sine at
+        let pre = 0;
+        rprintln!("pre {}", pre);
+        tim2.psc.write(|w| w.psc().bits(pre));
+
+        // we want 8 bits of resolution
+        // so our ARR = 2^8 - 1 = 256 - 1 = 255
+        let arr = 255;
+        rprintln!("arr {}", arr);
+        tim1.arr.write(|w| unsafe { w.bits(arr) });
+
+        //  Trigger update event to load the registers
+        tim1.cr1.modify(|_, w| w.urs().set_bit());
+        tim1.egr.write(|w| w.ug().set_bit());
+        tim1.cr1.modify(|_, w| w.urs().clear_bit());
+
+        // Set main output enable of all Output Compare (OC) registers
+        tim1.bdtr.modify(|_, w| w.moe().set_bit());
+
+        // Set output enable for channels 1 and 2
+        // Channel 1 (bit  0)
+        unsafe { bb::set(&tim1.ccer, 0) }
+        // Channel 4 (bit  0)
+        unsafe { bb::set(&tim1.ccer, 4) }
+
+        // Setup the timer
+        tim1.cr1.write(|w| {
+            w.cms()
+                .bits(0b00) // edge aligned mode
+                .dir() // counter used as upcounter
+                .clear_bit()
+                .opm() // one pulse mode
+                .clear_bit()
+                .cen() // enable counter
+                .set_bit()
+        });
+
         // Set duty cycle of Channels
         tim1.ccr1.write(|w| unsafe { w.ccr().bits(128) });
         tim1.ccr2.write(|w| unsafe { w.ccr().bits(128) });
@@ -119,14 +173,21 @@ const APP: () = {
         rprintln!("here");
         tim1.sr.modify(|_, w| w.uif().clear());
 
+        let mut dma_buff = [0u8; 256];
+        for i in 0..256 {
+            dma_buff[i] = i as u8;
+        }
+
         loop {
             for i in 0..256 {
                 // wait until next update event
                 while tim1.sr.read().uif().is_clear() {}
                 tim1.sr.modify(|_, w| w.uif().clear());
 
-                tim1.ccr1.write(|w| unsafe { w.ccr().bits(i) });
-                tim1.ccr2.write(|w| unsafe { w.ccr().bits(i) });
+                tim1.ccr1
+                    .write(|w| unsafe { w.ccr().bits(dma_buff[i] as u16) });
+                tim1.ccr2
+                    .write(|w| unsafe { w.ccr().bits(dma_buff[i] as u16) });
             }
         }
     }
diff --git a/examples/rtt-pwm-sine.rs b/examples/rtt-pwm-sine.rs
new file mode 100644
index 0000000000000000000000000000000000000000..75d4880bdaed48a5ae5f3f1bdce932119529caea
--- /dev/null
+++ b/examples/rtt-pwm-sine.rs
@@ -0,0 +1,219 @@
+//! examples/rtt-pwm-sine.rs
+//! cargo run --examples rtt-pwm-sine
+
+// #![deny(unsafe_code)]
+// #![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use core::f32::consts::PI;
+use cortex_m::{asm, peripheral::DWT};
+use panic_halt as _;
+use rtt_target::{rprint, rprintln, rtt_init_print};
+
+use stm32f4xx_hal::{bb, dma, gpio::Speed, prelude::*, pwm, stm32};
+
+include!(concat!(env!("OUT_DIR"), "/sin_abs_const.rs"));
+
+#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true)]
+const APP: () = {
+    #[init]
+    fn init(mut cx: init::Context) {
+        rtt_init_print!();
+        rprintln!("init");
+        let dp = cx.device;
+
+        // Initialize (enable) the monotonic timer (CYCCNT)
+        cx.core.DCB.enable_trace();
+        cx.core.DWT.enable_cycle_counter();
+
+        let rcc = dp.RCC.constrain();
+        // Set up the system clock. 48 MHz?
+        let clocks = rcc
+            .cfgr
+            // .use_hse(8.mhz())
+            .sysclk(48.mhz())
+            .pclk1(24.mhz())
+            .freeze();
+
+        let gpioa = dp.GPIOA.split();
+        // we set the pins to VeryHigh to get the sharpest waveform possible
+        // (rise and fall times should have similar characteristics)
+        let channels = (
+            gpioa.pa8.into_alternate_af1().set_speed(Speed::VeryHigh),
+            gpioa.pa9.into_alternate_af1().set_speed(Speed::VeryHigh),
+        );
+
+        // Setup PWM RAW
+        let tim1 = dp.TIM1;
+        // Here we need unsafe as we are "stealing" the RCC peripheral
+        // At this point it has been contrained into SysConf and used to set clocks
+        let rcc = unsafe { &(*stm32::RCC::ptr()) };
+
+        rcc.apb2enr.modify(|_, w| w.tim1en().set_bit());
+        rcc.apb2rstr.modify(|_, w| w.tim1rst().set_bit());
+        rcc.apb2rstr.modify(|_, w| w.tim1rst().clear_bit());
+
+        // Setup chanel 1 and 2 as pwm_mode1
+        tim1.ccmr1_output()
+            .modify(|_, w| w.oc1pe().set_bit().oc1m().pwm_mode1());
+
+        tim1.ccmr1_output()
+            .modify(|_, w| w.oc2pe().set_bit().oc2m().pwm_mode1());
+
+        // The reference manual is a bit ambiguous about when enabling this bit is really
+        // necessary, but since we MUST enable the preload for the output channels then we
+        // might as well enable for the auto-reload too
+        tim1.cr1.modify(|_, w| w.arpe().set_bit());
+
+        let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
+        // check that its actually 48_000_000
+        rprintln!("clk {}", clk);
+
+        // we want maximum performance, thus we set the prescaler to 0
+        let pre = 0;
+        rprintln!("pre {}", pre);
+        tim1.psc.write(|w| w.psc().bits(pre));
+
+        // we want 8 bits of resolution
+        // so our ARR = 2^8 - 1 = 256 - 1 = 255
+        let arr = 255;
+        rprintln!("arr {}", arr);
+        tim1.arr.write(|w| unsafe { w.bits(arr) });
+
+        //  Trigger update event to load the registers
+        tim1.cr1.modify(|_, w| w.urs().set_bit());
+        tim1.egr.write(|w| w.ug().set_bit());
+        tim1.cr1.modify(|_, w| w.urs().clear_bit());
+
+        // Set main output enable of all Output Compare (OC) registers
+        tim1.bdtr.modify(|_, w| w.moe().set_bit());
+
+        // Set output enable for channels 1 and 2
+        // Channel 1 (bit  0)
+        unsafe { bb::set(&tim1.ccer, 0) }
+        // Channel 4 (bit  0)
+        unsafe { bb::set(&tim1.ccer, 4) }
+
+        // Setup the timer
+        tim1.cr1.write(|w| {
+            w.cms()
+                .bits(0b00) // edge aligned mode
+                .dir() // counter used as upcounter
+                .clear_bit()
+                .opm() // one pulse mode
+                .clear_bit()
+                .cen() // enable counter
+                .set_bit()
+        });
+
+        // Setup TIMER2
+        let tim2 = dp.TIM2;
+        // Here we need unsafe as we are "stealing" the RCC peripheral
+        // At this point it has been contrained into SysConf and used to set clocks
+        let rcc = unsafe { &(*stm32::RCC::ptr()) };
+
+        rcc.apb1enr.modify(|_, w| w.tim2en().set_bit());
+        rcc.apb1rstr.modify(|_, w| w.tim2rst().set_bit());
+        rcc.apb1rstr.modify(|_, w| w.tim2rst().clear_bit());
+
+        // auto-reload, preload
+        tim1.cr1.modify(|_, w| w.arpe().set_bit());
+
+        let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
+        // check that its actually 48_000_000
+        rprintln!("clk {}", clk);
+
+        // assuinclude!(concat!(env!("OUT_DIR"), "/sin.rs"));c.write(|w| w.psc().bits(pre));
+
+        // we want 8 bits of resolution
+        // so our ARR = 2^8 - 1 = 256 - 1 = 255
+        let arr = 255;
+        rprintln!("arr {}", arr);
+        tim1.arr.write(|w| unsafe { w.bits(arr) });
+
+        //  Trigger update event to load the registers
+        tim1.cr1.modify(|_, w| w.urs().set_bit());
+        tim1.egr.write(|w| w.ug().set_bit());
+        tim1.cr1.modify(|_, w| w.urs().clear_bit());
+
+        // Set main output enable of all Output Compare (OC) registers
+        tim1.bdtr.modify(|_, w| w.moe().set_bit());
+
+        // Set output enable for channels 1 and 2
+        // Channel 1 (bit  0)
+        unsafe { bb::set(&tim1.ccer, 0) }
+        // Channel 4 (bit  0)
+        unsafe { bb::set(&tim1.ccer, 4) }
+
+        // Setup the timer
+        tim1.cr1.write(|w| {
+            w.cms()
+                .bits(0b00) // edge aligned mode
+                .dir() // counter used as upcounter
+                .clear_bit()
+                .opm() // one pulse mode
+                .clear_bit()
+                .cen() // enable counter
+                .set_bit()
+        });
+
+        // Set duty cycle of Channels
+        tim1.ccr1.write(|w| unsafe { w.ccr().bits(128) });
+        tim1.ccr2.write(|w| unsafe { w.ccr().bits(128) });
+
+        // Set preload for the CCx
+        tim1.cr2.write(|w| w.ccpc().set_bit());
+
+        tim1.dier.write(|w| w.uie().enabled());
+        tim1.sr.modify(|_, w| w.uif().clear());
+
+        // Set divider to 4, (48_000_000/256)/4
+        tim1.rcr.modify(|_, w| unsafe { w.rep().bits(4) });
+
+        while tim1.sr.read().uif().is_clear() {
+            rprint!("-");
+        }
+        rprintln!("here");
+        tim1.sr.modify(|_, w| w.uif().clear());
+
+        loop {
+            for i in 0..SINE_BUF_SIZE {
+                // wait until next update event
+
+                while tim1.sr.read().uif().is_clear() {}
+                tim1.sr.modify(|_, w| w.uif().clear());
+
+                tim1.ccr1
+                    .write(|w| unsafe { w.ccr().bits(SINE_BUF[i] as u16) });
+                tim1.ccr2
+                    .write(|w| unsafe { w.ccr().bits(SINE_BUF[i] as u16) });
+            }
+        }
+    }
+
+    // [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();
+    // }
+
+    #[idle]
+    fn idle(_cx: idle::Context) -> ! {
+        rprintln!("idle");
+        loop {
+            continue;
+        }
+    }
+};