commit:45e72d5396e48ab9681b2d1be0713f76455b29b8
author:Chip
committer:Chip
date:Fri Aug 18 21:10:24 2023 -0500
parents:
Maybe this works?
diff --git a/.gitignore b/.gitignore
line changes: +11/-0
index 0000000..ff47c2d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk

diff --git a/CHANGELOG.md b/CHANGELOG.md
line changes: +74/-0
index 0000000..8fbf5c5
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,74 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 0.7.0 - 2023-02-18
+
+### Changed
+
+- Update to rp2040-hal 0.8.0
+- Update to ws2812-pio 0.6.0
+- Update to i2c-pio 0.6.0
+- Update to embedded-sdmmc 0.4.0
+
+## 0.6.0 - 2022-12-11
+
+### Changed
+
+- Update to rp2040-hal 0.7.0
+- Update to ws2812-pio 0.5.0
+- Update to i2c-pio 0.5.0
+
+## 0.5.0 - 2022-08-26
+
+### Added
+
+- `rp2040-e5` feature enabling the workaround for errata 5 on the USB device peripheral.
+- Support for critical-section 1.0.0 in the examples.
+- Example for the interpolator
+
+### Changed
+
+- Use `rp2040-hal`'s entry function.
+- Migrate from `embedded-time` to `fugit`
+- Bump `ws2812-pio` to 0.4.0
+- Bump `i2c-pio` to 0.4.0
+- Update to rp2040-hal 0.6.0
+
+### Removed
+
+- Unused dependencies
+
+## 0.4.0 - 2022-06-13
+
+### Changed
+
+- Update to rp2040-hal 0.5.0
+
+## 0.3.0 - 2022-03-11
+
+### Changed
+
+- Update to rp-hal 0.4.0
+
+## 0.2.0 - 2021-12-23
+
+### Added
+
+- Lots of things!
+
+### Changed
+
+- Basically re-written.
+
+## 0.1.3 - 2021-02-03
+
+- Last release outside the [rp-rs] organisation by [@jannic].
+
+[@jannic]: https://github.com/jannic
+[rp-rs]: https://github.com/rp-rs

diff --git a/Cargo.toml b/Cargo.toml
line changes: +72/-0
index 0000000..8715952
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,72 @@
+[package]
+name = "doa-hallonbrod"
+version = "0.7.0"
+authors = ["Chip <bytex64@bytex64.net>", "The Dominion of Awesome"]
+edition = "2018"
+homepage = "https://dominionofawesome.com/vca/hallonbrod/"
+description = "Board Support Package for the DoA Hallonbröd interface board"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/bytex64/doa-hallonbrod.git"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cortex-m = "0.7.2"
+rp2040-boot2 = { version = "0.2.0", optional = true }
+rp2040-hal = { version = "0.8.0" }
+cortex-m-rt = { version = "0.7", optional = true }
+fugit = "0.3.5"
+usb-device= "0.2.9"
+
+[dev-dependencies]
+rp2040-hal = { version = "0.8.0", features = [ "defmt" ] }
+panic-halt= "0.2.0"
+embedded-hal ="0.2.5"
+cortex-m-rtic = "1.1.2"
+nb = "1.0"
+i2c-pio = "0.6.0"
+heapless = "0.7.9"
+embedded-sdmmc = "0.5.0"
+smart-leds = "0.3.0"
+ws2812-pio = "0.6.0"
+ssd1306 = "0.7.0"
+embedded-graphics = "0.7.1"
+hd44780-driver = "0.4.0"
+pio = "0.2.0"
+pio-proc = "0.2.1"
+critical-section = "1.0.0"
+usbd-serial = "0.1.1"
+usbd-hid = "0.5.1"
+
+defmt = "0.3.0"
+defmt-rtt = "0.4.0"
+
+[features]
+# This is the set of features we enable by default
+default = ["boot2", "rt", "critical-section-impl", "rom-func-cache"]
+
+# critical section that is safe for multicore use
+critical-section-impl = ["rp2040-hal/critical-section-impl"]
+
+# 2nd stage bootloaders for rp2040
+boot2 = ["rp2040-boot2"]
+
+# Minimal startup / runtime for Cortex-M microcontrollers
+rt = ["cortex-m-rt","rp2040-hal/rt"]
+
+# This enables a fix for USB errata 5: USB device fails to exit RESET state on busy USB bus.
+# Only required for RP2040 B0 and RP2040 B1, but it also works for RP2040 B2 and above
+rp2040-e5 = ["rp2040-hal/rp2040-e5"]
+
+# Memoize(cache) ROM function pointers on first use to improve performance
+rom-func-cache = ["rp2040-hal/rom-func-cache"]
+
+# Disable automatic mapping of language features (like floating point math) to ROM functions
+disable-intrinsics = ["rp2040-hal/disable-intrinsics"]
+
+# This enables ROM functions for f64 math that were not present in the earliest RP2040s
+rom-v2-intrinsics = ["rp2040-hal/rom-v2-intrinsics"]
+
+[[example]]
+name = "pico_rtic_monotonic"
+required-features = ["rp2040-hal/rtic-monotonic"]

diff --git a/README.md b/README.md
line changes: +167/-0
index 0000000..9529824
--- /dev/null
+++ b/README.md
@@ -0,0 +1,167 @@
+# [rp-pico] - Board Support for the [Raspberry Pi Pico]
+
+You should include this crate if you are writing code that you want to run on
+a [Raspberry Pi Pico] - the original launch PCB for the RP2040 chip.
+
+This crate includes the [rp2040-hal], but also configures each pin of the
+RP2040 chip according to how it is connected up on the Pico.
+
+[Raspberry Pi Pico]: https://www.raspberrypi.org/products/raspberry-pi-pico/
+[rp-pico]: https://github.com/rp-rs/rp-hal-boards/tree/main/boards/rp-pico
+[rp2040-hal]: https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal
+[Raspberry Silicon RP2040]: https://www.raspberrypi.org/products/rp2040/
+
+## Using
+
+To use this crate, your `Cargo.toml` file should contain:
+
+```toml
+rp-pico = "0.7.0"
+```
+
+In your program, you will need to call `rp_pico::Pins::new` to create
+a new `Pins` structure. This will set up all the GPIOs for any on-board
+devices. See the [examples](./examples) folder for more details.
+
+## Examples
+
+### General Instructions
+
+To compile an example, clone the _rp-hal-boards_ repository and run:
+
+```console
+rp-hal-boards/boards/rp-pico $ cargo build --release --example <name>
+```
+
+You will get an ELF file called
+`./target/thumbv6m-none-eabi/release/examples/<name>`, where the `target`
+folder is located at the top of the _rp-hal-boards_ repository checkout. Normally
+you would also need to specify `--target=thumbv6m-none-eabi` but when
+building examples from this git repository, that is set as the default.
+
+If you want to convert the ELF file to a UF2 and automatically copy it to the
+USB drive exported by the RP2040 bootloader, simply boot your board into
+bootloader mode and run:
+
+```console
+rp-hal-boards/boards/rp-pico $ cargo run --release --example <name>
+```
+
+If you get an error about not being able to find `elf2uf2-rs`, try:
+
+```console
+$ cargo install elf2uf2-rs
+```
+then try repeating the `cargo run` command above.
+
+### From Scratch
+
+To start a basic project from scratch, create a project using `cargo new project-name`. Within the
+project directory, run `cargo add rp-pico`, `cargo add cortex-m-rt`, and `cargo add panic-halt`. The
+first command will this HAL (Hardware Abstraction Layer), the second is required for the `#[entry]` macro, and _panic-halt_ creates a simple panic function, which just halts.
+
+You'll also need to copy the cargo config file from the [repo](https://github.com/rp-rs/rp-hal-boards/blob/main/.cargo/config). It specifies the target and optimizing flags to the linker. You'll also need to copy [_memory.x_](https://github.com/rp-rs/rp-hal-boards/blob/main/memory.x) to your project root. This file tells the linker the flash and RAM layout, so it won't clobber the bootloader or write to an out of bounds memory address. 
+
+The simplest working example, which does nothing except loop forever, is:
+
+```ignore
+#![no_std]
+#![no_main]
+use rp_pico::entry;
+use panic_halt as _;
+#[entry]
+fn see_doesnt_have_to_be_called_main() -> ! {
+  loop {}
+}
+```
+
+It can be placed in _/src/main.rs_. 
+
+You can use `cargo run` to compile and install it. 
+**Note**: You won't see any activity since this program does nothing. You can use the examples provided
+to add more functionality. 
+### [pico_blinky](./examples/pico_blinky.rs)
+
+Flashes the Pico's on-board LED on and off.
+
+### [pico_gpio_in_out](./examples/pico_gpio_in_out.rs)
+
+Reads a push button attached to GPIO 15 and drives the on-board LED to match it (i.e. on when pressed, off when not pressed).
+
+### [pico_rtic](./examples/pico_rtic.rs)
+
+Demonstrates the use of the [Real-Time Interrupt-driven Concurrency Framework] on the Raspberry Pi Pico.
+
+[Real-Time Interrupt-driven Concurrency Framework]: https://rtic.rs
+
+### [pico_countdown_blinky](./examples/pico_countdown_blinky.rs)
+
+Another LED blinking example, but using a Timer in count-down mode.
+
+### [pico_pwm_blink](./examples/pico_pwm_blink.rs)
+
+Puts out an analog 'triangle wave' on GPIO 25, using the PWM hardware.
+
+### [pico_pwm_servo](./examples/pico_pwm_servo.rs)
+
+Demonstrates handling a micro servo, using the PWM hardware.
+
+### [pico_usb_serial](./examples/pico_usb_serial.rs)
+
+Creates a USB Serial device on a Pico board.
+
+The USB Serial device will print `HelloWorld` on start-up, and then echo any
+incoming characters - except that any lower-case ASCII characters are
+converted to the upper-case equivalent.
+
+### [pico_usb_serial_interrupt](./examples/pico_usb_serial_interrupt.rs)
+
+Creates a USB Serial device on a Pico board, but demonstrating handling
+interrupts when USB data arrives.
+
+### [pico_usb_twitchy_mouse](./examples/pico_usb_twitchy_mouse.rs)
+
+Demonstrates emulating a USB Human Input Device (HID) Mouse. The mouse
+cursor will jiggle up and down.
+
+### [pico_spi_sd_card](./examples/pico_spi_sd_card.rs)
+
+Example that shows how to use the
+[embedded_sdmmc crate](https://github.com/rust-embedded-community/embedded-sdmmc-rs)
+with the Raspberry Pi Pico.
+
+## Contributing
+
+Contributions are what make the open source community such an amazing place to
+be learn, inspire, and create. Any contributions you make are **greatly
+appreciated**.
+
+The steps are:
+
+1. Fork the Project by clicking the 'Fork' button at the top of the page.
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Make some changes to the code or documentation.
+4. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+5. Push to the Feature Branch (`git push origin feature/AmazingFeature`)
+6. Create a [New Pull Request](https://github.com/rp-rs/rp-hal-boards/pulls)
+7. An admin will review the Pull Request and discuss any changes that may be required.
+8. Once everyone is happy, the Pull Request can be merged by an admin, and your work is part of our project!
+
+## Code of Conduct
+
+Contribution to this crate is organized under the terms of the [Rust Code of
+Conduct][CoC], and the maintainer of this crate, the [rp-rs team], promises
+to intervene to uphold that code of conduct.
+
+[CoC]: CODE_OF_CONDUCT.md
+[rp-rs team]: https://github.com/orgs/rp-rs/teams/rp-rs
+
+## License
+
+The contents of this repository are dual-licensed under the _MIT OR Apache
+2.0_ License. That means you can choose either the MIT license or the
+Apache-2.0 license when you re-use this code. See `MIT` or `APACHE2.0` for more
+information on each specific license.
+
+Any submissions to this project (e.g. as Pull Requests) must be made available
+under these terms.

diff --git a/build.rs b/build.rs
line changes: +6/-0
index 0000000..dd9a371
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,6 @@
+//! This build script makes sure the linker flag -Tdefmt.x is added
+//! for the examples.
+
+fn main() {
+    println!("cargo:rustc-link-arg-examples=-Tdefmt.x");
+}

diff --git a/examples/pico_blinky.rs b/examples/pico_blinky.rs
line changes: +92/-0
index 0000000..3d70a86
--- /dev/null
+++ b/examples/pico_blinky.rs
@@ -0,0 +1,92 @@
+//! # Pico Blinky Example
+//!
+//! Blinks the LED on a Pico board.
+//!
+//! This will blink an LED attached to GP25, which is the pin the Pico uses for
+//! the on-board LED.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use doa_hallonbrod::entry;
+
+// GPIO traits
+use embedded_hal::digital::v2::OutputPin;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use doa_hallonbrod::hal::prelude::*;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use doa_hallonbrod::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use doa_hallonbrod::hal;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then blinks the LED in an
+/// infinite loop.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        doa_hallonbrod::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The delay object lets us wait for specified amounts of time (in
+    // milliseconds)
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = doa_hallonbrod::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Set the LED to be an output
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    // Blink the LED at 1 Hz
+    loop {
+        led_pin.set_high().unwrap();
+        delay.delay_ms(500);
+        led_pin.set_low().unwrap();
+        delay.delay_ms(500);
+    }
+}
+
+// End of file

diff --git a/examples/pico_countdown_blinky.rs b/examples/pico_countdown_blinky.rs
line changes: +88/-0
index 0000000..8eb0548
--- /dev/null
+++ b/examples/pico_countdown_blinky.rs
@@ -0,0 +1,88 @@
+//! # Pico Countdown Blinky Example
+//!
+//! Blinks the LED on a Pico board, using an RP2040 Timer in Count-down mode.
+//!
+//! This will blink an LED attached to GP25, which is the pin the Pico uses for
+//! the on-board LED.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+use cortex_m::prelude::*;
+
+// GPIO traits
+use embedded_hal::digital::v2::OutputPin;
+
+// Traits for converting integers to amounts of time
+use fugit::ExtU32;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let _clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // Configure the Timer peripheral in count-down mode
+    let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS);
+    let mut count_down = timer.count_down();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    // Blink the LED at 1 Hz
+    loop {
+        // LED on, and wait for 500ms
+        led_pin.set_high().unwrap();
+        count_down.start(500.millis());
+        let _ = nb::block!(count_down.wait());
+
+        // LED off, and wait for 500ms
+        led_pin.set_low().unwrap();
+        count_down.start(500.millis());
+        let _ = nb::block!(count_down.wait());
+    }
+}

diff --git a/examples/pico_gpio_in_out.rs b/examples/pico_gpio_in_out.rs
line changes: +75/-0
index 0000000..45ecc99
--- /dev/null
+++ b/examples/pico_gpio_in_out.rs
@@ -0,0 +1,75 @@
+//! # Pico GPIO In/Out Example
+//!
+//! Toggles the LED based on GPIO input.
+//!
+//! This will control an LED on GP25 based on a button hooked up to GP15. The
+//! button should cause the line to be grounded, as the input pin is pulled high
+//! internally by this example. When the button is pressed, the LED will turn
+//! off.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// GPIO traits
+use embedded_hal::digital::v2::{InputPin, OutputPin};
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then just reads the button
+/// and sets the LED appropriately.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+
+    // Note - we don't do any clock set-up in this example. The RP2040 will run
+    // at it's default clock speed.
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Our LED output
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    // Our button input
+    let button_pin = pins.gpio15.into_pull_up_input();
+
+    // Run forever, setting the LED according to the button
+    loop {
+        if button_pin.is_low().unwrap() {
+            led_pin.set_high().unwrap();
+        } else {
+            led_pin.set_low().unwrap();
+        }
+    }
+}
+
+// End of file

diff --git a/examples/pico_hd44780_display.rs b/examples/pico_hd44780_display.rs
line changes: +136/-0
index 0000000..c7e6715
--- /dev/null
+++ b/examples/pico_hd44780_display.rs
@@ -0,0 +1,136 @@
+//! # LCD Display Example
+//!
+//! In this example, the RP2040 is configured to drive a small two-line
+//! alphanumeric LCD using the
+//! [HD44780](https://crates.io/crates/hd44780-driver) driver.
+//!
+//! This example drives the LCD by pushing data out of six GPIO pins, writing
+//! the data four bits at a time. A faster alternative can be created using
+//! HD44780::new_8bit() but requiring an additional four GPIO pins.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+//!
+//! ```text
+//!                      /--------------------------------------\
+//!  ____________        |        /-------------------------\   |
+//! | 1       GND|-------+---\    |           _|USB|_       |   |
+//! | 2       VDD|-------+---+----/          |1  R 40|-VBUS-o   v
+//! | 3        VS|-------/   |               |2  P 39|       ||POT||
+//! | 4        RS|--\        o-----------GND-|3    38|-GND----------o
+//! | 5        RW|--+--------/    /------GP2-|4  P 37|
+//! | 6        EN|--+-\        /--+------GP3-|5  I 36|
+//! | 7          |  | |     /--+--+------GP4-|6  C   |
+//! | 8          |  | |  /--+--+--+------GP5-|7  O   |
+//! | 9          |  | \--+--+--+--+---\      |8      |
+//! | 10         |  \----+--+--+--+-\  \-GP6-|9      |
+//! | 11       D4|-------/  |  |  |  \---GP7-|10     |
+//! | 12       D5|----------/  |  |          .........
+//! | 13       D6|-------------/  |          |20   21|
+//! | 14       D7|----------------/           """""""
+//! ..............
+//! Symbols:
+//!     - (+) crossing lines, not connected
+//!     - (o) connected lines
+//! ```
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// GPIO traits
+use embedded_hal::digital::v2::OutputPin;
+
+// For LCD display
+use hd44780_driver::HD44780;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[rp_pico::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+#[rp_pico::entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = rp_pico::hal::pac::Peripherals::take().unwrap();
+    let core = rp_pico::hal::pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = rp_pico::hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    // The default is to generate a 125 MHz system clock
+    let clocks = rp_pico::hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = rp_pico::hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    // The delay object lets us wait for specified amounts of time
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // Init pins
+    let rs = pins.gpio7.into_push_pull_output();
+    let en = pins.gpio6.into_push_pull_output();
+    let d4 = pins.gpio5.into_push_pull_output();
+    let d5 = pins.gpio4.into_push_pull_output();
+    let d6 = pins.gpio3.into_push_pull_output();
+    let d7 = pins.gpio2.into_push_pull_output();
+
+    // LCD Init
+    let mut lcd = HD44780::new_4bit(rs, en, d4, d5, d6, d7, &mut delay).unwrap();
+
+    loop {
+        // Clear the screen
+        lcd.reset(&mut delay).unwrap();
+        lcd.clear(&mut delay).unwrap();
+
+        // Write to the top line
+        lcd.write_str("rp-hal on", &mut delay).unwrap();
+
+        // Move the cursor
+        lcd.set_cursor_pos(40, &mut delay).unwrap();
+
+        // Write more more text
+        lcd.write_str("HD44780! ", &mut delay).unwrap();
+        let mut char_count = 9;
+        for ch in "move along!.. ".chars() {
+            if char_count > 15 {
+                // Switch autoscroll on
+                lcd.set_autoscroll(true, &mut delay).unwrap();
+            }
+            led_pin.set_high().unwrap();
+            lcd.write_char(ch, &mut delay).unwrap();
+            char_count += 1;
+            delay.delay_us(400_000); //0.4s
+            led_pin.set_low().unwrap();
+            delay.delay_us(100_000); //0.1s
+        }
+        lcd.set_autoscroll(false, &mut delay).unwrap();
+    }
+}

diff --git a/examples/pico_i2c_oled_display_ssd1306.rs b/examples/pico_i2c_oled_display_ssd1306.rs
line changes: +227/-0
index 0000000..428f28d
--- /dev/null
+++ b/examples/pico_i2c_oled_display_ssd1306.rs
@@ -0,0 +1,227 @@
+//! # Raspberry Pi Pico (monochome) 128x64 OLED Display with SSD1306 Driver Example
+//!
+//! This example assumes you got an 128x64 OLED Display with an SSD1306 driver
+//! connected to your Raspberry Pi Pico. The +3.3V voltage source of the
+//! Raspberry Pi Pico will be used, and the output pins 21 and 22 of the board
+//! (on the lower right).
+//!
+//! It will demonstrate how to get an I2C device and use it with the ssd1306 crate.
+//! Additionally you can also see how to format a number into a string using
+//! [core::fmt].
+//!
+//! The following diagram will show how things should be connected.
+//! These displays usually can take 3.3V up to 5V.
+//!
+//! ```text
+//!                              VCC   SCL
+//!                   /------------\    /----------\
+//!                   |        GND  \  /  SDA      |
+//!   _|USB|_         |    /-----\  |  |  /--------+--\
+//!  |1  R 40|        |   /    __|__|__|__|___     |  |
+//!  |2  P 39|        |  /    | ____________  |    |  |
+//!  |3    38|- GND --+-/     | |Hello worl|  |    |  |
+//!  |4  P 37|        |       | |Hello Rust|  |    |  |
+//!  |5  I 36|-+3.3V -/       | |counter: 1|  |    |  |
+//!  |6  C   |                | |          |  |    |  |
+//!  |7  O   |                | """"""""""""  |    |  |
+//!  |       |                 """""""""""""""     |  |
+//!  |       |       (SSD1306 128x64 OLED Display) |  |
+//!  .........                                     /  /
+//!  |       |                                    /  /
+//!  |     22|-GP17 I2C0 SCL---------------------/  /
+//!  |20   21|-GP16 I2C0 SDA-----------------------/
+//!   """""""
+//! Symbols:
+//!     - (+) crossing lines, not connected
+//!     - (o) connected lines
+//! ```
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// For string formatting.
+use core::fmt::Write;
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// Time handling traits:
+use fugit::{ExtU32, RateExtU32};
+
+// CountDown timer for the counter on the display:
+use embedded_hal::timer::CountDown;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// For in the graphics drawing utilities like the font
+// and the drawing routines:
+use embedded_graphics::{
+    mono_font::{ascii::FONT_9X18_BOLD, MonoTextStyleBuilder},
+    pixelcolor::BinaryColor,
+    prelude::*,
+    text::{Baseline, Text},
+};
+
+// The display driver:
+use ssd1306::{prelude::*, Ssd1306};
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals,
+/// gets a handle on the I2C peripheral,
+/// initializes the SSD1306 driver, initializes the text builder
+/// and then draws some text on the display.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Configure two pins as being I²C, not GPIO
+    let sda_pin = pins.gpio16.into_mode::<hal::gpio::FunctionI2C>();
+    let scl_pin = pins.gpio17.into_mode::<hal::gpio::FunctionI2C>();
+
+    // Create the I²C driver, using the two pre-configured pins. This will fail
+    // at compile time if the pins are in the wrong mode, or if this I²C
+    // peripheral isn't available on these pins!
+    let i2c = hal::I2C::i2c0(
+        pac.I2C0,
+        sda_pin,
+        scl_pin,
+        400.kHz(),
+        &mut pac.RESETS,
+        &clocks.peripheral_clock,
+    );
+
+    // Create the I²C display interface:
+    let interface = ssd1306::I2CDisplayInterface::new(i2c);
+
+    // Create a driver instance and initialize:
+    let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
+        .into_buffered_graphics_mode();
+    display.init().unwrap();
+
+    // Create a text style for drawing the font:
+    let text_style = MonoTextStyleBuilder::new()
+        .font(&FONT_9X18_BOLD)
+        .text_color(BinaryColor::On)
+        .build();
+
+    let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS);
+    let mut delay = timer.count_down();
+
+    let mut count = 0;
+
+    let mut buf = FmtBuf::new();
+    loop {
+        buf.reset();
+        // Format some text into a static buffer:
+        write!(&mut buf, "counter: {}", count).unwrap();
+        count += 1;
+
+        // Empty the display:
+        display.clear();
+
+        // Draw 3 lines of text:
+        Text::with_baseline("Hello world!", Point::zero(), text_style, Baseline::Top)
+            .draw(&mut display)
+            .unwrap();
+
+        Text::with_baseline("Hello Rust!", Point::new(0, 16), text_style, Baseline::Top)
+            .draw(&mut display)
+            .unwrap();
+
+        Text::with_baseline(buf.as_str(), Point::new(0, 32), text_style, Baseline::Top)
+            .draw(&mut display)
+            .unwrap();
+
+        display.flush().unwrap();
+
+        // Wait a bit:
+        delay.start(500.millis());
+        let _ = nb::block!(delay.wait());
+    }
+}
+
+/// This is a very simple buffer to pre format a short line of text
+/// limited arbitrarily to 64 bytes.
+struct FmtBuf {
+    buf: [u8; 64],
+    ptr: usize,
+}
+
+impl FmtBuf {
+    fn new() -> Self {
+        Self {
+            buf: [0; 64],
+            ptr: 0,
+        }
+    }
+
+    fn reset(&mut self) {
+        self.ptr = 0;
+    }
+
+    fn as_str(&self) -> &str {
+        core::str::from_utf8(&self.buf[0..self.ptr]).unwrap()
+    }
+}
+
+impl core::fmt::Write for FmtBuf {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        let rest_len = self.buf.len() - self.ptr;
+        let len = if rest_len < s.len() {
+            rest_len
+        } else {
+            s.len()
+        };
+        self.buf[self.ptr..(self.ptr + len)].copy_from_slice(&s.as_bytes()[0..len]);
+        self.ptr += len;
+        Ok(())
+    }
+}
+
+// End of file

diff --git a/examples/pico_i2c_pio.rs b/examples/pico_i2c_pio.rs
line changes: +159/-0
index 0000000..0ee03b0
--- /dev/null
+++ b/examples/pico_i2c_pio.rs
@@ -0,0 +1,159 @@
+//! # Pico I2C PIO Example
+//!
+//! Reads the temperature from an LM75B
+//!
+//! This read over I2C the temerature from an LM75B temperature sensor wired on pins 20 and 21
+//! using the PIO peripheral as an I2C bus controller.
+//! The pins used for the I2C can be remapped to any other pin available to the PIO0 peripheral.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The trait used by formatting macros like write! and writeln!
+use core::fmt::Write as FmtWrite;
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// I2C HAL traits & Types.
+use embedded_hal::blocking::i2c::{Operation, Read, Transactional, Write};
+
+// Time handling traits
+use fugit::RateExtU32;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Prints the temperature received from the sensor
+fn print_temperature(serial: &mut impl FmtWrite, temp: [u8; 2]) {
+    let temp_i16 = i16::from_be_bytes(temp) >> 5;
+    let temp_f32 = f32::from(temp_i16) * 0.125;
+
+    // Write formatted output but ignore any error.
+    let _ = writeln!(serial, "Temperature: {:0.2}°C", temp_f32);
+}
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, reads the temperature from
+/// the attached LM75B using PIO0.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    let uart_pins = (
+        // UART TX (characters sent from RP2040) on pin 1 (GPIO0)
+        pins.gpio0.into_mode::<hal::gpio::FunctionUart>(),
+        // UART RX (characters received by RP2040) on pin 2 (GPIO1)
+        pins.gpio1.into_mode::<hal::gpio::FunctionUart>(),
+    );
+
+    let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+        .enable(
+            UartConfig::new(115_200.Hz(), DataBits::Eight, None, StopBits::One),
+            clocks.peripheral_clock.freq(),
+        )
+        .unwrap();
+
+    let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
+
+    let mut i2c_pio = i2c_pio::I2C::new(
+        &mut pio,
+        pins.gpio20,
+        pins.gpio21,
+        sm0,
+        100.kHz(),
+        clocks.system_clock.freq(),
+    );
+
+    let mut temp = [0; 2];
+    i2c_pio
+        .read(0x48u8, &mut temp)
+        .expect("Failed to read from the peripheral");
+    print_temperature(&mut uart, temp);
+
+    i2c_pio
+        .write(0x48u8, &[0])
+        .expect("Failed to write to the peripheral");
+
+    let mut temp = [0; 2];
+    i2c_pio
+        .read(0x48u8, &mut temp)
+        .expect("Failed to read from the peripheral");
+    print_temperature(&mut uart, temp);
+
+    let mut config = [0];
+    let mut thyst = [0; 2];
+    let mut tos = [0; 2];
+    let mut temp = [0; 2];
+    let mut operations = [
+        Operation::Write(&[1]),
+        Operation::Read(&mut config),
+        Operation::Write(&[2]),
+        Operation::Read(&mut thyst),
+        Operation::Write(&[3]),
+        Operation::Read(&mut tos),
+        Operation::Write(&[0]),
+        Operation::Read(&mut temp),
+    ];
+    i2c_pio
+        .exec(0x48u8, &mut operations)
+        .expect("Failed to run all operations");
+    print_temperature(&mut uart, temp);
+
+    loop {
+        cortex_m::asm::wfi();
+    }
+}
+
+// End of file

diff --git a/examples/pico_interpolator.rs b/examples/pico_interpolator.rs
line changes: +438/-0
index 0000000..1d8545d
--- /dev/null
+++ b/examples/pico_interpolator.rs
@@ -0,0 +1,438 @@
+//! # Pico Interpolator Example
+//!
+//! Example demonstrating the usage of the hardware interpolator.
+//!
+//! Runs several test programs, outputs the result on LEDs.
+//! Green led for successful test connects to GPIO3.
+//! Red led for unsuccessful test connects to GPIO4.
+//! In case of failure, the system LED blinks the number of the test.
+//! In case of success, the system LED stays lit.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// GPIO traits
+use embedded_hal::digital::v2::OutputPin;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+use rp_pico::hal::sio::{Interp, Interp0, Interp1, Lane, LaneCtrl};
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then just reads the button
+/// and sets the LED appropriately.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The delay object lets us wait for specified amounts of time (in
+    // milliseconds)
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // The single-cycle I/O block controls our GPIO pins
+    let mut sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Our LED outputs
+    let mut system_led_pin = pins.led.into_push_pull_output();
+    let mut green_led_pin = pins.gpio3.into_push_pull_output();
+    let mut red_led_pin = pins.gpio4.into_push_pull_output();
+
+    system_led_pin.set_low().unwrap();
+    green_led_pin.set_low().unwrap();
+    red_led_pin.set_low().unwrap();
+
+    let mut choose_led = |index: u32, result: bool| {
+        if result {
+            // blink the green led once to indicate success
+            green_led_pin.set_high().unwrap();
+            delay.delay_ms(500);
+            green_led_pin.set_low().unwrap();
+            delay.delay_ms(500);
+        } else {
+            // turn the red led on to indicate failure
+            // and blink the on board led to indicate which test failed, looping forever
+            red_led_pin.set_high().unwrap();
+            loop {
+                for _ in 0..index {
+                    system_led_pin.set_high().unwrap();
+                    delay.delay_ms(200);
+                    system_led_pin.set_low().unwrap();
+                    delay.delay_ms(200);
+                }
+                delay.delay_ms(1000);
+            }
+        }
+    };
+
+    // Run forever, setting the LED according to the button
+
+    choose_led(1, multiplication_table(&mut sio.interp0));
+    choose_led(2, moving_mask(&mut sio.interp0));
+    choose_led(3, cross_lanes(&mut sio.interp0));
+    choose_led(4, simple_blend1(&mut sio.interp0));
+    choose_led(5, simple_blend2(&mut sio.interp0));
+    choose_led(6, clamp(&mut sio.interp1));
+    choose_led(7, texture_mapping(&mut sio.interp0));
+
+    // turn the on board led on to indicate testing is done
+    system_led_pin.set_high().unwrap();
+    loop {
+        delay.delay_ms(1000);
+    }
+}
+
+fn multiplication_table(interp: &mut Interp0) -> bool {
+    //get the default configuration that just keep adding base into accum
+    let config = LaneCtrl::new();
+
+    //write the configuration to the hardware.
+    interp.get_lane0().set_ctrl(config.encode());
+
+    //set the accumulator to 0 and the base to 9
+    interp.get_lane0().set_accum(0);
+    interp.get_lane0().set_base(9);
+
+    //the expected output for comparison
+    let expected = [9, 18, 27, 36, 45, 54, 63, 72, 81, 90];
+
+    for i in expected {
+        //returns the value of accum + base and sets accum to the same value
+        let value = interp.get_lane0().pop();
+
+        if value != i {
+            return false; //inform that the interpolator did not return the expected value
+        }
+    }
+    true
+}
+
+fn moving_mask(interp: &mut Interp0) -> bool {
+    //get the default configuration that just keep adding base into accum
+    let mut config = LaneCtrl::new();
+
+    interp.get_lane0().set_accum(0x1234ABCD);
+
+    let expected = [
+        0x0000_000D,
+        0x0000_00C0,
+        0x0000_0B00,
+        0x0000_A000,
+        0x0004_0000,
+        0x0030_0000,
+        0x0200_0000,
+        0x1000_0000,
+    ];
+    for i in 0..8 {
+        // LSB, then MSB. These are inclusive, so 0,31 means "the entire 32 bit register"
+        config.mask_lsb = i * 4;
+        config.mask_msb = i * 4 + 3;
+        interp.get_lane0().set_ctrl(config.encode());
+
+        // Reading read_raw() returns the lane data
+        //   after shifting, masking and sign extending, without adding base
+        if interp.get_lane0().read_raw() != expected[i as usize] {
+            return false;
+        }
+    }
+
+    let signed_expected = [
+        0xFFFF_FFFD,
+        0xFFFF_FFC0,
+        0xFFFF_FB00,
+        0xFFFF_A000,
+        0x0004_0000,
+        0x0030_0000,
+        0x0200_0000,
+        0x1000_0000,
+    ];
+
+    config.signed = true;
+    for i in 0..8 {
+        config.mask_lsb = i * 4;
+        config.mask_msb = i * 4 + 3;
+        interp.get_lane0().set_ctrl(config.encode());
+
+        if interp.get_lane0().read_raw() != signed_expected[i as usize] {
+            return false;
+        }
+    }
+    true
+}
+
+fn cross_lanes(interp: &mut Interp0) -> bool {
+    // this configuration will at the time of pop()
+    // when applied to lane0 : set lane0 accumulator to the result from lane1
+    // when applied to lane1 : set lane1 accumulator to the result from lane0
+    let config = LaneCtrl {
+        cross_result: true,
+        ..LaneCtrl::new()
+    };
+    let encoded_config = config.encode();
+
+    // each lane is used through an accessor,
+    // as lanes mutate each other, they can not be borrowed at the same time
+    interp.get_lane0().set_ctrl(encoded_config);
+    interp.get_lane1().set_ctrl(encoded_config);
+
+    interp.get_lane0().set_accum(123);
+    interp.get_lane1().set_accum(456);
+
+    // lane0 will add 1 to its result, lane1 will add nothing
+    interp.get_lane0().set_base(1);
+    interp.get_lane1().set_base(0);
+
+    let expected = [
+        (124, 456),
+        (457, 124),
+        (125, 457),
+        (458, 125),
+        (126, 458),
+        (459, 126),
+        (127, 459),
+        (460, 127),
+        (128, 460),
+        (461, 128),
+    ];
+
+    for i in expected {
+        if i != (interp.get_lane0().peek(), interp.get_lane1().pop()) {
+            return false;
+        }
+    }
+    true
+}
+
+fn simple_blend1(interp: &mut Interp0) -> bool {
+    let config = LaneCtrl {
+        blend: true,
+        ..LaneCtrl::new()
+    };
+
+    //enable blend mode
+    interp.get_lane0().set_ctrl(config.encode());
+    //make sure the default configuration is in lane1 as the value may be shifted and masked.
+    interp.get_lane1().set_ctrl(LaneCtrl::new().encode());
+
+    //set the minimum value for interp.get_lane0().set_accum(0) 0/256
+    interp.get_lane0().set_base(500);
+    //set the maximum value which is inaccessible
+    // as the blend is done between 0/256 and 255/256
+    interp.get_lane1().set_base(1000);
+
+    let expected = [500, 582, 666, 748, 832, 914, 998];
+    for i in 0..=6 {
+        interp.get_lane1().set_accum(255 * i / 6);
+        if expected[i as usize] != interp.get_lane1().peek() {
+            return false;
+        }
+    }
+    true
+}
+
+fn simple_blend2(interp: &mut Interp0) -> bool {
+    let config = LaneCtrl {
+        blend: true,
+        ..LaneCtrl::new()
+    };
+    //enable blend mode
+    interp.get_lane0().set_ctrl(config.encode());
+
+    interp.get_lane0().set_base((-1000i32) as u32);
+    interp.get_lane1().set_base(1000);
+
+    let mut config1 = LaneCtrl {
+        signed: true,
+        ..LaneCtrl::new()
+    };
+    interp.get_lane1().set_ctrl(config1.encode());
+    let expected_signed = [-1000, -672, -336, -8, 328, 656, 992];
+    for i in 0..=6 {
+        // write a value between 0 and 256 (exclusive)
+        interp.get_lane1().set_accum(255 * i / 6);
+        // reads it as a value between -1000 and 1000 (exclusive)
+        if interp.get_lane1().peek() as i32 != expected_signed[i as usize] {
+            return false;
+        }
+    }
+    config1.signed = false;
+    interp.get_lane1().set_ctrl(config1.encode());
+    let expected_unsigned = [
+        0xfffffc18, 0xd5fffd60, 0xaafffeb0, 0x80fffff8, 0x56000148, 0x2c000290, 0x010003e0,
+    ];
+    for i in 0..=6 {
+        interp.get_lane1().set_accum(255 * i / 6);
+        // reads a value between 4294966296 and 1000
+        if interp.get_lane1().peek() != expected_unsigned[i as usize] {
+            return false;
+        }
+    }
+    true
+}
+
+///Divides by 4 and clamp the value between 0 and 255 inclusive
+fn clamp(interp: &mut Interp1) -> bool {
+    // Enables Clamp ONLY AVAILABLE ON Interp1
+    // shift two bits to the right and mask the two most significant bits
+    // because sign extension is made after the mask
+    let config = LaneCtrl {
+        clamp: true,
+        shift: 2,
+        mask_lsb: 0,
+        mask_msb: 29,
+        signed: true,
+        ..LaneCtrl::new()
+    };
+    interp.get_lane0().set_ctrl(config.encode());
+    //set minimum value of result
+    interp.get_lane0().set_base(0);
+    //set maximum value of result
+    interp.get_lane1().set_base(255);
+    let values: [(i32, i32); 9] = [
+        (-1024, 0),
+        (-768, 0),
+        (-512, 0),
+        (-256, 0),
+        (0, 0),
+        (256, 64),
+        (512, 128),
+        (768, 192),
+        (1024, 255),
+    ];
+    for (arg, result) in values {
+        interp.get_lane0().set_accum(arg as u32);
+        if result != interp.get_lane0().peek() as i32 {
+            return false;
+        }
+    }
+    true
+}
+
+fn texture_mapping(interp: &mut Interp0) -> bool {
+    #[rustfmt::skip]
+    let texture: [u8;16] = [
+        0x00, 0x01, 0x02, 0x03,
+        0x10, 0x11, 0x12, 0x13,
+        0x20, 0x21, 0x22, 0x23,
+        0x30, 0x31, 0x32, 0x33,
+    ];
+
+    // the position will be given in fixed point with 16 bits
+    // fractional part
+    let uv_fractional_bits = 16;
+    let texture_width_bits = 2;
+    let texture_height_bits = 2;
+
+    // bits
+    //                       3322222222221111 1111110000000000
+    //                       1098765432109876 5432109876543210
+    // accum0 u axis coordinate            xx xxxxxxxxxxxxxxxx  18 bits
+    // after shift and mask                                 xx
+    // accum1 v axis                       xx xxxxxxxxxxxxxxxx  18 bits
+    // after shift and mask                               xx
+
+    // add_raw make the interpolator increment the accumulator
+    // with the base value without masking or shifting
+    let config0 = LaneCtrl {
+        add_raw: true,
+        shift: uv_fractional_bits,
+        mask_lsb: 0,
+        mask_msb: texture_width_bits - 1,
+        ..LaneCtrl::new()
+    };
+    interp.get_lane0().set_ctrl(config0.encode());
+    let config1 = LaneCtrl {
+        add_raw: true,
+        shift: uv_fractional_bits - texture_width_bits,
+        mask_lsb: texture_width_bits,
+        mask_msb: texture_width_bits + texture_height_bits - 1,
+        ..LaneCtrl::new()
+    };
+    interp.get_lane1().set_ctrl(config1.encode());
+
+    interp.set_base(0);
+
+    // set starting position to 0x0
+    // will move 1/2 a pixel horizontally
+    // and 1/3 a pixel vertically per call to pop()
+    interp.get_lane0().set_accum(0);
+    interp.get_lane0().set_base(65536 / 2);
+    interp.get_lane1().set_accum(0);
+    interp.get_lane1().set_base(65536 / 3);
+
+    let expected = [
+        0x00, 0x00, 0x01, 0x01, 0x12, 0x12, 0x13, 0x23, 0x20, 0x20, 0x31, 0x31,
+    ];
+
+    for i in expected {
+        if i != texture[interp.pop() as usize] {
+            return false;
+        }
+    }
+
+    // reset the starting position
+    interp.get_lane0().set_accum(0);
+    interp.get_lane1().set_accum(0);
+    interp.set_base(texture.as_ptr() as u32);
+
+    for i in expected {
+        // This is unsafe and should be done extremely carefully
+        // remember to follow memory alignment,
+        // reading or writing an unaligned address will crash
+        if i != unsafe { *(interp.pop() as *const u8) } {
+            return false;
+        }
+    }
+
+    true
+}
+// End of file

diff --git a/examples/pico_pio_pwm.rs b/examples/pico_pio_pwm.rs
line changes: +165/-0
index 0000000..770b63c
--- /dev/null
+++ b/examples/pico_pio_pwm.rs
@@ -0,0 +1,165 @@
+//! # Pico PIO PWM Blink Example
+//!
+//! Fades the LED on a Pico board using the PIO peripheral with an pwm program.
+//!
+//! This will fade in the LED attached to GP25, which is the pin the Pico
+//! uses for the on-board LED.
+//!
+//! This example uses a few advance pio tricks such as side setting pins and instruction injection.
+//!
+//! See the `Cargo.toml` file for Copyright and license details. Except for the pio program which is subject to a different license.
+
+#![no_std]
+#![no_main]
+
+use defmt::info;
+use defmt_rtt as _;
+// The macro for our start-up function
+use rp_pico::entry;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// Import pio crates
+use hal::pio::{PIOBuilder, Running, StateMachine, Tx, ValidStateMachine, SM0};
+use pio::{Instruction, InstructionOperands, OutDestination};
+use pio_proc::pio_file;
+
+/// Set pio pwm period
+///
+/// This uses a sneaky trick to set a second value besides the duty cycle.
+/// We first write a value to the tx fifo. But instead of the normal instructions we
+/// have stopped the state machine and inject our own instructions that move the written value to the ISR.
+fn pio_pwm_set_period<T: ValidStateMachine>(
+    sm: StateMachine<(hal::pac::PIO0, SM0), Running>,
+    tx: &mut Tx<T>,
+    period: u32,
+) -> StateMachine<(hal::pac::PIO0, SM0), Running> {
+    // To make sure the inserted instructions actually use our newly written value
+    // We first busy loop to empty the queue. (Which typically should be the case)
+    while !tx.is_empty() {}
+
+    let mut sm = sm.stop();
+    tx.write(period);
+    sm.exec_instruction(Instruction {
+        operands: InstructionOperands::PULL {
+            if_empty: false,
+            block: false,
+        },
+        delay: 0,
+        side_set: None,
+    });
+    sm.exec_instruction(Instruction {
+        operands: InstructionOperands::OUT {
+            destination: OutDestination::ISR,
+            bit_count: 32,
+        },
+        delay: 0,
+        side_set: None,
+    });
+    sm.start()
+}
+
+/// Set pio pwm duty cycle
+///
+/// The value written to the TX FIFO is used directly by the normal pio program
+fn pio_pwm_set_level<T: ValidStateMachine>(tx: &mut Tx<T>, level: u32) {
+    // Write duty cycle to TX Fifo
+    tx.write(level);
+}
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then fades the LED in an
+/// infinite loop.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // The delay object lets us wait for specified amounts of time (in
+    // milliseconds)
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    let (mut pio0, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
+
+    // Create a pio program
+    let program = pio_file!("./examples/pwm.pio", select_program("pwm"),);
+    let installed = pio0.install(&program.program).unwrap();
+
+    // Set gpio25 to pio
+    let _led: hal::gpio::Pin<_, hal::gpio::FunctionPio0> = pins.led.into_mode();
+    let led_pin_id = 25;
+
+    // Build the pio program and set pin both for set and side set!
+    // We are running with the default divider which is 1 (max speed)
+    let (mut sm, _, mut tx) = PIOBuilder::from_program(installed)
+        .set_pins(led_pin_id, 1)
+        .side_set_pin_base(led_pin_id)
+        .build(sm0);
+
+    // Set pio pindir for gpio25
+    sm.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]);
+
+    // Start state machine
+    let sm = sm.start();
+
+    // Set period
+    pio_pwm_set_period(sm, &mut tx, u16::MAX as u32 - 1);
+
+    // Loop forever and adjust duty cycle to make te led brighter
+    let mut level = 0;
+    loop {
+        info!("Level = {}", level);
+        pio_pwm_set_level(&mut tx, level * level);
+        level = (level + 1) % 256;
+        delay.delay_ms(10);
+    }
+}
+
+// End of file

diff --git a/examples/pico_pwm_blink.rs b/examples/pico_pwm_blink.rs
line changes: +116/-0
index 0000000..b6c90fb
--- /dev/null
+++ b/examples/pico_pwm_blink.rs
@@ -0,0 +1,116 @@
+//! # Pico PWM Blink Example
+//!
+//! Fades the LED on a Pico board using the PWM peripheral.
+//!
+//! This will fade in/out the LED attached to GP25, which is the pin the Pico
+//! uses for the on-board LED.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// GPIO traits
+use embedded_hal::PwmPin;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// The minimum PWM value (i.e. LED brightness) we want
+const LOW: u16 = 0;
+
+// The maximum PWM value (i.e. LED brightness) we want
+const HIGH: u16 = 25000;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then fades the LED in an
+/// infinite loop.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // The delay object lets us wait for specified amounts of time (in
+    // milliseconds)
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // Init PWMs
+    let mut pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
+
+    // Configure PWM4
+    let pwm = &mut pwm_slices.pwm4;
+    pwm.set_ph_correct();
+    pwm.enable();
+
+    // Output channel B on PWM4 to the LED pin
+    let channel = &mut pwm.channel_b;
+    channel.output_to(pins.led);
+
+    // Infinite loop, fading LED up and down
+    loop {
+        // Ramp brightness up
+        for i in (LOW..=HIGH).skip(100) {
+            delay.delay_us(8);
+            channel.set_duty(i);
+        }
+
+        // Ramp brightness down
+        for i in (LOW..=HIGH).rev().skip(100) {
+            delay.delay_us(8);
+            channel.set_duty(i);
+        }
+
+        delay.delay_ms(500);
+    }
+}
+
+// End of file

diff --git a/examples/pico_pwm_servo.rs b/examples/pico_pwm_servo.rs
line changes: +116/-0
index 0000000..8a938b5
--- /dev/null
+++ b/examples/pico_pwm_servo.rs
@@ -0,0 +1,116 @@
+//! # Pico PWM Micro Servo Example
+//!
+//! Moves the micro servo on a Pico board using the PWM peripheral.
+//!
+//! This will move in different positions the motor attached to GP1.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+use cortex_m::prelude::*;
+
+// GPIO traits
+use embedded_hal::PwmPin;
+
+// Traits for converting integers to amounts of time
+use fugit::ExtU32;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the RP2040 peripherals, then fades the LED in an
+/// infinite loop.
+#[rp2040_hal::entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let _clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // Configure the Timer peripheral in count-down mode
+    let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS);
+    let mut count_down = timer.count_down();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Init PWMs
+    let mut pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
+
+    // Configure PWM0
+    let pwm = &mut pwm_slices.pwm0;
+    pwm.set_ph_correct();
+    pwm.set_div_int(20u8); // 50 hz
+    pwm.enable();
+
+    // Output channel B on PWM0 to the GPIO1 pin
+    let channel = &mut pwm.channel_b;
+    channel.output_to(pins.gpio1);
+
+    // Infinite loop, moving micro servo from one position to another.
+    // You may need to adjust the pulse width since several servos from
+    // different manufacturers respond differently.
+    loop {
+        // move to 0°
+        channel.set_duty(2500);
+        count_down.start(400.millis());
+        let _ = nb::block!(count_down.wait());
+
+        // 0° to 90°
+        channel.set_duty(3930);
+        count_down.start(400.millis());
+        let _ = nb::block!(count_down.wait());
+
+        // 90° to 180°
+        channel.set_duty(7860);
+        count_down.start(400.millis());
+        let _ = nb::block!(count_down.wait());
+
+        // 180° to 90°
+        channel.set_duty(3930);
+        count_down.start(400.millis());
+        let _ = nb::block!(count_down.wait());
+    }
+}
+
+// End of file

diff --git a/examples/pico_rtic.rs b/examples/pico_rtic.rs
line changes: +87/-0
index 0000000..3d83735
--- /dev/null
+++ b/examples/pico_rtic.rs
@@ -0,0 +1,87 @@
+#![no_std]
+#![no_main]
+
+use panic_halt as _;
+
+#[rtic::app(device = rp_pico::hal::pac, peripherals = true)]
+mod app {
+
+    use embedded_hal::digital::v2::OutputPin;
+    use fugit::MicrosDurationU32;
+    use rp_pico::{
+        hal::{self, clocks::init_clocks_and_plls, timer::Alarm, watchdog::Watchdog, Sio},
+        XOSC_CRYSTAL_FREQ,
+    };
+
+    const SCAN_TIME_US: MicrosDurationU32 = MicrosDurationU32::secs(1);
+
+    #[shared]
+    struct Shared {
+        timer: hal::Timer,
+        alarm: hal::timer::Alarm0,
+        led: hal::gpio::Pin<hal::gpio::pin::bank0::Gpio25, hal::gpio::PushPullOutput>,
+    }
+
+    #[local]
+    struct Local {}
+
+    #[init]
+    fn init(c: init::Context) -> (Shared, Local, init::Monotonics) {
+        // Soft-reset does not release the hardware spinlocks
+        // Release them now to avoid a deadlock after debug or watchdog reset
+        unsafe {
+            hal::sio::spinlock_reset();
+        }
+        let mut resets = c.device.RESETS;
+        let mut watchdog = Watchdog::new(c.device.WATCHDOG);
+        let _clocks = init_clocks_and_plls(
+            XOSC_CRYSTAL_FREQ,
+            c.device.XOSC,
+            c.device.CLOCKS,
+            c.device.PLL_SYS,
+            c.device.PLL_USB,
+            &mut resets,
+            &mut watchdog,
+        )
+        .ok()
+        .unwrap();
+
+        let sio = Sio::new(c.device.SIO);
+        let pins = rp_pico::Pins::new(
+            c.device.IO_BANK0,
+            c.device.PADS_BANK0,
+            sio.gpio_bank0,
+            &mut resets,
+        );
+        let mut led = pins.led.into_push_pull_output();
+        led.set_low().unwrap();
+
+        let mut timer = hal::Timer::new(c.device.TIMER, &mut resets);
+        let mut alarm = timer.alarm_0().unwrap();
+        let _ = alarm.schedule(SCAN_TIME_US);
+        alarm.enable_interrupt();
+
+        (Shared { timer, alarm, led }, Local {}, init::Monotonics())
+    }
+
+    #[task(
+        binds = TIMER_IRQ_0,
+        priority = 1,
+        shared = [timer, alarm, led],
+        local = [tog: bool = true],
+    )]
+    fn timer_irq(mut c: timer_irq::Context) {
+        if *c.local.tog {
+            c.shared.led.lock(|l| l.set_high().unwrap());
+        } else {
+            c.shared.led.lock(|l| l.set_low().unwrap());
+        }
+        *c.local.tog = !*c.local.tog;
+
+        let mut alarm = c.shared.alarm;
+        (alarm).lock(|a| {
+            a.clear_interrupt();
+            let _ = a.schedule(SCAN_TIME_US);
+        });
+    }
+}

diff --git a/examples/pico_rtic_monotonic.rs b/examples/pico_rtic_monotonic.rs
line changes: +89/-0
index 0000000..4db5197
--- /dev/null
+++ b/examples/pico_rtic_monotonic.rs
@@ -0,0 +1,89 @@
+#![no_std]
+#![no_main]
+
+use panic_halt as _;
+
+#[rtic::app(device = rp_pico::hal::pac, peripherals = true, dispatchers = [I2C0_IRQ])]
+mod app {
+
+    use embedded_hal::digital::v2::OutputPin;
+    use fugit::ExtU64;
+    use rp_pico::{
+        hal::{
+            self,
+            clocks::init_clocks_and_plls,
+            timer::{monotonic::Monotonic, Alarm0},
+            watchdog::Watchdog,
+            Sio,
+        },
+        XOSC_CRYSTAL_FREQ,
+    };
+
+    #[shared]
+    struct Shared {
+        led: hal::gpio::Pin<hal::gpio::pin::bank0::Gpio25, hal::gpio::PushPullOutput>,
+    }
+
+    #[monotonic(binds = TIMER_IRQ_0, default = true)]
+    type MyMono = Monotonic<Alarm0>;
+
+    #[local]
+    struct Local {}
+
+    #[init]
+    fn init(c: init::Context) -> (Shared, Local, init::Monotonics) {
+        // Soft-reset does not release the hardware spinlocks
+        // Release them now to avoid a deadlock after debug or watchdog reset
+        unsafe {
+            hal::sio::spinlock_reset();
+        }
+        let mut resets = c.device.RESETS;
+        let mut watchdog = Watchdog::new(c.device.WATCHDOG);
+        let _clocks = init_clocks_and_plls(
+            XOSC_CRYSTAL_FREQ,
+            c.device.XOSC,
+            c.device.CLOCKS,
+            c.device.PLL_SYS,
+            c.device.PLL_USB,
+            &mut resets,
+            &mut watchdog,
+        )
+        .ok()
+        .unwrap();
+
+        let sio = Sio::new(c.device.SIO);
+        let pins = rp_pico::Pins::new(
+            c.device.IO_BANK0,
+            c.device.PADS_BANK0,
+            sio.gpio_bank0,
+            &mut resets,
+        );
+        let mut led = pins.led.into_push_pull_output();
+        led.set_low().unwrap();
+
+        let mut timer = hal::Timer::new(c.device.TIMER, &mut resets);
+        let alarm = timer.alarm_0().unwrap();
+        blink_led::spawn_after(500.millis()).unwrap();
+
+        (
+            Shared { led },
+            Local {},
+            init::Monotonics(Monotonic::new(timer, alarm)),
+        )
+    }
+
+    #[task(
+        shared = [led],
+        local = [tog: bool = true],
+    )]
+    fn blink_led(mut c: blink_led::Context) {
+        if *c.local.tog {
+            c.shared.led.lock(|l| l.set_high().unwrap());
+        } else {
+            c.shared.led.lock(|l| l.set_low().unwrap());
+        }
+        *c.local.tog = !*c.local.tog;
+
+        blink_led::spawn_after(500.millis()).unwrap();
+    }
+}

diff --git a/examples/pico_spi_sd_card.rs b/examples/pico_spi_sd_card.rs
line changes: +405/-0
index 0000000..8ced29b
--- /dev/null
+++ b/examples/pico_spi_sd_card.rs
@@ -0,0 +1,405 @@
+//! # Pico SD Card Example
+//!
+//! Reads and writes a file from/to the SD Card that is formatted in FAT32.
+//! This example uses the SPI0 device of the Raspberry Pi Pico on the
+//! pins 4,5,6 and 7. If you don't use an external 3.3V power source,
+//! you can connect the +3.3V output on pin 36 to the SD card.
+//!
+//! SD Cards up to 2TB are supported by the `embedded_sdmmc` crate.
+//! I've tested this with a 64GB micro SD card.
+//!
+//! You need to format the card with an regular old FAT32 filesystem
+//! and also make sure the first partition has the right type. This is how your
+//! `fdisk` output should look like:
+//!
+//! ```text
+//!     fdisk /dev/sdj
+//!
+//!     Welcome to fdisk (util-linux 2.34).
+//!     Changes will remain in memory only, until you decide to write them.
+//!     Be careful before using the write command.
+//!
+//!     Command (m for help): Disk /dev/sdj:
+//!     59,49 GiB, 63864569856 bytes, 124735488 sectors
+//!     Disk model: SD/MMC/MS/MSPRO
+//!     Units: sectors of 1 * 512 = 512 bytes
+//!     Sector size (logical/physical): 512 bytes / 512 bytes
+//!     I/O size (minimum/optimal): 512 bytes / 512 bytes
+//!     Disklabel type: dos
+//!     Disk identifier: 0x00000000
+//!
+//!     Device     Boot Start       End   Sectors  Size Id Type
+//!     /dev/sdj1        2048 124735487 124733440 59,5G  c W95 FAT32 (LBA)
+//! ```
+//!
+//! The important bit here is the _Type_ with `W95 FAT32 (LBA)`, other types
+//! are rejected by the `embedded_sdmmc` filesystem implementation.
+//!
+//! Formatting the partition can be done using `mkfs.fat`:
+//!
+//!     $ mkfs.fat /dev/sdj1
+//!
+//! In the following ASCII art the SD card is also connected to 5 strong pull up
+//! resistors. I've found varying values for these, from 50kOhm, 10kOhm
+//! down to 5kOhm.
+//! Stronger pull up resistors will eat more amperes, but also allow faster
+//! data rates.
+//!
+//! ```text
+//!                    +3.3V
+//!          Pull Ups ->||||
+//!                 4x[5kOhm]
+//!                     ||| \
+//!  _______________    |||  \
+//! |     DAT2/NC  9\---o||   \                            _|USB|_
+//! | S   DAT3/CS   1|---o+----+------SS--\               |1  R 40|
+//! | D   CMD/DI    2|----o----+-----MOSI-+-\             |2  P 39|
+//! |     VSS1      3|-- GND   |          | |         GND-|3    38|- GND
+//! | C   VDD       4|-- +3.3V |  /--SCK--+-+----SPI0 SCK-|4  P 37|
+//! | A   CLK/SCK   5|---------+-/        | \----SPI0 TX--|5  I 36|- +3.3V
+//! | R   VSS2      6|-- GND   |  /--MISO-+------SPI0 RX--|6  C   |
+//! | D   DAT0/DO   7|---------o-/        \------SPI0 CSn-|7  O   |
+//! |     DAT1/IRQ  8|-[5k]- +3.3V                        |       |
+//!  """"""""""""""""                                     |       |
+//!                                                       |       |
+//!                                                       .........
+//!                                                       |20   21|
+//!                                                        """""""
+//! Symbols:
+//!     - (+) crossing lines, not connected
+//!     - (o) connected lines
+//! ```
+//!
+//! The example can either be used with a probe to receive debug output
+//! and also the LED is used as status output. There are different blinking
+//! patterns.
+//!
+//! For every successful stage in the example the LED will blink long once.
+//! If everything is successful (9 long blink signals), the example will go
+//! into a loop and either blink in a _"short long"_ or _"short short long"_ pattern.
+//!
+//! If there are 4 different error patterns, all with short blinking pulses:
+//!
+//! - **3 short blink (in a loop)**: Card size could not be retrieved.
+//! - **4 short blink (in a loop)**: Error getting volume/partition 0.
+//! - **5 short blink (in a loop)**: Error opening root directory.
+//! - **6 short blink (in a loop)**: Could not open file 'O.TST'.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+use core::cell::RefCell;
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// info!() and error!() macros for printing information to the debug output
+use defmt::*;
+use defmt_rtt as _;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// Embed the `Hz` function/trait:
+use fugit::RateExtU32;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// Import the SPI abstraction:
+use rp_pico::hal::spi;
+
+// Import the GPIO abstraction:
+use rp_pico::hal::gpio;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// Link in the embedded_sdmmc crate.
+// The `SdMmcSpi` is used for block level access to the card.
+// And the `VolumeManager` gives access to the FAT filesystem functions.
+use embedded_sdmmc::{SdCard, TimeSource, Timestamp, VolumeIdx, VolumeManager};
+
+// Get the file open mode enum:
+use embedded_sdmmc::filesystem::Mode;
+
+use embedded_hal::blocking::delay::DelayMs;
+use embedded_hal::blocking::delay::DelayUs;
+
+/// A dummy timesource, which is mostly important for creating files.
+#[derive(Default)]
+pub struct DummyTimesource();
+
+impl TimeSource for DummyTimesource {
+    // In theory you could use the RTC of the rp2040 here, if you had
+    // any external time synchronizing device.
+    fn get_timestamp(&self) -> Timestamp {
+        Timestamp {
+            year_since_1970: 0,
+            zero_indexed_month: 0,
+            zero_indexed_day: 0,
+            hours: 0,
+            minutes: 0,
+            seconds: 0,
+        }
+    }
+}
+
+// Setup some blinking codes:
+const BLINK_OK_LONG: [u8; 1] = [8u8];
+const BLINK_OK_SHORT_LONG: [u8; 4] = [1u8, 0u8, 6u8, 0u8];
+const BLINK_OK_SHORT_SHORT_LONG: [u8; 6] = [1u8, 0u8, 1u8, 0u8, 6u8, 0u8];
+const BLINK_ERR_3_SHORT: [u8; 6] = [1u8, 0u8, 1u8, 0u8, 1u8, 0u8];
+const BLINK_ERR_4_SHORT: [u8; 8] = [1u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8, 0u8];
+const BLINK_ERR_5_SHORT: [u8; 10] = [1u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8, 0u8];
+const BLINK_ERR_6_SHORT: [u8; 12] = [1u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8, 0u8];
+
+fn blink_signals(
+    pin: &mut dyn embedded_hal::digital::v2::OutputPin<Error = core::convert::Infallible>,
+    delay: &mut dyn DelayMs<u32>,
+    sig: &[u8],
+) {
+    for bit in sig {
+        if *bit != 0 {
+            pin.set_high().unwrap();
+        } else {
+            pin.set_low().unwrap();
+        }
+
+        let length = if *bit > 0 { *bit } else { 1 };
+
+        for _ in 0..length {
+            delay.delay_ms(100);
+        }
+    }
+
+    pin.set_low().unwrap();
+
+    delay.delay_ms(500);
+}
+
+fn blink_signals_loop(
+    pin: &mut dyn embedded_hal::digital::v2::OutputPin<Error = core::convert::Infallible>,
+    delay: &mut dyn DelayMs<u32>,
+    sig: &[u8],
+) -> ! {
+    loop {
+        blink_signals(pin, delay, sig);
+        delay.delay_ms(1000);
+    }
+}
+
+#[entry]
+fn main() -> ! {
+    info!("Program start");
+
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Set the LED to be an output
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    // These are implicitly used by the spi driver if they are in the correct mode
+    let _spi_sclk = pins.gpio2.into_mode::<gpio::FunctionSpi>();
+    let _spi_mosi = pins.gpio3.into_mode::<gpio::FunctionSpi>();
+    let _spi_miso = pins.gpio4.into_mode::<gpio::FunctionSpi>();
+    let spi_cs = pins.gpio5.into_push_pull_output();
+
+    // Create an SPI driver instance for the SPI0 device
+    let spi = spi::Spi::<_, _, 8>::new(pac.SPI0);
+
+    // Exchange the uninitialised SPI driver for an initialised one
+    let spi = spi.init(
+        &mut pac.RESETS,
+        clocks.peripheral_clock.freq(),
+        400.kHz(), // card initialization happens at low baud rate
+        &embedded_hal::spi::MODE_0,
+    );
+
+    // We need a delay implementation that can be passed to SdCard and still be used
+    // for the blink signals.
+    let mut delay = &SharedDelay::new(cortex_m::delay::Delay::new(
+        core.SYST,
+        clocks.system_clock.freq().to_Hz(),
+    ));
+
+    info!("Initialize SPI SD/MMC data structures...");
+    let sdcard = SdCard::new(spi, spi_cs, delay);
+    let mut volume_mgr = VolumeManager::new(sdcard, DummyTimesource::default());
+
+    blink_signals(&mut led_pin, &mut delay, &BLINK_OK_LONG);
+
+    info!("Init SD card controller and retrieve card size...");
+    match volume_mgr.device().num_bytes() {
+        Ok(size) => info!("card size is {} bytes", size),
+        Err(e) => {
+            error!("Error retrieving card size: {}", defmt::Debug2Format(&e));
+            blink_signals_loop(&mut led_pin, &mut delay, &BLINK_ERR_3_SHORT);
+        }
+    }
+
+    blink_signals(&mut led_pin, &mut delay, &BLINK_OK_LONG);
+
+    // Now that the card is initialized, clock can go faster
+    volume_mgr
+        .device()
+        .spi(|spi| spi.set_baudrate(clocks.peripheral_clock.freq(), 16.MHz()));
+
+    info!("Getting Volume 0...");
+    let mut volume = match volume_mgr.get_volume(VolumeIdx(0)) {
+        Ok(v) => v,
+        Err(e) => {
+            error!("Error getting volume 0: {}", defmt::Debug2Format(&e));
+            blink_signals_loop(&mut led_pin, &mut delay, &BLINK_ERR_4_SHORT);
+        }
+    };
+
+    blink_signals(&mut led_pin, &mut delay, &BLINK_OK_LONG);
+
+    // After we have the volume (partition) of the drive we got to open the
+    // root directory:
+    let dir = match volume_mgr.open_root_dir(&volume) {
+        Ok(dir) => dir,
+        Err(e) => {
+            error!("Error opening root dir: {}", defmt::Debug2Format(&e));
+            blink_signals_loop(&mut led_pin, &mut delay, &BLINK_ERR_5_SHORT);
+        }
+    };
+
+    info!("Root directory opened!");
+    blink_signals(&mut led_pin, &mut delay, &BLINK_OK_LONG);
+
+    // This shows how to iterate through the directory and how
+    // to get the file names (and print them in hope they are UTF-8 compatible):
+    volume_mgr
+        .iterate_dir(&volume, &dir, |ent| {
+            info!(
+                "/{}.{}",
+                core::str::from_utf8(ent.name.base_name()).unwrap(),
+                core::str::from_utf8(ent.name.extension()).unwrap()
+            );
+        })
+        .unwrap();
+
+    blink_signals(&mut led_pin, &mut delay, &BLINK_OK_LONG);
+
+    let mut successful_read = false;
+
+    // Next we going to read a file from the SD card:
+    if let Ok(mut file) = volume_mgr.open_file_in_dir(&mut volume, &dir, "O.TST", Mode::ReadOnly) {
+        let mut buf = [0u8; 32];
+        let read_count = volume_mgr.read(&volume, &mut file, &mut buf).unwrap();
+        volume_mgr.close_file(&volume, file).unwrap();
+
+        if read_count >= 2 {
+            info!("READ {} bytes: {}", read_count, buf);
+
+            // If we read what we wrote before the last reset,
+            // we set a flag so that the success blinking at the end
+            // changes it's pattern.
+            if buf[0] == 0x42 && buf[1] == 0x1E {
+                successful_read = true;
+            }
+        }
+    }
+
+    blink_signals(&mut led_pin, &mut delay, &BLINK_OK_LONG);
+
+    match volume_mgr.open_file_in_dir(&mut volume, &dir, "O.TST", Mode::ReadWriteCreateOrTruncate) {
+        Ok(mut file) => {
+            volume_mgr
+                .write(&mut volume, &mut file, b"\x42\x1E")
+                .unwrap();
+            volume_mgr.close_file(&volume, file).unwrap();
+        }
+        Err(e) => {
+            error!("Error opening file 'O.TST': {}", defmt::Debug2Format(&e));
+            blink_signals_loop(&mut led_pin, &mut delay, &BLINK_ERR_6_SHORT);
+        }
+    }
+
+    volume_mgr.free();
+
+    blink_signals(&mut led_pin, &mut delay, &BLINK_OK_LONG);
+
+    if successful_read {
+        info!("Successfully read previously written file 'O.TST'");
+    } else {
+        info!("Could not read file, which is ok for the first run.");
+        info!("Reboot the pico!");
+    }
+
+    loop {
+        if successful_read {
+            blink_signals(&mut led_pin, &mut delay, &BLINK_OK_SHORT_SHORT_LONG);
+        } else {
+            blink_signals(&mut led_pin, &mut delay, &BLINK_OK_SHORT_LONG);
+        }
+
+        delay.delay_ms(1000);
+    }
+}
+
+// Can be removed once we have https://github.com/rp-rs/rp-hal/pull/614,
+// ie. when we move to rp2040-hal 0.9
+struct SharedDelay {
+    inner: RefCell<cortex_m::delay::Delay>,
+}
+
+impl SharedDelay {
+    fn new(delay: cortex_m::delay::Delay) -> Self {
+        Self {
+            inner: delay.into(),
+        }
+    }
+}
+
+impl DelayMs<u32> for &SharedDelay {
+    fn delay_ms(&mut self, ms: u32) {
+        self.inner.borrow_mut().delay_ms(ms);
+    }
+}
+
+impl DelayUs<u8> for &SharedDelay {
+    fn delay_us(&mut self, us: u8) {
+        self.inner.borrow_mut().delay_us(us as u32);
+    }
+}

diff --git a/examples/pico_uart_irq_buffer.rs b/examples/pico_uart_irq_buffer.rs
line changes: +291/-0
index 0000000..c4681d9
--- /dev/null
+++ b/examples/pico_uart_irq_buffer.rs
@@ -0,0 +1,291 @@
+//! # UART IRQ TX Buffer Example
+//!
+//! This application demonstrates how to use the UART Driver to talk to a
+//! serial connection. In this example, the IRQ owns the UART and you cannot
+//! do any UART access from the main thread. You can, however, write to a
+//! static queue, and have the queue contents transferred to the UART under
+//! interrupt.
+//!
+//! The pinouts are:
+//!
+//! * GPIO 0 - UART TX (out of the RP2040)
+//! * GPIO 1 - UART RX (in to the RP2040)
+//! * GPIO 25 - An LED we can blink (active high)
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// These are the traits we need from Embedded HAL to treat our hardware
+// objects as generic embedded devices.
+use embedded_hal::{digital::v2::OutputPin, serial::Write as UartWrite};
+
+// The writeln! trait.
+use core::fmt::Write;
+
+// We also need this for the 'Delay' object to work.
+use rp2040_hal::Clock;
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// Time handling traits
+use fugit::RateExtU32;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp2040_hal as hal;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use hal::pac;
+
+// Our interrupt macro
+use pac::interrupt;
+
+// Some short-cuts to useful types
+use core::cell::RefCell;
+use critical_section::Mutex;
+use heapless::spsc::Queue;
+
+/// Import the GPIO pins we use
+use hal::gpio::pin::bank0::{Gpio0, Gpio1};
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Alias the type for our UART pins to make things clearer.
+type UartPins = (
+    hal::gpio::Pin<Gpio0, hal::gpio::Function<hal::gpio::Uart>>,
+    hal::gpio::Pin<Gpio1, hal::gpio::Function<hal::gpio::Uart>>,
+);
+
+/// Alias the type for our UART to make things clearer.
+type Uart = hal::uart::UartPeripheral<hal::uart::Enabled, pac::UART0, UartPins>;
+
+/// This describes the queue we use for outbound UART data
+struct UartQueue {
+    mutex_cell_queue: Mutex<RefCell<Queue<u8, 64>>>,
+    interrupt: pac::Interrupt,
+}
+
+/// This how we transfer the UART into the Interrupt Handler
+static GLOBAL_UART: Mutex<RefCell<Option<Uart>>> = Mutex::new(RefCell::new(None));
+
+/// This is our outbound UART queue. We write to it from the main thread, and
+/// read from it in the UART IRQ.
+static UART_TX_QUEUE: UartQueue = UartQueue {
+    mutex_cell_queue: Mutex::new(RefCell::new(Queue::new())),
+    interrupt: hal::pac::Interrupt::UART0_IRQ,
+};
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then writes to the UART in
+/// an infinite loop.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // Lets us wait for fixed periods of time
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins to their default state
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    let uart_pins = (
+        // UART TX (characters sent from RP2040) on pin 1 (GPIO0)
+        pins.gpio0.into_mode::<hal::gpio::FunctionUart>(),
+        // UART RX (characters received by RP2040) on pin 2 (GPIO1)
+        pins.gpio1.into_mode::<hal::gpio::FunctionUart>(),
+    );
+
+    // Make a UART on the given pins
+    let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+        .enable(
+            UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
+            clocks.peripheral_clock.freq(),
+        )
+        .unwrap();
+
+    // Tell the UART to raise its interrupt line on the NVIC when the TX FIFO
+    // has space in it.
+    uart.enable_tx_interrupt();
+
+    // Now we give away the entire UART peripheral, via the variable
+    // `GLOBAL_UART`. We can no longer access the UART from this main thread.
+    critical_section::with(|cs| {
+        GLOBAL_UART.borrow(cs).replace(Some(uart));
+    });
+
+    // But we can blink an LED.
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    loop {
+        // Light the LED whilst the main thread is in the transmit routine. It
+        // shouldn't be on very long, but it will be on until we get enough
+        // data /out/ of the queue and over the UART for this remainder of
+        // this string to fit.
+        led_pin.set_high().unwrap();
+        // Note we can only write to &UART_TX_QUEUE, because it's not mutable and
+        // `core::fmt::Write` takes mutable references.
+        writeln!(
+            &UART_TX_QUEUE,
+            "Hello, this was sent under interrupt! It's quite a \
+            long message, designed not to fit in either the \
+            hardware FIFO or the software queue."
+        )
+        .unwrap();
+        led_pin.set_low().unwrap();
+        // Wait for a second - the UART TX IRQ will transmit the remainder of
+        // our queue contents in the background.
+        delay.delay_ms(1000);
+    }
+}
+
+impl UartQueue {
+    /// Try and get some data out of the UART Queue. Returns None if queue empty.
+    fn read_byte(&self) -> Option<u8> {
+        critical_section::with(|cs| {
+            let cell_queue = self.mutex_cell_queue.borrow(cs);
+            let mut queue = cell_queue.borrow_mut();
+            queue.dequeue()
+        })
+    }
+
+    /// Peek at the next byte in the queue without removing it.
+    fn peek_byte(&self) -> Option<u8> {
+        critical_section::with(|cs| {
+            let cell_queue = self.mutex_cell_queue.borrow(cs);
+            let queue = cell_queue.borrow_mut();
+            queue.peek().cloned()
+        })
+    }
+
+    /// Write some data to the queue, spinning until it all fits.
+    fn write_bytes_blocking(&self, data: &[u8]) {
+        // Go through all the bytes we need to write.
+        for byte in data.iter() {
+            // Keep trying until there is space in the queue. But release the
+            // mutex between each attempt, otherwise the IRQ will never run
+            // and we will never have space!
+            let mut written = false;
+            while !written {
+                // Grab the mutex, by turning interrupts off. NOTE: This
+                // doesn't work if you are using Core 1 as we only turn
+                // interrupts off on one core.
+                critical_section::with(|cs| {
+                    // Grab the mutex contents.
+                    let cell_queue = self.mutex_cell_queue.borrow(cs);
+                    // Grab mutable access to the queue. This can't fail
+                    // because there are no interrupts running.
+                    let mut queue = cell_queue.borrow_mut();
+                    // Try and put the byte in the queue.
+                    if queue.enqueue(*byte).is_ok() {
+                        // It worked! We must have had space.
+                        if !pac::NVIC::is_enabled(self.interrupt) {
+                            unsafe {
+                                // Now enable the UART interrupt in the *Nested
+                                // Vectored Interrupt Controller*, which is part
+                                // of the Cortex-M0+ core. If the FIFO has space,
+                                // the interrupt will run as soon as we're out of
+                                // the closure.
+                                pac::NVIC::unmask(self.interrupt);
+                                // We also have to kick the IRQ in case the FIFO
+                                // was already below the threshold level.
+                                pac::NVIC::pend(self.interrupt);
+                            }
+                        }
+                        written = true;
+                    }
+                });
+            }
+        }
+    }
+}
+
+impl core::fmt::Write for &UartQueue {
+    /// This function allows us to `writeln!` on our global static UART queue.
+    /// Note we have an impl for &UartQueue, because our global static queue
+    /// is not mutable and `core::fmt::Write` takes mutable references.
+    fn write_str(&mut self, data: &str) -> core::fmt::Result {
+        self.write_bytes_blocking(data.as_bytes());
+        Ok(())
+    }
+}
+
+#[interrupt]
+fn UART0_IRQ() {
+    // This variable is special. It gets mangled by the `#[interrupt]` macro
+    // into something that we can access without the `unsafe` keyword. It can
+    // do this because this function cannot be called re-entrantly. We know
+    // this because the function's 'real' name is unknown, and hence it cannot
+    // be called from the main thread. We also know that the NVIC will not
+    // re-entrantly call an interrupt.
+    static mut UART: Option<hal::uart::UartPeripheral<hal::uart::Enabled, pac::UART0, UartPins>> =
+        None;
+
+    // This is one-time lazy initialisation. We steal the variable given to us
+    // via `GLOBAL_UART`.
+    if UART.is_none() {
+        critical_section::with(|cs| {
+            *UART = GLOBAL_UART.borrow(cs).take();
+        });
+    }
+
+    // Check if we have a UART to work with
+    if let Some(uart) = UART {
+        // Check if we have data to transmit
+        while let Some(byte) = UART_TX_QUEUE.peek_byte() {
+            if uart.write(byte).is_ok() {
+                // The UART took it, so pop it off the queue.
+                let _ = UART_TX_QUEUE.read_byte();
+            } else {
+                break;
+            }
+        }
+
+        if UART_TX_QUEUE.peek_byte().is_none() {
+            pac::NVIC::mask(hal::pac::Interrupt::UART0_IRQ);
+        }
+    }
+
+    // Set an event to ensure the main thread always wakes up, even if it's in
+    // the process of going to sleep.
+    cortex_m::asm::sev();
+}
+
+// End of file

diff --git a/examples/pico_uart_irq_echo.rs b/examples/pico_uart_irq_echo.rs
line changes: +198/-0
index 0000000..fdf6918
--- /dev/null
+++ b/examples/pico_uart_irq_echo.rs
@@ -0,0 +1,198 @@
+//! # UART IRQ Echo Example
+//!
+//! This application demonstrates how to use the UART Driver to talk to a serial
+//! connection. In this example, the IRQ owns the UART and you cannot do any UART
+//! access from the main thread.
+//!
+//! The pinouts are:
+//!
+//! * GPIO 0 - UART TX (out of the RP2040)
+//! * GPIO 1 - UART RX (in to the RP2040)
+//! * GPIO 25 - An LED we can blink (active high)
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// These are the traits we need from Embedded HAL to treat our hardware
+// objects as generic embedded devices.
+use embedded_hal::{
+    digital::v2::OutputPin,
+    serial::{Read, Write},
+};
+
+// We also need this for the 'Delay' object to work.
+use rp2040_hal::Clock;
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// Time handling traits
+use fugit::RateExtU32;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp2040_hal as hal;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use hal::pac;
+
+// Our interrupt macro
+use hal::pac::interrupt;
+
+// Some short-cuts to useful types
+use core::cell::RefCell;
+use critical_section::Mutex;
+
+/// Import the GPIO pins we use
+use hal::gpio::pin::bank0::{Gpio0, Gpio1};
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Alias the type for our UART pins to make things clearer.
+type UartPins = (
+    hal::gpio::Pin<Gpio0, hal::gpio::Function<hal::gpio::Uart>>,
+    hal::gpio::Pin<Gpio1, hal::gpio::Function<hal::gpio::Uart>>,
+);
+
+/// Alias the type for our UART to make things clearer.
+type Uart = hal::uart::UartPeripheral<hal::uart::Enabled, pac::UART0, UartPins>;
+
+/// This how we transfer the UART into the Interrupt Handler
+static GLOBAL_UART: Mutex<RefCell<Option<Uart>>> = Mutex::new(RefCell::new(None));
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then writes to the UART in
+/// an infinite loop.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // Lets us wait for fixed periods of time
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins to their default state
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    let uart_pins = (
+        // UART TX (characters sent from RP2040) on pin 1 (GPIO0)
+        pins.gpio0.into_mode::<hal::gpio::FunctionUart>(),
+        // UART RX (characters received by RP2040) on pin 2 (GPIO1)
+        pins.gpio1.into_mode::<hal::gpio::FunctionUart>(),
+    );
+
+    // Make a UART on the given pins
+    let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+        .enable(
+            UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
+            clocks.peripheral_clock.freq(),
+        )
+        .unwrap();
+
+    unsafe {
+        // Enable the UART interrupt in the *Nested Vectored Interrupt
+        // Controller*, which is part of the Cortex-M0+ core.
+        pac::NVIC::unmask(hal::pac::Interrupt::UART0_IRQ);
+    }
+
+    // Tell the UART to raise its interrupt line on the NVIC when the RX FIFO
+    // has data in it.
+    uart.enable_rx_interrupt();
+
+    // Write something to the UART on start-up so we can check the output pin
+    // is wired correctly.
+    uart.write_full_blocking(b"uart_interrupt example started...\n");
+
+    // Now we give away the entire UART peripheral, via the variable
+    // `GLOBAL_UART`. We can no longer access the UART from this main thread.
+    critical_section::with(|cs| {
+        GLOBAL_UART.borrow(cs).replace(Some(uart));
+    });
+
+    // But we can blink an LED.
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    loop {
+        // The normal *Wait For Interrupts* (WFI) has a race-hazard - the
+        // interrupt could occur between the CPU checking for interrupts and
+        // the CPU going to sleep. We wait for events (and interrupts), and
+        // then we set an event in every interrupt handler. This ensures we
+        // always wake up correctly.
+        cortex_m::asm::wfe();
+        // Light the LED to indicate we saw an interrupt.
+        led_pin.set_high().unwrap();
+        delay.delay_ms(100);
+        led_pin.set_low().unwrap();
+    }
+}
+
+#[interrupt]
+fn UART0_IRQ() {
+    // This variable is special. It gets mangled by the `#[interrupt]` macro
+    // into something that we can access without the `unsafe` keyword. It can
+    // do this because this function cannot be called re-entrantly. We know
+    // this because the function's 'real' name is unknown, and hence it cannot
+    // be called from the main thread. We also know that the NVIC will not
+    // re-entrantly call an interrupt.
+    static mut UART: Option<hal::uart::UartPeripheral<hal::uart::Enabled, pac::UART0, UartPins>> =
+        None;
+
+    // This is one-time lazy initialisation. We steal the variable given to us
+    // via `GLOBAL_UART`.
+    if UART.is_none() {
+        critical_section::with(|cs| {
+            *UART = GLOBAL_UART.borrow(cs).take();
+        });
+    }
+
+    // Check if we have a UART to work with
+    if let Some(uart) = UART {
+        // Echo the input back to the output until the FIFO is empty. Reading
+        // from the UART should also clear the UART interrupt flag.
+        while let Ok(byte) = uart.read() {
+            let _ = uart.write(byte);
+        }
+    }
+
+    // Set an event to ensure the main thread always wakes up, even if it's in
+    // the process of going to sleep.
+    cortex_m::asm::sev();
+}
+
+// End of file

diff --git a/examples/pico_usb_serial.rs b/examples/pico_usb_serial.rs
line changes: +152/-0
index 0000000..bc253aa
--- /dev/null
+++ b/examples/pico_usb_serial.rs
@@ -0,0 +1,152 @@
+//! # Pico USB Serial Example
+//!
+//! Creates a USB Serial device on a Pico board, with the USB driver running in
+//! the main thread.
+//!
+//! This will create a USB Serial device echoing anything it receives. Incoming
+//! ASCII characters are converted to upercase, so you can tell it is working
+//! and not just local-echo!
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// USB Device support
+use usb_device::{class_prelude::*, prelude::*};
+
+// USB Communications Class Device support
+use usbd_serial::SerialPort;
+
+// Used to demonstrate writing formatted strings
+use core::fmt::Write;
+use heapless::String;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then echoes any characters
+/// received over USB Serial.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    #[cfg(feature = "rp2040-e5")]
+    {
+        let sio = hal::Sio::new(pac.SIO);
+        let _pins = rp_pico::Pins::new(
+            pac.IO_BANK0,
+            pac.PADS_BANK0,
+            sio.gpio_bank0,
+            &mut pac.RESETS,
+        );
+    }
+
+    // Set up the USB driver
+    let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
+        pac.USBCTRL_REGS,
+        pac.USBCTRL_DPRAM,
+        clocks.usb_clock,
+        true,
+        &mut pac.RESETS,
+    ));
+
+    // Set up the USB Communications Class Device driver
+    let mut serial = SerialPort::new(&usb_bus);
+
+    // Create a USB device with a fake VID and PID
+    let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
+        .manufacturer("Fake company")
+        .product("Serial port")
+        .serial_number("TEST")
+        .device_class(2) // from: https://www.usb.org/defined-class-codes
+        .build();
+
+    let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS);
+    let mut said_hello = false;
+    loop {
+        // A welcome message at the beginning
+        if !said_hello && timer.get_counter().ticks() >= 2_000_000 {
+            said_hello = true;
+            let _ = serial.write(b"Hello, World!\r\n");
+
+            let time = timer.get_counter().ticks();
+            let mut text: String<64> = String::new();
+            writeln!(&mut text, "Current timer ticks: {}", time).unwrap();
+
+            // This only works reliably because the number of bytes written to
+            // the serial port is smaller than the buffers available to the USB
+            // peripheral. In general, the return value should be handled, so that
+            // bytes not transferred yet don't get lost.
+            let _ = serial.write(text.as_bytes());
+        }
+
+        // Check for new data
+        if usb_dev.poll(&mut [&mut serial]) {
+            let mut buf = [0u8; 64];
+            match serial.read(&mut buf) {
+                Err(_e) => {
+                    // Do nothing
+                }
+                Ok(0) => {
+                    // Do nothing
+                }
+                Ok(count) => {
+                    // Convert to upper case
+                    buf.iter_mut().take(count).for_each(|b| {
+                        b.make_ascii_uppercase();
+                    });
+                    // Send back to the host
+                    let mut wr_ptr = &buf[..count];
+                    while !wr_ptr.is_empty() {
+                        match serial.write(wr_ptr) {
+                            Ok(len) => wr_ptr = &wr_ptr[len..],
+                            // On error, just drop unwritten data.
+                            // One possible error is Err(WouldBlock), meaning the USB
+                            // write buffer is full.
+                            Err(_) => break,
+                        };
+                    }
+                }
+            }
+        }
+    }
+}
+
+// End of file

diff --git a/examples/pico_usb_serial_interrupt.rs b/examples/pico_usb_serial_interrupt.rs
line changes: +207/-0
index 0000000..676d576
--- /dev/null
+++ b/examples/pico_usb_serial_interrupt.rs
@@ -0,0 +1,207 @@
+//! # Pico USB Serial (with Interrupts) Example
+//!
+//! Creates a USB Serial device on a Pico board, with the USB driver running in
+//! the USB interrupt.
+//!
+//! This will create a USB Serial device echoing anything it receives. Incoming
+//! ASCII characters are converted to upercase, so you can tell it is working
+//! and not just local-echo!
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// The macro for marking our interrupt functions
+use rp_pico::hal::pac::interrupt;
+
+// GPIO traits
+use embedded_hal::digital::v2::OutputPin;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// USB Device support
+use usb_device::{class_prelude::*, prelude::*};
+
+// USB Communications Class Device support
+use usbd_serial::SerialPort;
+
+/// The USB Device Driver (shared with the interrupt).
+static mut USB_DEVICE: Option<UsbDevice<hal::usb::UsbBus>> = None;
+
+/// The USB Bus Driver (shared with the interrupt).
+static mut USB_BUS: Option<UsbBusAllocator<hal::usb::UsbBus>> = None;
+
+/// The USB Serial Device Driver (shared with the interrupt).
+static mut USB_SERIAL: Option<SerialPort<hal::usb::UsbBus>> = None;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then blinks the LED in an
+/// infinite loop.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // Set up the USB driver
+    let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
+        pac.USBCTRL_REGS,
+        pac.USBCTRL_DPRAM,
+        clocks.usb_clock,
+        true,
+        &mut pac.RESETS,
+    ));
+    unsafe {
+        // Note (safety): This is safe as interrupts haven't been started yet
+        USB_BUS = Some(usb_bus);
+    }
+
+    // Grab a reference to the USB Bus allocator. We are promising to the
+    // compiler not to take mutable access to this global variable whilst this
+    // reference exists!
+    let bus_ref = unsafe { USB_BUS.as_ref().unwrap() };
+
+    // Set up the USB Communications Class Device driver
+    let serial = SerialPort::new(bus_ref);
+    unsafe {
+        USB_SERIAL = Some(serial);
+    }
+
+    // Create a USB device with a fake VID and PID
+    let usb_dev = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x16c0, 0x27dd))
+        .manufacturer("Fake company")
+        .product("Serial port")
+        .serial_number("TEST")
+        .device_class(2) // from: https://www.usb.org/defined-class-codes
+        .build();
+    unsafe {
+        // Note (safety): This is safe as interrupts haven't been started yet
+        USB_DEVICE = Some(usb_dev);
+    }
+
+    // Enable the USB interrupt
+    unsafe {
+        pac::NVIC::unmask(hal::pac::Interrupt::USBCTRL_IRQ);
+    };
+
+    // No more USB code after this point in main! We can do anything we want in
+    // here since USB is handled in the interrupt - let's blink an LED!
+
+    // The delay object lets us wait for specified amounts of time (in
+    // milliseconds)
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Set the LED to be an output
+    let mut led_pin = pins.led.into_push_pull_output();
+
+    // Blink the LED at 1 Hz
+    loop {
+        led_pin.set_high().unwrap();
+        delay.delay_ms(500);
+        led_pin.set_low().unwrap();
+        delay.delay_ms(500);
+    }
+}
+
+/// This function is called whenever the USB Hardware generates an Interrupt
+/// Request.
+///
+/// We do all our USB work under interrupt, so the main thread can continue on
+/// knowing nothing about USB.
+#[allow(non_snake_case)]
+#[interrupt]
+unsafe fn USBCTRL_IRQ() {
+    use core::sync::atomic::{AtomicBool, Ordering};
+
+    /// Note whether we've already printed the "hello" message.
+    static SAID_HELLO: AtomicBool = AtomicBool::new(false);
+
+    // Grab the global objects. This is OK as we only access them under interrupt.
+    let usb_dev = USB_DEVICE.as_mut().unwrap();
+    let serial = USB_SERIAL.as_mut().unwrap();
+
+    // Say hello exactly once on start-up
+    if !SAID_HELLO.load(Ordering::Relaxed) {
+        SAID_HELLO.store(true, Ordering::Relaxed);
+        let _ = serial.write(b"Hello, World!\r\n");
+    }
+
+    // Poll the USB driver with all of our supported USB Classes
+    if usb_dev.poll(&mut [serial]) {
+        let mut buf = [0u8; 64];
+        match serial.read(&mut buf) {
+            Err(_e) => {
+                // Do nothing
+            }
+            Ok(0) => {
+                // Do nothing
+            }
+            Ok(count) => {
+                // Convert to upper case
+                buf.iter_mut().take(count).for_each(|b| {
+                    b.make_ascii_uppercase();
+                });
+
+                // Send back to the host
+                let mut wr_ptr = &buf[..count];
+                while !wr_ptr.is_empty() {
+                    let _ = serial.write(wr_ptr).map(|len| {
+                        wr_ptr = &wr_ptr[len..];
+                    });
+                }
+            }
+        }
+    }
+}
+
+// End of file

diff --git a/examples/pico_usb_twitchy_mouse.rs b/examples/pico_usb_twitchy_mouse.rs
line changes: +189/-0
index 0000000..5779b09
--- /dev/null
+++ b/examples/pico_usb_twitchy_mouse.rs
@@ -0,0 +1,189 @@
+//! # Pico USB 'Twitchy' Mouse Example
+//!
+//! Creates a USB HID Class Pointing device (i.e. a virtual mouse) on a Pico
+//! board, with the USB driver running in the main thread.
+//!
+//! It generates movement reports which will twitch the cursor up and down by a
+//! few pixels, several times a second.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+//!
+//! This is a port of
+//! https://github.com/atsamd-rs/atsamd/blob/master/boards/itsybitsy_m0/examples/twitching_usb_mouse.rs
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// The macro for marking our interrupt functions
+use rp_pico::hal::pac::interrupt;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// USB Device support
+use usb_device::{class_prelude::*, prelude::*};
+
+// USB Human Interface Device (HID) Class support
+use usbd_hid::descriptor::generator_prelude::*;
+use usbd_hid::descriptor::MouseReport;
+use usbd_hid::hid_class::HIDClass;
+
+/// The USB Device Driver (shared with the interrupt).
+static mut USB_DEVICE: Option<UsbDevice<hal::usb::UsbBus>> = None;
+
+/// The USB Bus Driver (shared with the interrupt).
+static mut USB_BUS: Option<UsbBusAllocator<hal::usb::UsbBus>> = None;
+
+/// The USB Human Interface Device Driver (shared with the interrupt).
+static mut USB_HID: Option<HIDClass<hal::usb::UsbBus>> = None;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2040 peripherals, then submits cursor movement
+/// updates periodically.
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    #[cfg(feature = "rp2040-e5")]
+    {
+        let sio = hal::Sio::new(pac.SIO);
+        let _pins = rp_pico::Pins::new(
+            pac.IO_BANK0,
+            pac.PADS_BANK0,
+            sio.gpio_bank0,
+            &mut pac.RESETS,
+        );
+    }
+
+    // Set up the USB driver
+    let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
+        pac.USBCTRL_REGS,
+        pac.USBCTRL_DPRAM,
+        clocks.usb_clock,
+        true,
+        &mut pac.RESETS,
+    ));
+    unsafe {
+        // Note (safety): This is safe as interrupts haven't been started yet
+        USB_BUS = Some(usb_bus);
+    }
+
+    // Grab a reference to the USB Bus allocator. We are promising to the
+    // compiler not to take mutable access to this global variable whilst this
+    // reference exists!
+    let bus_ref = unsafe { USB_BUS.as_ref().unwrap() };
+
+    // Set up the USB HID Class Device driver, providing Mouse Reports
+    let usb_hid = HIDClass::new(bus_ref, MouseReport::desc(), 60);
+    unsafe {
+        // Note (safety): This is safe as interrupts haven't been started yet.
+        USB_HID = Some(usb_hid);
+    }
+
+    // Create a USB device with a fake VID and PID
+    let usb_dev = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x16c0, 0x27da))
+        .manufacturer("Fake company")
+        .product("Twitchy Mousey")
+        .serial_number("TEST")
+        .device_class(0)
+        .build();
+    unsafe {
+        // Note (safety): This is safe as interrupts haven't been started yet
+        USB_DEVICE = Some(usb_dev);
+    }
+
+    unsafe {
+        // Enable the USB interrupt
+        pac::NVIC::unmask(hal::pac::Interrupt::USBCTRL_IRQ);
+    };
+    let core = pac::CorePeripherals::take().unwrap();
+    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // Move the cursor up and down every 200ms
+    loop {
+        delay.delay_ms(100);
+
+        let rep_up = MouseReport {
+            x: 0,
+            y: 4,
+            buttons: 0,
+            wheel: 0,
+            pan: 0,
+        };
+        push_mouse_movement(rep_up).ok().unwrap_or(0);
+
+        delay.delay_ms(100);
+
+        let rep_down = MouseReport {
+            x: 0,
+            y: -4,
+            buttons: 0,
+            wheel: 0,
+            pan: 0,
+        };
+        push_mouse_movement(rep_down).ok().unwrap_or(0);
+    }
+}
+
+/// Submit a new mouse movement report to the USB stack.
+///
+/// We do this with interrupts disabled, to avoid a race hazard with the USB IRQ.
+fn push_mouse_movement(report: MouseReport) -> Result<usize, usb_device::UsbError> {
+    critical_section::with(|_| unsafe {
+        // Now interrupts are disabled, grab the global variable and, if
+        // available, send it a HID report
+        USB_HID.as_mut().map(|hid| hid.push_input(&report))
+    })
+    .unwrap()
+}
+
+/// This function is called whenever the USB Hardware generates an Interrupt
+/// Request.
+#[allow(non_snake_case)]
+#[interrupt]
+unsafe fn USBCTRL_IRQ() {
+    // Handle USB request
+    let usb_dev = USB_DEVICE.as_mut().unwrap();
+    let usb_hid = USB_HID.as_mut().unwrap();
+    usb_dev.poll(&mut [usb_hid]);
+}
+
+// End of file

diff --git a/examples/pico_ws2812_led.rs b/examples/pico_ws2812_led.rs
line changes: +219/-0
index 0000000..6ae2565
--- /dev/null
+++ b/examples/pico_ws2812_led.rs
@@ -0,0 +1,219 @@
+//! # Pico WS2812 RGB LED Example
+//!
+//! Drives 3 WS2812 LEDs connected directly to the Raspberry Pi Pico.
+//! This assumes you drive the Raspberry Pi Pico via USB power, so that VBUS
+//! delivers the 5V and at least enough amperes to drive the LEDs.
+//!
+//! For a more large scale and longer strips you should use an extra power
+//! supply for the LED strip (or know what you are doing ;-) ).
+//!
+//! The example also comes with an utility function to calculate the colors
+//! from HSV color space. It also limits the brightness a bit to save a
+//! few milliamperes - be careful if you increase the strip length you will
+//! quickly get into power consumption of multiple amperes.
+//!
+//! The example assumes you connected the data input to pin 6 of the
+//! Raspberry Pi Pico, which is GPIO4 of the rp2040. Here is a circuit
+//! diagram that shows the assumed setup:
+//!
+//! ```text
+//!  _______________      /----------------------\
+//! |+5V  /---\  +5V|----/         _|USB|_       |
+//! |DO <-|LED|<- DI|-\           |1  R 40|-VBUS-/
+//! |GND  \---/  GND|--+---\      |2  P 39|
+//!  """""""""""""""   |    \-GND-|3    38|
+//!                    |          |4  P 37|
+//!                    |          |5  I 36|
+//!                    \------GP4-|6  C   |
+//!                               |7  O   |
+//!                               |       |
+//!                               .........
+//!                               |20   21|
+//!                                """""""
+//! Symbols:
+//!     - (+) crossing lines, not connected
+//!     - (o) connected lines
+//! ```
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// The macro for our start-up function
+use rp_pico::entry;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Pull in any important traits
+use rp_pico::hal::prelude::*;
+
+// A shorter alias for the Peripheral Access Crate, which provides low-level
+// register access
+use rp_pico::hal::pac;
+
+// Import the Timer for Ws2812:
+use rp_pico::hal::timer::Timer;
+
+// A shorter alias for the Hardware Abstraction Layer, which provides
+// higher-level drivers.
+use rp_pico::hal;
+
+// PIOExt for the split() method that is needed to bring
+// PIO0 into useable form for Ws2812:
+use rp_pico::hal::pio::PIOExt;
+
+// Import useful traits to handle the ws2812 LEDs:
+use smart_leds::{brightness, SmartLedsWrite, RGB8};
+
+// Import the actual crate to handle the Ws2812 protocol:
+use ws2812_pio::Ws2812;
+
+// Currently 3 consecutive LEDs are driven by this example
+// to keep the power draw compatible with USB:
+const STRIP_LEN: usize = 3;
+
+#[entry]
+fn main() -> ! {
+    // Grab our singleton objects
+    let mut pac = pac::Peripherals::take().unwrap();
+    let core = pac::CorePeripherals::take().unwrap();
+
+    // Set up the watchdog driver - needed by the clock setup code
+    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+    // Configure the clocks
+    //
+    // The default is to generate a 125 MHz system clock
+    let clocks = hal::clocks::init_clocks_and_plls(
+        rp_pico::XOSC_CRYSTAL_FREQ,
+        pac.XOSC,
+        pac.CLOCKS,
+        pac.PLL_SYS,
+        pac.PLL_USB,
+        &mut pac.RESETS,
+        &mut watchdog,
+    )
+    .ok()
+    .unwrap();
+
+    // The single-cycle I/O block controls our GPIO pins
+    let sio = hal::Sio::new(pac.SIO);
+
+    // Set the pins up according to their function on this particular board
+    let pins = rp_pico::Pins::new(
+        pac.IO_BANK0,
+        pac.PADS_BANK0,
+        sio.gpio_bank0,
+        &mut pac.RESETS,
+    );
+
+    // Setup a delay for the LED blink signals:
+    let mut frame_delay =
+        cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
+
+    // Import the `sin` function for a smooth hue animation from the
+    // Pico rp2040 ROM:
+    let sin = hal::rom_data::float_funcs::fsin::ptr();
+
+    // Create a count down timer for the Ws2812 instance:
+    let timer = Timer::new(pac.TIMER, &mut pac.RESETS);
+
+    // Split the PIO state machine 0 into individual objects, so that
+    // Ws2812 can use it:
+    let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
+
+    // Instanciate a Ws2812 LED strip:
+    let mut ws = Ws2812::new(
+        // Use pin 6 on the Raspberry Pi Pico (which is GPIO4 of the rp2040 chip)
+        // for the LED data output:
+        pins.gpio4.into_mode(),
+        &mut pio,
+        sm0,
+        clocks.peripheral_clock.freq(),
+        timer.count_down(),
+    );
+
+    let mut leds: [RGB8; STRIP_LEN] = [(0, 0, 0).into(); STRIP_LEN];
+    let mut t = 0.0;
+
+    // Bring down the overall brightness of the strip to not blow
+    // the USB power supply: every LED draws ~60mA, RGB means 3 LEDs per
+    // ws2812 LED, for 3 LEDs that would be: 3 * 3 * 60mA, which is
+    // already 540mA for just 3 white LEDs!
+    let strip_brightness = 64u8; // Limit brightness to 64/256
+
+    // Slow down timer by this factor (0.1 will result in 10 seconds):
+    let animation_speed = 0.1;
+
+    loop {
+        for (i, led) in leds.iter_mut().enumerate() {
+            // An offset to give 3 consecutive LEDs a different color:
+            let hue_offs = match i % 3 {
+                1 => 0.25,
+                2 => 0.5,
+                _ => 0.0,
+            };
+
+            let sin_11 = sin((t + hue_offs) * 2.0 * core::f32::consts::PI);
+            // Bring -1..1 sine range to 0..1 range:
+            let sin_01 = (sin_11 + 1.0) * 0.5;
+
+            let hue = 360.0 * sin_01;
+            let sat = 1.0;
+            let val = 1.0;
+
+            let rgb = hsv2rgb_u8(hue, sat, val);
+            *led = rgb.into();
+        }
+
+        // Here the magic happens and the `leds` buffer is written to the
+        // ws2812 LEDs:
+        ws.write(brightness(leds.iter().copied(), strip_brightness))
+            .unwrap();
+
+        // Wait a bit until calculating the next frame:
+        frame_delay.delay_ms(16); // ~60 FPS
+
+        // Increase the time counter variable and make sure it
+        // stays inbetween 0.0 to 1.0 range:
+        t += (16.0 / 1000.0) * animation_speed;
+        while t > 1.0 {
+            t -= 1.0;
+        }
+    }
+}
+
+pub fn hsv2rgb(hue: f32, sat: f32, val: f32) -> (f32, f32, f32) {
+    let c = val * sat;
+    let v = (hue / 60.0) % 2.0 - 1.0;
+    let v = if v < 0.0 { -v } else { v };
+    let x = c * (1.0 - v);
+    let m = val - c;
+    let (r, g, b) = if hue < 60.0 {
+        (c, x, 0.0)
+    } else if hue < 120.0 {
+        (x, c, 0.0)
+    } else if hue < 180.0 {
+        (0.0, c, x)
+    } else if hue < 240.0 {
+        (0.0, x, c)
+    } else if hue < 300.0 {
+        (x, 0.0, c)
+    } else {
+        (c, 0.0, x)
+    };
+    (r + m, g + m, b + m)
+}
+
+pub fn hsv2rgb_u8(h: f32, s: f32, v: f32) -> (u8, u8, u8) {
+    let r = hsv2rgb(h, s, v);
+
+    (
+        (r.0 * 255.0) as u8,
+        (r.1 * 255.0) as u8,
+        (r.2 * 255.0) as u8,
+    )
+}

diff --git a/examples/pwm.pio b/examples/pwm.pio
line changes: +31/-0
index 0000000..af1e725
--- /dev/null
+++ b/examples/pwm.pio
@@ -0,0 +1,31 @@
+;
+; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+;
+; SPDX-License-Identifier: BSD-3-Clause
+;
+
+; Side-set pin 0 is used for PWM output
+
+.program pwm
+.side_set 1 opt
+
+    pull noblock    side 0 ; Pull from FIFO to OSR if available, else copy X to OSR.
+    mov x, osr             ; Copy most-recently-pulled value back to scratch X
+    mov y, isr             ; ISR contains PWM period. Y used as counter.
+countloop:
+    jmp x!=y noset         ; Set pin high if X == Y, keep the two paths length matched
+    jmp skip        side 1
+noset:
+    nop                    ; Single dummy cycle to keep the two paths the same length
+skip:
+    jmp y-- countloop      ; Loop until Y hits 0, then pull a fresh PWM value from FIFO
+
+% c-sdk {
+static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) {
+   pio_gpio_init(pio, pin);
+   pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
+   pio_sm_config c = pwm_program_get_default_config(offset);
+   sm_config_set_sideset_pins(&c, pin);
+   pio_sm_init(pio, sm, offset, &c);
+}
+%} 
\ No newline at end of file

diff --git a/src/lib.rs b/src/lib.rs
line changes: +499/-0
index 0000000..7157bec
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,499 @@
+#![no_std]
+
+//! A Hardware Abstraction Layer for the DoA Hallönbrod.
+//!
+//! This crate serves as a HAL (Hardware Abstraction Layer) for the DoA Hallönbrod. Since the DoA Hallönbrod
+//! is based on the RP2040 chip, it re-exports the [rp2040_hal] crate which contains the tooling to work with the
+//! rp2040 chip.
+//!
+//! # Examples:
+//!
+//! The following example turns on the onboard LED. Note that most of the logic works through the [rp2040_hal] crate.
+//! ```ignore
+//! #![no_main]
+//! use rp_pico::entry;
+//! use panic_halt as _;
+//! use embedded_hal::digital::v2::OutputPin;
+//! use rp_pico::hal::pac;
+//! use rp_pico::hal;
+
+//! #[entry]
+//! fn does_not_have_to_be_main() -> ! {
+//!   let mut pac = pac::Peripherals::take().unwrap();
+//!   let sio = hal::Sio::new(pac.SIO);
+//!   let pins = rp_pico::Pins::new(
+//!        pac.IO_BANK0,
+//!        pac.PADS_BANK0,
+//!        sio.gpio_bank0,
+//!        &mut pac.RESETS,
+//!   );
+//!   let mut led_pin = pins.led.into_push_pull_output();
+//!   led_pin.set_high().unwrap();
+//!   loop {
+//!   }
+//! }
+//! ```
+
+pub extern crate rp2040_hal as hal;
+
+#[cfg(feature = "rt")]
+extern crate cortex_m_rt;
+
+/// The `entry` macro declares the starting function to the linker.
+/// This is similar to the `main` function in console applications.
+///
+/// It is based on the [cortex_m_rt](https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.entry.html) crate.
+///
+/// # Examples
+/// ```ignore
+/// #![no_std]
+/// #![no_main]
+/// use rp_pico::entry;
+/// #[entry]
+/// fn you_can_use_a_custom_main_name_here() -> ! {
+///   loop {}
+/// }
+/// ```
+
+#[cfg(feature = "rt")]
+pub use hal::entry;
+
+/// The linker will place this boot block at the start of our program image. We
+/// need this to help the ROM bootloader get our code up and running.
+#[cfg(feature = "boot2")]
+#[link_section = ".boot2"]
+#[no_mangle]
+#[used]
+pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
+
+pub use hal::pac;
+
+hal::bsp_pins!(
+    /// GPIO 0 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 RX`    | [crate::Gp0Spi0Rx]          |
+    /// | `UART0 TX`   | [crate::Gp0Uart0Tx]         |
+    /// | `I2C0 SDA`   | [crate::Gp0I2C0Sda]         |
+    /// | `PWM0 A`     | [crate::Gp0Pwm0A]           |
+    /// | `PIO0`       | [crate::Gp0Pio0]            |
+    /// | `PIO1`       | [crate::Gp0Pio1]            |
+    Gpio0 {
+        name: gpio0,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio0].
+            FunctionUart: Gp0Uart0Tx,
+            /// SPI Function alias for pin [crate::Pins::gpio0].
+            FunctionSpi: Gp0Spi0Rx,
+            /// I2C Function alias for pin [crate::Pins::gpio0].
+            FunctionI2C: Gp0I2C0Sda,
+            /// PWM Function alias for pin [crate::Pins::gpio0].
+            FunctionPwm: Gp0Pwm0A,
+            /// PIO0 Function alias for pin [crate::Pins::gpio0].
+            FunctionPio0: Gp0Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio0].
+            FunctionPio1: Gp0Pio1
+        }
+    },
+
+    /// GPIO 1 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 CSn`   | [crate::Gp1Spi0Csn]         |
+    /// | `UART0 RX`   | [crate::Gp1Uart0Rx]         |
+    /// | `I2C0 SCL`   | [crate::Gp1I2C0Scl]         |
+    /// | `PWM0 B`     | [crate::Gp1Pwm0B]           |
+    /// | `PIO0`       | [crate::Gp1Pio0]            |
+    /// | `PIO1`       | [crate::Gp1Pio1]            |
+    Gpio1 {
+        name: gpio1,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio1].
+            FunctionUart: Gp1Uart0Rx,
+            /// SPI Function alias for pin [crate::Pins::gpio1].
+            FunctionSpi: Gp1Spi0Csn,
+            /// I2C Function alias for pin [crate::Pins::gpio1].
+            FunctionI2C: Gp1I2C0Scl,
+            /// PWM Function alias for pin [crate::Pins::gpio1].
+            FunctionPwm: Gp1Pwm0B,
+            /// PIO0 Function alias for pin [crate::Pins::gpio1].
+            FunctionPio0: Gp1Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio1].
+            FunctionPio1: Gp1Pio1
+        }
+    },
+
+    /// GPIO 2 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 SCK`   | [crate::Gp2Spi0Sck]         |
+    /// | `UART0 CTS`  | [crate::Gp2Uart0Cts]        |
+    /// | `I2C1 SDA`   | [crate::Gp2I2C1Sda]         |
+    /// | `PWM1 A`     | [crate::Gp2Pwm1A]           |
+    /// | `PIO0`       | [crate::Gp2Pio0]            |
+    /// | `PIO1`       | [crate::Gp2Pio1]            |
+    Gpio2 {
+        name: gpio2,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio2].
+            FunctionUart: Gp2Uart0Cts,
+            /// SPI Function alias for pin [crate::Pins::gpio2].
+            FunctionSpi: Gp2Spi0Sck,
+            /// I2C Function alias for pin [crate::Pins::gpio2].
+            FunctionI2C: Gp2I2C1Sda,
+            /// PWM Function alias for pin [crate::Pins::gpio2].
+            FunctionPwm: Gp2Pwm1A,
+            /// PIO0 Function alias for pin [crate::Pins::gpio2].
+            FunctionPio0: Gp2Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio2].
+            FunctionPio1: Gp2Pio1
+        }
+    },
+
+    /// GPIO 3 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 TX`    | [crate::Gp3Spi0Tx]          |
+    /// | `UART0 RTS`  | [crate::Gp3Uart0Rts]        |
+    /// | `I2C1 SCL`   | [crate::Gp3I2C1Scl]         |
+    /// | `PWM1 B`     | [crate::Gp3Pwm1B]           |
+    /// | `PIO0`       | [crate::Gp3Pio0]            |
+    /// | `PIO1`       | [crate::Gp3Pio1]            |
+    Gpio3 {
+        name: gpio3,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio3].
+            FunctionUart: Gp3Uart0Rts,
+            /// SPI Function alias for pin [crate::Pins::gpio3].
+            FunctionSpi: Gp3Spi0Tx,
+            /// I2C Function alias for pin [crate::Pins::gpio3].
+            FunctionI2C: Gp3I2C1Scl,
+            /// PWM Function alias for pin [crate::Pins::gpio3].
+            FunctionPwm: Gp3Pwm1B,
+            /// PIO0 Function alias for pin [crate::Pins::gpio3].
+            FunctionPio0: Gp3Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio3].
+            FunctionPio1: Gp3Pio1
+        }
+    },
+
+    /// GPIO 4 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 RX`    | [crate::Gp4Spi0Rx]          |
+    /// | `UART1 TX`   | [crate::Gp4Uart1Tx]         |
+    /// | `I2C0 SDA`   | [crate::Gp4I2C0Sda]         |
+    /// | `PWM2 A`     | [crate::Gp4Pwm2A]           |
+    /// | `PIO0`       | [crate::Gp4Pio0]            |
+    /// | `PIO1`       | [crate::Gp4Pio1]            |
+    Gpio4 {
+        name: gpio4,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio4].
+            FunctionUart: Gp4Uart1Tx,
+            /// SPI Function alias for pin [crate::Pins::gpio4].
+            FunctionSpi: Gp4Spi0Rx,
+            /// I2C Function alias for pin [crate::Pins::gpio4].
+            FunctionI2C: Gp4I2C0Sda,
+            /// PWM Function alias for pin [crate::Pins::gpio4].
+            FunctionPwm: Gp4Pwm2A,
+            /// PIO0 Function alias for pin [crate::Pins::gpio4].
+            FunctionPio0: Gp4Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio4].
+            FunctionPio1: Gp4Pio1
+        }
+    },
+
+    /// GPIO 5 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 CSn`   | [crate::Gp5Spi0Csn]         |
+    /// | `UART1 RX`   | [crate::Gp5Uart1Rx]         |
+    /// | `I2C0 SCL`   | [crate::Gp5I2C0Scl]         |
+    /// | `PWM2 B`     | [crate::Gp5Pwm2B]           |
+    /// | `PIO0`       | [crate::Gp5Pio0]            |
+    /// | `PIO1`       | [crate::Gp5Pio1]            |
+    Gpio5 {
+        name: gpio5,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio5].
+            FunctionUart: Gp5Uart1Rx,
+            /// SPI Function alias for pin [crate::Pins::gpio5].
+            FunctionSpi: Gp5Spi0Csn,
+            /// I2C Function alias for pin [crate::Pins::gpio5].
+            FunctionI2C: Gp5I2C0Scl,
+            /// PWM Function alias for pin [crate::Pins::gpio5].
+            FunctionPwm: Gp5Pwm2B,
+            /// PIO0 Function alias for pin [crate::Pins::gpio5].
+            FunctionPio0: Gp5Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio5].
+            FunctionPio1: Gp5Pio1
+        }
+    },
+
+    /// GPIO 6 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 SCK`   | [crate::Gp6Spi0Sck]         |
+    /// | `UART1 CTS`  | [crate::Gp6Uart1Cts]        |
+    /// | `I2C1 SDA`   | [crate::Gp6I2C1Sda]         |
+    /// | `PWM3 A`     | [crate::Gp6Pwm3A]           |
+    /// | `PIO0`       | [crate::Gp6Pio0]            |
+    /// | `PIO1`       | [crate::Gp6Pio1]            |
+    Gpio6 {
+        name: gpio6,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio6].
+            FunctionUart: Gp6Uart1Cts,
+            /// SPI Function alias for pin [crate::Pins::gpio6].
+            FunctionSpi: Gp6Spi0Sck,
+            /// I2C Function alias for pin [crate::Pins::gpio6].
+            FunctionI2C: Gp6I2C1Sda,
+            /// PWM Function alias for pin [crate::Pins::gpio6].
+            FunctionPwm: Gp6Pwm3A,
+            /// PIO0 Function alias for pin [crate::Pins::gpio6].
+            FunctionPio0: Gp6Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio6].
+            FunctionPio1: Gp6Pio1
+        }
+    },
+
+    /// GPIO 7 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI0 TX`    | [crate::Gp7Spi0Tx]          |
+    /// | `UART1 RTS`  | [crate::Gp7Uart1Rts]        |
+    /// | `I2C1 SCL`   | [crate::Gp7I2C1Scl]         |
+    /// | `PWM3 B`     | [crate::Gp7Pwm3B]           |
+    /// | `PIO0`       | [crate::Gp7Pio0]            |
+    /// | `PIO1`       | [crate::Gp7Pio1]            |
+    Gpio7 {
+        name: led,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::led].
+            FunctionUart: Gp7Uart1Rts,
+            /// SPI Function alias for pin [crate::Pins::led].
+            FunctionSpi: Gp7Spi0Tx,
+            /// I2C Function alias for pin [crate::Pins::led].
+            FunctionI2C: Gp7I2C1Scl,
+            /// PWM Function alias for pin [crate::Pins::led].
+            FunctionPwm: Gp7Pwm3B,
+            /// PIO0 Function alias for pin [crate::Pins::led].
+            FunctionPio0: Gp7Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::led].
+            FunctionPio1: Gp7Pio1
+        }
+    },
+
+    /// GPIO 8 is connected to keyboard column 9
+    Gpio8 {
+        name: kb_c9
+    },
+
+    /// GPIO 9 is connected to keyboard column 8
+    Gpio9 {
+        name: kb_c8
+    },
+
+    /// GPIO 10 is connected to keyboard :
+    Gpio10 {
+        name: kb_c7,
+    },
+
+    /// GPIO 11 is connected to keyboard :
+    Gpio11 {
+        name: kb_c6,
+    },
+
+    /// GPIO 12 is connected to keyboard :
+    Gpio12 {
+        name: kb_c5,
+    },
+
+    /// GPIO 13 is connected to keyboard :
+    Gpio13 {
+        name: kb_c4,
+    },
+
+    /// GPIO 14 is connected to keyboard :
+    Gpio14 {
+        name: kb_c3,
+    },
+
+    /// GPIO 15 is connected to keyboard :
+    Gpio15 {
+        name: kb_c2,
+    },
+
+    /// GPIO 16 is connected to keyboard :
+    Gpio16 {
+        name: kb_c1,
+    },
+
+    /// GPIO 17 is connected to keyboard :
+    Gpio17 {
+        name: kb_r9,
+    },
+
+    /// GPIO 18 is connected to keyboard :
+    Gpio18 {
+        name: kb_r8,
+    },
+
+    /// GPIO 19 is connected to keyboard :
+    Gpio19 {
+        name: kb_r7,
+    },
+
+    /// GPIO 20 is connected to keyboard :
+    Gpio20 {
+        name: kb_r6,
+    },
+
+    /// GPIO 21 is connected to keyboard :
+    Gpio21 {
+        name: kb_r5,
+    },
+
+    /// GPIO 22 is connected to keyboard :
+    Gpio22 {
+        name: kb_r4,
+    },
+
+    /// GPIO 23 is connected to keyboard row 3
+    Gpio23 {
+        name: kb_r3,
+    },
+
+    /// GPIO 24 is connected to keyboard row 2
+    Gpio24 {
+        name: kb_r2,
+    },
+
+    /// GPIO 25 is connected to keyboard row 1
+    Gpio25 {
+        name: kb_r1,
+    },
+
+    /// GPIO 26 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI1 SCK`   | [crate::Gp26Spi1Sck]        |
+    /// | `UART1 CTS`  | [crate::Gp26Uart1Cts]       |
+    /// | `I2C1 SDA`   | [crate::Gp26I2C1Sda]        |
+    /// | `PWM5 A`     | [crate::Gp26Pwm5A]          |
+    /// | `PIO0`       | [crate::Gp26Pio0]           |
+    /// | `PIO1`       | [crate::Gp26Pio1]           |
+    Gpio26 {
+        name: gpio26,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio26].
+            FunctionUart: Gp26Uart1Cts,
+            /// SPI Function alias for pin [crate::Pins::gpio26].
+            FunctionSpi: Gp26Spi1Sck,
+            /// I2C Function alias for pin [crate::Pins::gpio26].
+            FunctionI2C: Gp26I2C1Sda,
+            /// PWM Function alias for pin [crate::Pins::gpio26].
+            FunctionPwm: Gp26Pwm5A,
+            /// PIO0 Function alias for pin [crate::Pins::gpio26].
+            FunctionPio0: Gp26Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio26].
+            FunctionPio1: Gp26Pio1
+        }
+    },
+
+    /// GPIO 27 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI1 TX`    | [crate::Gp27Spi1Tx]         |
+    /// | `UART1 RTS`  | [crate::Gp27Uart1Rts]       |
+    /// | `I2C1 SCL`   | [crate::Gp27I2C1Scl]        |
+    /// | `PWM5 B`     | [crate::Gp27Pwm5B]          |
+    /// | `PIO0`       | [crate::Gp27Pio0]           |
+    /// | `PIO1`       | [crate::Gp27Pio1]           |
+    Gpio27 {
+        name: gpio27,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio27].
+            FunctionUart: Gp27Uart1Rts,
+            /// SPI Function alias for pin [crate::Pins::gpio27].
+            FunctionSpi: Gp27Spi1Tx,
+            /// I2C Function alias for pin [crate::Pins::gpio27].
+            FunctionI2C: Gp27I2C1Scl,
+            /// PWM Function alias for pin [crate::Pins::gpio27].
+            FunctionPwm: Gp27Pwm5B,
+            /// PIO0 Function alias for pin [crate::Pins::gpio27].
+            FunctionPio0: Gp27Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio27].
+            FunctionPio1: Gp27Pio1
+        }
+    },
+
+    /// GPIO 28 supports following functions:
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI1 RX`    | [crate::Gp28Spi1Rx]         |
+    /// | `UART0 TX`   | [crate::Gp28Uart0Tx]        |
+    /// | `I2C0 SDA`   | [crate::Gp28I2C0Sda]        |
+    /// | `PWM6 A`     | [crate::Gp28Pwm6A]          |
+    /// | `PIO0`       | [crate::Gp28Pio0]           |
+    /// | `PIO1`       | [crate::Gp28Pio1]           |
+    Gpio28 {
+        name: gpio28,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio28].
+            FunctionUart: Gp28Uart0Tx,
+            /// SPI Function alias for pin [crate::Pins::gpio28].
+            FunctionSpi: Gp28Spi1Rx,
+            /// I2C Function alias for pin [crate::Pins::gpio28].
+            FunctionI2C: Gp28I2C0Sda,
+            /// PWM Function alias for pin [crate::Pins::gpio28].
+            FunctionPwm: Gp28Pwm6A,
+            /// PIO0 Function alias for pin [crate::Pins::gpio28].
+            FunctionPio0: Gp28Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio28].
+            FunctionPio1: Gp28Pio1
+        }
+    },
+
+    /// GPIO 29 supports following functions
+    ///
+    /// | Function     | Alias with applied function |
+    /// |--------------|-----------------------------|
+    /// | `SPI1 RX`    | [crate::Gp29Spi1Rx]         |
+    /// | `UART0 TX`   | [crate::Gp29Uart0Tx]        |
+    /// | `I2C0 SDA`   | [crate::Gp29I2C0Sda]        |
+    /// | `PWM6 A`     | [crate::Gp29Pwm6A]          |
+    /// | `PIO0`       | [crate::Gp29Pio0]           |
+    /// | `PIO1`       | [crate::Gp29Pio1]           |
+    Gpio29 {
+        name: gpio29,
+        aliases: {
+            /// UART Function alias for pin [crate::Pins::gpio29].
+            FunctionUart: Gp29Uart0Tx,
+            /// SPI Function alias for pin [crate::Pins::gpio29].
+            FunctionSpi: Gp29Spi1Rx,
+            /// I2C Function alias for pin [crate::Pins::gpio29].
+            FunctionI2C: Gp29I2C0Sda,
+            /// PWM Function alias for pin [crate::Pins::gpio29].
+            FunctionPwm: Gp29Pwm6A,
+            /// PIO0 Function alias for pin [crate::Pins::gpio29].
+            FunctionPio0: Gp29Pio0,
+            /// PIO1 Function alias for pin [crate::Pins::gpio29].
+            FunctionPio1: Gp29Pio1
+        }
+    },
+);
+
+pub const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;