diff --git a/examples/bare1.rs b/examples/bare1.rs
index 3c76d7c6f7a48c54a8ddcaf34d7c37c60b3628cf..5f077c8ff01ced929cc96c7f02d57cf38be85ffe 100644
--- a/examples/bare1.rs
+++ b/examples/bare1.rs
@@ -3,7 +3,7 @@
 //! Inspecting the generated assembly
 //!
 //! What it covers
-//! - ITM tracing
+//! - Rust panic tracing using ITM
 //! - assembly calls and inline assembly
 //! - more on arithmetics
 
@@ -45,7 +45,7 @@ fn main() -> ! {
 //
 //    You may need/want to install additional components also.
 //    To that end look at the install section in the README.md.
-//    If you change toolchain, you may need to exit and re-start `vscode`.
+//    (If you change toolchain, you may need to exit and re-start `vscode`.)
 //
 // 1. Build and run the application
 //
diff --git a/examples/bare2.rs b/examples/bare2.rs
index d99f533cf37ebe3e139a9da9e8c3bafedc32e4d3..ce6acb20fb9221507961a8586a4ba8211917db1c 100644
--- a/examples/bare2.rs
+++ b/examples/bare2.rs
@@ -6,13 +6,14 @@
 //! - Generating documentation
 //! - Using core peripherals
 //! - Measuring time using the DWT
-//! - ITM tracing
+//! - ITM tracing using `iprintln`
+//! - Panic halt
 //!
 
 #![no_main]
 #![no_std]
 
-extern crate panic_halt;
+use panic_halt as _;
 
 use cortex_m::{iprintln, peripheral::DWT, Peripherals};
 use cortex_m_rt::entry;
@@ -37,7 +38,7 @@ fn main() -> ! {
     dwt.enable_cycle_counter();
 
     // Reading the cycle counter can be done without `owning` access
-    // the DWT (since it has no side effetc).
+    // the DWT (since it has no side effect).
     //
     // Look in the docs:
     // pub fn enable_cycle_counter(&mut self)
@@ -89,7 +90,7 @@ fn main() -> ! {
 //
 //    ** your answer here **
 //
-// commit your answers (bare2_1)
+//    commit your answers (bare2_1)
 //
 // 3. *Optional
 //    Inspect the generated binaries, and try stepping through the code
diff --git a/examples/bare3.rs b/examples/bare3.rs
index 0c0c0ae8f2915205b3808c4197c4c38f0750e795..665b4da687d3ca0285abdf1ae5268b9ba0a4c340 100644
--- a/examples/bare3.rs
+++ b/examples/bare3.rs
@@ -5,7 +5,7 @@
 //! What it covers:
 //! - Types, str, arrays ([u8; usize]), slices (&[u8])
 //! - Iteration, copy
-//! - Semihosting (tracing)
+//! - Semihosting (tracing using `hprintln`
 
 #![no_main]
 #![no_std]
@@ -103,3 +103,46 @@ fn main() -> ! {
 //    Implement and test your solution.
 //
 //    Commit your answers (bare3_5)
+//
+// 6. Optional
+//    Rust is heavily influenced by functional languages.
+//    Figure out how you can use an iterator to work over both
+//    the `a` and `bs` to copy the content of `bs` to `a`.
+//
+//    You may use
+//    - `iter` (to turn a slice into an iterator)
+//    - `zip` (to merge two slices into an iterator)
+//    - a for loop to assign the elements
+//
+//    Commit your solution (bare3_6)
+//
+// 7. Optional
+//    Iter using `foreach` and a closure instead of the for loop.
+//
+//    Commit your solution (bare3_7)
+//
+// 8. Optional*
+//    Now benchmark your different solutions using the cycle accurate
+//    DWT based approach (in release mode).
+//
+//    Cycle count for `raw` indexing
+//
+//    ** your answer here **
+//
+//    Cycle count for the primitive slice approach.
+//
+//    ** your answer here **
+//
+//    Cycle count for the primitive slice approach.
+//
+//    ** your answer here **
+//
+//    Cycle count for the zip + for loop approach.
+//
+//    ** your answer here **
+//
+//    Cycle count for the zip + for_each approach.
+//
+//    What conclusions can you draw, does Rust give you zero-cost abstractions?
+//
+//    ** your answer here **
diff --git a/examples/bare4.rs b/examples/bare4.rs
new file mode 100644
index 0000000000000000000000000000000000000000..07ce483f31f97174ac046b5a43a6548af79a212d
--- /dev/null
+++ b/examples/bare4.rs
@@ -0,0 +1,134 @@
+//! bare4.rs
+//!
+//! Access to Peripherals
+//!
+//! What it covers:
+//! - Raw pointers
+//! - Volatile read/write
+//! - Busses and clocking
+//! - GPIO (a primitive abstraction)
+
+#![no_std]
+#![no_main]
+
+extern crate panic_halt;
+
+extern crate cortex_m;
+use cortex_m_rt::entry;
+
+// Peripheral addresses as constants
+#[rustfmt::skip]
+mod address {
+    pub const PERIPH_BASE: u32      = 0x40000000;
+    pub const AHB1PERIPH_BASE: u32  = PERIPH_BASE + 0x00020000;
+    pub const RCC_BASE: u32         = AHB1PERIPH_BASE + 0x3800;
+    pub const RCC_AHB1ENR: u32      = RCC_BASE + 0x30;
+    pub const GBPIA_BASE: u32       = AHB1PERIPH_BASE + 0x0000;
+    pub const GPIOA_MODER: u32      = GBPIA_BASE + 0x00;
+    pub const GPIOA_BSRR: u32       = GBPIA_BASE + 0x18;
+}
+
+use address::*;
+
+// see the Reference Manual RM0368 (www.st.com/resource/en/reference_manual/dm00096844.pdf)
+// rcc,     chapter 6
+// gpio,    chapter 8
+
+#[inline(always)]
+fn read_u32(addr: u32) -> u32 {
+    unsafe { core::ptr::read_volatile(addr as *const _) }
+    //core::ptr::read_volatile(addr as *const _)
+}
+
+#[inline(always)]
+fn write_u32(addr: u32, val: u32) {
+    unsafe {
+        core::ptr::write_volatile(addr as *mut _, val);
+    }
+}
+
+fn wait(i: u32) {
+    for _ in 0..i {
+        cortex_m::asm::nop(); // no operation (cannot be optimized out)
+    }
+}
+
+#[entry]
+fn main() -> ! {
+    // power on GPIOA
+    let r = read_u32(RCC_AHB1ENR); // read
+    write_u32(RCC_AHB1ENR, r | 1); // set enable
+
+    // configure PA5 as output
+    let r = read_u32(GPIOA_MODER) & !(0b11 << (5 * 2)); // read and mask
+    write_u32(GPIOA_MODER, r | 0b01 << (5 * 2)); // set output mode
+
+    // and alter the data output through the BSRR register
+    // this is more efficient as the read register is not needed.
+
+    loop {
+        // set PA5 high
+        write_u32(GPIOA_BSRR, 1 << 5); // set bit, output hight (turn on led)
+        wait(10_000);
+
+        // set PA5 low
+        write_u32(GPIOA_BSRR, 1 << (5 + 16)); // clear bit, output low (turn off led)
+        wait(10_000);
+    }
+}
+
+// 0.  Build and run the application (debug build).
+//
+//    > cargo run --example bare4
+//    (or use the vscode)
+//
+// 1.  Did you enjoy the blinking?
+//
+//    ** your answer here **
+//
+//    Now lookup the data-sheets, and read each section referred,
+//    6.3.11, 8.4.1, 8.4.7
+//
+//    Document each low level access *code* by the appropriate section in the
+//    data sheet.
+//
+//    Commit your answers (bare4_1)
+//
+// 2. Comment out line 40 and uncomment line 41 (essentially omitting the `unsafe`)
+//
+//    //unsafe { core::ptr::read_volatile(addr as *const _) }
+//    core::ptr::read_volatile(addr as *const _)
+//
+//    What was the error message and explain why.
+//
+//    ** your answer here **
+//
+//    Digging a bit deeper, why do you think `read_volatile` is declared `unsafe`.
+//    (https://doc.rust-lang.org/core/ptr/fn.read_volatile.html, for some food for thought )
+//
+//    ** your answer here **
+//
+//    Commit your answers (bare4_2)
+//
+// 3. Volatile read/writes are explicit *volatile operations* in Rust, while in C they
+//    are declared at type level (i.e., access to varibles declared volatile amounts to
+//    volatile reads/and writes).
+//
+//    Both C and Rust (even more) allows code optimization to re-order operations, as long
+//    as data dependencies are preserved.
+//
+//    Why is it important that ordering of volatile operations are ensured by the compiler?
+//
+//    ** your answer here **
+//
+//    Give an example in the above code, where reordering might make things go horribly wrong
+//    (hint, accessing a peripheral not being powered...)
+//
+//    ** your answer here **
+//
+//    Without the non-reordering property of `write_volatile/read_volatile` could that happen in theory
+//    (argue from the point of data dependencies).
+//
+//    ** your answer here **
+//
+//    Commit your answers (bare4_3)