Browse Source

Preliminary remodelled version of japarics stm32f103xx-hal crate

Signed-off-by: Daniel Egger <daniel@eggers-club.de>
Daniel Egger 5 years ago
commit
83a0a7908f
42 changed files with 5622 additions and 0 deletions
  1. 8 0
      .cargo/config
  2. 18 0
      .gdbinit
  3. 6 0
      .gitignore
  4. 92 0
      Cargo.toml
  5. 201 0
      LICENSE-APACHE
  6. 25 0
      LICENSE-MIT
  7. 21 0
      README.md
  8. 56 0
      examples/blinky.rs
  9. 49 0
      examples/delay.rs
  10. 330 0
      examples/enc28j60-coap.rs
  11. 283 0
      examples/enc28j60.rs
  12. 21 0
      examples/hello.rs
  13. 32 0
      examples/itm.rs
  14. 36 0
      examples/led.rs
  15. 69 0
      examples/mfrc522.rs
  16. 113 0
      examples/motor.rs
  17. 82 0
      examples/mpu9250.rs
  18. 39 0
      examples/nojtag.rs
  19. 91 0
      examples/pwm.rs
  20. 73 0
      examples/qei.rs
  21. 87 0
      examples/serial-dma-circ.rs
  22. 84 0
      examples/serial-dma-peek.rs
  23. 78 0
      examples/serial-dma-rx.rs
  24. 84 0
      examples/serial-dma-tx.rs
  25. 85 0
      examples/serial.rs
  26. 6 0
      memory.x
  27. 110 0
      src/afio.rs
  28. 22 0
      src/bb.rs
  29. 86 0
      src/delay.rs
  30. 426 0
      src/dma.rs
  31. 35 0
      src/flash.rs
  32. 493 0
      src/gpio.rs
  33. 476 0
      src/i2c.rs
  34. 71 0
      src/lib.rs
  35. 13 0
      src/prelude.rs
  36. 337 0
      src/pwm.rs
  37. 133 0
      src/qei.rs
  38. 345 0
      src/rcc.rs
  39. 467 0
      src/serial.rs
  40. 228 0
      src/spi.rs
  41. 117 0
      src/time.rs
  42. 194 0
      src/timer.rs

+ 8 - 0
.cargo/config

@@ -0,0 +1,8 @@
+[target.thumbv7m-none-eabi]
+runner = 'arm-none-eabi-gdb'
+rustflags = [
+  "-C", "link-arg=-Tlink.x",
+]
+
+[build]
+target = "thumbv7m-none-eabi"

+ 18 - 0
.gdbinit

@@ -0,0 +1,18 @@
+target remote :3333
+
+monitor arm semihosting enable
+
+# # send captured ITM to the file itm.fifo
+# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
+# # 8000000 must match the core clock frequency
+# monitor tpiu config internal itm.fifo uart off 8000000
+
+# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
+# # 2000000 is the frequency of the SWO pin
+# monitor tpiu config external uart off 8000000 2000000
+
+# # enable ITM port 0
+# monitor itm port 0 on
+
+load
+step

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+**/*.rs.bk
+*.org
+.#*
+.gdb_history
+Cargo.lock
+target/

+ 92 - 0
Cargo.toml

@@ -0,0 +1,92 @@
+[package]
+authors = ["Jorge Aparicio <jorge@japaric.io>"]
+categories = ["embedded", "hardware-support", "no-std"]
+description = "HAL for the STM32F100xx family of microcontrollers"
+keywords = ["arm", "cortex-m", "stm32", "hal"]
+license = "MIT OR Apache-2.0"
+name = "stm32f1xx-hal"
+repository = "https://github.com/stm32-rs/stm32f1xx-hal"
+version = "0.0.0"
+
+[dependencies]
+cortex-m = "0.5.8"
+nb = "0.1.1"
+cortex-m-rt = "0.6.6"
+stm32f1 = "0.4.0"
+
+[dependencies.void]
+default-features = false
+version = "1.0.2"
+
+[dependencies.cast]
+default-features = false
+version = "0.2.2"
+
+[dependencies.embedded-hal]
+features = ["unproven"]
+version = "0.2.2"
+
+[dev-dependencies]
+panic-semihosting = "0.5.1"
+panic-itm = "0.4.0"
+# cortex-m-rtfm = "0.3.1"
+cortex-m-semihosting = "0.3.2"
+enc28j60 = "0.2.0"
+heapless = "0.4.0"
+m = "0.1.1"
+mfrc522 = "0.2.0"
+serde_derive = "1.0.81"
+
+[dev-dependencies.byteorder]
+default-features = false
+version = "1.2.7"
+
+[dev-dependencies.cobs]
+default-features = false
+version = "0.1.3"
+
+[dev-dependencies.crc16]
+default-features = false
+version = "0.4.0"
+
+[dev-dependencies.either]
+default-features = false
+version = "1.5.0"
+
+[dev-dependencies.jnet]
+git = "https://github.com/japaric/jnet"
+rev = "df96b408049ca952ad7844d6552e87cf8fc18d2a"
+
+[dev-dependencies.motor-driver]
+git = "https://github.com/japaric/motor-driver"
+rev = "00c8b15223643090d69f1acfb8b7a7a43a440417"
+
+[dev-dependencies.mpu9250]
+git = "https://github.com/japaric/mpu9250"
+rev = "8f9ecad690093cb71c41301ca5e5705706150610"
+
+[dev-dependencies.serde]
+default-features = false
+version = "1.0.81"
+
+[dev-dependencies.serde-json-core]
+git = "https://github.com/japaric/serde-json-core"
+rev = "6f12b77c1ffeae167989fe06e0d8b15978bd6d18"
+
+[dev-dependencies.stm32f103xx]
+features = ["rt"]
+version = "0.11.0"
+
+[features]
+doc = []
+rt = ["stm32f1/rt"]
+stm32f103 = ["stm32f1/stm32f103"]
+
+[profile.dev]
+incremental = false
+codegen-units = 1
+
+[profile.release]
+codegen-units = 1
+debug = true
+lto = true

+ 201 - 0
LICENSE-APACHE

@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 25 - 0
LICENSE-MIT

@@ -0,0 +1,25 @@
+Copyright (c) 2017-2018 Jorge Aparicio
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.

+ 21 - 0
README.md

@@ -0,0 +1,21 @@
+# `stm32f1xx-hal`
+
+> [HAL] for the STM32F1 family of microcontrollers
+
+[HAL]: https://crates.io/crates/embedded-hal
+
+## License
+
+Licensed under either of
+
+- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
+  http://www.apache.org/licenses/LICENSE-2.0)
+- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.

+ 56 - 0
examples/blinky.rs

@@ -0,0 +1,56 @@
+//! Blinks an LED
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_std]
+#![no_main]
+
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+#[macro_use(block)]
+extern crate nb;
+
+use hal::prelude::*;
+use hal::stm32f103xx;
+use hal::timer::Timer;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let cp = cortex_m::Peripherals::take().unwrap();
+    let dp = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = dp.FLASH.constrain();
+    let mut rcc = dp.RCC.constrain();
+
+    // Try a different clock configuration
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+    // let clocks = rcc.cfgr
+    //     .sysclk(64.mhz())
+    //     .pclk1(32.mhz())
+    //     .freeze(&mut flash.acr);
+
+    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
+
+    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
+    // Try a different timer (even SYST)
+    let mut timer = Timer::syst(cp.SYST, 1.hz(), clocks);
+    loop {
+        block!(timer.wait()).unwrap();
+        led.set_high();
+        block!(timer.wait()).unwrap();
+        led.set_low();
+    }
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 49 - 0
examples/delay.rs

@@ -0,0 +1,49 @@
+//! "Blinky" using delays instead of a timer
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use hal::delay::Delay;
+use hal::prelude::*;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let dp = stm32f103xx::Peripherals::take().unwrap();
+    let cp = cortex_m::Peripherals::take().unwrap();
+
+    let mut flash = dp.FLASH.constrain();
+    let mut rcc = dp.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
+
+    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
+    let mut delay = Delay::new(cp.SYST, clocks);
+
+    loop {
+        led.set_high();
+        delay.delay_ms(1_000_u16);
+        led.set_low();
+        delay.delay_ms(1_000_u16);
+    }
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 330 - 0
examples/enc28j60-coap.rs

@@ -0,0 +1,330 @@
+//! ENC28J60 demo: a RESTful LED using CoAP
+//!
+//! The server will expose the LED as a resource under the `/led` path. You can use the CoAP client
+//! in the [`jnet`] crate to interact with the server.
+//!
+//! - `coap GET coap://192.168.1.33/led` will return the state of the LED: either "on" or "off".
+//! - `coap PUT coap://192.168.1.33/led on` will change the state of the LED; the payload must be
+//!   either "on" or "off".
+//!
+//! [`jnet`]: https://github.com/japaric/jnet
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![feature(nll)]
+#![feature(try_from)]
+#![no_main]
+#![no_std]
+
+#[macro_use]
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate enc28j60;
+extern crate heapless;
+extern crate jnet;
+extern crate panic_itm;
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_json_core as json;
+extern crate stm32f1xx_hal as hal;
+
+use core::convert::TryInto;
+
+use enc28j60::Enc28j60;
+use hal::delay::Delay;
+use hal::prelude::*;
+use hal::spi::Spi;
+use hal::stm32f103xx;
+use heapless::consts::*;
+use heapless::FnvIndexMap;
+use jnet::{arp, coap, ether, icmp, ipv4, mac, udp, Buffer};
+use rt::{entry, exception, ExceptionFrame};
+
+/* Constants */
+const KB: u16 = 1024;
+
+/* Network configuration */
+const MAC: mac::Addr = mac::Addr([0x20, 0x18, 0x03, 0x01, 0x00, 0x00]);
+const IP: ipv4::Addr = ipv4::Addr([192, 168, 1, 33]);
+
+// disable tracing
+// macro_rules! iprintln {
+//     ($($tt: tt)*) => {};
+// }
+
+// LED resource
+#[derive(Deserialize, Serialize)]
+struct Led {
+    led: bool,
+}
+
+#[entry]
+fn main() -> ! {
+    let mut cp = cortex_m::Peripherals::take().unwrap();
+    let dp = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut rcc = dp.RCC.constrain();
+    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
+    let mut flash = dp.FLASH.constrain();
+    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
+    let _stim = &mut cp.ITM.stim[0];
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    // LED
+    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
+    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
+    // turn the LED off during initialization
+    led.set_high();
+
+    // SPI
+    let mut rst = gpioa.pa3.into_push_pull_output(&mut gpioa.crl);
+    rst.set_high();
+    let mut ncs = gpioa.pa4.into_push_pull_output(&mut gpioa.crl);
+    ncs.set_high();
+    let sck = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl);
+    let miso = gpioa.pa6;
+    let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl);
+    let spi = Spi::spi1(
+        dp.SPI1,
+        (sck, miso, mosi),
+        &mut afio.mapr,
+        enc28j60::MODE,
+        1.mhz(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    // ENC28J60
+    let mut delay = Delay::new(cp.SYST, clocks);
+    let mut enc28j60 = Enc28j60::new(
+        spi,
+        ncs,
+        enc28j60::Unconnected,
+        rst,
+        &mut delay,
+        7 * KB,
+        MAC.0,
+    )
+    .ok()
+    .unwrap();
+
+    // LED on after initialization
+    led.set_low();
+
+    // FIXME some frames are lost when sending right after initialization
+    delay.delay_ms(100_u8);
+
+    let mut cache = FnvIndexMap::<_, _, U8>::new();
+
+    let mut buf = [0; 128];
+    loop {
+        let mut buf = Buffer::new(&mut buf);
+        let len = enc28j60.receive(buf.as_mut()).ok().unwrap();
+        buf.truncate(len);
+
+        if let Ok(mut eth) = ether::Frame::parse(buf) {
+            iprintln!(_stim, "\nRx({})", eth.as_bytes().len());
+            iprintln!(_stim, "* {:?}", eth);
+
+            let mac_src = eth.get_source();
+
+            match eth.get_type() {
+                ether::Type::Arp => {
+                    if let Ok(arp) = arp::Packet::parse(eth.payload_mut()) {
+                        match arp.downcast() {
+                            Ok(mut arp) => {
+                                iprintln!(_stim, "** {:?}", arp);
+
+                                if !arp.is_a_probe() {
+                                    cache.insert(arp.get_spa(), arp.get_sha()).ok();
+                                }
+
+                                // are they asking for us?
+                                if arp.get_oper() == arp::Operation::Request && arp.get_tpa() == IP
+                                {
+                                    // reply the ARP request
+                                    let tha = arp.get_sha();
+                                    let tpa = arp.get_spa();
+
+                                    arp.set_oper(arp::Operation::Reply);
+                                    arp.set_sha(MAC);
+                                    arp.set_spa(IP);
+                                    arp.set_tha(tha);
+                                    arp.set_tpa(tpa);
+                                    iprintln!(_stim, "\n** {:?}", arp);
+
+                                    // update the Ethernet header
+                                    eth.set_destination(tha);
+                                    eth.set_source(MAC);
+                                    iprintln!(_stim, "* {:?}", eth);
+
+                                    iprintln!(_stim, "Tx({})", eth.as_bytes().len());
+                                    enc28j60.transmit(eth.as_bytes()).ok().unwrap();
+                                }
+                            }
+                            Err(_arp) => {
+                                iprintln!(_stim, "** {:?}", _arp);
+                            }
+                        }
+                    } else {
+                        iprintln!(_stim, "Err(B)");
+                    }
+                }
+                ether::Type::Ipv4 => {
+                    if let Ok(mut ip) = ipv4::Packet::parse(eth.payload_mut()) {
+                        iprintln!(_stim, "** {:?}", ip);
+
+                        let ip_src = ip.get_source();
+
+                        if !mac_src.is_broadcast() {
+                            cache.insert(ip_src, mac_src).ok();
+                        }
+
+                        match ip.get_protocol() {
+                            ipv4::Protocol::Icmp => {
+                                if let Ok(icmp) = icmp::Packet::parse(ip.payload_mut()) {
+                                    iprintln!(_stim, "*** {:?}", icmp);
+
+                                    if icmp.get_type() == icmp::Type::EchoRequest
+                                        && icmp.get_code() == 0
+                                    {
+                                        let _icmp =
+                                            icmp.set_type(icmp::Type::EchoReply).update_checksum();
+                                        iprintln!(_stim, "\n*** {:?}", _icmp);
+
+                                        // update the IP header
+                                        let mut ip = ip.set_source(IP);
+                                        ip.set_destination(ip_src);
+                                        let _ip = ip.update_checksum();
+                                        iprintln!(_stim, "** {:?}", _ip);
+
+                                        // update the Ethernet header
+                                        eth.set_destination(*cache.get(&ip_src).unwrap());
+                                        eth.set_source(MAC);
+                                        iprintln!(_stim, "* {:?}", eth);
+
+                                        iprintln!(_stim, "Tx({})", eth.as_bytes().len());
+                                        enc28j60.transmit(eth.as_bytes()).ok().unwrap();
+                                    }
+                                } else {
+                                    iprintln!(_stim, "Err(C)");
+                                }
+                            }
+                            ipv4::Protocol::Udp => {
+                                if let Ok(udp) = udp::Packet::parse(ip.payload()) {
+                                    iprintln!(_stim, "*** {:?}", udp);
+
+                                    let udp_src = udp.get_source();
+
+                                    if udp.get_destination() == coap::PORT {
+                                        if let Ok(coap) = coap::Message::parse(udp.payload()) {
+                                            iprintln!(_stim, "**** {:?}", coap);
+
+                                            let path_is_led = coap
+                                                .options()
+                                                .filter_map(|opt| {
+                                                    if opt.number() == coap::OptionNumber::UriPath {
+                                                        Some(opt.value())
+                                                    } else {
+                                                        None
+                                                    }
+                                                })
+                                                .eq([b"led"].iter().cloned());
+
+                                            let mut resp = coap::Response::BadRequest;
+
+                                            match coap.get_code().try_into() {
+                                                Ok(coap::Method::Get) => {
+                                                    if path_is_led {
+                                                        resp = coap::Response::Content;
+                                                    }
+                                                }
+                                                Ok(coap::Method::Put) => {
+                                                    if path_is_led {
+                                                        if let Ok(json) = json::de::from_slice::<Led>(
+                                                            coap.payload(),
+                                                        ) {
+                                                            if json.led {
+                                                                led.set_low();
+                                                            } else {
+                                                                led.set_high();
+                                                            }
+                                                            resp = coap::Response::Changed;
+                                                        }
+                                                    }
+                                                }
+                                                _ => {}
+                                            }
+
+                                            let mut buf = eth.free();
+                                            buf.reset();
+                                            let mut eth = ether::Frame::new(buf);
+                                            eth.set_destination(*cache.get(&ip_src).unwrap());
+                                            eth.set_source(MAC);
+
+                                            eth.ipv4(|ip| {
+                                                ip.set_source(IP);
+                                                ip.set_destination(ip_src);
+
+                                                ip.udp(|udp| {
+                                                    udp.set_destination(udp_src);
+                                                    udp.set_source(coap::PORT);
+                                                    udp.coap(0, |coap| {
+                                                        coap.set_type(coap::Type::Acknowledgement);
+                                                        coap.set_code(resp);
+
+                                                        if resp == coap::Response::Content {
+                                                            coap.set_payload(
+                                                                &json::ser::to_vec::<[u8; 16], _>(
+                                                                    &Led {
+                                                                        led: led.is_set_low(),
+                                                                    },
+                                                                )
+                                                                .unwrap(),
+                                                            );
+                                                        } else {
+                                                            coap.set_payload(&[]);
+                                                        }
+
+                                                        iprintln!(_stim, "\n**** {:?}", coap);
+                                                    });
+
+                                                    iprintln!(_stim, "*** {:?}", udp);
+                                                });
+
+                                                iprintln!(_stim, "** {:?}", ip);
+                                            });
+
+                                            iprintln!(_stim, "* {:?}", eth);
+
+                                            let bytes = eth.as_bytes();
+                                            iprintln!(_stim, "Tx({})", bytes.len());
+                                            enc28j60.transmit(bytes).ok().unwrap();
+                                        }
+                                    }
+                                }
+                            }
+                            _ => {}
+                        }
+                    } else {
+                        iprintln!(_stim, "Err(D)");
+                    }
+                }
+                _ => {}
+            }
+        } else {
+            iprintln!(_stim, "Err(E)");
+        }
+    }
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 283 - 0
examples/enc28j60.rs

@@ -0,0 +1,283 @@
+//! ENC28J60 demo: pong server + UDP echo server
+//!
+//! This program:
+//!
+//! - Responds to ARP requests
+//! - Responds to ICMP echo requests, thus you can `ping` the device
+//! - Responds to *all* UDP datagrams by sending them back
+//!
+//! You can test this program by running the following commands:
+//!
+//! - `ping 192.168.1.33`. The device should respond and toggle the state of the LED on every `ping`
+//! request.
+//! - `nc -u 192.168.1.33 1337` and sending any string. The device should respond back by sending
+//! back the received string; the LED will toggle each time a UDP datagram is sent.
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![feature(nll)]
+#![no_std]
+#![no_main]
+
+extern crate cortex_m_rt as rt;
+#[macro_use]
+extern crate cortex_m;
+extern crate enc28j60;
+extern crate heapless;
+extern crate jnet;
+extern crate panic_itm;
+extern crate stm32f1xx_hal as hal;
+
+use enc28j60::Enc28j60;
+use hal::delay::Delay;
+use hal::prelude::*;
+use hal::spi::Spi;
+use hal::stm32f103xx;
+use heapless::consts::*;
+use heapless::FnvIndexMap;
+use jnet::{arp, ether, icmp, ipv4, mac, udp, Buffer};
+use rt::{entry, exception, ExceptionFrame};
+
+// uncomment to disable tracing
+// macro_rules! iprintln {
+//     ($($tt: tt)*) => {};
+// }
+
+/* Configuration */
+const MAC: mac::Addr = mac::Addr([0x20, 0x18, 0x03, 0x01, 0x00, 0x00]);
+const IP: ipv4::Addr = ipv4::Addr([192, 168, 1, 33]);
+
+/* Constants */
+const KB: u16 = 1024; // bytes
+
+#[entry]
+fn main() -> ! {
+    let mut cp = cortex_m::Peripherals::take().unwrap();
+    let dp = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut rcc = dp.RCC.constrain();
+    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
+    let mut flash = dp.FLASH.constrain();
+    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
+    let _stim = &mut cp.ITM.stim[0];
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    cp.DWT.enable_cycle_counter();
+
+    // LED
+    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
+    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
+    // turn the LED off during initialization
+    led.set_high();
+
+    // SPI
+    let mut ncs = gpioa.pa4.into_push_pull_output(&mut gpioa.crl);
+    ncs.set_high();
+    let sck = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl);
+    let miso = gpioa.pa6;
+    let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl);
+    let spi = Spi::spi1(
+        dp.SPI1,
+        (sck, miso, mosi),
+        &mut afio.mapr,
+        enc28j60::MODE,
+        1.mhz(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    // ENC28J60
+    let mut reset = gpioa.pa3.into_push_pull_output(&mut gpioa.crl);
+    reset.set_high();
+    let mut delay = Delay::new(cp.SYST, clocks);
+    let mut enc28j60 = Enc28j60::new(
+        spi,
+        ncs,
+        enc28j60::Unconnected,
+        reset,
+        &mut delay,
+        7 * KB,
+        MAC.0,
+    )
+    .ok()
+    .unwrap();
+
+    // LED on after initialization
+    led.set_low();
+
+    // FIXME some frames are lost when sent right after initialization
+    delay.delay_ms(100_u8);
+
+    // ARP cache
+    let mut cache = FnvIndexMap::<_, _, U8>::new();
+
+    let mut buf = [0; 256];
+    loop {
+        let mut buf = Buffer::new(&mut buf);
+        let len = enc28j60.receive(buf.as_mut()).ok().unwrap();
+        buf.truncate(len);
+
+        if let Ok(mut eth) = ether::Frame::parse(buf) {
+            iprintln!(_stim, "\nRx({})", eth.as_bytes().len());
+            iprintln!(_stim, "* {:?}", eth);
+
+            let src_mac = eth.get_source();
+
+            match eth.get_type() {
+                ether::Type::Arp => {
+                    if let Ok(arp) = arp::Packet::parse(eth.payload_mut()) {
+                        match arp.downcast() {
+                            Ok(mut arp) => {
+                                iprintln!(_stim, "** {:?}", arp);
+
+                                if !arp.is_a_probe() {
+                                    cache.insert(arp.get_spa(), arp.get_sha()).ok();
+                                }
+
+                                // are they asking for us?
+                                if arp.get_oper() == arp::Operation::Request && arp.get_tpa() == IP
+                                {
+                                    // reply to the ARP request
+                                    let tha = arp.get_sha();
+                                    let tpa = arp.get_spa();
+
+                                    arp.set_oper(arp::Operation::Reply);
+                                    arp.set_sha(MAC);
+                                    arp.set_spa(IP);
+                                    arp.set_tha(tha);
+                                    arp.set_tpa(tpa);
+                                    iprintln!(_stim, "\n** {:?}", arp);
+                                    let arp_len = arp.len();
+
+                                    // update the Ethernet header
+                                    eth.set_destination(tha);
+                                    eth.set_source(MAC);
+                                    eth.truncate(arp_len);
+                                    iprintln!(_stim, "* {:?}", eth);
+
+                                    iprintln!(_stim, "Tx({})", eth.as_bytes().len());
+                                    enc28j60.transmit(eth.as_bytes()).ok().unwrap();
+                                }
+                            }
+                            Err(_arp) => {
+                                // Not a Ethernet/IPv4 ARP packet
+                                iprintln!(_stim, "** {:?}", _arp);
+                            }
+                        }
+                    } else {
+                        // malformed ARP packet
+                        iprintln!(_stim, "Err(A)");
+                    }
+                }
+                ether::Type::Ipv4 => {
+                    if let Ok(mut ip) = ipv4::Packet::parse(eth.payload_mut()) {
+                        iprintln!(_stim, "** {:?}", ip);
+
+                        let src_ip = ip.get_source();
+
+                        if !src_mac.is_broadcast() {
+                            cache.insert(src_ip, src_mac).ok();
+                        }
+
+                        match ip.get_protocol() {
+                            ipv4::Protocol::Icmp => {
+                                if let Ok(icmp) = icmp::Packet::parse(ip.payload_mut()) {
+                                    match icmp.downcast::<icmp::EchoRequest>() {
+                                        Ok(request) => {
+                                            // is an echo request
+                                            iprintln!(_stim, "*** {:?}", request);
+
+                                            let src_mac = cache
+                                                .get(&src_ip)
+                                                .unwrap_or_else(|| unimplemented!());
+
+                                            let _reply: icmp::Packet<_, icmp::EchoReply, _> =
+                                                    request.into();
+                                            iprintln!(_stim, "\n*** {:?}", _reply);
+
+                                            // update the IP header
+                                            let mut ip = ip.set_source(IP);
+                                            ip.set_destination(src_ip);
+                                            let _ip = ip.update_checksum();
+                                            iprintln!(_stim, "** {:?}", _ip);
+
+                                            // update the Ethernet header
+                                            eth.set_destination(*src_mac);
+                                            eth.set_source(MAC);
+                                            iprintln!(_stim, "* {:?}", eth);
+
+                                            led.toggle();
+                                            iprintln!(_stim, "Tx({})", eth.as_bytes().len());
+                                            enc28j60.transmit(eth.as_bytes()).ok().unwrap();
+                                        }
+                                        Err(_icmp) => {
+                                            iprintln!(_stim, "*** {:?}", _icmp);
+                                        }
+                                    }
+                                } else {
+                                    // Malformed ICMP packet
+                                    iprintln!(_stim, "Err(B)");
+                                }
+                            }
+                            ipv4::Protocol::Udp => {
+                                if let Ok(mut udp) = udp::Packet::parse(ip.payload_mut()) {
+                                    iprintln!(_stim, "*** {:?}", udp);
+
+                                    if let Some(src_mac) = cache.get(&src_ip) {
+                                        let src_port = udp.get_source();
+                                        let dst_port = udp.get_destination();
+
+                                        // update the UDP header
+                                        udp.set_source(dst_port);
+                                        udp.set_destination(src_port);
+                                        udp.zero_checksum();
+                                        iprintln!(_stim, "\n*** {:?}", udp);
+
+                                        // update the IP header
+                                        let mut ip = ip.set_source(IP);
+                                        ip.set_destination(src_ip);
+                                        let ip = ip.update_checksum();
+                                        let ip_len = ip.len();
+                                        iprintln!(_stim, "** {:?}", ip);
+
+                                        // update the Ethernet header
+                                        eth.set_destination(*src_mac);
+                                        eth.set_source(MAC);
+                                        eth.truncate(ip_len);
+                                        iprintln!(_stim, "* {:?}", eth);
+
+                                        led.toggle();
+                                        iprintln!(_stim, "Tx({})", eth.as_bytes().len());
+                                        enc28j60.transmit(eth.as_bytes()).ok().unwrap();
+                                    }
+                                } else {
+                                    // malformed UDP packet
+                                    iprintln!(_stim, "Err(C)");
+                                }
+                            }
+                            _ => {}
+                        }
+                    } else {
+                        // malformed IPv4 packet
+                        iprintln!(_stim, "Err(D)");
+                    }
+                }
+                _ => {}
+            }
+        } else {
+            // malformed Ethernet frame
+            iprintln!(_stim, "Err(E)");
+        }
+    }
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 21 - 0
examples/hello.rs

@@ -0,0 +1,21 @@
+//! Prints "Hello, world" on the OpenOCD console
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+extern crate cortex_m_semihosting as sh;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal;
+
+use core::fmt::Write;
+use rt::entry;
+
+#[entry]
+fn main() -> ! {
+    let mut hstdout = sh::hio::hstdout().unwrap();
+    writeln!(hstdout, "Hello, world!").unwrap();
+    loop {}
+}

+ 32 - 0
examples/itm.rs

@@ -0,0 +1,32 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+#[macro_use]
+extern crate cortex_m;
+extern crate panic_itm;
+extern crate stm32f1xx_hal;
+
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = cortex_m::Peripherals::take().unwrap();
+    let mut itm = p.ITM;
+
+    iprintln!(&mut itm.stim[0], "Hello, world!");
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 36 - 0
examples/led.rs

@@ -0,0 +1,36 @@
+//! Turns the user LED on
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use hal::prelude::*;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut rcc = p.RCC.constrain();
+    let mut gpioc = p.GPIOC.split(&mut rcc.apb2);
+
+    gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 69 - 0
examples/mfrc522.rs

@@ -0,0 +1,69 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+#[macro_use]
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate mfrc522;
+extern crate panic_itm;
+extern crate stm32f1xx_hal as hal;
+
+use hal::prelude::*;
+use hal::spi::Spi;
+use hal::stm32f103xx;
+use mfrc522::Mfrc522;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let mut cp = cortex_m::Peripherals::take().unwrap();
+    let dp = stm32f103xx::Peripherals::take().unwrap();
+
+    let _stim = &mut cp.ITM.stim[0];
+    let mut rcc = dp.RCC.constrain();
+    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
+    let mut flash = dp.FLASH.constrain();
+    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
+    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let sck = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl);
+    let miso = gpioa.pa6;
+    let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl);
+    let spi = Spi::spi1(
+        dp.SPI1,
+        (sck, miso, mosi),
+        &mut afio.mapr,
+        mfrc522::MODE,
+        1.mhz(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    let nss = gpioa.pa4.into_push_pull_output(&mut gpioa.crl);
+    let mut mfrc522 = Mfrc522::new(spi, nss).unwrap();
+
+    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
+    led.set_high();
+
+    loop {
+        if let Ok(atqa) = mfrc522.reqa() {
+            if let Ok(uid) = mfrc522.select(&atqa) {
+                iprintln!(_stim, "* {:?}", uid);
+            }
+        }
+    }
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 113 - 0
examples/motor.rs

@@ -0,0 +1,113 @@
+//! Open loop motor control
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+extern crate cortex_m_semihosting as sh;
+extern crate motor_driver;
+extern crate panic_semihosting;
+#[macro_use(block)]
+extern crate nb;
+extern crate stm32f1xx_hal as hal;
+
+use core::fmt::Write;
+
+use hal::prelude::*;
+use hal::serial::Serial;
+use hal::stm32f103xx;
+use motor_driver::Motor;
+use rt::{entry, exception, ExceptionFrame};
+use sh::hio;
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+
+    let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
+
+    let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
+    let rx = gpioa.pa10;
+
+    let serial = Serial::usart1(
+        p.USART1,
+        (tx, rx),
+        &mut afio.mapr,
+        115_200.bps(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    let mut rx = serial.split().1;
+
+    let pwm = p.TIM2.pwm(
+        gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl),
+        &mut afio.mapr,
+        1.khz(),
+        clocks,
+        &mut rcc.apb1,
+    );
+
+    let max_duty = pwm.get_max_duty() as i16;
+    let mut motor = Motor::tb6612fng(
+        gpioa.pa1.into_push_pull_output(&mut gpioa.crl),
+        gpioa.pa2.into_push_pull_output(&mut gpioa.crl),
+        pwm,
+    );
+
+    let mut duty = max_duty;
+    let mut brake = true;
+
+    motor.duty(duty as u16);
+
+    let mut hstdout = hio::hstdout().unwrap();
+    writeln!(hstdout, "{} {}", max_duty, brake).unwrap();
+    loop {
+        match block!(rx.read()).unwrap() {
+            b'*' => duty *= 2,
+            b'+' => duty += 1,
+            b'-' => duty -= 1,
+            b'/' => duty /= 2,
+            b'r' => duty *= -1,
+            b's' => brake = !brake,
+            _ => continue,
+        }
+
+        if duty > max_duty {
+            duty = max_duty;
+        } else if duty < -max_duty {
+            duty = -max_duty;
+        }
+
+        if brake {
+            motor.brake();
+        } else if duty > 0 {
+            motor.cw();
+        } else {
+            motor.ccw();
+        }
+
+        motor.duty(duty.abs() as u16);
+
+        writeln!(hstdout, "{} {}", duty, brake).unwrap();
+    }
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 82 - 0
examples/mpu9250.rs

@@ -0,0 +1,82 @@
+//! Interfacing the MPU9250
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate mpu9250;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use cortex_m::asm;
+use hal::delay::Delay;
+use hal::prelude::*;
+use hal::spi::Spi;
+use hal::stm32f103xx;
+use mpu9250::Mpu9250;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let cp = cortex_m::Peripherals::take().unwrap();
+    let dp = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = dp.FLASH.constrain();
+    let mut rcc = dp.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
+
+    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
+    // let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
+
+    let nss = gpioa.pa4.into_push_pull_output(&mut gpioa.crl);
+
+    // SPI1
+    let sck = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl);
+    let miso = gpioa.pa6;
+    let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl);
+
+    // SPI2
+    // let sck = gpiob.pb13.into_alternate_push_pull(&mut gpiob.crh);
+    // let miso = gpiob.pb14;
+    // let mosi = gpiob.pb15.into_alternate_push_pull(&mut gpiob.crh);
+
+    let spi = Spi::spi1(
+        dp.SPI1,
+        (sck, miso, mosi),
+        &mut afio.mapr,
+        mpu9250::MODE,
+        1.mhz(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    let mut delay = Delay::new(cp.SYST, clocks);
+
+    let mut mpu9250 = Mpu9250::marg(spi, nss, &mut delay).unwrap();
+
+    // sanity checks
+    assert_eq!(mpu9250.who_am_i().unwrap(), 0x71);
+    assert_eq!(mpu9250.ak8963_who_am_i().unwrap(), 0x48);
+
+    let _a = mpu9250.all().unwrap();
+
+    asm::bkpt();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 39 - 0
examples/nojtag.rs

@@ -0,0 +1,39 @@
+//! Disables the JTAG ports to give access to pb3, pb4 and PA15
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use hal::prelude::*;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut rcc = p.RCC.constrain();
+    let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+
+    afio.mapr.disable_jtag();
+
+    gpiob.pb4.into_push_pull_output(&mut gpiob.crl).set_low();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 91 - 0
examples/pwm.rs

@@ -0,0 +1,91 @@
+//! Testing PWM output
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use cortex_m::asm;
+use hal::prelude::*;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+
+    // let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
+    let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
+
+    // TIM2
+    // let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl);
+    // let c2 = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl);
+    // let c3 = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
+    // let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl);
+
+    // TIM3
+    // let c1 = gpioa.pa6.into_alternate_push_pull(&mut gpioa.crl);
+    // let c2 = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl);
+    // let c3 = gpiob.pb0.into_alternate_push_pull(&mut gpiob.crl);
+    // let c4 = gpiob.pb1.into_alternate_push_pull(&mut gpiob.crl);
+
+    // TIM4
+    let c1 = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
+    let c2 = gpiob.pb7.into_alternate_push_pull(&mut gpiob.crl);
+    let c3 = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh);
+    let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh);
+
+    let mut pwm = p
+        .TIM4
+        .pwm(
+            (c1, c2, c3, c4),
+            &mut afio.mapr,
+            1.khz(),
+            clocks,
+            &mut rcc.apb1,
+        )
+        .3;
+
+    let max = pwm.get_max_duty();
+
+    pwm.enable();
+
+    // full
+    pwm.set_duty(max);
+
+    asm::bkpt();
+
+    // dim
+    pwm.set_duty(max / 4);
+
+    asm::bkpt();
+
+    // zero
+    pwm.set_duty(0);
+
+    asm::bkpt();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 73 - 0
examples/qei.rs

@@ -0,0 +1,73 @@
+//! Testing the Quadrature Encoder Interface
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate cortex_m_semihosting as semihosting;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use core::fmt::Write;
+
+use hal::delay::Delay;
+use hal::prelude::*;
+use hal::qei::Qei;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+use semihosting::hio;
+
+#[entry]
+fn main() -> ! {
+    let dp = stm32f103xx::Peripherals::take().unwrap();
+    let cp = cortex_m::Peripherals::take().unwrap();
+
+    let mut flash = dp.FLASH.constrain();
+    let mut rcc = dp.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
+
+    // let gpioa = dp.GPIOA.split(&mut rcc.apb2);
+    let gpiob = dp.GPIOB.split(&mut rcc.apb2);
+
+    // TIM2
+    // let c1 = gpioa.pa0;
+    // let c2 = gpioa.pa1;
+
+    // TIM3
+    // let c1 = gpioa.pa6;
+    // let c2 = gpioa.pa7;
+
+    // TIM4
+    let c1 = gpiob.pb6;
+    let c2 = gpiob.pb7;
+
+    let qei = Qei::tim4(dp.TIM4, (c1, c2), &mut afio.mapr, &mut rcc.apb1);
+    let mut delay = Delay::new(cp.SYST, clocks);
+
+    let mut hstdout = hio::hstdout().unwrap();
+    loop {
+        let before = qei.count();
+        delay.delay_ms(1_000_u16);
+        let after = qei.count();
+
+        let elapsed = after.wrapping_sub(before) as i16;
+
+        writeln!(hstdout, "{}", elapsed).unwrap();
+    }
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 87 - 0
examples/serial-dma-circ.rs

@@ -0,0 +1,87 @@
+//! Serial interface circular DMA RX transfer test
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_std]
+#![no_main]
+
+extern crate cortex_m_rt as rt;
+#[macro_use(singleton)]
+extern crate cortex_m;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use cortex_m::asm;
+use hal::dma::Half;
+use hal::prelude::*;
+use hal::serial::Serial;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+    let channels = p.DMA1.split(&mut rcc.ahb);
+
+    let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
+    // let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
+
+    // USART1
+    let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
+    let rx = gpioa.pa10;
+
+    // USART1
+    // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
+    // let rx = gpiob.pb7;
+
+    // USART2
+    // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
+    // let rx = gpioa.pa3;
+
+    // USART3
+    // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
+    // let rx = gpiob.pb11;
+
+    let serial = Serial::usart1(
+        p.USART1,
+        (tx, rx),
+        &mut afio.mapr,
+        9_600.bps(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    let rx = serial.split().1;
+    let buf = singleton!(: [[u8; 8]; 2] = [[0; 8]; 2]).unwrap();
+
+    let mut circ_buffer = rx.circ_read(channels.5, buf);
+
+    while circ_buffer.readable_half().unwrap() != Half::First {}
+
+    let _first_half = circ_buffer.peek(|half, _| *half).unwrap();
+
+    while circ_buffer.readable_half().unwrap() != Half::Second {}
+
+    let _second_half = circ_buffer.peek(|half, _| *half).unwrap();
+
+    asm::bkpt();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 84 - 0
examples/serial-dma-peek.rs

@@ -0,0 +1,84 @@
+//! Serial interface DMA RX transfer test
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+#[macro_use(singleton)]
+extern crate cortex_m;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use cortex_m::asm;
+use hal::prelude::*;
+use hal::serial::Serial;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+    let channels = p.DMA1.split(&mut rcc.ahb);
+
+    let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
+    // let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
+
+    // USART1
+    let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
+    let rx = gpioa.pa10;
+
+    // USART1
+    // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
+    // let rx = gpiob.pb7;
+
+    // USART2
+    // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
+    // let rx = gpioa.pa3;
+
+    // USART3
+    // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
+    // let rx = gpiob.pb11;
+
+    let serial = Serial::usart1(
+        p.USART1,
+        (tx, rx),
+        &mut afio.mapr,
+        115_200.bps(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    let rx = serial.split().1;
+    let buf = singleton!(: [u8; 8] = [0; 8]).unwrap();
+
+    let t = rx.read_exact(channels.5, buf);
+
+    while !t.is_done() {
+        let _slice = t.peek();
+
+        asm::bkpt();
+    }
+
+    asm::bkpt();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 78 - 0
examples/serial-dma-rx.rs

@@ -0,0 +1,78 @@
+//! Serial interface DMA RX transfer test
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+#[macro_use(singleton)]
+extern crate cortex_m;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use cortex_m::asm;
+use hal::prelude::*;
+use hal::serial::Serial;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+    let channels = p.DMA1.split(&mut rcc.ahb);
+
+    let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
+    // let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
+
+    // USART1
+    let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
+    let rx = gpioa.pa10;
+
+    // USART1
+    // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
+    // let rx = gpiob.pb7;
+
+    // USART2
+    // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
+    // let rx = gpioa.pa3;
+
+    // USART3
+    // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
+    // let rx = gpiob.pb11;
+
+    let serial = Serial::usart1(
+        p.USART1,
+        (tx, rx),
+        &mut afio.mapr,
+        9_600.bps(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    let rx = serial.split().1;
+    let buf = singleton!(: [u8; 8] = [0; 8]).unwrap();
+
+    let (_buf, _c, _rx) = rx.read_exact(channels.5, buf).wait();
+
+    asm::bkpt();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 84 - 0
examples/serial-dma-tx.rs

@@ -0,0 +1,84 @@
+//! Serial interface DMA TX transfer test
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use cortex_m::asm;
+use hal::prelude::*;
+use hal::serial::Serial;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+    let channels = p.DMA1.split(&mut rcc.ahb);
+
+    let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
+    // let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
+
+    // USART1
+    let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
+    let rx = gpioa.pa10;
+
+    // USART1
+    // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
+    // let rx = gpiob.pb7;
+
+    // USART2
+    // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
+    // let rx = gpioa.pa3;
+
+    // USART3
+    // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
+    // let rx = gpiob.pb11;
+
+    let serial = Serial::usart1(
+        p.USART1,
+        (tx, rx),
+        &mut afio.mapr,
+        9_600.bps(),
+        clocks,
+        &mut rcc.apb2,
+    );
+
+    let tx = serial.split().0;
+
+    let (_, c, tx) = tx.write_all(channels.4, b"The quick brown fox").wait();
+
+    asm::bkpt();
+
+    let (_, c, tx) = tx.write_all(c, b" jumps").wait();
+
+    asm::bkpt();
+
+    tx.write_all(c, b" over the lazy dog.").wait();
+
+    asm::bkpt();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 85 - 0
examples/serial.rs

@@ -0,0 +1,85 @@
+//! Serial interface loopback test
+//!
+//! You have to short the TX and RX pins to make this program work
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m;
+extern crate cortex_m_rt as rt;
+#[macro_use(block)]
+extern crate nb;
+extern crate panic_semihosting;
+extern crate stm32f1xx_hal as hal;
+
+use cortex_m::asm;
+use hal::prelude::*;
+use hal::serial::Serial;
+use hal::stm32f103xx;
+use rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn main() -> ! {
+    let p = stm32f103xx::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
+
+    // let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
+    let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
+
+    // USART1
+    // let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
+    // let rx = gpioa.pa10;
+
+    // USART1
+    // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
+    // let rx = gpiob.pb7;
+
+    // USART2
+    // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
+    // let rx = gpioa.pa3;
+
+    // USART3
+    let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
+    let rx = gpiob.pb11;
+
+    let serial = Serial::usart3(
+        p.USART3,
+        (tx, rx),
+        &mut afio.mapr,
+        9_600.bps(),
+        clocks,
+        &mut rcc.apb1,
+    );
+
+    let (mut tx, mut rx) = serial.split();
+
+    let sent = b'X';
+
+    block!(tx.write(sent)).ok();
+
+    let received = block!(rx.read()).unwrap();
+
+    assert_eq!(received, sent);
+
+    asm::bkpt();
+
+    loop {}
+}
+
+#[exception]
+fn HardFault(ef: &ExceptionFrame) -> ! {
+    panic!("{:#?}", ef);
+}
+
+#[exception]
+fn DefaultHandler(irqn: i16) {
+    panic!("Unhandled exception (IRQn = {})", irqn);
+}

+ 6 - 0
memory.x

@@ -0,0 +1,6 @@
+/* Linker script for the STM32F103C8T6 */
+MEMORY
+{
+  FLASH : ORIGIN = 0x08000000, LENGTH = 64K
+  RAM : ORIGIN = 0x20000000, LENGTH = 20K
+}

+ 110 - 0
src/afio.rs

@@ -0,0 +1,110 @@
+use stm32::{afio, AFIO};
+
+use rcc::APB2;
+
+pub trait AfioExt {
+    fn constrain(self, apb2: &mut APB2) -> Parts;
+}
+
+impl AfioExt for AFIO {
+    fn constrain(self, apb2: &mut APB2) -> Parts {
+        apb2.enr().modify(|_, w| w.afioen().set_bit());
+        apb2.rstr().modify(|_, w| w.afiorst().set_bit());
+        apb2.rstr().modify(|_, w| w.afiorst().clear_bit());
+
+        Parts {
+            evcr: EVCR { _0: () },
+            mapr: MAPR { _0: () },
+            exticr1:  EXTICR1 { _0: () },
+            exticr2:  EXTICR2 { _0: () },
+            exticr3:  EXTICR3 { _0: () },
+            exticr4:  EXTICR4 { _0: () },
+            mapr2: MAPR2 { _0: () },
+        }
+    }
+}
+
+pub struct Parts {
+    pub evcr: EVCR,
+    pub mapr: MAPR,
+    pub exticr1: EXTICR1,
+    pub exticr2: EXTICR2,
+    pub exticr3: EXTICR3,
+    pub exticr4: EXTICR4,
+    pub mapr2: MAPR2,
+}
+
+pub struct EVCR {
+    _0: (),
+}
+
+impl EVCR {
+    pub fn evcr(&mut self) -> &afio::EVCR {
+        unsafe { &(*AFIO::ptr()).evcr }
+    }
+}
+
+pub struct MAPR {
+    _0: (),
+}
+
+impl MAPR {
+    pub fn mapr(&mut self) -> &afio::MAPR {
+        unsafe { &(*AFIO::ptr()).mapr }
+    }
+
+    /// Disables the JTAG to free up pb3, pb4 and pa15 for normal use
+    pub fn disable_jtag(&mut self) {
+        self.mapr().modify(|_, w| unsafe{w.swj_cfg().bits(0b010)})
+    }
+}
+
+pub struct EXTICR1 {
+    _0: (),
+}
+
+impl EXTICR1 {
+    pub fn exticr1(&mut self) -> &afio::EXTICR1 {
+        unsafe { &(*AFIO::ptr()).exticr1 }
+    }
+}
+
+pub struct EXTICR2 {
+    _0: (),
+}
+
+impl EXTICR2 {
+    pub fn exticr2(&mut self) -> &afio::EXTICR2 {
+        unsafe { &(*AFIO::ptr()).exticr2 }
+    }
+}
+
+pub struct EXTICR3 {
+    _0: (),
+}
+
+impl EXTICR3 {
+    pub fn exticr3(&mut self) -> &afio::EXTICR3 {
+        unsafe { &(*AFIO::ptr()).exticr3 }
+    }
+}
+
+pub struct EXTICR4 {
+    _0: (),
+}
+
+impl EXTICR4 {
+    pub fn exticr4(&mut self) -> &afio::EXTICR4 {
+        unsafe { &(*AFIO::ptr()).exticr4 }
+    }
+}
+
+pub struct MAPR2 {
+    _0: (),
+}
+
+impl MAPR2 {
+    pub fn mapr2(&mut self) -> &afio::MAPR2 {
+        unsafe { &(*AFIO::ptr()).mapr2 }
+    }
+}

+ 22 - 0
src/bb.rs

@@ -0,0 +1,22 @@
+//! Bit banding
+
+use core::ptr;
+
+pub fn clear<T>(register: *const T, bit: u8) {
+    write(register, bit, false);
+}
+
+pub fn set<T>(register: *const T, bit: u8) {
+    write(register, bit, true);
+}
+
+pub fn write<T>(register: *const T, bit: u8, set: bool) {
+    let addr = register as usize;
+
+    assert!(addr >= 0x4000_0000 && addr <= 0x4010_0000);
+    assert!(bit < 32);
+
+    let bit = bit as usize;
+    let bb_addr = (0x4200_0000 + (addr - 0x4000_0000) * 32) + 4 * bit;
+    unsafe { ptr::write_volatile(bb_addr as *mut u32, if set { 1 } else { 0 }) }
+}

+ 86 - 0
src/delay.rs

@@ -0,0 +1,86 @@
+//! Delays
+
+use cast::u32;
+use cortex_m::peripheral::syst::SystClkSource;
+use cortex_m::peripheral::SYST;
+
+use hal::blocking::delay::{DelayMs, DelayUs};
+use rcc::Clocks;
+
+/// System timer (SysTick) as a delay provider
+pub struct Delay {
+    clocks: Clocks,
+    syst: SYST,
+}
+
+impl Delay {
+    /// Configures the system timer (SysTick) as a delay provider
+    pub fn new(mut syst: SYST, clocks: Clocks) -> Self {
+        syst.set_clock_source(SystClkSource::Core);
+
+        Delay { syst, clocks }
+    }
+
+    /// Releases the system timer (SysTick) resource
+    pub fn free(self) -> SYST {
+        self.syst
+    }
+}
+
+impl DelayMs<u32> for Delay {
+    fn delay_ms(&mut self, ms: u32) {
+        self.delay_us(ms * 1_000);
+    }
+}
+
+impl DelayMs<u16> for Delay {
+    fn delay_ms(&mut self, ms: u16) {
+        self.delay_ms(u32(ms));
+    }
+}
+
+impl DelayMs<u8> for Delay {
+    fn delay_ms(&mut self, ms: u8) {
+        self.delay_ms(u32(ms));
+    }
+}
+
+impl DelayUs<u32> for Delay {
+    fn delay_us(&mut self, us: u32) {
+        // The SysTick Reload Value register supports values between 1 and 0x00FFFFFF.
+        const MAX_RVR: u32 =  0x00FF_FFFF;
+
+        let mut total_rvr = us * (self.clocks.sysclk().0 / 1_000_000);
+
+        while total_rvr != 0 {
+            let current_rvr = if total_rvr <= MAX_RVR {
+                total_rvr
+            } else {
+                MAX_RVR
+            };
+
+            self.syst.set_reload(current_rvr);
+            self.syst.clear_current();
+            self.syst.enable_counter();
+
+            // Update the tracking variable while we are waiting...
+            total_rvr -= current_rvr;
+
+            while !self.syst.has_wrapped() {}
+
+            self.syst.disable_counter();
+        }
+    }
+}
+
+impl DelayUs<u16> for Delay {
+    fn delay_us(&mut self, us: u16) {
+        self.delay_us(u32(us))
+    }
+}
+
+impl DelayUs<u8> for Delay {
+    fn delay_us(&mut self, us: u8) {
+        self.delay_us(u32(us))
+    }
+}

+ 426 - 0
src/dma.rs

@@ -0,0 +1,426 @@
+#![allow(dead_code)]
+
+use core::marker::PhantomData;
+use core::ops;
+
+use rcc::AHB;
+
+#[derive(Debug)]
+pub enum Error {
+    Overrun,
+    #[doc(hidden)]
+    _Extensible,
+}
+
+pub enum Event {
+    HalfTransfer,
+    TransferComplete,
+}
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum Half {
+    First,
+    Second,
+}
+
+pub struct CircBuffer<BUFFER, CHANNEL>
+where
+    BUFFER: 'static,
+{
+    buffer: &'static mut [BUFFER; 2],
+    channel: CHANNEL,
+    readable_half: Half,
+}
+
+impl<BUFFER, CHANNEL> CircBuffer<BUFFER, CHANNEL> {
+    pub(crate) fn new(buf: &'static mut [BUFFER; 2], chan: CHANNEL) -> Self {
+        CircBuffer {
+            buffer: buf,
+            channel: chan,
+            readable_half: Half::Second,
+        }
+    }
+}
+
+pub trait Static<B> {
+    fn borrow(&self) -> &B;
+}
+
+impl<B> Static<B> for &'static B {
+    fn borrow(&self) -> &B {
+        *self
+    }
+}
+
+impl<B> Static<B> for &'static mut B {
+    fn borrow(&self) -> &B {
+        *self
+    }
+}
+
+pub trait DmaExt {
+    type Channels;
+
+    fn split(self, ahb: &mut AHB) -> Self::Channels;
+}
+
+pub struct Transfer<MODE, BUFFER, CHANNEL, PAYLOAD> {
+    _mode: PhantomData<MODE>,
+    buffer: BUFFER,
+    channel: CHANNEL,
+    payload: PAYLOAD,
+}
+
+impl<BUFFER, CHANNEL, PAYLOAD> Transfer<R, BUFFER, CHANNEL, PAYLOAD> {
+    pub(crate) fn r(buffer: BUFFER, channel: CHANNEL, payload: PAYLOAD) -> Self {
+        Transfer {
+            _mode: PhantomData,
+            buffer,
+            channel,
+            payload,
+        }
+    }
+}
+
+impl<BUFFER, CHANNEL, PAYLOAD> Transfer<W, BUFFER, CHANNEL, PAYLOAD> {
+    pub(crate) fn w(buffer: BUFFER, channel: CHANNEL, payload: PAYLOAD) -> Self {
+        Transfer {
+            _mode: PhantomData,
+            buffer,
+            channel,
+            payload,
+        }
+    }
+}
+
+impl<BUFFER, CHANNEL, PAYLOAD> ops::Deref for Transfer<R, BUFFER, CHANNEL, PAYLOAD> {
+    type Target = BUFFER;
+
+    fn deref(&self) -> &BUFFER {
+        &self.buffer
+    }
+}
+
+/// Read transfer
+pub struct R;
+
+/// Write transfer
+pub struct W;
+
+macro_rules! dma {
+    ($($DMAX:ident: ($dmaX:ident, $dmaXen:ident, $dmaXrst:ident, {
+        $($CX:ident: (
+            $ccrX:ident,
+            $CCRX:ident,
+            $cndtrX:ident,
+            $CNDTRX:ident,
+            $cparX:ident,
+            $CPARX:ident,
+            $cmarX:ident,
+            $CMARX:ident,
+            $htifX:ident,
+            $tcifX:ident,
+            $chtifX:ident,
+            $ctcifX:ident,
+            $cgifX:ident
+        ),)+
+    }),)+) => {
+        $(
+            pub mod $dmaX {
+                use core::sync::atomic::{self, Ordering};
+
+                use stm32::{$DMAX, dma1};
+
+                use dma::{CircBuffer, DmaExt, Error, Event, Half, Transfer, W};
+                use rcc::AHB;
+
+                pub struct Channels((), $(pub $CX),+);
+
+                $(
+                    pub struct $CX { _0: () }
+
+                    impl $CX {
+                        pub fn listen(&mut self, event: Event) {
+                            match event {
+                                Event::HalfTransfer => self.ccr().modify(|_, w| w.htie().set_bit()),
+                                Event::TransferComplete => {
+                                    self.ccr().modify(|_, w| w.tcie().set_bit())
+                                }
+                            }
+                        }
+
+                        pub fn unlisten(&mut self, event: Event) {
+                            match event {
+                                Event::HalfTransfer => {
+                                    self.ccr().modify(|_, w| w.htie().clear_bit())
+                                },
+                                Event::TransferComplete => {
+                                    self.ccr().modify(|_, w| w.tcie().clear_bit())
+                                }
+                            }
+                        }
+
+                        pub(crate) fn isr(&self) -> dma1::isr::R {
+                            // NOTE(unsafe) atomic read with no side effects
+                            unsafe { (*$DMAX::ptr()).isr.read() }
+                        }
+
+                        pub(crate) fn ifcr(&self) -> &dma1::IFCR {
+                            unsafe { &(*$DMAX::ptr()).ifcr }
+                        }
+
+                        pub(crate) fn ccr(&mut self) -> &dma1::$CCRX {
+                            unsafe { &(*$DMAX::ptr()).$ccrX }
+                        }
+
+                        pub(crate) fn cndtr(&mut self) -> &dma1::$CNDTRX {
+                            unsafe { &(*$DMAX::ptr()).$cndtrX }
+                        }
+
+                        pub(crate) fn cpar(&mut self) -> &dma1::$CPARX {
+                            unsafe { &(*$DMAX::ptr()).$cparX }
+                        }
+
+                        pub(crate) fn cmar(&mut self) -> &dma1::$CMARX {
+                            unsafe { &(*$DMAX::ptr()).$cmarX }
+                        }
+
+                        pub(crate) fn get_cndtr(&self) -> u32 {
+                            // NOTE(unsafe) atomic read with no side effects
+                            unsafe { (*$DMAX::ptr()).$cndtrX.read().bits() }
+                        }
+
+                    }
+
+                    impl<B> CircBuffer<B, $CX> {
+                        /// Peeks into the readable half of the buffer
+                        pub fn peek<R, F>(&mut self, f: F) -> Result<R, Error>
+                            where
+                            F: FnOnce(&B, Half) -> R,
+                        {
+                            let half_being_read = self.readable_half()?;
+
+                            let buf = match half_being_read {
+                                Half::First => &self.buffer[0],
+                                Half::Second => &self.buffer[1],
+                            };
+
+                            // XXX does this need a compiler barrier?
+                            let ret = f(buf, half_being_read);
+
+
+                            let isr = self.channel.isr();
+                            let first_half_is_done = isr.$htifX().bit_is_set();
+                            let second_half_is_done = isr.$tcifX().bit_is_set();
+
+                            if (half_being_read == Half::First && second_half_is_done) ||
+                                (half_being_read == Half::Second && first_half_is_done) {
+                                Err(Error::Overrun)
+                            } else {
+                                Ok(ret)
+                            }
+                        }
+
+                        /// Returns the `Half` of the buffer that can be read
+                        pub fn readable_half(&mut self) -> Result<Half, Error> {
+                            let isr = self.channel.isr();
+                            let first_half_is_done = isr.$htifX().bit_is_set();
+                            let second_half_is_done = isr.$tcifX().bit_is_set();
+
+                            if first_half_is_done && second_half_is_done {
+                                return Err(Error::Overrun);
+                            }
+
+                            let last_read_half = self.readable_half;
+
+                            Ok(match last_read_half {
+                                Half::First => {
+                                    if second_half_is_done {
+                                        self.channel.ifcr().write(|w| w.$ctcifX().set_bit());
+
+                                        self.readable_half = Half::Second;
+                                        Half::Second
+                                    } else {
+                                        last_read_half
+                                    }
+                                }
+                                Half::Second => {
+                                    if first_half_is_done {
+                                        self.channel.ifcr().write(|w| w.$chtifX().set_bit());
+
+                                        self.readable_half = Half::First;
+                                        Half::First
+                                    } else {
+                                        last_read_half
+                                    }
+                                }
+                            })
+                        }
+                    }
+
+                    impl<BUFFER, PAYLOAD, MODE> Transfer<MODE, BUFFER, $CX, PAYLOAD> {
+                        pub fn is_done(&self) -> bool {
+                            self.channel.isr().$tcifX().bit_is_set()
+                        }
+
+                        pub fn wait(mut self) -> (BUFFER, $CX, PAYLOAD) {
+                            // XXX should we check for transfer errors here?
+                            // The manual says "A DMA transfer error can be generated by reading
+                            // from or writing to a reserved address space". I think it's impossible
+                            // to get to that state with our type safe API and *safe* Rust.
+                            while !self.is_done() {}
+
+                            self.channel.ifcr().write(|w| w.$cgifX().set_bit());
+
+                            self.channel.ccr().modify(|_, w| w.en().clear_bit());
+
+                            // TODO can we weaken this compiler barrier?
+                            // NOTE(compiler_fence) operations on `buffer` should not be reordered
+                            // before the previous statement, which marks the DMA transfer as done
+                            atomic::compiler_fence(Ordering::SeqCst);
+
+                            (self.buffer, self.channel, self.payload)
+                        }
+                    }
+
+                    impl<BUFFER, PAYLOAD> Transfer<W, &'static mut BUFFER, $CX, PAYLOAD> {
+                        pub fn peek<T>(&self) -> &[T]
+                        where
+                            BUFFER: AsRef<[T]>,
+                        {
+                            let pending = self.channel.get_cndtr() as usize;
+
+                            let slice = self.buffer.as_ref();
+                            let capacity = slice.len();
+
+                            &slice[..(capacity - pending)]
+                        }
+                    }
+                )+
+
+                impl DmaExt for $DMAX {
+                    type Channels = Channels;
+
+                    fn split(self, ahb: &mut AHB) -> Channels {
+                        ahb.enr().modify(|_, w| w.$dmaXen().set_bit());
+
+                        // reset the DMA control registers (stops all on-going transfers)
+                        $(
+                            self.$ccrX.reset();
+                        )+
+
+                        Channels((), $($CX { _0: () }),+)
+                    }
+                }
+            }
+        )+
+    }
+}
+
+dma! {
+    DMA1: (dma1, dma1en, dma1rst, {
+        C1: (
+            ccr1, CCR1,
+            cndtr1, CNDTR1,
+            cpar1, CPAR1,
+            cmar1, CMAR1,
+            htif1, tcif1,
+            chtif1, ctcif1, cgif1
+        ),
+        C2: (
+            ccr2, CCR2,
+            cndtr2, CNDTR2,
+            cpar2, CPAR2,
+            cmar2, CMAR2,
+            htif2, tcif2,
+            chtif2, ctcif2, cgif2
+        ),
+        C3: (
+            ccr3, CCR3,
+            cndtr3, CNDTR3,
+            cpar3, CPAR3,
+            cmar3, CMAR3,
+            htif3, tcif3,
+            chtif3, ctcif3, cgif3
+        ),
+        C4: (
+            ccr4, CCR4,
+            cndtr4, CNDTR4,
+            cpar4, CPAR4,
+            cmar4, CMAR4,
+            htif4, tcif4,
+            chtif4, ctcif4, cgif4
+        ),
+        C5: (
+            ccr5, CCR5,
+            cndtr5, CNDTR5,
+            cpar5, CPAR5,
+            cmar5, CMAR5,
+            htif5, tcif5,
+            chtif5, ctcif5, cgif5
+        ),
+        C6: (
+            ccr6, CCR6,
+            cndtr6, CNDTR6,
+            cpar6, CPAR6,
+            cmar6, CMAR6,
+            htif6, tcif6,
+            chtif6, ctcif6, cgif6
+        ),
+        C7: (
+            ccr7, CCR7,
+            cndtr7, CNDTR7,
+            cpar7, CPAR7,
+            cmar7, CMAR7,
+            htif7, tcif7,
+            chtif7, ctcif7, cgif7
+        ),
+    }),
+
+    DMA2: (dma2, dma2en, dma2rst, {
+        C1: (
+            ccr1, CCR1,
+            cndtr1, CNDTR1,
+            cpar1, CPAR1,
+            cmar1, CMAR1,
+            htif1, tcif1,
+            chtif1, ctcif1, cgif1
+        ),
+        C2: (
+            ccr2, CCR2,
+            cndtr2, CNDTR2,
+            cpar2, CPAR2,
+            cmar2, CMAR2,
+            htif2, tcif2,
+            chtif2, ctcif2, cgif2
+        ),
+        C3: (
+            ccr3, CCR3,
+            cndtr3, CNDTR3,
+            cpar3, CPAR3,
+            cmar3, CMAR3,
+            htif3, tcif3,
+            chtif3, ctcif3, cgif3
+        ),
+        C4: (
+            ccr4, CCR4,
+            cndtr4, CNDTR4,
+            cpar4, CPAR4,
+            cmar4, CMAR4,
+            htif4, tcif4,
+            chtif4, ctcif4, cgif4
+        ),
+        C5: (
+            ccr5, CCR5,
+            cndtr5, CNDTR5,
+            cpar5, CPAR5,
+            cmar5, CMAR5,
+            htif5, tcif5,
+            chtif5, ctcif5, cgif5
+        ),
+    }),
+}
+
+pub trait DmaChannel {
+    type Dma;
+}

+ 35 - 0
src/flash.rs

@@ -0,0 +1,35 @@
+//! Flash memory
+
+use stm32::{flash, FLASH};
+
+/// Extension trait to constrain the FLASH peripheral
+pub trait FlashExt {
+    /// Constrains the FLASH peripheral to play nicely with the other abstractions
+    fn constrain(self) -> Parts;
+}
+
+impl FlashExt for FLASH {
+    fn constrain(self) -> Parts {
+        Parts {
+            acr: ACR { _0: () },
+        }
+    }
+}
+
+/// Constrained FLASH peripheral
+pub struct Parts {
+    /// Opaque ACR register
+    pub acr: ACR,
+}
+
+/// Opaque ACR register
+pub struct ACR {
+    _0: (),
+}
+
+impl ACR {
+    pub(crate) fn acr(&mut self) -> &flash::ACR {
+        // NOTE(unsafe) this proxy grants exclusive access to this register
+        unsafe { &(*FLASH::ptr()).acr }
+    }
+}

+ 493 - 0
src/gpio.rs

@@ -0,0 +1,493 @@
+//! General Purpose Input / Output
+
+// TODO the pins here currently correspond to the LQFP-48 package. There should be Cargo features
+// that let you select different microcontroller packages
+
+use core::marker::PhantomData;
+
+use rcc::APB2;
+
+/// Extension trait to split a GPIO peripheral in independent pins and registers
+pub trait GpioExt {
+    /// The to split the GPIO into
+    type Parts;
+
+    /// Splits the GPIO block into independent pins and registers
+    fn split(self, apb2: &mut APB2) -> Self::Parts;
+}
+
+/// Input mode (type state)
+pub struct Input<MODE> {
+    _mode: PhantomData<MODE>,
+}
+
+/// Floating input (type state)
+pub struct Floating;
+/// Pulled down input (type state)
+pub struct PullDown;
+/// Pulled up input (type state)
+pub struct PullUp;
+
+/// Output mode (type state)
+pub struct Output<MODE> {
+    _mode: PhantomData<MODE>,
+}
+
+/// Push pull output (type state)
+pub struct PushPull;
+/// Open drain output (type state)
+pub struct OpenDrain;
+
+/// Analog mode (type state)
+pub struct Analog;
+
+/// Alternate function
+pub struct Alternate<MODE> {
+    _mode: PhantomData<MODE>,
+}
+
+macro_rules! gpio {
+    ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $iopxenr:ident, $iopxrst:ident, $PXx:ident, [
+        $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty, $CR:ident),)+
+    ]) => {
+        /// GPIO
+        pub mod $gpiox {
+            use core::marker::PhantomData;
+
+            use hal::digital::{InputPin, OutputPin, StatefulOutputPin, toggleable};
+            use stm32::{$gpioy, $GPIOX};
+
+            use rcc::APB2;
+            use super::{
+                Alternate, Floating, GpioExt, Input,
+                OpenDrain,
+                Output,
+                PullDown, 
+                PullUp,
+                PushPull,
+                Analog,
+            };
+
+            /// GPIO parts
+            pub struct Parts {
+                /// Opaque CRL register
+                pub crl: CRL,
+                /// Opaque CRH register
+                pub crh: CRH,
+                $(
+                    /// Pin
+                    pub $pxi: $PXi<$MODE>,
+                )+
+            }
+
+            impl GpioExt for $GPIOX {
+                type Parts = Parts;
+
+                fn split(self, apb2: &mut APB2) -> Parts {
+                    apb2.enr().modify(|_, w| w.$iopxenr().set_bit());
+                    apb2.rstr().modify(|_, w| w.$iopxrst().set_bit());
+                    apb2.rstr().modify(|_, w| w.$iopxrst().clear_bit());
+
+                    Parts {
+                        crl: CRL { _0: () },
+                        crh: CRH { _0: () },
+                        $(
+                            $pxi: $PXi { _mode: PhantomData },
+                        )+
+                    }
+                }
+            }
+
+            /// Opaque CRL register
+            pub struct CRL {
+                _0: (),
+            }
+
+            impl CRL {
+                // NOTE(allow) we get a warning on GPIOC because it only has 3 high pins
+                #[allow(dead_code)]
+                pub(crate) fn cr(&mut self) -> &$gpioy::CRL {
+                    unsafe { &(*$GPIOX::ptr()).crl }
+                }
+            }
+
+            /// Opaque CRH register
+            pub struct CRH {
+                _0: (),
+            }
+
+            impl CRH {
+                pub(crate) fn cr(&mut self) -> &$gpioy::CRH {
+                    unsafe { &(*$GPIOX::ptr()).crh }
+                }
+            }
+
+            /// Partially erased pin
+            pub struct $PXx<MODE> {
+                i: u8,
+                _mode: PhantomData<MODE>,
+            }
+
+            impl<MODE> OutputPin for $PXx<Output<MODE>> {
+                fn set_high(&mut self) {
+                    // NOTE(unsafe) atomic write to a stateless register
+                    unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << self.i)) }
+                }
+
+                fn set_low(&mut self) {
+                    // NOTE(unsafe) atomic write to a stateless register
+                    unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + self.i))) }
+                }
+            }
+
+            impl<MODE> InputPin for $PXx<Input<MODE>> {
+                fn is_high(&self) -> bool {
+                    !self.is_low()
+                }
+
+                fn is_low(&self) -> bool {
+                    // NOTE(unsafe) atomic read with no side effects
+                    unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 }
+                }
+            }
+
+            impl <MODE> StatefulOutputPin for $PXx<Output<MODE>> {
+                fn is_set_high(&self) -> bool {
+                    !self.is_set_low()
+                }
+
+                fn is_set_low(&self) -> bool {
+                    // NOTE(unsafe) atomic read with no side effects
+                    unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << self.i) == 0 }
+                }
+            }
+
+            impl <MODE> toggleable::Default for $PXx<Output<MODE>> {}
+
+            impl InputPin for $PXx<Output<OpenDrain>> {
+                fn is_high(&self) -> bool {
+                    !self.is_low()
+                }
+
+                fn is_low(&self) -> bool {
+                    // NOTE(unsafe) atomic read with no side effects
+                    unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 }
+                }
+            }
+
+            $(
+                /// Pin
+                pub struct $PXi<MODE> {
+                    _mode: PhantomData<MODE>,
+                }
+
+                impl<MODE> $PXi<MODE> {
+                    /// Configures the pin to operate as an alternate function push pull output pin
+                    pub fn into_alternate_push_pull(
+                        self,
+                        cr: &mut $CR,
+                    ) -> $PXi<Alternate<PushPull>> {
+                        let offset = (4 * $i) % 32;
+                        // Alternate function output push pull
+                        let cnf = 0b10;
+                        // Output mode, max speed 50 MHz
+                        let mode = 0b11;
+                        let bits = (cnf << 2) | mode;
+
+                        // input mode
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+
+                    pub fn into_alternate_open_drain(
+                        self,
+                        cr: &mut $CR,
+                    ) -> $PXi<Alternate<OpenDrain>> {
+                        let offset = (4 * $i) % 32;
+                        // Alternate function output open drain
+                        let cnf = 0b11;
+                        // Output mode, max speed 50 MHz
+                        let mode = 0b11;
+                        let bits = (cnf << 2) | mode;
+
+                        // input mode
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+
+                    /// Configures the pin to operate as a floating input pin
+                    pub fn into_floating_input(
+                        self,
+                        cr: &mut $CR,
+                    ) -> $PXi<Input<Floating>> {
+                        let offset = (4 * $i) % 32;
+                        // Floating input
+                        let cnf = 0b01;
+                        // Input mode
+                        let mode = 0b00;
+                        let bits = (cnf << 2) | mode;
+
+                        // input mode
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+
+                    /// Configures the pin to operate as a pulled down input pin
+                    pub fn into_pull_down_input(
+                        self,
+                        cr: &mut $CR,
+                    ) -> $PXi<Input<PullDown>> {
+                        let offset = (4 * $i) % 32;
+                        // Pull up/down input
+                        let cnf = 0b10;
+                        // Input mode
+                        let mode = 0b00;
+                        let bits = (cnf << 2) | mode;
+
+                        //pull down:
+                        // NOTE(unsafe) atomic write to a stateless register
+                        unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i))) }
+
+                        // input mode
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+
+                    /// Configures the pin to operate as a pulled up input pin
+                    pub fn into_pull_up_input(
+                        self,
+                        cr: &mut $CR,
+                    ) -> $PXi<Input<PullUp>> {
+                        let offset = (4 * $i) % 32;
+                        // Pull up/down input
+                        let cnf = 0b10;
+                        // Input mode
+                        let mode = 0b00;
+                        let bits = (cnf << 2) | mode;
+
+                        //pull up:
+                        // NOTE(unsafe) atomic write to a stateless register
+                        unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) }
+
+                        // input mode
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+
+                    /// Configures the pin to operate as an open drain output pin
+                    pub fn into_open_drain_output(
+                        self,
+                        cr: &mut $CR,
+                    ) -> $PXi<Output<OpenDrain>> {
+                        let offset = (4 * $i) % 32;
+                        // General purpose output open-drain
+                        let cnf = 0b01;
+                        // Open-Drain Output mode, max speed 50 MHz
+                        let mode = 0b11;
+                        let bits = (cnf << 2) | mode;
+
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+
+                    /// Configures the pin to operate as an push pull output pin
+                    pub fn into_push_pull_output(
+                        self,
+                        cr: &mut $CR,
+                    ) -> $PXi<Output<PushPull>> {
+                        let offset = (4 * $i) % 32;
+                        // General purpose output push-pull
+                        let cnf = 0b00;
+                        // Output mode, max speed 50 MHz
+                        let mode = 0b11;
+                        let bits = (cnf << 2) | mode;
+
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+                    
+                    /// Configures the pin to operate as an analog input pin
+                    pub fn into_analog(self, cr: &mut $CR) -> $PXi<Analog> {
+                        let offset = (4 * $i) % 32;
+                        // Analog input
+                        let cnf = 0b00;
+                        // Input mode
+                        let mode = 0b00;
+                        let bits = (cnf << 2) | mode;
+
+                        // analog mode
+                        cr
+                            .cr()
+                            .modify(|r, w| unsafe {
+                                w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset))
+                            });
+
+                        $PXi { _mode: PhantomData }
+                    }
+                }
+
+                impl<MODE> $PXi<MODE> {
+                    /// Erases the pin number from the type
+                    ///
+                    /// This is useful when you want to collect the pins into an array where you
+                    /// need all the elements to have the same type
+                    pub fn downgrade(self) -> $PXx<MODE> {
+                        $PXx {
+                            i: $i,
+                            _mode: self._mode,
+                        }
+                    }
+                }
+
+                impl<MODE> OutputPin for $PXi<Output<MODE>> {
+                    fn set_high(&mut self) {
+                        // NOTE(unsafe) atomic write to a stateless register
+                        unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) }
+                    }
+
+                    fn set_low(&mut self) {
+                        // NOTE(unsafe) atomic write to a stateless register
+                        unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i))) }
+                    }
+                }
+
+                impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
+                    fn is_set_high(&self) -> bool {
+                        !self.is_set_low()
+                    }
+
+                    fn is_set_low(&self) -> bool {
+                        // NOTE(unsafe) atomic read with no side effects
+                        unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 }
+                    }
+                }
+
+                impl<MODE> toggleable::Default for $PXi<Output<MODE>> {}
+
+                impl<MODE> OutputPin for $PXi<Alternate<MODE>> {
+                    fn set_high(&mut self) {
+                        // NOTE(unsafe) atomic write to a stateless register
+                        unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) }
+                    }
+
+                    fn set_low(&mut self) {
+                        // NOTE(unsafe) atomic write to a stateless register
+                        unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i))) }
+                    }
+                }
+
+                impl<MODE> StatefulOutputPin for $PXi<Alternate<MODE>> {
+                    fn is_set_high(&self) -> bool {
+                        !self.is_set_low()
+                    }
+
+                    fn is_set_low(&self) -> bool {
+                        // NOTE(unsafe) atomic read with no side effects
+                        unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 }
+                    }
+                }
+
+                impl<MODE> InputPin for $PXi<Input<MODE>> {
+                    fn is_high(&self) -> bool {
+                        !self.is_low()
+                    }
+
+                    fn is_low(&self) -> bool {
+                        // NOTE(unsafe) atomic read with no side effects
+                        unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }
+                    }
+                }
+
+                impl InputPin for $PXi<Output<OpenDrain>> {
+                    fn is_high(&self) -> bool {
+                        !self.is_low()
+                    }
+
+                    fn is_low(&self) -> bool {
+                        // NOTE(unsafe) atomic read with no side effects
+                        unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }
+                    }
+                }
+            )+
+        }
+    }
+}
+
+gpio!(GPIOA, gpioa, gpioa, iopaen, ioparst, PAx, [
+    PA0: (pa0, 0, Input<Floating>, CRL),
+    PA1: (pa1, 1, Input<Floating>, CRL),
+    PA2: (pa2, 2, Input<Floating>, CRL),
+    PA3: (pa3, 3, Input<Floating>, CRL),
+    PA4: (pa4, 4, Input<Floating>, CRL),
+    PA5: (pa5, 5, Input<Floating>, CRL),
+    PA6: (pa6, 6, Input<Floating>, CRL),
+    PA7: (pa7, 7, Input<Floating>, CRL),
+    PA8: (pa8, 8, Input<Floating>, CRH),
+    PA9: (pa9, 9, Input<Floating>, CRH),
+    PA10: (pa10, 10, Input<Floating>, CRH),
+    PA11: (pa11, 11, Input<Floating>, CRH),
+    PA12: (pa12, 12, Input<Floating>, CRH),
+    PA13: (pa13, 13, Input<Floating>, CRH),
+    PA14: (pa14, 14, Input<Floating>, CRH),
+    PA15: (pa15, 15, Input<Floating>, CRH),
+]);
+
+gpio!(GPIOB, gpiob, gpioa, iopben, iopbrst, PBx, [
+    PB0: (pb0, 0, Input<Floating>, CRL),
+    PB1: (pb1, 1, Input<Floating>, CRL),
+    PB2: (pb2, 2, Input<Floating>, CRL),
+    PB3: (pb3, 3, Input<Floating>, CRL),
+    PB4: (pb4, 4, Input<Floating>, CRL),
+    PB5: (pb5, 5, Input<Floating>, CRL),
+    PB6: (pb6, 6, Input<Floating>, CRL),
+    PB7: (pb7, 7, Input<Floating>, CRL),
+    PB8: (pb8, 8, Input<Floating>, CRH),
+    PB9: (pb9, 9, Input<Floating>, CRH),
+    PB10: (pb10, 10, Input<Floating>, CRH),
+    PB11: (pb11, 11, Input<Floating>, CRH),
+    PB12: (pb12, 12, Input<Floating>, CRH),
+    PB13: (pb13, 13, Input<Floating>, CRH),
+    PB14: (pb14, 14, Input<Floating>, CRH),
+    PB15: (pb15, 15, Input<Floating>, CRH),
+]);
+
+gpio!(GPIOC, gpioc, gpioa, iopcen, iopcrst, PCx, [
+    PC13: (pc13, 13, Input<Floating>, CRH),
+    PC14: (pc14, 14, Input<Floating>, CRH),
+    PC15: (pc15, 15, Input<Floating>, CRH),
+]);

+ 476 - 0
src/i2c.rs

@@ -0,0 +1,476 @@
+//! Inter-Integrated Circuit (I2C) bus
+
+use afio::MAPR;
+use gpio::{Alternate, OpenDrain};
+use gpio::gpiob::{PB10, PB11, PB6, PB7, PB8, PB9};
+use hal::blocking::i2c::{Read, Write, WriteRead};
+use nb::{Error as NbError, Result as NbResult};
+use nb::Error::{Other, WouldBlock};
+use rcc::{APB1, Clocks};
+use stm32::{I2C1, I2C2};
+use stm32::DWT;
+
+/// I2C error
+#[derive(Debug, Eq, PartialEq)]
+pub enum Error {
+    /// Bus error
+    Bus,
+    /// Arbitration loss
+    Arbitration,
+    /// No ack received
+    Acknowledge,
+    /// Overrun/underrun
+    Overrun,
+    // Pec, // SMBUS mode only
+    // Timeout, // SMBUS mode only
+    // Alert, // SMBUS mode only
+    #[doc(hidden)] _Extensible,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum DutyCycle {
+    Ratio2to1,
+    Ratio16to9,
+}
+
+#[derive(Debug, PartialEq)]
+pub enum Mode {
+    Standard { frequency: u32 },
+    Fast { frequency: u32, duty_cycle: DutyCycle },
+}
+
+impl Mode {
+    pub fn get_frequency(&self) -> u32 {
+        match self {
+            &Mode::Standard { frequency } => frequency,
+            &Mode::Fast { frequency, .. } => frequency,
+        }
+    }
+}
+
+
+pub trait Pins<I2C> {
+    const REMAP: bool;
+}
+
+impl Pins<I2C1>
+for (
+    PB6<Alternate<OpenDrain>>,
+    PB7<Alternate<OpenDrain>>,
+) {
+    const REMAP: bool = false;
+}
+
+impl Pins<I2C1>
+for (
+    PB8<Alternate<OpenDrain>>,
+    PB9<Alternate<OpenDrain>>,
+) {
+    const REMAP: bool = true;
+}
+
+impl Pins<I2C2>
+for (
+    PB10<Alternate<OpenDrain>>,
+    PB11<Alternate<OpenDrain>>,
+) {
+    const REMAP: bool = false;
+}
+
+/// I2C peripheral operating in master mode
+pub struct I2c<I2C, PINS> {
+    i2c: I2C,
+    pins: PINS,
+    mode: Mode,
+    pclk1: u32,
+}
+
+pub struct BlockingI2c<I2C, PINS> {
+    nb: I2c<I2C, PINS>,
+    start_timeout: u32,
+    start_retries: u8,
+    addr_timeout: u32,
+    data_timeout: u32,
+}
+
+impl<PINS> I2c<I2C1, PINS> {
+    pub fn i2c1(
+        i2c: I2C1,
+        pins: PINS,
+        mapr: &mut MAPR,
+        mode: Mode,
+        clocks: Clocks,
+        apb: &mut APB1,
+    ) -> Self
+        where
+            PINS: Pins<I2C1>,
+    {
+        mapr.mapr().modify(|_, w| w.i2c1_remap().bit(PINS::REMAP));
+        I2c::_i2c1(i2c, pins, mode, clocks, apb)
+    }
+}
+
+impl<PINS> BlockingI2c<I2C1, PINS> {
+    pub fn i2c1(
+        i2c: I2C1,
+        pins: PINS,
+        mapr: &mut MAPR,
+        mode: Mode,
+        clocks: Clocks,
+        apb: &mut APB1,
+        start_timeout_us: u32,
+        start_retries: u8,
+        addr_timeout_us: u32,
+        data_timeout_us: u32,
+    ) -> Self
+        where
+            PINS: Pins<I2C1>,
+    {
+        mapr.mapr().modify(|_, w| w.i2c1_remap().bit(PINS::REMAP));
+        BlockingI2c::_i2c1(i2c, pins, mode, clocks, apb,
+                           start_timeout_us, start_retries, addr_timeout_us, data_timeout_us)
+    }
+}
+
+impl<PINS> I2c<I2C2, PINS> {
+    pub fn i2c2(
+        i2c: I2C2,
+        pins: PINS,
+        mode: Mode,
+        clocks: Clocks,
+        apb: &mut APB1,
+    ) -> Self
+        where
+            PINS: Pins<I2C2>,
+    {
+        I2c::_i2c2(i2c, pins, mode, clocks, apb)
+    }
+}
+
+impl<PINS> BlockingI2c<I2C2, PINS> {
+    pub fn i2c2(
+        i2c: I2C2,
+        pins: PINS,
+        mode: Mode,
+        clocks: Clocks,
+        apb: &mut APB1,
+        start_timeout_us: u32,
+        start_retries: u8,
+        addr_timeout_us: u32,
+        data_timeout_us: u32,
+    ) -> Self
+        where
+            PINS: Pins<I2C2>,
+    {
+        BlockingI2c::_i2c2(i2c, pins, mode, clocks, apb,
+                           start_timeout_us, start_retries, addr_timeout_us, data_timeout_us)
+    }
+}
+
+pub fn blocking_i2c<I2C, PINS>(i2c: I2c<I2C, PINS>,
+                               clocks: Clocks,
+                               start_timeout_us: u32,
+                               start_retries: u8,
+                               addr_timeout_us: u32,
+                               data_timeout_us: u32) -> BlockingI2c<I2C, PINS> {
+    let sysclk_mhz = clocks.sysclk().0 / 1_000_000;
+    return BlockingI2c {
+        nb: i2c,
+        start_timeout: start_timeout_us * sysclk_mhz,
+        start_retries,
+        addr_timeout: addr_timeout_us * sysclk_mhz,
+        data_timeout: data_timeout_us * sysclk_mhz,
+    };
+}
+
+macro_rules! wait_for_flag {
+    ($i2c:expr, $flag:ident) => {
+        {
+            let sr1 = $i2c.sr1.read();
+
+            if sr1.berr().bit_is_set() {
+                Err(Other(Error::Bus))
+            } else if sr1.arlo().bit_is_set() {
+                Err(Other(Error::Arbitration))
+            } else if sr1.af().bit_is_set() {
+                Err(Other(Error::Acknowledge))
+            } else if sr1.ovr().bit_is_set() {
+                Err(Other(Error::Overrun))
+            } else if sr1.$flag().bit_is_set() {
+                Ok(())
+            } else {
+                Err(WouldBlock)
+            }
+        }
+    }
+}
+
+macro_rules! busy_wait {
+    ($nb_expr:expr, $exit_cond:expr) => {
+        {
+            loop {
+                let res = $nb_expr;
+                if res != Err(WouldBlock) {
+                    break res;
+                }
+                if $exit_cond {
+                    break res;
+                }
+            }
+        }
+    }
+}
+
+macro_rules! busy_wait_cycles {
+    ($nb_expr:expr, $cycles:expr) => {
+        {
+            let started = DWT::get_cycle_count();
+            let cycles = $cycles;
+            busy_wait!($nb_expr, DWT::get_cycle_count().wrapping_sub(started) >= cycles)
+        }
+    }
+}
+
+macro_rules! hal {
+    ($($I2CX:ident: ($i2cX:ident, $i2cXen:ident, $i2cXrst:ident),)+) => {
+        $(
+            impl<PINS> I2c<$I2CX, PINS> {
+                /// Configures the I2C peripheral to work in master mode
+                pub fn $i2cX(
+                    i2c: $I2CX,
+                    pins: PINS,
+                    mode: Mode,
+                    clocks: Clocks,
+                    apb: &mut APB1,
+                ) -> Self {
+                    apb.enr().modify(|_, w| w.$i2cXen().set_bit());
+                    apb.rstr().modify(|_, w| w.$i2cXrst().set_bit());
+                    apb.rstr().modify(|_, w| w.$i2cXrst().clear_bit());
+
+                    let pclk1 = clocks.pclk1().0;
+
+                    assert!(mode.get_frequency() <= 400_000);
+
+                    let mut i2c = I2c { i2c, pins, mode, pclk1 };
+                    i2c.init();
+                    i2c
+                }
+
+                fn init(&mut self) {
+                    let freq = self.mode.get_frequency();
+                    let pclk1_mhz = (self.pclk1 / 1000000) as u16;
+
+                    self.i2c.cr2.write(|w| unsafe {
+                        w.freq().bits(pclk1_mhz as u8)
+                    });
+                    self.i2c.cr1.write(|w| w.pe().clear_bit());
+
+                    match self.mode {
+                        Mode::Standard { .. } => {
+                            self.i2c.trise.write(|w| unsafe {
+                                w.trise().bits((pclk1_mhz + 1) as u8)
+                            });
+                            self.i2c.ccr.write(|w| unsafe {
+                                w.ccr().bits(((self.pclk1 / (freq * 2)) as u16).max(4))
+                            });
+                        },
+                        Mode::Fast { ref duty_cycle, .. } => {
+                            self.i2c.trise.write(|w| unsafe {
+                                w.trise().bits((pclk1_mhz * 300 / 1000 + 1) as u8)
+                            });
+
+                            self.i2c.ccr.write(|w| {
+                                let (freq, duty) = match duty_cycle {
+                                    &DutyCycle::Ratio2to1 => (((self.pclk1 / (freq * 3)) as u16).max(1), false),
+                                    &DutyCycle::Ratio16to9 => (((self.pclk1 / (freq * 25)) as u16).max(1), true)
+                                };
+
+                                unsafe {
+                                    w.ccr().bits(freq).duty().bit(duty).f_s().set_bit()
+                                }
+                            });
+                        }
+                    };
+
+                    self.i2c.cr1.modify(|_, w| w.pe().set_bit());
+                }
+
+                fn reset(&mut self) {
+                    self.i2c.cr1.write(|w| w.pe().set_bit().swrst().set_bit());
+                    self.i2c.cr1.reset();
+                    self.init();
+                }
+
+                fn send_start(&mut self) {
+                    self.i2c.cr1.modify(|_, w| w.start().set_bit());
+                }
+
+                fn wait_after_sent_start(&mut self) -> NbResult<(), Error> {
+                    wait_for_flag!(self.i2c, sb)
+                }
+
+                fn send_addr(&self, addr: u8, read: bool) {
+                    self.i2c.dr.write(|w| unsafe { w.dr().bits(addr << 1 | (if read {1} else {0})) });
+                }
+
+                fn wait_after_sent_addr(&self) -> NbResult<(), Error> {
+                    wait_for_flag!(self.i2c, addr)?;
+                    self.i2c.sr2.read();
+                    Ok(())
+                }
+
+                fn send_stop(&self) {
+                    self.i2c.cr1.modify(|_, w| w.stop().set_bit());
+                }
+
+                /// Releases the I2C peripheral and associated pins
+                pub fn free(self) -> ($I2CX, PINS) {
+                    (self.i2c, self.pins)
+                }
+            }
+
+            impl<PINS> BlockingI2c<$I2CX, PINS> {
+                pub fn $i2cX(
+                    i2c: $I2CX,
+                    pins: PINS,
+                    mode: Mode,
+                    clocks: Clocks,
+                    apb: &mut APB1,
+                    start_timeout_us: u32,
+                    start_retries: u8,
+                    addr_timeout_us: u32,
+                    data_timeout_us: u32
+                ) -> Self {
+                    blocking_i2c(I2c::$i2cX(i2c, pins, mode, clocks, apb),
+                        clocks, start_timeout_us, start_retries,
+                        addr_timeout_us, data_timeout_us)
+                }
+
+                fn send_start_and_wait(&mut self) -> NbResult<(), Error> {
+                    // According to http://www.st.com/content/ccc/resource/technical/document/errata_sheet/f5/50/c9/46/56/db/4a/f6/CD00197763.pdf/files/CD00197763.pdf/jcr:content/translations/en.CD00197763.pdf
+                    // 2.14.4 Wrong behavior of I2C peripheral in master mode after a misplaced Stop
+                    let mut retries_left = self.start_retries;
+                    let mut last_ret: NbResult<(), Error> = Err(WouldBlock);
+                    while retries_left > 0 {
+                        self.nb.send_start();
+                        last_ret = busy_wait_cycles!(self.nb.wait_after_sent_start(), self.start_timeout);
+                        if let Err(_) = last_ret {
+                            self.nb.reset();
+                        } else {
+                            break;
+                        }
+                        retries_left -= 1;
+                    }
+                    last_ret
+                }
+
+                fn send_addr_and_wait(&self, addr: u8, read: bool) -> NbResult<(), Error> {
+                    self.nb.send_addr(addr, read);
+                    busy_wait_cycles!(self.nb.wait_after_sent_addr(), self.addr_timeout)
+                }
+
+                fn write_without_stop(&mut self, addr: u8, bytes: &[u8]) -> NbResult<(), Error> {
+                    self.send_start_and_wait()?;
+                    self.send_addr_and_wait(addr, false)?;
+
+                    for byte in bytes {
+                        busy_wait_cycles!(wait_for_flag!(self.nb.i2c, tx_e), self.data_timeout)?;
+                        self.nb.i2c.dr.write(|w| unsafe { w.dr().bits(*byte) });
+                    }
+                    busy_wait_cycles!(wait_for_flag!(self.nb.i2c, tx_e), self.data_timeout)?;
+
+                    Ok(())
+                }
+            }
+
+            impl<PINS> Write for BlockingI2c<$I2CX, PINS> {
+                type Error = NbError<Error>;
+
+                fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
+                    self.write_without_stop(addr, bytes)?;
+                    self.nb.send_stop();
+
+                    Ok(())
+                }
+            }
+
+            impl<PINS> Read for BlockingI2c<$I2CX, PINS> {
+                type Error = NbError<Error>;
+
+                fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
+                    self.send_start_and_wait()?;
+
+                    match buffer.len() {
+                        1 => {
+                            self.nb.send_addr(addr, true);
+                            busy_wait_cycles!(wait_for_flag!(self.nb.i2c, addr), self.addr_timeout)?;
+                            self.nb.i2c.cr1.modify(|_, w| w.ack().clear_bit());
+                            let _ = self.nb.i2c.sr2.read();
+                            self.nb.send_stop();
+
+                            busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rx_ne), self.data_timeout)?;
+                            buffer[0] = self.nb.i2c.dr.read().dr().bits();
+                        }
+                        2 => {
+                            self.nb.i2c.cr1.modify(|_, w| w.pos().set_bit().ack().set_bit());
+                            self.send_addr_and_wait(addr, true)?;
+                            self.nb.i2c.cr1.modify(|_, w| w.pos().clear_bit().ack().clear_bit());
+
+                            busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btf), self.data_timeout)?;
+                            self.nb.send_stop();
+                            buffer[0] = self.nb.i2c.dr.read().dr().bits();
+                            buffer[1] = self.nb.i2c.dr.read().dr().bits();
+                        }
+                        buffer_len => {
+                            self.nb.i2c.cr1.modify(|_, w| w.ack().set_bit());
+                            self.send_addr_and_wait(addr, true)?;
+
+                            let (mut first_bytes, mut last_two_bytes) = buffer.split_at_mut(buffer_len - 3);
+                            for mut byte in first_bytes {
+                                self.nb.i2c.cr1.modify(|_, w| w.ack().set_bit());
+                                busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rx_ne), self.data_timeout)?;
+                                *byte = self.nb.i2c.dr.read().dr().bits();
+                            }
+
+                            busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btf), self.data_timeout)?;
+                            self.nb.i2c.cr1.modify(|_, w| w.ack().clear_bit());
+                            last_two_bytes[0] = self.nb.i2c.dr.read().dr().bits();
+                            self.nb.send_stop();
+                            last_two_bytes[1] = self.nb.i2c.dr.read().dr().bits();
+                            busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rx_ne), self.data_timeout)?;
+                            last_two_bytes[2] = self.nb.i2c.dr.read().dr().bits();
+                        }
+                    }
+
+                    Ok(())
+                }
+            }
+
+            impl<PINS> WriteRead for BlockingI2c<$I2CX, PINS> {
+                type Error = NbError<Error>;
+
+                fn write_read(
+                    &mut self,
+                    addr: u8,
+                    bytes: &[u8],
+                    buffer: &mut [u8],
+                ) -> Result<(), Self::Error> {
+                    assert!(buffer.len() > 0);
+
+                    if bytes.len() != 0 {
+                        self.write_without_stop(addr, bytes)?;
+                    }
+
+                    self.read(addr, buffer)?;
+
+                    Ok(())
+                }
+            }
+        )+
+    }
+}
+
+hal! {
+    I2C1: (_i2c1, i2c1en, i2c1rst),
+    I2C2: (_i2c2, i2c2en, i2c2rst),
+}

+ 71 - 0
src/lib.rs

@@ -0,0 +1,71 @@
+//! HAL for the STM32F1 family of microcontrollers
+//!
+//! This is an implementation of the [`embedded-hal`] traits for the STM32F1 family of
+//! microcontrollers.
+//!
+//! [`embedded-hal`]: https://crates.io/crates/embedded-hal
+//!
+//! # Usage
+//!
+//! - Trying out the examples
+//!
+//! ``` text
+//! $ git clone https://github.com/stm32-rs/stm32f1xx-hal
+//!
+//! # on another terminal
+//! $ openocd -f interface/$INTERFACE.cfg -f target/stm32f1x.cfg
+//!
+//! # flash and debug the "Hello, world" example
+//! # NOTE examples assume 64KB of Flash and 20KB of RAM; you can tweak layout in memory.x
+//! $ cd stm32f1xx-hal
+//! $ rustup target add thumbv7m-none-eabi
+//! $ cargo run --example hello
+//! ```
+//!
+//! - Building an application (binary crate)
+//!
+//! Follow the [cortex-m-quickstart] instructions and add this crate as a dependency
+//! and make sure you enable the "rt" Cargo feature of this crate.
+//!
+//! [cortex-m-quickstart]: https://docs.rs/cortex-m-quickstart/~0.2.3
+//!
+//! # Examples
+//!
+//! See the [examples] module.
+//!
+//! [examples]: examples/index.html
+
+#![no_std]
+
+extern crate cast;
+extern crate cortex_m;
+extern crate embedded_hal as hal;
+extern crate nb;
+extern crate void;
+
+pub extern crate stm32f1;
+
+#[cfg(feature = "stm32f103")]
+pub use stm32f1::stm32f103 as stm32;
+
+// Enable use of interrupt macro
+#[cfg(feature = "rt")]
+pub use stm32f1::interrupt;
+
+pub mod afio;
+pub mod bb;
+pub mod delay;
+pub mod dma;
+#[cfg(feature = "doc")]
+pub mod examples;
+pub mod flash;
+pub mod gpio;
+pub mod i2c;
+pub mod prelude;
+pub mod pwm;
+pub mod qei;
+pub mod rcc;
+pub mod serial;
+pub mod spi;
+pub mod time;
+pub mod timer;

+ 13 - 0
src/prelude.rs

@@ -0,0 +1,13 @@
+pub use afio::AfioExt as _stm32_hal_afio_AfioExt;
+pub use dma::DmaExt as _stm32_hal_dma_DmaExt;
+pub use dma::DmaChannel as _stm32_hal_dma_DmaChannel;
+pub use flash::FlashExt as _stm32_hal_flash_FlashExt;
+pub use gpio::GpioExt as _stm32_hal_gpio_GpioExt;
+pub use hal::digital::StatefulOutputPin as _embedded_hal_digital_StatefulOutputPin;
+pub use hal::digital::ToggleableOutputPin as _embedded_hal_digital_ToggleableOutputPin;
+pub use hal::prelude::*;
+pub use pwm::PwmExt as _stm32_hal_pwm_PwmExt;
+pub use rcc::RccExt as _stm32_hal_rcc_RccExt;
+pub use serial::ReadDma as _stm32_hal_serial_ReadDma;
+pub use serial::WriteDma as _stm32_hal_serial_WriteDma;
+pub use time::U32Ext as _stm32_hal_time_U32Ext;

+ 337 - 0
src/pwm.rs

@@ -0,0 +1,337 @@
+use core::marker::PhantomData;
+use core::mem;
+
+use cast::{u16, u32};
+use hal;
+use stm32::{TIM2, TIM3, TIM4};
+
+use afio::MAPR;
+use bb;
+use gpio::gpioa::{PA0, PA1, PA2, PA3, PA6, PA7};
+use gpio::gpiob::{PB0, PB1, PB6, PB7, PB8, PB9};
+use gpio::{Alternate, PushPull};
+use rcc::{APB1, Clocks};
+use time::Hertz;
+
+pub trait Pins<TIM> {
+    const REMAP: u8;
+    const C1: bool;
+    const C2: bool;
+    const C3: bool;
+    const C4: bool;
+    type Channels;
+}
+
+impl Pins<TIM2>
+    for (
+        PA0<Alternate<PushPull>>,
+        PA1<Alternate<PushPull>>,
+        PA2<Alternate<PushPull>>,
+        PA3<Alternate<PushPull>>,
+    )
+{
+    const REMAP: u8 = 0b00;
+    const C1: bool = true;
+    const C2: bool = true;
+    const C3: bool = true;
+    const C4: bool = true;
+    type Channels = (Pwm<TIM2, C1>, Pwm<TIM2, C2>, Pwm<TIM2, C3>, Pwm<TIM2, C4>);
+}
+
+impl Pins<TIM2> for PA0<Alternate<PushPull>> {
+    const REMAP: u8 = 0b00;
+    const C1: bool = true;
+    const C2: bool = false;
+    const C3: bool = false;
+    const C4: bool = false;
+    type Channels = Pwm<TIM2, C1>;
+}
+
+impl Pins<TIM3>
+    for (
+        PA6<Alternate<PushPull>>,
+        PA7<Alternate<PushPull>>,
+        PB0<Alternate<PushPull>>,
+        PB1<Alternate<PushPull>>,
+    )
+{
+    const REMAP: u8 = 0b00;
+    const C1: bool = true;
+    const C2: bool = true;
+    const C3: bool = true;
+    const C4: bool = true;
+    type Channels = (Pwm<TIM3, C1>, Pwm<TIM3, C2>, Pwm<TIM3, C3>, Pwm<TIM3, C4>);
+}
+
+impl Pins<TIM3> for (PB0<Alternate<PushPull>>, PB1<Alternate<PushPull>>) {
+    const REMAP: u8 = 0b00;
+    const C1: bool = false;
+    const C2: bool = false;
+    const C3: bool = true;
+    const C4: bool = true;
+    type Channels = (Pwm<TIM3, C3>, Pwm<TIM3, C4>);
+}
+
+impl Pins<TIM4>
+    for (
+        PB6<Alternate<PushPull>>,
+        PB7<Alternate<PushPull>>,
+        PB8<Alternate<PushPull>>,
+        PB9<Alternate<PushPull>>,
+    )
+{
+    const REMAP: u8 = 0b0;
+    const C1: bool = true;
+    const C2: bool = true;
+    const C3: bool = true;
+    const C4: bool = true;
+    type Channels = (Pwm<TIM4, C1>, Pwm<TIM4, C2>, Pwm<TIM4, C3>, Pwm<TIM4, C4>);
+}
+
+pub trait PwmExt: Sized {
+    fn pwm<PINS, T>(
+        self,
+        PINS,
+        mapr: &mut MAPR,
+        frequency: T,
+        clocks: Clocks,
+        apb: &mut APB1,
+    ) -> PINS::Channels
+    where
+        PINS: Pins<Self>,
+        T: Into<Hertz>;
+}
+
+impl PwmExt for TIM2 {
+    fn pwm<PINS, T>(
+        self,
+        _pins: PINS,
+        mapr: &mut MAPR,
+        freq: T,
+        clocks: Clocks,
+        apb: &mut APB1,
+    ) -> PINS::Channels
+    where
+        PINS: Pins<Self>,
+        T: Into<Hertz>,
+    {
+        mapr.mapr()
+            .modify(|_, w| unsafe { w.tim2_remap().bits(PINS::REMAP) });
+
+        tim2(self, _pins, freq.into(), clocks, apb)
+    }
+}
+
+impl PwmExt for TIM3 {
+    fn pwm<PINS, T>(
+        self,
+        _pins: PINS,
+        mapr: &mut MAPR,
+        freq: T,
+        clocks: Clocks,
+        apb: &mut APB1,
+    ) -> PINS::Channels
+    where
+        PINS: Pins<Self>,
+        T: Into<Hertz>,
+    {
+        mapr.mapr()
+            .modify(|_, w| unsafe { w.tim3_remap().bits(PINS::REMAP) });
+
+        tim3(self, _pins, freq.into(), clocks, apb)
+    }
+}
+
+impl PwmExt for TIM4 {
+    fn pwm<PINS, T>(
+        self,
+        _pins: PINS,
+        mapr: &mut MAPR,
+        freq: T,
+        clocks: Clocks,
+        apb: &mut APB1,
+    ) -> PINS::Channels
+    where
+        PINS: Pins<Self>,
+        T: Into<Hertz>,
+    {
+        mapr.mapr()
+            .modify(|_, w| w.tim4_remap().bit(PINS::REMAP == 1));
+
+        tim4(self, _pins, freq.into(), clocks, apb)
+    }
+}
+
+pub struct Pwm<TIM, CHANNEL> {
+    _channel: PhantomData<CHANNEL>,
+    _tim: PhantomData<TIM>,
+}
+
+pub struct C1;
+pub struct C2;
+pub struct C3;
+pub struct C4;
+
+macro_rules! hal {
+    ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident),)+) => {
+        $(
+            fn $timX<PINS>(
+                tim: $TIMX,
+                _pins: PINS,
+                freq: Hertz,
+                clocks: Clocks,
+                apb: &mut APB1,
+            ) -> PINS::Channels
+            where
+                PINS: Pins<$TIMX>,
+            {
+                apb.enr().modify(|_, w| w.$timXen().set_bit());
+                apb.rstr().modify(|_, w| w.$timXrst().set_bit());
+                apb.rstr().modify(|_, w| w.$timXrst().clear_bit());
+
+                if PINS::C1 {
+                    tim.ccmr1_output
+                        .modify(|_, w| w.oc1pe().set_bit().oc1m().bits(6));
+                }
+
+                if PINS::C2 {
+                    tim.ccmr1_output
+                        .modify(|_, w| w.oc2pe().set_bit().oc2m().bits(6));
+                }
+
+                if PINS::C3 {
+                    tim.ccmr2_output
+                        .modify(|_, w| w.oc3pe().set_bit().oc3m().bits(6));
+                }
+
+                if PINS::C4 {
+                    tim.ccmr2_output
+                        .modify(|_, w| w.oc4pe().set_bit().oc4m().bits(6));
+                }
+
+                let clk = clocks.pclk1().0 * if clocks.ppre1() == 1 { 1 } else { 2 };
+                let freq = freq.0;
+                let ticks = clk / freq;
+                let psc = u16(ticks / (1 << 16)).unwrap();
+                tim.psc.write(|w| w.psc().bits(psc));
+                let arr = u16(ticks / u32(psc + 1)).unwrap();
+                tim.arr.write(|w| w.arr().bits(arr));
+
+                tim.cr1.write(|w| unsafe {
+                    w.cms()
+                        .bits(0b00)
+                        .dir()
+                        .clear_bit()
+                        .opm()
+                        .clear_bit()
+                        .cen()
+                        .set_bit()
+                });
+
+                unsafe { mem::uninitialized() }
+            }
+
+            impl hal::PwmPin for Pwm<$TIMX, C1> {
+                type Duty = u16;
+
+                fn disable(&mut self) {
+                    unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 0) }
+                }
+
+                fn enable(&mut self) {
+                    unsafe { bb::set(&(*$TIMX::ptr()).ccer, 0) }
+                }
+
+                fn get_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).ccr1.read().ccr1().bits() }
+                }
+
+                fn get_max_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() }
+                }
+
+                fn set_duty(&mut self, duty: u16) {
+                    unsafe { (*$TIMX::ptr()).ccr1.write(|w| w.ccr1().bits(duty)) }
+                }
+            }
+
+            impl hal::PwmPin for Pwm<$TIMX, C2> {
+                type Duty = u16;
+
+                fn disable(&mut self) {
+                    unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 4) }
+                }
+
+                fn enable(&mut self) {
+                    unsafe { bb::set(&(*$TIMX::ptr()).ccer, 4) }
+                }
+
+                fn get_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).ccr2.read().ccr2().bits() }
+                }
+
+                fn get_max_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() }
+                }
+
+                fn set_duty(&mut self, duty: u16) {
+                    unsafe { (*$TIMX::ptr()).ccr2.write(|w| w.ccr2().bits(duty)) }
+                }
+            }
+
+            impl hal::PwmPin for Pwm<$TIMX, C3> {
+                type Duty = u16;
+
+                fn disable(&mut self) {
+                    unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 8) }
+                }
+
+                fn enable(&mut self) {
+                    unsafe { bb::set(&(*$TIMX::ptr()).ccer, 8) }
+                }
+
+                fn get_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).ccr3.read().ccr3().bits() }
+                }
+
+                fn get_max_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() }
+                }
+
+                fn set_duty(&mut self, duty: u16) {
+                    unsafe { (*$TIMX::ptr()).ccr3.write(|w| w.ccr3().bits(duty)) }
+                }
+            }
+
+            impl hal::PwmPin for Pwm<$TIMX, C4> {
+                type Duty = u16;
+
+                fn disable(&mut self) {
+                    unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 12) }
+                }
+
+                fn enable(&mut self) {
+                    unsafe { bb::set(&(*$TIMX::ptr()).ccer, 12) }
+                }
+
+                fn get_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).ccr4.read().ccr4().bits() }
+                }
+
+                fn get_max_duty(&self) -> u16 {
+                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() }
+                }
+
+                fn set_duty(&mut self, duty: u16) {
+                    unsafe { (*$TIMX::ptr()).ccr4.write(|w| w.ccr4().bits(duty)) }
+                }
+            }
+        )+
+    }
+}
+
+hal! {
+    TIM2: (tim2, tim2en, tim2rst),
+    TIM3: (tim3, tim3en, tim3rst),
+    TIM4: (tim4, tim4en, tim4rst),
+}

+ 133 - 0
src/qei.rs

@@ -0,0 +1,133 @@
+use core::u16;
+
+use hal::{self, Direction};
+use stm32::{TIM2, TIM3, TIM4};
+
+use afio::MAPR;
+use gpio::gpioa::{PA0, PA1, PA6, PA7};
+use gpio::gpiob::{PB6, PB7};
+use gpio::{Floating, Input};
+use rcc::APB1;
+
+pub trait Pins<TIM> {
+    const REMAP: u8;
+}
+
+impl Pins<TIM2> for (PA0<Input<Floating>>, PA1<Input<Floating>>) {
+    const REMAP: u8 = 0b00;
+}
+
+impl Pins<TIM3> for (PA6<Input<Floating>>, PA7<Input<Floating>>) {
+    const REMAP: u8 = 0b00;
+}
+
+impl Pins<TIM4> for (PB6<Input<Floating>>, PB7<Input<Floating>>) {
+    const REMAP: u8 = 0b00;
+}
+
+pub struct Qei<TIM, PINS> {
+    tim: TIM,
+    pins: PINS,
+}
+
+impl<PINS> Qei<TIM2, PINS> {
+    pub fn tim2(tim: TIM2, pins: PINS, mapr: &mut MAPR, apb: &mut APB1) -> Self
+    where
+        PINS: Pins<TIM2>,
+    {
+        mapr.mapr()
+            .modify(|_, w| unsafe { w.tim2_remap().bits(PINS::REMAP) });
+
+        Qei::_tim2(tim, pins, apb)
+    }
+}
+
+impl<PINS> Qei<TIM3, PINS> {
+    pub fn tim3(tim: TIM3, pins: PINS, mapr: &mut MAPR, apb: &mut APB1) -> Self
+    where
+        PINS: Pins<TIM3>,
+    {
+        mapr.mapr()
+            .modify(|_, w| unsafe { w.tim3_remap().bits(PINS::REMAP) });
+
+        Qei::_tim3(tim, pins, apb)
+    }
+}
+
+impl<PINS> Qei<TIM4, PINS> {
+    pub fn tim4(tim: TIM4, pins: PINS, mapr: &mut MAPR, apb: &mut APB1) -> Self
+    where
+        PINS: Pins<TIM4>,
+    {
+        mapr.mapr()
+            .modify(|_, w| w.tim4_remap().bit(PINS::REMAP == 1));
+
+        Qei::_tim4(tim, pins, apb)
+    }
+}
+
+macro_rules! hal {
+    ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident),)+) => {
+        $(
+            impl<PINS> Qei<$TIMX, PINS> {
+                fn $timX(tim: $TIMX, pins: PINS, apb: &mut APB1) -> Self {
+                    // enable and reset peripheral to a clean slate state
+                    apb.enr().modify(|_, w| w.$timXen().set_bit());
+                    apb.rstr().modify(|_, w| w.$timXrst().set_bit());
+                    apb.rstr().modify(|_, w| w.$timXrst().clear_bit());
+
+                    // Configure TxC1 and TxC2 as captures
+                    tim.ccmr1_output
+                        .write(|w| unsafe { w.bits({ (0b01 << 0) | (0b01 << 8) }) });
+
+                    // enable and configure to capture on rising edge
+                    tim.ccer.write(|w| {
+                        w.cc1e()
+                            .set_bit()
+                            .cc1p()
+                            .clear_bit()
+                            .cc2e()
+                            .set_bit()
+                            .cc2p()
+                            .clear_bit()
+                    });
+
+                    // configure as quadrature encoder
+                    tim.smcr.write(|w| w.sms().bits(3));
+
+                    tim.arr.write(|w| w.arr().bits(u16::MAX));
+                    tim.cr1.write(|w| w.cen().set_bit());
+
+                    Qei { tim, pins }
+                }
+
+                pub fn release(self) -> ($TIMX, PINS) {
+                    (self.tim, self.pins)
+                }
+            }
+
+            impl<PINS> hal::Qei for Qei<$TIMX, PINS> {
+                type Count = u16;
+
+                fn count(&self) -> u16 {
+                    self.tim.cnt.read().cnt().bits()
+                }
+
+                fn direction(&self) -> Direction {
+                    if self.tim.cr1.read().dir().bit_is_clear() {
+                        hal::Direction::Upcounting
+                    } else {
+                        hal::Direction::Downcounting
+                    }
+                }
+            }
+
+        )+
+    }
+}
+
+hal! {
+    TIM2: (_tim2, tim2en, tim2rst),
+    TIM3: (_tim3, tim3en, tim3rst),
+    TIM4: (_tim4, tim4en, tim4rst),
+}

+ 345 - 0
src/rcc.rs

@@ -0,0 +1,345 @@
+use core::cmp;
+
+use cast::u32;
+use stm32::rcc::cfgr::{SWW, USBPREW};
+use stm32::{rcc, RCC};
+
+use flash::ACR;
+use time::Hertz;
+
+/// Extension trait that constrains the `RCC` peripheral
+pub trait RccExt {
+    /// Constrains the `RCC` peripheral so it plays nicely with the other abstractions
+    fn constrain(self) -> Rcc;
+}
+
+impl RccExt for RCC {
+    fn constrain(self) -> Rcc {
+        Rcc {
+            ahb: AHB { _0: () },
+            apb1: APB1 { _0: () },
+            apb2: APB2 { _0: () },
+            cfgr: CFGR {
+                hse: None,
+                hclk: None,
+                pclk1: None,
+                pclk2: None,
+                sysclk: None,
+            },
+        }
+    }
+}
+
+/// Constrained RCC peripheral
+pub struct Rcc {
+    /// AMBA High-performance Bus (AHB) registers
+    pub ahb: AHB,
+    /// Advanced Peripheral Bus 1 (APB1) registers
+    pub apb1: APB1,
+    /// Advanced Peripheral Bus 2 (APB2) registers
+    pub apb2: APB2,
+    pub cfgr: CFGR,
+}
+
+/// AMBA High-performance Bus (AHB) registers
+pub struct AHB {
+    _0: (),
+}
+
+impl AHB {
+    pub(crate) fn enr(&mut self) -> &rcc::AHBENR {
+        // NOTE(unsafe) this proxy grants exclusive access to this register
+        unsafe { &(*RCC::ptr()).ahbenr }
+    }
+}
+
+/// Advanced Peripheral Bus 1 (APB1) registers
+pub struct APB1 {
+    _0: (),
+}
+
+impl APB1 {
+    pub(crate) fn enr(&mut self) -> &rcc::APB1ENR {
+        // NOTE(unsafe) this proxy grants exclusive access to this register
+        unsafe { &(*RCC::ptr()).apb1enr }
+    }
+
+    pub(crate) fn rstr(&mut self) -> &rcc::APB1RSTR {
+        // NOTE(unsafe) this proxy grants exclusive access to this register
+        unsafe { &(*RCC::ptr()).apb1rstr }
+    }
+}
+
+/// Advanced Peripheral Bus 2 (APB2) registers
+pub struct APB2 {
+    _0: (),
+}
+
+impl APB2 {
+    pub(crate) fn enr(&mut self) -> &rcc::APB2ENR {
+        // NOTE(unsafe) this proxy grants exclusive access to this register
+        unsafe { &(*RCC::ptr()).apb2enr }
+    }
+
+    pub(crate) fn rstr(&mut self) -> &rcc::APB2RSTR {
+        // NOTE(unsafe) this proxy grants exclusive access to this register
+        unsafe { &(*RCC::ptr()).apb2rstr }
+    }
+}
+
+const HSI: u32 = 8_000_000; // Hz
+
+pub struct CFGR {
+    hse: Option<u32>,
+    hclk: Option<u32>,
+    pclk1: Option<u32>,
+    pclk2: Option<u32>,
+    sysclk: Option<u32>,
+}
+
+impl CFGR {
+    /// Uses HSE (external oscillator) instead of HSI (internal RC oscillator) as the clock source.
+    /// Will result in a hang if an external oscillator is not connected or it fails to start.
+    pub fn use_hse<F>(mut self, freq: F) -> Self
+    where
+        F: Into<Hertz>,
+    {
+        self.hse = Some(freq.into().0);
+        self
+    }
+
+    /// Sets the desired frequency for the HCLK clock
+    pub fn hclk<F>(mut self, freq: F) -> Self
+    where
+        F: Into<Hertz>,
+    {
+        self.hclk = Some(freq.into().0);
+        self
+    }
+
+    /// Sets the desired frequency for the PCKL1 clock
+    pub fn pclk1<F>(mut self, freq: F) -> Self
+    where
+        F: Into<Hertz>,
+    {
+        self.pclk1 = Some(freq.into().0);
+        self
+    }
+
+    /// Sets the desired frequency for the PCLK2 clock
+    pub fn pclk2<F>(mut self, freq: F) -> Self
+    where
+        F: Into<Hertz>,
+    {
+        self.pclk2 = Some(freq.into().0);
+        self
+    }
+
+    /// Sets the desired frequency for the SYSCLK clock
+    pub fn sysclk<F>(mut self, freq: F) -> Self
+    where
+        F: Into<Hertz>,
+    {
+        self.sysclk = Some(freq.into().0);
+        self
+    }
+
+    pub fn freeze(self, acr: &mut ACR) -> Clocks {
+        // TODO ADC clock
+
+        let pllsrcclk = self.hse.unwrap_or(HSI / 2);
+
+        let pllmul = self.sysclk.unwrap_or(pllsrcclk) / pllsrcclk;
+        let pllmul = cmp::min(cmp::max(pllmul, 1), 16);
+
+        let (pllmul_bits, sysclk) = if pllmul == 1 {
+            (None, self.hse.unwrap_or(HSI))
+        } else {
+            (Some(pllmul as u8 - 2), pllsrcclk * pllmul)
+        };
+
+        assert!(sysclk <= 72_000_000);
+
+        let hpre_bits = self.hclk
+            .map(|hclk| match sysclk / hclk {
+                0 => unreachable!(),
+                1 => 0b0111,
+                2 => 0b1000,
+                3...5 => 0b1001,
+                6...11 => 0b1010,
+                12...39 => 0b1011,
+                40...95 => 0b1100,
+                96...191 => 0b1101,
+                192...383 => 0b1110,
+                _ => 0b1111,
+            })
+            .unwrap_or(0b0111);
+
+        let hclk = sysclk / (1 << (hpre_bits - 0b0111));
+
+        assert!(hclk <= 72_000_000);
+
+        let ppre1_bits = self.pclk1
+            .map(|pclk1| match hclk / pclk1 {
+                0 => unreachable!(),
+                1 => 0b011,
+                2 => 0b100,
+                3...5 => 0b101,
+                6...11 => 0b110,
+                _ => 0b111,
+            })
+            .unwrap_or(0b011);
+
+        let ppre1 = 1 << (ppre1_bits - 0b011);
+        let pclk1 = hclk / u32(ppre1);
+
+        assert!(pclk1 <= 36_000_000);
+
+        let ppre2_bits = self.pclk2
+            .map(|pclk2| match hclk / pclk2 {
+                0 => unreachable!(),
+                1 => 0b011,
+                2 => 0b100,
+                3...5 => 0b101,
+                6...11 => 0b110,
+                _ => 0b111,
+            })
+            .unwrap_or(0b011);
+
+        let ppre2 = 1 << (ppre2_bits - 0b011);
+        let pclk2 = hclk / u32(ppre2);
+
+        assert!(pclk2 <= 72_000_000);
+
+        // adjust flash wait states
+        unsafe {
+            acr.acr().write(|w| {
+                w.latency().bits(if sysclk <= 24_000_000 {
+                    0b000
+                } else if sysclk <= 48_000_000 {
+                    0b001
+                } else {
+                    0b010
+                })
+            })
+        }
+
+        // the USB clock is only valid if an external crystal is used, the PLL is enabled, and the
+        // PLL output frequency is a supported one.
+        let (usbpre, usbclk_valid) = match (self.hse, pllmul_bits, sysclk) {
+            (Some(_), Some(_), 72_000_000) => (USBPREW::DIV15, true),
+            (Some(_), Some(_), 48_000_000) => (USBPREW::NODIV, true),
+            _ => (USBPREW::NODIV, false),
+        };
+
+        let rcc = unsafe { &*RCC::ptr() };
+
+        if self.hse.is_some() {
+            // enable HSE and wait for it to be ready
+
+            rcc.cr.modify(|_, w| w.hseon().set_bit());
+
+            while rcc.cr.read().hserdy().bit_is_clear() {}
+        }
+
+        if let Some(pllmul_bits) = pllmul_bits {
+            // enable PLL and wait for it to be ready
+
+            rcc.cfgr.modify(|_, w| unsafe {
+                w.pllmul()
+                    .bits(pllmul_bits)
+                    .pllsrc()
+                    .bit(if self.hse.is_some() {
+                        true
+                    } else {
+                        false
+                    })
+            });
+
+            rcc.cr.modify(|_, w| w.pllon().set_bit());
+
+            while rcc.cr.read().pllrdy().bit_is_clear() {}
+        }
+
+        // set prescalers and clock source
+        rcc.cfgr.modify(|_, w| unsafe {
+            w.ppre2()
+                .bits(ppre2_bits)
+                .ppre1()
+                .bits(ppre1_bits)
+                .hpre()
+                .bits(hpre_bits)
+                .usbpre()
+                .variant(usbpre)
+                .sw()
+                .variant(if pllmul_bits.is_some() {
+                    SWW::PLL
+                } else if self.hse.is_some() {
+                    SWW::HSE
+                } else {
+                    SWW::HSI
+                })
+        });
+
+        Clocks {
+            hclk: Hertz(hclk),
+            pclk1: Hertz(pclk1),
+            pclk2: Hertz(pclk2),
+            ppre1,
+            ppre2,
+            sysclk: Hertz(sysclk),
+            usbclk_valid,
+        }
+    }
+}
+
+/// Frozen clock frequencies
+///
+/// The existence of this value indicates that the clock configuration can no longer be changed
+#[derive(Clone, Copy)]
+pub struct Clocks {
+    hclk: Hertz,
+    pclk1: Hertz,
+    pclk2: Hertz,
+    ppre1: u8,
+    ppre2: u8,
+    sysclk: Hertz,
+    usbclk_valid: bool,
+}
+
+impl Clocks {
+    /// Returns the frequency of the AHB
+    pub fn hclk(&self) -> Hertz {
+        self.hclk
+    }
+
+    /// Returns the frequency of the APB1
+    pub fn pclk1(&self) -> Hertz {
+        self.pclk1
+    }
+
+    /// Returns the frequency of the APB2
+    pub fn pclk2(&self) -> Hertz {
+        self.pclk2
+    }
+
+    pub(crate) fn ppre1(&self) -> u8 {
+        self.ppre1
+    }
+
+    // TODO remove `allow`
+    #[allow(dead_code)]
+    pub(crate) fn ppre2(&self) -> u8 {
+        self.ppre2
+    }
+
+    /// Returns the system (core) frequency
+    pub fn sysclk(&self) -> Hertz {
+        self.sysclk
+    }
+
+    /// Returns whether the USBCLK clock frequency is valid for the USB peripheral
+    pub fn usbclk_valid(&self) -> bool {
+        self.usbclk_valid
+    }
+}

+ 467 - 0
src/serial.rs

@@ -0,0 +1,467 @@
+use core::marker::PhantomData;
+use core::ptr;
+use core::sync::atomic::{self, Ordering};
+
+use cast::u16;
+use hal;
+use nb;
+use stm32::{USART1, USART2, USART3};
+use void::Void;
+
+use afio::MAPR;
+use dma::{dma1, CircBuffer, Static, Transfer, R, W};
+use gpio::gpioa::{PA10, PA2, PA3, PA9};
+use gpio::gpiob::{PB10, PB11, PB6, PB7};
+use gpio::{Alternate, Floating, Input, PushPull};
+use rcc::{APB1, APB2, Clocks};
+use time::Bps;
+
+/// Interrupt event
+pub enum Event {
+    /// New data has been received
+    Rxne,
+    /// New data can be sent
+    Txe,
+}
+
+/// Serial error
+#[derive(Debug)]
+pub enum Error {
+    /// Framing error
+    Framing,
+    /// Noise error
+    Noise,
+    /// RX buffer overrun
+    Overrun,
+    /// Parity check error
+    Parity,
+    #[doc(hidden)]
+    _Extensible,
+}
+
+pub trait Pins<USART> {
+    const REMAP: u8;
+}
+
+impl Pins<USART1> for (PA9<Alternate<PushPull>>, PA10<Input<Floating>>) {
+    const REMAP: u8 = 0;
+}
+
+impl Pins<USART1> for (PB6<Alternate<PushPull>>, PB7<Input<Floating>>) {
+    const REMAP: u8 = 1;
+}
+
+impl Pins<USART2> for (PA2<Alternate<PushPull>>, PA3<Input<Floating>>) {
+    const REMAP: u8 = 0;
+}
+
+// impl Pins<USART2> for (PD5<Alternate<PushPull>>, PD6<Input<Floating>>) {
+//     const REMAP: u8 = 0;
+// }
+
+impl Pins<USART3> for (PB10<Alternate<PushPull>>, PB11<Input<Floating>>) {
+    const REMAP: u8 = 0;
+}
+
+// impl Pins<USART3> for (PC10<Alternate<PushPull>>, PC11<Input<Floating>>) {
+//     const REMAP: u8 = 1;
+// }
+
+// impl Pins<USART3> for (PD8<Alternate<PushPull>>, PD9<Input<Floating>>) {
+//     const REMAP: u8 = 0b11;
+// }
+
+/// Serial abstraction
+pub struct Serial<USART, PINS> {
+    usart: USART,
+    pins: PINS,
+}
+
+/// Serial receiver
+pub struct Rx<USART> {
+    _usart: PhantomData<USART>,
+}
+
+/// Serial transmitter
+pub struct Tx<USART> {
+    _usart: PhantomData<USART>,
+}
+
+macro_rules! hal {
+    ($(
+        $USARTX:ident: (
+            $usartX:ident,
+            $usartXen:ident,
+            $usartXrst:ident,
+            $usartX_remap:ident,
+            $bit:ident,
+            $closure:expr,
+            $APB:ident
+        ),
+    )+) => {
+        $(
+            impl<PINS> Serial<$USARTX, PINS> {
+                pub fn $usartX(
+                    usart: $USARTX,
+                    pins: PINS,
+                    mapr: &mut MAPR,
+                    baud_rate: Bps,
+                    clocks: Clocks,
+                    apb: &mut $APB,
+                ) -> Self
+                where
+                    PINS: Pins<$USARTX>,
+                {
+                    // enable and reset $USARTX
+                    apb.enr().modify(|_, w| w.$usartXen().set_bit());
+                    apb.rstr().modify(|_, w| w.$usartXrst().set_bit());
+                    apb.rstr().modify(|_, w| w.$usartXrst().clear_bit());
+
+                    #[allow(unused_unsafe)]
+                    mapr.mapr()
+                        .modify(|_, w| unsafe{
+                            w.$usartX_remap().$bit(($closure)(PINS::REMAP))
+                        });
+
+                    // enable DMA transfers
+                    usart.cr3.write(|w| w.dmat().set_bit().dmar().set_bit());
+
+                    let brr = clocks.pclk2().0 / baud_rate.0;
+                    assert!(brr >= 16, "impossible baud rate");
+                    usart.brr.write(|w| unsafe { w.bits(brr) });
+
+                    // UE: enable USART
+                    // RE: enable receiver
+                    // TE: enable transceiver
+                    usart
+                        .cr1
+                        .write(|w| w.ue().set_bit().re().set_bit().te().set_bit());
+
+                    Serial { usart, pins }
+                }
+
+                pub fn listen(&mut self, event: Event) {
+                    match event {
+                        Event::Rxne => self.usart.cr1.modify(|_, w| w.rxneie().set_bit()),
+                        Event::Txe => self.usart.cr1.modify(|_, w| w.txeie().set_bit()),
+                    }
+                }
+
+                pub fn unlisten(&mut self, event: Event) {
+                    match event {
+                        Event::Rxne => self.usart.cr1.modify(|_, w| w.rxneie().clear_bit()),
+                        Event::Txe => self.usart.cr1.modify(|_, w| w.txeie().clear_bit()),
+                    }
+                }
+
+                pub fn release(self) -> ($USARTX, PINS) {
+                    (self.usart, self.pins)
+                }
+
+                pub fn split(self) -> (Tx<$USARTX>, Rx<$USARTX>) {
+                    (
+                        Tx {
+                            _usart: PhantomData,
+                        },
+                        Rx {
+                            _usart: PhantomData,
+                        },
+                    )
+                }
+            }
+
+            impl hal::serial::Read<u8> for Rx<$USARTX> {
+                type Error = Error;
+
+                fn read(&mut self) -> nb::Result<u8, Error> {
+                    // NOTE(unsafe) atomic read with no side effects
+                    let sr = unsafe { (*$USARTX::ptr()).sr.read() };
+
+                    // Check for any errors
+                    let err = if sr.pe().bit_is_set() {
+                        Some(Error::Parity)
+                    } else if sr.fe().bit_is_set() {
+                        Some(Error::Framing)
+                    } else if sr.ne().bit_is_set() {
+                        Some(Error::Noise)
+                    } else if sr.ore().bit_is_set() {
+                        Some(Error::Overrun)
+                    } else {
+                        None
+                    };
+
+                    if let Some(err) = err {
+                        // Some error occured. In order to clear that error flag, you have to
+                        // do a read from the sr register followed by a read from the dr
+                        // register
+                        // NOTE(read_volatile) see `write_volatile` below
+                        unsafe {
+                            ptr::read_volatile(&(*$USARTX::ptr()).sr as *const _ as *const _);
+                            ptr::read_volatile(&(*$USARTX::ptr()).dr as *const _ as *const _);
+                        }
+                        Err(nb::Error::Other(err))
+                    } else {
+                        // Check if a byte is available
+                        if sr.rxne().bit_is_set() {
+                            // Read the received byte
+                            // NOTE(read_volatile) see `write_volatile` below
+                            Ok(unsafe {
+                                ptr::read_volatile(&(*$USARTX::ptr()).dr as *const _ as *const _)
+                            })
+                        } else {
+                            Err(nb::Error::WouldBlock)
+                        }
+                    }
+                }
+            }
+
+            impl<B> ReadDma<B> for Rx<$USARTX> where B: AsMut<[u8]> {
+                fn circ_read(self, mut chan: Self::Dma, buffer: &'static mut [B; 2],
+                ) -> CircBuffer<B, Self::Dma>
+                {
+                    {
+                        let buffer = buffer[0].as_mut();
+                        chan.cmar().write(|w| unsafe {
+                            w.ma().bits(buffer.as_ptr() as usize as u32)
+                        });
+                        chan.cndtr().write(|w| unsafe{
+                            w.ndt().bits(u16(buffer.len() * 2).unwrap())
+                        });
+                        chan.cpar().write(|w| unsafe {
+                            w.pa().bits(&(*$USARTX::ptr()).dr as *const _ as usize as u32)
+                        });
+
+                        // TODO can we weaken this compiler barrier?
+                        // NOTE(compiler_fence) operations on `buffer` should not be reordered after
+                        // the next statement, which starts the DMA transfer
+                        atomic::compiler_fence(Ordering::SeqCst);
+
+                        chan.ccr().modify(|_, w| {
+                            w.mem2mem()
+                                .clear_bit()
+                                .pl()
+                                .medium()
+                                .msize()
+                                .bit8()
+                                .psize()
+                                .bit8()
+                                .minc()
+                                .set_bit()
+                                .pinc()
+                                .clear_bit()
+                                .circ()
+                                .set_bit()
+                                .dir()
+                                .clear_bit()
+                                .en()
+                                .set_bit()
+                        });
+                    }
+
+                    CircBuffer::new(buffer, chan)
+                }
+
+                fn read_exact(self, mut chan: Self::Dma, buffer: &'static mut B,
+                ) -> Transfer<W, &'static mut B, Self::Dma, Self>
+                {
+                    {
+                        let buffer = buffer.as_mut();
+                        chan.cmar().write(|w| unsafe {
+                            w.ma().bits(buffer.as_ptr() as usize as u32)
+                        });
+                        chan.cndtr().write(|w| unsafe{
+                            w.ndt().bits(u16(buffer.len()).unwrap())
+                        });
+                        chan.cpar().write(|w| unsafe {
+                            w.pa().bits(&(*$USARTX::ptr()).dr as *const _ as usize as u32)
+                        });
+
+                        // TODO can we weaken this compiler barrier?
+                        // NOTE(compiler_fence) operations on `buffer` should not be reordered after
+                        // the next statement, which starts the DMA transfer
+                        atomic::compiler_fence(Ordering::SeqCst);
+
+                        chan.ccr().modify(|_, w| {
+                            w.mem2mem()
+                                .clear_bit()
+                                .pl()
+                                .medium()
+                                .msize()
+                                .bit8()
+                                .psize()
+                                .bit8()
+                                .minc()
+                                .set_bit()
+                                .pinc()
+                                .clear_bit()
+                                .circ()
+                                .clear_bit()
+                                .dir()
+                                .clear_bit()
+                                .en()
+                                .set_bit()
+                        });
+                    }
+
+                    Transfer::w(buffer, chan, self)
+                }
+            }
+
+            impl<A, B> WriteDma<A, B> for Tx<$USARTX> where A: AsRef<[u8]>, B: Static<A> {
+                fn write_all(self, mut chan: Self::Dma, buffer: B
+                ) -> Transfer<R, B, Self::Dma, Self>
+                {
+                    {
+                        let buffer = buffer.borrow().as_ref();
+                        chan.cmar().write(|w| unsafe {
+                            w.ma().bits(buffer.as_ptr() as usize as u32)
+                        });
+                        chan.cndtr().write(|w| unsafe{
+                            w.ndt().bits(u16(buffer.len()).unwrap())
+                        });
+                        chan.cpar().write(|w| unsafe {
+                            w.pa().bits(&(*$USARTX::ptr()).dr as *const _ as usize as u32)
+                        });
+
+                        // TODO can we weaken this compiler barrier?
+                        // NOTE(compiler_fence) operations on `buffer` should not be reordered after
+                        // the next statement, which starts the DMA transfer
+                        atomic::compiler_fence(Ordering::SeqCst);
+
+                        chan.ccr().modify(|_, w| {
+                            w.mem2mem()
+                                .clear_bit()
+                                .pl()
+                                .medium()
+                                .msize()
+                                .bit8()
+                                .psize()
+                                .bit8()
+                                .minc()
+                                .set_bit()
+                                .pinc()
+                                .clear_bit()
+                                .circ()
+                                .clear_bit()
+                                .dir()
+                                .set_bit()
+                                .en()
+                                .set_bit()
+                        });
+                    }
+
+                    Transfer::r(buffer, chan, self)
+                }
+            }
+
+            impl hal::serial::Write<u8> for Tx<$USARTX> {
+                type Error = Void;
+
+                fn flush(&mut self) -> nb::Result<(), Self::Error> {
+                    // NOTE(unsafe) atomic read with no side effects
+                    let sr = unsafe { (*$USARTX::ptr()).sr.read() };
+
+                    if sr.tc().bit_is_set() {
+                        Ok(())
+                    } else {
+                        Err(nb::Error::WouldBlock)
+                    }
+                }
+
+                fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
+                    // NOTE(unsafe) atomic read with no side effects
+                    let sr = unsafe { (*$USARTX::ptr()).sr.read() };
+
+                    if sr.txe().bit_is_set() {
+                        // NOTE(unsafe) atomic write to stateless register
+                        // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API
+                        unsafe {
+                            ptr::write_volatile(&(*$USARTX::ptr()).dr as *const _ as *mut _, byte)
+                        }
+                        Ok(())
+                    } else {
+                        Err(nb::Error::WouldBlock)
+                    }
+                }
+            }
+        )+
+    }
+}
+
+hal! {
+    USART1: (
+        usart1,
+        usart1en,
+        usart1rst,
+        usart1_remap,
+        bit,
+        |remap| remap == 1,
+        APB2
+    ),
+    USART2: (
+        usart2,
+        usart2en,
+        usart2rst,
+        usart2_remap,
+        bit,
+        |remap| remap == 1,
+        APB1
+    ),
+    USART3: (
+        usart3,
+        usart3en,
+        usart3rst,
+        usart3_remap,
+        bits,
+        |remap| remap,
+        APB1
+    ),
+}
+
+use dma::DmaChannel;
+
+impl DmaChannel for Rx<USART1> {
+    type Dma = dma1::C5;
+}
+
+impl DmaChannel for Tx<USART1> {
+    type Dma = dma1::C4;
+}
+
+impl DmaChannel for Rx<USART2> {
+    type Dma = dma1::C6;
+}
+
+impl DmaChannel for Tx<USART2> {
+    type Dma = dma1::C7;
+}
+
+impl DmaChannel for Rx<USART3> {
+    type Dma = dma1::C3;
+}
+
+impl DmaChannel for Tx<USART3> {
+    type Dma = dma1::C2;
+}
+
+pub trait ReadDma<B>: DmaChannel
+where
+    B: AsMut<[u8]>,
+    Self: core::marker::Sized,
+{
+    fn circ_read(self, chan: Self::Dma, buffer: &'static mut [B; 2]) -> CircBuffer<B, Self::Dma>;
+    fn read_exact(
+        self,
+        chan: Self::Dma,
+        buffer: &'static mut B,
+    ) -> Transfer<W, &'static mut B, Self::Dma, Self>;
+}
+
+pub trait WriteDma<A, B>: DmaChannel
+where
+    A: AsRef<[u8]>,
+    B: Static<A>,
+    Self: core::marker::Sized,
+{
+    fn write_all(self, chan: Self::Dma, buffer: B) -> Transfer<R, B, Self::Dma, Self>;
+}

+ 228 - 0
src/spi.rs

@@ -0,0 +1,228 @@
+use core::ptr;
+
+use hal;
+pub use hal::spi::{Mode, Phase, Polarity};
+use nb;
+use stm32::{SPI1, SPI2};
+
+use afio::MAPR;
+use gpio::gpioa::{PA5, PA6, PA7};
+use gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5};
+use gpio::{Alternate, Floating, Input, PushPull};
+use rcc::{APB1, APB2, Clocks};
+use time::Hertz;
+
+/// SPI error
+#[derive(Debug)]
+pub enum Error {
+    /// Overrun occurred
+    Overrun,
+    /// Mode fault occurred
+    ModeFault,
+    /// CRC error
+    Crc,
+    #[doc(hidden)]
+    _Extensible,
+}
+
+pub trait Pins<SPI> {
+    const REMAP: bool;
+}
+
+impl Pins<SPI1>
+    for (
+        PA5<Alternate<PushPull>>,
+        PA6<Input<Floating>>,
+        PA7<Alternate<PushPull>>,
+    )
+{
+    const REMAP: bool = false;
+}
+
+impl Pins<SPI1>
+    for (
+        PB3<Alternate<PushPull>>,
+        PB4<Input<Floating>>,
+        PB5<Alternate<PushPull>>,
+    )
+{
+    const REMAP: bool = true;
+}
+
+impl Pins<SPI2>
+    for (
+        PB13<Alternate<PushPull>>,
+        PB14<Input<Floating>>,
+        PB15<Alternate<PushPull>>,
+    )
+{
+    const REMAP: bool = false;
+}
+
+pub struct Spi<SPI, PINS> {
+    spi: SPI,
+    pins: PINS,
+}
+
+impl<PINS> Spi<SPI1, PINS> {
+    pub fn spi1<F>(
+        spi: SPI1,
+        pins: PINS,
+        mapr: &mut MAPR,
+        mode: Mode,
+        freq: F,
+        clocks: Clocks,
+        apb: &mut APB2,
+    ) -> Self
+    where
+        F: Into<Hertz>,
+        PINS: Pins<SPI1>,
+    {
+        mapr.mapr().modify(|_, w| w.spi1_remap().bit(PINS::REMAP));
+        Spi::_spi1(spi, pins, mode, freq.into(), clocks, apb)
+    }
+}
+
+impl<PINS> Spi<SPI2, PINS> {
+    pub fn spi2<F>(
+        spi: SPI2,
+        pins: PINS,
+        mode: Mode,
+        freq: F,
+        clocks: Clocks,
+        apb: &mut APB1,
+    ) -> Self
+    where
+        F: Into<Hertz>,
+        PINS: Pins<SPI2>,
+    {
+        Spi::_spi2(spi, pins, mode, freq.into(), clocks, apb)
+    }
+}
+
+macro_rules! hal {
+    ($($SPIX:ident: ($spiX:ident, $spiXen:ident, $spiXrst:ident, $APB:ident),)+) => {
+        $(
+            impl<PINS> Spi<$SPIX, PINS> {
+                fn $spiX(
+                    spi: $SPIX,
+                    pins: PINS,
+                    mode: Mode,
+                    freq: Hertz,
+                    clocks: Clocks,
+                    apb: &mut $APB,
+                ) -> Self {
+                    // enable or reset $SPIX
+                    apb.enr().modify(|_, w| w.$spiXen().set_bit());
+                    apb.rstr().modify(|_, w| w.$spiXrst().set_bit());
+                    apb.rstr().modify(|_, w| w.$spiXrst().clear_bit());
+
+                    // disable SS output
+                    spi.cr2.write(|w| w.ssoe().clear_bit());
+
+                    let br = match clocks.pclk2().0 / freq.0 {
+                        0 => unreachable!(),
+                        1...2 => 0b000,
+                        3...5 => 0b001,
+                        6...11 => 0b010,
+                        12...23 => 0b011,
+                        24...47 => 0b100,
+                        48...95 => 0b101,
+                        96...191 => 0b110,
+                        _ => 0b111,
+                    };
+
+                    // mstr: master configuration
+                    // lsbfirst: MSB first
+                    // ssm: enable software slave management (NSS pin free for other uses)
+                    // ssi: set nss high = master mode
+                    // dff: 8 bit frames
+                    // bidimode: 2-line unidirectional
+                    // spe: enable the SPI bus
+                    spi.cr1.write(|w| {
+                        w.cpha()
+                            .bit(mode.phase == Phase::CaptureOnSecondTransition)
+                            .cpol()
+                            .bit(mode.polarity == Polarity::IdleHigh)
+                            .mstr()
+                            .set_bit()
+                            .br()
+                            .bits(br)
+                            .lsbfirst()
+                            .clear_bit()
+                            .ssm()
+                            .set_bit()
+                            .ssi()
+                            .set_bit()
+                            .rxonly()
+                            .clear_bit()
+                            .dff()
+                            .clear_bit()
+                            .bidimode()
+                            .clear_bit()
+                            .spe()
+                            .set_bit()
+                    });
+
+                    Spi { spi, pins }
+                }
+
+                pub fn free(self) -> ($SPIX, PINS) {
+                    (self.spi, self.pins)
+                }
+            }
+
+            impl<PINS> hal::spi::FullDuplex<u8> for Spi<$SPIX, PINS> {
+                type Error = Error;
+
+                fn read(&mut self) -> nb::Result<u8, Error> {
+                    let sr = self.spi.sr.read();
+
+                    Err(if sr.ovr().bit_is_set() {
+                        nb::Error::Other(Error::Overrun)
+                    } else if sr.modf().bit_is_set() {
+                        nb::Error::Other(Error::ModeFault)
+                    } else if sr.crcerr().bit_is_set() {
+                        nb::Error::Other(Error::Crc)
+                    } else if sr.rxne().bit_is_set() {
+                        // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows
+                        // reading a half-word)
+                        return Ok(unsafe {
+                            ptr::read_volatile(&self.spi.dr as *const _ as *const u8)
+                        });
+                    } else {
+                        nb::Error::WouldBlock
+                    })
+                }
+
+                fn send(&mut self, byte: u8) -> nb::Result<(), Error> {
+                    let sr = self.spi.sr.read();
+
+                    Err(if sr.ovr().bit_is_set() {
+                        nb::Error::Other(Error::Overrun)
+                    } else if sr.modf().bit_is_set() {
+                        nb::Error::Other(Error::ModeFault)
+                    } else if sr.crcerr().bit_is_set() {
+                        nb::Error::Other(Error::Crc)
+                    } else if sr.txe().bit_is_set() {
+                        // NOTE(write_volatile) see note above
+                        unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) }
+                        return Ok(());
+                    } else {
+                        nb::Error::WouldBlock
+                    })
+                }
+
+            }
+
+            impl<PINS> ::hal::blocking::spi::transfer::Default<u8> for Spi<$SPIX, PINS> {}
+
+            impl<PINS> ::hal::blocking::spi::write::Default<u8> for Spi<$SPIX, PINS> {}
+        )+
+    }
+}
+
+hal! {
+    SPI1: (_spi1, spi1en, spi1rst, APB2),
+    SPI2: (_spi2, spi2en, spi2rst, APB1),
+}

+ 117 - 0
src/time.rs

@@ -0,0 +1,117 @@
+//! Time units
+
+use cortex_m::peripheral::DWT;
+
+use rcc::Clocks;
+
+/// Bits per second
+#[derive(Clone, Copy)]
+pub struct Bps(pub u32);
+
+/// Hertz
+#[derive(Clone, Copy)]
+pub struct Hertz(pub u32);
+
+/// KiloHertz
+#[derive(Clone, Copy)]
+pub struct KiloHertz(pub u32);
+
+/// MegaHertz
+#[derive(Clone, Copy)]
+pub struct MegaHertz(pub u32);
+
+/// Extension trait that adds convenience methods to the `u32` type
+pub trait U32Ext {
+    /// Wrap in `Bps`
+    fn bps(self) -> Bps;
+
+    /// Wrap in `Hertz`
+    fn hz(self) -> Hertz;
+
+    /// Wrap in `KiloHertz`
+    fn khz(self) -> KiloHertz;
+
+    /// Wrap in `MegaHertz`
+    fn mhz(self) -> MegaHertz;
+}
+
+impl U32Ext for u32 {
+    fn bps(self) -> Bps {
+        Bps(self)
+    }
+
+    fn hz(self) -> Hertz {
+        Hertz(self)
+    }
+
+    fn khz(self) -> KiloHertz {
+        KiloHertz(self)
+    }
+
+    fn mhz(self) -> MegaHertz {
+        MegaHertz(self)
+    }
+}
+
+impl Into<Hertz> for KiloHertz {
+    fn into(self) -> Hertz {
+        Hertz(self.0 * 1_000)
+    }
+}
+
+impl Into<Hertz> for MegaHertz {
+    fn into(self) -> Hertz {
+        Hertz(self.0 * 1_000_000)
+    }
+}
+
+impl Into<KiloHertz> for MegaHertz {
+    fn into(self) -> KiloHertz {
+        KiloHertz(self.0 * 1_000)
+    }
+}
+
+/// A monotonic nondecreasing timer
+#[derive(Clone, Copy)]
+pub struct MonoTimer {
+    frequency: Hertz,
+}
+
+impl MonoTimer {
+    /// Creates a new `Monotonic` timer
+    pub fn new(mut dwt: DWT, clocks: Clocks) -> Self {
+        dwt.enable_cycle_counter();
+
+        // now the CYCCNT counter can't be stopped or resetted
+        drop(dwt);
+
+        MonoTimer {
+            frequency: clocks.sysclk(),
+        }
+    }
+
+    /// Returns the frequency at which the monotonic timer is operating at
+    pub fn frequency(&self) -> Hertz {
+        self.frequency
+    }
+
+    /// Returns an `Instant` corresponding to "now"
+    pub fn now(&self) -> Instant {
+        Instant {
+            now: DWT::get_cycle_count(),
+        }
+    }
+}
+
+/// A measurement of a monotonically nondecreasing clock
+#[derive(Clone, Copy)]
+pub struct Instant {
+    now: u32,
+}
+
+impl Instant {
+    /// Ticks elapsed since the `Instant` was created
+    pub fn elapsed(&self) -> u32 {
+        DWT::get_cycle_count().wrapping_sub(self.now)
+    }
+}

+ 194 - 0
src/timer.rs

@@ -0,0 +1,194 @@
+use cast::{u16, u32};
+use cortex_m::peripheral::syst::SystClkSource;
+use cortex_m::peripheral::SYST;
+use hal::timer::{CountDown, Periodic};
+use nb;
+use stm32::{TIM1, TIM2, TIM3, TIM4};
+use void::Void;
+
+use core::any::TypeId;
+
+use rcc::{APB1, APB2, Clocks};
+use time::Hertz;
+
+/// Interrupt events
+pub enum Event {
+    /// Timer timed out / count down ended
+    Update,
+}
+
+pub struct Timer<TIM> {
+    tim: TIM,
+    clocks: Clocks,
+}
+
+impl Timer<SYST> {
+    pub fn syst<T>(mut syst: SYST, timeout: T, clocks: Clocks) -> Self
+    where
+        T: Into<Hertz>,
+    {
+        syst.set_clock_source(SystClkSource::Core);
+        let mut timer = Timer { tim: syst, clocks };
+        timer.start(timeout);
+        timer
+    }
+
+    /// Starts listening for an `event`
+    pub fn listen(&mut self, event: Event) {
+        match event {
+            Event::Update => self.tim.enable_interrupt(),
+        }
+    }
+
+    /// Stops listening for an `event`
+    pub fn unlisten(&mut self, event: Event) {
+        match event {
+            Event::Update => self.tim.disable_interrupt(),
+        }
+    }
+}
+
+impl CountDown for Timer<SYST> {
+    type Time = Hertz;
+
+    fn start<T>(&mut self, timeout: T)
+    where
+        T: Into<Hertz>,
+    {
+        let rvr = self.clocks.sysclk().0 / timeout.into().0 - 1;
+
+        assert!(rvr < (1 << 24));
+
+        self.tim.set_reload(rvr);
+        self.tim.clear_current();
+        self.tim.enable_counter();
+    }
+
+    fn wait(&mut self) -> nb::Result<(), Void> {
+        if self.tim.has_wrapped() {
+            Ok(())
+        } else {
+            Err(nb::Error::WouldBlock)
+        }
+    }
+}
+
+impl Periodic for Timer<SYST> {}
+
+macro_rules! hal {
+    ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident, $apbX:ident),)+) => {
+        $(
+            impl Timer<$TIMX> {
+                pub fn $timX<T>(tim: $TIMX, timeout: T, clocks: Clocks, apb1: &mut $apbX) -> Self
+                where
+                    T: Into<Hertz>,
+                {
+                    // enable and reset peripheral to a clean slate state
+                    apb1.enr().modify(|_, w| w.$timXen().set_bit());
+                    apb1.rstr().modify(|_, w| w.$timXrst().set_bit());
+                    apb1.rstr().modify(|_, w| w.$timXrst().clear_bit());
+
+                    let mut timer = Timer { clocks, tim };
+                    timer.start(timeout);
+
+                    timer
+                }
+
+                /// Starts listening for an `event`
+                pub fn listen(&mut self, event: Event) {
+                    match event {
+                        Event::Update => self.tim.dier.write(|w| w.uie().set_bit()),
+                    }
+                }
+
+                /// Stops listening for an `event`
+                pub fn unlisten(&mut self, event: Event) {
+                    match event {
+                        Event::Update => self.tim.dier.write(|w| w.uie().clear_bit()),
+                    }
+                }
+
+                /// Return the bus clock frequency in hertz.
+                fn get_bus_clock(&self) -> Hertz {
+                    if TypeId::of::<$apbX>() == TypeId::of::<APB1>() {
+                            Hertz(self.clocks.pclk1().0 * self.get_bus_frequency_multiplier())
+                    } else if TypeId::of::<$apbX>() == TypeId::of::<APB2>() {
+                        Hertz(self.clocks.pclk2().0 * self.get_bus_frequency_multiplier())
+                    } else {
+                        unreachable!()
+                    }
+                }
+
+                /// Return the bus frequency multiplier.
+                fn get_bus_frequency_multiplier(&self) -> u32 {
+                    if TypeId::of::<$apbX>() == TypeId::of::<APB1>() {
+                        if self.clocks.ppre1() == 1 {
+                            1
+                        } else {
+                            2
+                        }
+                    } else if TypeId::of::<$apbX>() == TypeId::of::<APB2>() {
+                         if self.clocks.ppre2() == 1 {
+                            1
+                         } else {
+                            2
+                         }
+                    } else {
+                        unreachable!()
+                    }
+                }
+            }
+
+            impl CountDown for Timer<$TIMX> {
+                type Time = Hertz;
+
+                fn start<T>(&mut self, timeout: T)
+                where
+                    T: Into<Hertz>,
+                {
+                    // pause
+                    self.tim.cr1.modify(|_, w| w.cen().clear_bit());
+
+                    let frequency = timeout.into().0;
+                    let timer_clock = self.get_bus_clock();
+                    let ticks = timer_clock.0 / frequency;
+                    let psc = u16((ticks - 1) / (1 << 16)).unwrap();
+
+                    self.tim.psc.write(|w| w.psc().bits(psc));
+                    
+                    let arr = u16(ticks / u32(psc + 1)).unwrap();
+
+                    self.tim.arr.write(|w| unsafe { w.bits(u32(arr)) });
+
+                    // Trigger an update event to load the prescaler value to the clock
+                    self.tim.egr.write(|w| w.ug().set_bit());
+                    // The above line raises an update event which will indicate
+                    // that the timer is already finished. Since this is not the case,
+                    // it should be cleared
+                    self.tim.sr.modify(|_, w| w.uif().clear_bit());
+
+                    // start counter
+                    self.tim.cr1.modify(|_, w| w.cen().set_bit());
+                }
+
+                fn wait(&mut self) -> nb::Result<(), Void> {
+                    if self.tim.sr.read().uif().bit_is_clear() {
+                        Err(nb::Error::WouldBlock)
+                    } else {
+                        self.tim.sr.modify(|_, w| w.uif().clear_bit());
+                        Ok(())
+                    }
+                }
+            }
+
+            impl Periodic for Timer<$TIMX> {}
+        )+
+    }
+}
+
+hal! {
+    TIM1: (tim1, tim1en, tim1rst, APB2),
+    TIM2: (tim2, tim2en, tim2rst, APB1),
+    TIM3: (tim3, tim3en, tim3rst, APB1),
+    TIM4: (tim4, tim4en, tim4rst, APB1),
+}