mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-23 12:41:31 +11:00
Move board crates to separate repository rp-hal-boards
This commit is contained in:
parent
06a6e18862
commit
89d4083dca
2
.github/workflows/build_and_test.yml
vendored
2
.github/workflows/build_and_test.yml
vendored
|
@ -6,7 +6,7 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
features: ["", "--features eh1_0_alpha", "--features chrono", "--features rp2040-e5"]
|
||||
features: ["", "--features rp2040-e5"]
|
||||
mode: ["", "--release"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"rp2040-hal",
|
||||
"rp2040-hal-macros",
|
||||
"boards/adafruit-feather-rp2040",
|
||||
"boards/adafruit-itsy-bitsy-rp2040",
|
||||
"boards/adafruit-kb2040",
|
||||
|
@ -26,9 +24,6 @@ members = [
|
|||
"boards/waveshare-rp2040-lcd-0-96",
|
||||
]
|
||||
|
||||
[patch.'https://github.com/rp-rs/rp-hal.git']
|
||||
rp2040-hal = { path = "./rp2040-hal" }
|
||||
|
||||
[patch.crates-io]
|
||||
rp2040-hal = { path = "./rp2040-hal" }
|
||||
rp2040-hal = { git = "https://github.com/rp-rs/rp-hal", ref = "06a6e18862d05456cc541b8dd75ee2005a580a82" }
|
||||
i2c-pio = { git = "https://github.com/rp-rs/i2c-pio-rs" }
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7.0", optional = true }
|
||||
embedded-hal = { version = "0.2.4", features = ["unproven"] }
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7.0", optional = true }
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.3"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7.0", optional = true }
|
||||
embedded-hal = { version = "0.2.4", features = ["unproven"] }
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0"}
|
||||
rp2040-hal = { version = "0.6.0"}
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
fugit = "0.3.5"
|
||||
embedded-hal ="0.2.5"
|
||||
|
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
embedded-hal = { version = "0.2.4", features = ["unproven"] }
|
||||
st7789 = "0.6.1"
|
||||
|
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
|
||||
|
|
|
@ -13,11 +13,11 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0", features = [ "defmt" ] }
|
||||
rp2040-hal = { version = "0.6.0", features = [ "defmt" ] }
|
||||
panic-halt= "0.2.0"
|
||||
embedded-hal ="0.2.5"
|
||||
smart-leds = "0.3.0"
|
||||
|
|
|
@ -13,11 +13,11 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0", features = [ "defmt" ] }
|
||||
rp2040-hal = { version = "0.6.0", features = [ "defmt" ] }
|
||||
panic-halt= "0.2.0"
|
||||
defmt = "0.3.0"
|
||||
defmt-rtt = "0.4.0"
|
||||
|
|
|
@ -13,11 +13,11 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0", features = [ "defmt" ] }
|
||||
rp2040-hal = { version = "0.6.0", features = [ "defmt" ] }
|
||||
panic-halt= "0.2.0"
|
||||
embedded-hal ="0.2.5"
|
||||
fugit = "0.3.5"
|
||||
|
|
|
@ -13,13 +13,13 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
fugit = "0.3.5"
|
||||
usb-device= "0.2.9"
|
||||
|
||||
[dev-dependencies]
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0", features = [ "defmt" ] }
|
||||
rp2040-hal = { version = "0.6.0", features = [ "defmt" ] }
|
||||
panic-halt= "0.2.0"
|
||||
embedded-hal ="0.2.5"
|
||||
cortex-m-rtic = "1.1.2"
|
||||
|
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7.0", optional = true }
|
||||
embedded-hal = { version = "0.2.4", features = ["unproven"] }
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
|
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7.0", optional = true }
|
||||
embedded-hal = { version = "0.2.4", features = ["unproven"] }
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
fugit = "0.3.5"
|
||||
usb-device= "0.2.9"
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal.git"
|
|||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-boot2 = { version = "0.2.0", optional = true }
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.6.0" }
|
||||
rp2040-hal = { version = "0.6.0" }
|
||||
cortex-m-rt = { version = "0.7", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
[package]
|
||||
description = "Macros used by rp2040-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
name = "rp2040-hal-macros"
|
||||
readme = "README.md"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
features = ["extra-traits", "full"]
|
||||
version = "1.0"
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
# `rp2040-hal-macros`
|
||||
|
||||
Macros used by rp2040-hal.
|
||||
|
||||
## Entry macro
|
||||
|
||||
Extension of the `cortex-m-rt` `#[entry]` with rp2040 specific initialization code.
|
||||
|
||||
Currently, it just unlocks all spinlocks before calling the entry function.
|
||||
|
||||
# License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 (`APACHE2.0` or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
- MIT license (`MIT` or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
## Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{parse, parse_macro_input, Item, ItemFn, Stmt};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut f = parse_macro_input!(input as ItemFn);
|
||||
|
||||
if !args.is_empty() {
|
||||
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let clear_locks: TokenStream = quote!(unsafe {
|
||||
const SIO_BASE: u32 = 0xd0000000;
|
||||
const SPINLOCK0_PTR: *mut u32 = (SIO_BASE + 0x100) as *mut u32;
|
||||
const SPINLOCK_COUNT: usize = 32;
|
||||
for i in 0..SPINLOCK_COUNT {
|
||||
SPINLOCK0_PTR.wrapping_add(i).write_volatile(1);
|
||||
}
|
||||
})
|
||||
.into();
|
||||
let clear_locks = parse_macro_input!(clear_locks as Stmt);
|
||||
|
||||
// statics must stay first so cortex_m_rt::entry still finds them
|
||||
let stmts = insert_after_static(f.block.stmts, clear_locks);
|
||||
f.block.stmts = stmts;
|
||||
|
||||
quote!(
|
||||
#[::cortex_m_rt::entry]
|
||||
#f
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Insert new statements after initial block of statics
|
||||
fn insert_after_static(stmts: impl IntoIterator<Item = Stmt>, insert: Stmt) -> Vec<Stmt> {
|
||||
let mut istmts = stmts.into_iter();
|
||||
let mut stmts = vec![];
|
||||
for stmt in istmts.by_ref() {
|
||||
match stmt {
|
||||
Stmt::Item(Item::Static(var)) => {
|
||||
stmts.push(Stmt::Item(Item::Static(var)));
|
||||
}
|
||||
_ => {
|
||||
stmts.push(insert);
|
||||
stmts.push(stmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stmts.extend(istmts);
|
||||
|
||||
stmts
|
||||
}
|
11
rp2040-hal/.gitignore
vendored
11
rp2040-hal/.gitignore
vendored
|
@ -1,11 +0,0 @@
|
|||
# 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
|
|
@ -1,211 +0,0 @@
|
|||
# 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]
|
||||
|
||||
### Changed
|
||||
|
||||
- Avoid losing USB status events by reading ints rather than sie_status in poll - @ithinuel
|
||||
- Allow setting clock divisors on running state machines - @jannic
|
||||
- Remove unnecessary `mut` from `static mut LOCK_OWNER: AtomicU8` in critical section impl - @zachs18
|
||||
- Update dependency on rp2040-pac to 0.4.0 - @jannic
|
||||
- Update embedded-hal alpha support to version 1.0.0-alpha.9 - @jannic
|
||||
(Non-blocking traits were moved to embedded-hal-nb, which is not yet supported)
|
||||
- Implement UartConfig::new constructor method - @jannic
|
||||
- Deprecate uart::common_configs - @jannic
|
||||
- Fix spelling error in UART error discarded field - @Sizurka
|
||||
- Fix contents of UART error discarded field - @Sizurka
|
||||
- Fix watchdog counter load value - @Sizurka
|
||||
- Fix concurrent accesses to sm_execctrl and sm_instr when sideset isn't optional - @ithinuel
|
||||
- pio: Move interrupt related (en|dis)abling/forcing methods to the statemachine - @ithinuel
|
||||
- Mark Timer & Alarm* Send and Sync - @ithinuel
|
||||
- The feature critical-section-impl is now enabled by default from the BSP crates - @jannic
|
||||
- Simplify signature of Alarm::schedule, removing generic parameter - @ithinuel
|
||||
- USB: Use the dedicated write_bitmask_* functions - @ithinuel
|
||||
|
||||
### Added
|
||||
|
||||
- Add docs.rs metadata - @icedrocket
|
||||
- Implement embedded-hal aplha SPI traits - @ptpaterson
|
||||
- Add derive(Debug) and derive(defmt::Format) to error types - @9names
|
||||
- Add ability to modify installed PIO program wrap bounds - @davidcole1340
|
||||
- Add rtic-monotonic support for timer & alarms (feature gated) - @ithinuel
|
||||
- Add SPI is_busy function - @papyDoctor
|
||||
- Add set_fifos/set_rx_watermark/set_tx_watermark - @papyDoctor
|
||||
- Add a method to allow setting the PIO's clock divisor without floats - @ithinuel
|
||||
- Use TimerInstant in Timer::GetCounter & add Alarm::schedule_at - @ithinuel
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed support for critical-section 0.2 (was already deprecated) - @jannic
|
||||
|
||||
## [0.6.0] - 2022-08-26
|
||||
|
||||
### Added
|
||||
|
||||
- Documentation Example for the bsp_pin! macro - @ hmvp
|
||||
- Timer: Documentation & doc examples for Timers - @9names
|
||||
- Add suspend, resume and remote wakeup support. - @ithinuel & @jannic
|
||||
- `rp2040-e5` feature enabling the workaround for errata 5 on the USB device peripheral. - @ithinuel
|
||||
- NonPwmPinMode for gpio::Disabled - @FlorianUekermann
|
||||
- RAM-based interrupt vector tables - @9names
|
||||
- Support for critical-section 1.0.0.
|
||||
Critical-section 0.2 is still supported (ie. a custom-impl is provided, compatible
|
||||
with the 1.0.0 implementation), to avoid a breaking change. It will be removed
|
||||
later. - @jannic
|
||||
- Add support for the Interpolator. @fenax
|
||||
|
||||
### Changed
|
||||
|
||||
- Update dev dependencies on cortex-m-rtic to 1.1.2 - @jannic
|
||||
- Use correct interrupt names in `timer::alarms` - @hmvp
|
||||
- Update embedded-hal alpha support to version 1.0.0-alpha.8 - @jannic
|
||||
- Fix PIO rx fifo status - @jannic
|
||||
- Implement `From<&SomeClock> for Hertz` instead of `From<SomeClock> for Hertz`
|
||||
for the clocks in `rp2040_hal::clocks`. - @jannic
|
||||
- Fix i2c example using the wrong clock. - @jannic
|
||||
- Fix duty cycle handing on disabled pwm channels. - @jannic
|
||||
- GPIO IRQ example: add check for interrupt source - @9names
|
||||
- Align USB synchronisation requirements with the manual & pico-sdk - @ithinuel
|
||||
- Update dependencies on usb-device to 0.2.9 - @ithinuel
|
||||
- Use wfi in otherwise empty infinite loops in examples. - @jannic
|
||||
- Use generic bootloader in examples - @jannic & @ithinuel
|
||||
- Use `rp2040-hal`'s entry function. - @ithinuel
|
||||
- Migrate from `embedded-time` to `fugit` - @ithinuel
|
||||
- Fix PIO's `set_pins` and `set_pindirs` when `out_sticky` is set. - @jannic & @ithinuel
|
||||
- Clarify usage of boot2 section - @a-gavin
|
||||
|
||||
### Removed
|
||||
|
||||
- Unnecessary cortex_m::interrupt::free in timer.rs - @jannic
|
||||
- Unused embassy-traits deps - @9names
|
||||
|
||||
## [0.5.0] - 2022-06-13
|
||||
|
||||
### MSRV
|
||||
|
||||
The Minimum-Supported Rust Version (MSRV) for this release is 1.61
|
||||
|
||||
### Added
|
||||
|
||||
- RP2040 specific #[entry] macro that releases spinlocks - @jannic
|
||||
- Start multiple state machines in sync with each other - @astraw
|
||||
- Unsafe fn for freeing all spinlocks when you can't use the RP2040 entry macro (eg RTIC) - @9names
|
||||
- Optional feature for enabling defmt formatting for i2c errors - @ithinuel
|
||||
- Accessor for getting the offset of an installed PIO program - @fenax
|
||||
|
||||
### Changed
|
||||
|
||||
- Use thread send safe UART* marker when splitting, improves UART ergonmics - @marius-meissner
|
||||
- Improve performance for hardware division instrinsics. Internal intrinsics cleanup - @Sizurka
|
||||
- Provide a better alarm abstraction - @ithinuel
|
||||
- Update Multicore::spawn to be able to take a closure without requiring alloc.
|
||||
Improve Multicore ergonomics and add example for how to use new API - @Liamolucko
|
||||
- Allow PIO program to be 32 instructions long, was previously limited to 31 - @jannic
|
||||
- Fix Typos - @mqy, @danbev
|
||||
- Replace generic pio::Tx::write<T> with write_u8_replicated, write_u16_replicated, and update
|
||||
write to take a u32. The generic version was too easy to misuse. - @9names
|
||||
|
||||
### Removed
|
||||
|
||||
- I2c async driver. Use new one at https://github.com/ithinuel/rp2040-async-i2c/ - @ithinuel
|
||||
- Unused fields from UartPeripheral and Reader - @jannic
|
||||
|
||||
## [0.4.0] - 2022-03-09
|
||||
|
||||
### Added
|
||||
|
||||
- ROM function caching
|
||||
- ROM version lookup function
|
||||
- Compiler intrinsics for ROM functions
|
||||
- Compiler intrinsics for hardware divider
|
||||
- Document bsp_pins! macro
|
||||
- UART IRQ examples
|
||||
- PIO side-set example
|
||||
- Stopped PIO state machines can change their clock divider
|
||||
- Added HAL IRQ example
|
||||
|
||||
### Changed
|
||||
|
||||
- Rewrite UART driver to own its pins
|
||||
- Improve UART defaults
|
||||
- Fix repeated-read in i2c embassy driver
|
||||
- Fix bug in i2c peripheral state machine
|
||||
- Fix race condition in alarm
|
||||
- Fix safety bugs in hardware divider
|
||||
- Enable watchdog reset trigger bits when watchdog enabled
|
||||
- Update spinlocks to use new PAC API
|
||||
- Use generics to improve spinlock implementation
|
||||
- Update critical_section to use new spinlock implementation
|
||||
- Update embedded-hal alpha support to version 1.0.0-alpha.7
|
||||
- Avoid 64-bit division in clock calculations
|
||||
- Update pio and pio-proc to 0.2.0
|
||||
|
||||
## [0.3.0] - 2021-12-19
|
||||
|
||||
### MSRV
|
||||
|
||||
The Minimum-Supported Rust Version (MSRV) for this release is 1.54.
|
||||
|
||||
### Added
|
||||
|
||||
- A README!
|
||||
- Implementation of the `critical-section` API
|
||||
- Embedded HAL 1.0-alpha6 support
|
||||
- I²C Controller and Peripheral support
|
||||
- Multi-core support, including spin-locks and SIO FIFO
|
||||
- RTC support
|
||||
- USB Device support
|
||||
- Timer support
|
||||
- PIO support
|
||||
- Implementation of `rng_core::RngCore` for `RingOscillator`
|
||||
- ADC example
|
||||
- GPIO Interrupt support
|
||||
- Multi-core FIFO example
|
||||
- PIO LED Blinky example
|
||||
- ROM Functions example
|
||||
- SPI example
|
||||
- Watchdog example
|
||||
- ADC documentation
|
||||
- Lots of bug fixes :)
|
||||
|
||||
### Changed
|
||||
|
||||
- Modified PIO API for better ergonomics
|
||||
- Updated PAC to 0.2.0
|
||||
- Exported common driver structs from top-level (e.g. it's now `Sio`, not `sio::Sio`)
|
||||
|
||||
## [0.2.0] - 2021-08-14
|
||||
|
||||
### Added
|
||||
|
||||
- Updated version with support for:
|
||||
- Ring Oscillator
|
||||
- Crystal Oscillator
|
||||
- Plls
|
||||
- Watchdog
|
||||
- Clocks
|
||||
- GPIO
|
||||
- PWM
|
||||
- ADC
|
||||
- SPI
|
||||
- I²C
|
||||
- Resets
|
||||
- UART
|
||||
- Hardware divide/modulo
|
||||
|
||||
## [0.1.0] - 2021-01-21
|
||||
|
||||
- Initial release
|
||||
|
||||
[Unreleased]: https://github.com/rp-rs/rp-hal/compare/v0.6.0...HEAD
|
||||
[0.6.0]: https://github.com/rp-rs/rp-hal/compare/v0.5.0...v0.6.0
|
||||
[0.5.0]: https://github.com/rp-rs/rp-hal/compare/v0.4.0...v0.5.0
|
||||
[0.4.0]: https://github.com/rp-rs/rp-hal/compare/v0.3.0...v0.4.0
|
||||
[0.3.0]: https://github.com/rp-rs/rp-hal/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/rp-rs/rp-hal/compare/v0.1.0...v0.2.0
|
||||
[0.1.0]: https://github.com/rp-rs/rp-hal/releases/tag/v0.1.0
|
|
@ -1,76 +0,0 @@
|
|||
[package]
|
||||
name = "rp2040-hal"
|
||||
version = "0.6.0"
|
||||
authors = ["The rp-rs Developers"]
|
||||
edition = "2018"
|
||||
homepage = "https://github.com/rp-rs/rp-hal"
|
||||
description = "A Rust Embeded-HAL impl for the rp2040 microcontroller"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["rt", "rom-v2-intrinsics", "defmt", "rtic-monotonic"]
|
||||
targets = ["thumbv6m-none-eabi"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
cortex-m-rt = ">=0.6.15,<0.8"
|
||||
embedded-hal = { version = "0.2.5", features = ["unproven"] }
|
||||
eh1_0_alpha = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true }
|
||||
fugit = "0.3.6"
|
||||
itertools = { version = "0.10.1", default-features = false }
|
||||
nb = "1.0"
|
||||
rp2040-pac = "0.4.0"
|
||||
paste = "1.0"
|
||||
pio = "0.2.0"
|
||||
rp2040-hal-macros = { version = "0.1.0", path = "../rp2040-hal-macros" }
|
||||
usb-device = "0.2.9"
|
||||
vcell = "0.1"
|
||||
void = { version = "1.0.2", default-features = false }
|
||||
rand_core = "0.6.3"
|
||||
critical-section = { version = "1.0.0" }
|
||||
|
||||
chrono = { version = "0.4", default-features = false, optional = true }
|
||||
|
||||
defmt = { version = ">=0.2.0, <0.4", optional = true }
|
||||
|
||||
rtic-monotonic = { version = "1.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cortex-m-rt = "0.7"
|
||||
panic-halt = "0.2.0"
|
||||
rp2040-boot2 = "0.2.1"
|
||||
hd44780-driver = "0.4.0"
|
||||
pio-proc = "0.2.0"
|
||||
dht-sensor = "0.2.1"
|
||||
|
||||
[features]
|
||||
# Minimal startup / runtime for Cortex-M microcontrollers
|
||||
rt = ["rp2040-pac/rt"]
|
||||
|
||||
# Memoize(cache) ROM function pointers on first use to improve performance
|
||||
rom-func-cache = []
|
||||
|
||||
# Disable automatic mapping of language features (like floating point math) to ROM functions
|
||||
disable-intrinsics = []
|
||||
|
||||
# This enables ROM functions for f64 math that were not present in the earliest RP2040s
|
||||
rom-v2-intrinsics = []
|
||||
|
||||
# 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 = []
|
||||
|
||||
# critical section that is safe for multicore use
|
||||
critical-section-impl = ["critical-section/restore-state-u8"]
|
||||
|
||||
[[example]]
|
||||
# irq example uses cortex-m-rt::interrupt, need rt feature for that
|
||||
name = "gpio_irq_example"
|
||||
required-features = ["rt", "critical-section-impl"]
|
||||
|
||||
[[example]]
|
||||
# vector_table example uses cortex-m-rt::interrupt, need rt feature for that
|
||||
name = "vector_table"
|
||||
required-features = ["rt", "critical-section-impl"]
|
|
@ -1,140 +0,0 @@
|
|||
<!-- PROJECT LOGO -->
|
||||
<br />
|
||||
<p align="center">
|
||||
<a href="https://github.com/rp-rs/rp-hal">
|
||||
<img src="https://www.svgrepo.com/show/281119/microchip.svg" alt="Logo" width="140" height="140">
|
||||
</a>
|
||||
|
||||
<h3 align="center">rp-hal</h3>
|
||||
|
||||
<p align="center">
|
||||
High-level Rust drivers for the Raspberry Silicon RP2040 Microcontroller
|
||||
<br />
|
||||
<a href="https://docs.rs/rp2040-hal"><strong>Explore the API docs »</strong></a>
|
||||
<br />
|
||||
<br />
|
||||
<a href="https://github.com/rp-rs/rp-hal/tree/main/boards/pico/examples">View Demos</a>
|
||||
·
|
||||
<a href="https://github.com/rp-rs/rp-hal/issues">Report a Bug</a>
|
||||
·
|
||||
<a href="https://matrix.to/#/#rp-rs:matrix.org">Chat on Matrix</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<!-- TABLE OF CONTENTS -->
|
||||
<details open="open">
|
||||
<summary><h2 style="display: inline-block">Table of Contents</h2></summary>
|
||||
<ol>
|
||||
<li><a href="#introduction">Introduction</a></li>
|
||||
<li>
|
||||
<a href="#getting-started">Getting Started</a>
|
||||
<ul>
|
||||
<li><a href="#prerequisites">Prerequisites</a></li>
|
||||
<li><a href="#installation">Installation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#roadmap">Roadmap</a></li>
|
||||
<li><a href="#contributing">Contributing</a></li>
|
||||
<li><a href="#license">License</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
<li><a href="#acknowledgements">Acknowledgements</a></li>
|
||||
</ol>
|
||||
</details>
|
||||
|
||||
<!-- INTRODUCTION -->
|
||||
## Introduction
|
||||
|
||||
This is the `rp2040-hal` package - a library crate of high-level Rust drivers
|
||||
for the Raspberry Silicon RP2040 microcontroller, along with a collection of
|
||||
non-board specific example programs for you to study. You should use this crate
|
||||
in your application if you want to write code for the RP2040 microcontroller.
|
||||
The *HAL* in the name standards for *Hardware Abstraction Layer*, and comes from
|
||||
the fact that many of the drivers included implement the generic
|
||||
hardware-abstraction interfaces defined in the Rust Embedded Working Group's
|
||||
[embedded-hal](https://github.com/rust-embedded/embedded-hal) crate.
|
||||
|
||||
We also provide a series of *Board Support Package* (BSP) crates, which take
|
||||
this HAL crate and pre-configure the pins according to a specific PCB design. If
|
||||
you are using one of the supported boards, you should use one of those crates in
|
||||
preference, and return here to see documentation about specific peripherals on
|
||||
the RP2040 and how to use them. See the `boards` folder in
|
||||
https://github.com/rp-rs/rp-hal/ for more details.
|
||||
|
||||
<!-- GETTING STARTED -->
|
||||
## Getting Started
|
||||
|
||||
To include this crate in your project, amend your `Cargo.toml` file to include
|
||||
|
||||
```toml
|
||||
rp2040-hal = "0.6.0"
|
||||
```
|
||||
|
||||
To obtain a copy of the source code (e.g. if you want to propose a bug-fix or
|
||||
new feature, or simply to study the code), run:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/rp-rs/rp-hal.git
|
||||
```
|
||||
|
||||
For details on how to program an RP2040 microcontroller, see the [top-level
|
||||
rp-hal README](https://github.com/rp-rs/rp-hal/).
|
||||
|
||||
<!-- ROADMAP -->
|
||||
## Roadmap
|
||||
|
||||
NOTE This HAL is under active development. As such, it is likely to remain
|
||||
volatile until a 1.0.0 release.
|
||||
|
||||
See the [open issues](https://github.com/rp-rs/rp-hal/issues) for a list of
|
||||
proposed features (and known issues).
|
||||
|
||||
### Support for embedded-hal 1.0
|
||||
|
||||
We plan to support embedded-hal 1.0 soon after it is released.
|
||||
|
||||
For now, there is preliminary support for alpha versions of embedded-hal, which can
|
||||
be enabled with the feature `eh1_0_alpha`. Please note that this support does not
|
||||
provide any semver compatibility guarantees: With that feature activated, there
|
||||
will be breaking changes even in minor versions of rp2040-hal.
|
||||
|
||||
Support for embedded-hal 1.0(-alpha) exists in parallel to support for
|
||||
embedded-hal 0.2: Traits of both versions are implemented and can be used
|
||||
at the same time.
|
||||
The new blocking [SPI traits](https://docs.rs/embedded-hal/1.0.0-alpha.8/embedded_hal/spi/blocking/index.html)
|
||||
are not yet implemented.
|
||||
|
||||
<!-- CONTRIBUTING -->
|
||||
## 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**.
|
||||
|
||||
1. Fork the Project
|
||||
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
|
||||
|
||||
<!-- LICENSE -->
|
||||
## License
|
||||
|
||||
Distributed under the MIT OR Apache2.0 License. See `MIT` or `APACHE2.0` for more information.
|
||||
|
||||
|
||||
|
||||
<!-- CONTACT -->
|
||||
## Contact
|
||||
|
||||
Project Link: [https://github.com/rp-rs/rp-hal/issues](https://github.com/rp-rs/rp-hal/issues)
|
||||
Matrix: [#rp-rs:matrix.org](https://matrix.to/#/#rp-rs:matrix.org)
|
||||
|
||||
|
||||
<!-- ACKNOWLEDGEMENTS -->
|
||||
## Acknowledgements
|
||||
|
||||
* [Othneil Drew's README template](https://github.com/othneildrew)
|
|
@ -1,128 +0,0 @@
|
|||
//! # ADC Example
|
||||
//!
|
||||
//! This application demonstrates how to read ADC samples from the temperature
|
||||
//! sensor and pin and output them to the UART on pins 1 and 2 at 9600 baud.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// Alias for our HAL crate
|
||||
use rp2040_hal as hal;
|
||||
|
||||
// Some traits we need
|
||||
use core::fmt::Write;
|
||||
use embedded_hal::adc::OneShot;
|
||||
use fugit::RateExtU32;
|
||||
use rp2040_hal::Clock;
|
||||
|
||||
// UART related types
|
||||
use hal::uart::{DataBits, StopBits, UartConfig};
|
||||
|
||||
// A shorter alias for the Peripheral Access Crate, which provides low-level
|
||||
// register access
|
||||
use hal::pac;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 prints the temperature
|
||||
/// in an infinite loop.
|
||||
#[rp2040_hal::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(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1)
|
||||
let uart_pins = (
|
||||
pins.gpio0.into_mode::<hal::gpio::FunctionUart>(),
|
||||
pins.gpio1.into_mode::<hal::gpio::FunctionUart>(),
|
||||
);
|
||||
|
||||
// Create a UART driver
|
||||
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();
|
||||
|
||||
// Write to the UART
|
||||
uart.write_full_blocking(b"ADC example\r\n");
|
||||
|
||||
// Enable ADC
|
||||
let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS);
|
||||
|
||||
// Enable the temperature sense channel
|
||||
let mut temperature_sensor = adc.enable_temp_sensor();
|
||||
|
||||
// Configure GPIO26 as an ADC input
|
||||
let mut adc_pin_0 = pins.gpio26.into_floating_input();
|
||||
loop {
|
||||
// Read the raw ADC counts from the temperature sensor channel.
|
||||
let temp_sens_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap();
|
||||
let pin_adc_counts: u16 = adc.read(&mut adc_pin_0).unwrap();
|
||||
writeln!(
|
||||
uart,
|
||||
"ADC readings: Temperature: {:02} Pin: {:02}\r\n",
|
||||
temp_sens_adc_counts, pin_adc_counts
|
||||
)
|
||||
.unwrap();
|
||||
delay.delay_ms(1000);
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,92 +0,0 @@
|
|||
//! # GPIO 'Blinky' Example
|
||||
//!
|
||||
//! This application demonstrates how to control a GPIO pin on the RP2040.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use rp2040_hal::clocks::Clock;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 toggles a GPIO pin in
|
||||
/// an infinite loop. If there is an LED connected to that pin, it will blink.
|
||||
#[rp2040_hal::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(
|
||||
XTAL_FREQ_HZ,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
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 = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// Configure GPIO25 as an output
|
||||
let mut led_pin = pins.gpio25.into_push_pull_output();
|
||||
loop {
|
||||
led_pin.set_high().unwrap();
|
||||
// TODO: Replace with proper 1s delays once we have clocks working
|
||||
delay.delay_ms(500);
|
||||
led_pin.set_low().unwrap();
|
||||
delay.delay_ms(500);
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,145 +0,0 @@
|
|||
//! # DHT11 Example
|
||||
//!
|
||||
//! This application demonstrates how to read a DHT11 sensor on the RP2040.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//! In this example, the DHT11 data pin should be connected to GPIO28.
|
||||
//!
|
||||
//! NOTE: The DHT11 driver only works reliably when compiled in release mode.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::InputPin;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use hal::gpio::dynpin::DynPin;
|
||||
use hal::Clock;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
use dht_sensor::{dht11, DhtReading};
|
||||
|
||||
/// A wrapper for DynPin, implementing both InputPin and OutputPin, to simulate
|
||||
/// an open-drain pin as needed by the wire protocol the DHT11 sensor speaks.
|
||||
/// https://how2electronics.com/interfacing-dht11-temperature-humidity-sensor-with-raspberry-pi-pico/
|
||||
struct InOutPin {
|
||||
inner: DynPin,
|
||||
}
|
||||
|
||||
impl InOutPin {
|
||||
fn new(inner: DynPin) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl InputPin for InOutPin {
|
||||
type Error = rp2040_hal::gpio::Error;
|
||||
fn is_high(&self) -> Result<bool, <Self as embedded_hal::digital::v2::InputPin>::Error> {
|
||||
self.inner.is_high()
|
||||
}
|
||||
fn is_low(&self) -> Result<bool, <Self as embedded_hal::digital::v2::InputPin>::Error> {
|
||||
self.inner.is_low()
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputPin for InOutPin {
|
||||
type Error = rp2040_hal::gpio::Error;
|
||||
fn set_low(&mut self) -> Result<(), <Self as embedded_hal::digital::v2::OutputPin>::Error> {
|
||||
// To actively pull the pin low, it must also be configured as a (readable) output pin
|
||||
self.inner.into_readable_output();
|
||||
// In theory, we should set the pin to low first, to make sure we never actively
|
||||
// pull it up. But if we try it on the input pin, we get Err(Gpio(InvalidPinType)).
|
||||
self.inner.set_low()?;
|
||||
Ok(())
|
||||
}
|
||||
fn set_high(&mut self) -> Result<(), <Self as embedded_hal::digital::v2::OutputPin>::Error> {
|
||||
// To set the open-drain pin to high, just disable the output driver by changing the
|
||||
// pin to input mode with pull-up. That way, the DHT11 can still pull the data line down
|
||||
// to send its response.
|
||||
self.inner.into_pull_up_input();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, assigns GPIO 28 to the
|
||||
/// DHT11 driver, and takes a single measurement.
|
||||
#[rp2040_hal::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(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
||||
|
||||
// Use GPIO 28 as an InOutPin
|
||||
let mut pin = InOutPin::new(pins.gpio28.into());
|
||||
pin.set_high().ok();
|
||||
|
||||
// Perform a sensor reading
|
||||
let _measurement = dht11::Reading::read(&mut delay, &mut pin);
|
||||
|
||||
// In this case, we just ignore the result. A real application
|
||||
// would do something with the measurement.
|
||||
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,94 +0,0 @@
|
|||
//! # GPIO 'Blinky' Example
|
||||
//!
|
||||
//! This application demonstrates how to control a GPIO pin on the RP2040.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::InputPin;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 toggles a GPIO pin in
|
||||
/// an infinite loop. If there is an LED connected to that pin, it will blink.
|
||||
#[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
|
||||
let _clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// Configure GPIO 25 as an output
|
||||
let mut out_pin = pins.gpio25.into_push_pull_output();
|
||||
|
||||
// Configure GPIO 23 as an input
|
||||
let in_pin = pins.gpio23.into_pull_down_input();
|
||||
|
||||
// Output is the opposite of the input
|
||||
loop {
|
||||
if in_pin.is_low().unwrap() {
|
||||
out_pin.set_high().unwrap();
|
||||
} else {
|
||||
out_pin.set_low().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,186 +0,0 @@
|
|||
//! # GPIO IRQ Example
|
||||
//!
|
||||
//! This application demonstrates use of GPIO Interrupts.
|
||||
//! It is also intended as a general introduction to interrupts with RP2040.
|
||||
//!
|
||||
//! Each GPIO can be triggered on the input being high (LevelHigh), being low (LevelLow)
|
||||
//! starting high and then going low (EdgeLow) or starting low and becoming high (EdgeHigh)
|
||||
//!
|
||||
//! In this example, we trigger on EdgeLow. Our input pin configured to be pulled to the high logic-level
|
||||
//! via an internal pullup resistor. This resistor is quite weak, so you can bring the logic level back to low
|
||||
//! via an external jumper wire or switch.
|
||||
//! Whenever we see the edge transition, we will toggle the output on GPIO25 - this is the LED pin on a Pico.
|
||||
//!
|
||||
//! Note that this demo does not perform any [software debouncing](https://en.wikipedia.org/wiki/Switch#Contact_bounce).
|
||||
//! You can fix that through hardware, or you could disable the button interrupt in the interrupt and re-enable it
|
||||
//! some time later using one of the Alarms of the Timer peripheral - this is left as an exercise for the reader.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::ToggleableOutputPin;
|
||||
|
||||
// Our interrupt macro
|
||||
use hal::pac::interrupt;
|
||||
|
||||
// Some short-cuts to useful types
|
||||
use core::cell::RefCell;
|
||||
use critical_section::Mutex;
|
||||
use rp2040_hal::gpio;
|
||||
|
||||
// The GPIO interrupt type we're going to generate
|
||||
use rp2040_hal::gpio::Interrupt::EdgeLow;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
// Pin types quickly become very long!
|
||||
// We'll create some type aliases using `type` to help with that
|
||||
|
||||
/// This pin will be our output - it will drive an LED if you run this on a Pico
|
||||
type LedPin = gpio::Pin<gpio::bank0::Gpio25, gpio::PushPullOutput>;
|
||||
|
||||
/// This pin will be our interrupt source.
|
||||
/// It will trigger an interrupt if pulled to ground (via a switch or jumper wire)
|
||||
type ButtonPin = gpio::Pin<gpio::bank0::Gpio26, gpio::PullUpInput>;
|
||||
|
||||
/// Since we're always accessing these pins together we'll store them in a tuple.
|
||||
/// Giving this tuple a type alias means we won't need to use () when putting them
|
||||
/// inside an Option. That will be easier to read.
|
||||
type LedAndButton = (LedPin, ButtonPin);
|
||||
|
||||
/// This how we transfer our Led and Button pins into the Interrupt Handler.
|
||||
/// We'll have the option hold both using the LedAndButton type.
|
||||
/// This will make it a bit easier to unpack them later.
|
||||
static GLOBAL_PINS: Mutex<RefCell<Option<LedAndButton>>> = Mutex::new(RefCell::new(None));
|
||||
|
||||
/// 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 toggles a GPIO pin in
|
||||
/// an infinite loop. If there is an LED connected to that pin, it will blink.
|
||||
#[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
|
||||
let _clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// Configure GPIO 25 as an output to drive our LED.
|
||||
// we can use into_mode() instead of into_pull_up_input()
|
||||
// since the variable we're pushing it into has that type
|
||||
let led = pins.gpio25.into_mode();
|
||||
|
||||
// Set up the GPIO pin that will be our input
|
||||
let in_pin = pins.gpio26.into_mode();
|
||||
|
||||
// Trigger on the 'falling edge' of the input pin.
|
||||
// This will happen as the button is being pressed
|
||||
in_pin.set_interrupt_enabled(EdgeLow, true);
|
||||
|
||||
// Give away our pins by moving them into the `GLOBAL_PINS` variable.
|
||||
// We won't need to access them in the main thread again
|
||||
critical_section::with(|cs| {
|
||||
GLOBAL_PINS.borrow(cs).replace(Some((led, in_pin)));
|
||||
});
|
||||
|
||||
// Unmask the IO_BANK0 IRQ so that the NVIC interrupt controller
|
||||
// will jump to the interrupt function when the interrupt occurs.
|
||||
// We do this last so that the interrupt can't go off while
|
||||
// it is in the middle of being configured
|
||||
unsafe {
|
||||
pac::NVIC::unmask(pac::Interrupt::IO_IRQ_BANK0);
|
||||
}
|
||||
|
||||
loop {
|
||||
// interrupts handle everything else in this example.
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn IO_IRQ_BANK0() {
|
||||
// The `#[interrupt]` attribute covertly converts this to `&'static mut Option<LedAndButton>`
|
||||
static mut LED_AND_BUTTON: Option<LedAndButton> = None;
|
||||
|
||||
// This is one-time lazy initialisation. We steal the variables given to us
|
||||
// via `GLOBAL_PINS`.
|
||||
if LED_AND_BUTTON.is_none() {
|
||||
critical_section::with(|cs| {
|
||||
*LED_AND_BUTTON = GLOBAL_PINS.borrow(cs).take();
|
||||
});
|
||||
}
|
||||
|
||||
// Need to check if our Option<LedAndButtonPins> contains our pins
|
||||
if let Some(gpios) = LED_AND_BUTTON {
|
||||
// borrow led and button by *destructuring* the tuple
|
||||
// these will be of type `&mut LedPin` and `&mut ButtonPin`, so we don't have
|
||||
// to move them back into the static after we use them
|
||||
let (led, button) = gpios;
|
||||
// Check if the interrupt source is from the pushbutton going from high-to-low.
|
||||
// Note: this will always be true in this example, as that is the only enabled GPIO interrupt source
|
||||
if button.interrupt_status(EdgeLow) {
|
||||
// toggle can't fail, but the embedded-hal traits always allow for it
|
||||
// we can discard the return value by assigning it to an unnamed variable
|
||||
let _ = led.toggle();
|
||||
|
||||
// Our interrupt doesn't clear itself.
|
||||
// Do that now so we don't immediately jump back to this interrupt handler.
|
||||
button.clear_interrupt(EdgeLow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,104 +0,0 @@
|
|||
//! # I²C Example
|
||||
//!
|
||||
//! This application demonstrates how to talk to I²C devices with an RP2040.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::blocking::i2c::Write;
|
||||
use fugit::RateExtU32;
|
||||
|
||||
// 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;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 performs a single I²C
|
||||
/// write to a fixed address.
|
||||
#[rp2040_hal::entry]
|
||||
fn main() -> ! {
|
||||
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
|
||||
let clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::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.gpio18.into_mode::<hal::gpio::FunctionI2C>();
|
||||
let scl_pin = pins.gpio19.into_mode::<hal::gpio::FunctionI2C>();
|
||||
// let not_an_scl_pin = pins.gpio20.into_mode::<hal::gpio::FunctionI2C>();
|
||||
|
||||
// Create the I²C drive, 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 mut i2c = hal::I2C::i2c1(
|
||||
pac.I2C1,
|
||||
sda_pin,
|
||||
scl_pin, // Try `not_an_scl_pin` here
|
||||
400.kHz(),
|
||||
&mut pac.RESETS,
|
||||
&clocks.system_clock,
|
||||
);
|
||||
|
||||
// Write three bytes to the I²C device with 7-bit address 0x2C
|
||||
i2c.write(0x2c, &[1, 2, 3]).unwrap();
|
||||
|
||||
// Demo finish - just loop until reset
|
||||
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,119 +0,0 @@
|
|||
//! # 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.
|
||||
//!
|
||||
//! It drives the LCD by pushing data out of six GPIO pins. It may need to be
|
||||
//! adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// Alias for our HAL crate
|
||||
use rp2040_hal as hal;
|
||||
|
||||
// Our LCD driver
|
||||
use hd44780_driver as hd44780;
|
||||
|
||||
// Some traits we need
|
||||
use rp2040_hal::clocks::Clock;
|
||||
|
||||
// A shorter alias for the Peripheral Access Crate, which provides low-level
|
||||
// register access
|
||||
use hal::pac;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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, writes to the LCD, then goes
|
||||
/// to sleep.
|
||||
#[rp2040_hal::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(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// Create the LCD driver from some GPIO pins
|
||||
let mut lcd = hd44780::HD44780::new_4bit(
|
||||
pins.gpio16.into_push_pull_output(), // Register Select
|
||||
pins.gpio17.into_push_pull_output(), // Enable
|
||||
pins.gpio18.into_push_pull_output(), // d4
|
||||
pins.gpio19.into_push_pull_output(), // d5
|
||||
pins.gpio20.into_push_pull_output(), // d6
|
||||
pins.gpio21.into_push_pull_output(), // d7
|
||||
&mut delay,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// 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();
|
||||
|
||||
// Do nothing - we're finished
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,178 +0,0 @@
|
|||
//! # Multicore FIFO + GPIO 'Blinky' Example
|
||||
//!
|
||||
//! This application demonstrates FIFO communication between the CPU cores on the RP2040.
|
||||
//! Core 0 will calculate and send a delay value to Core 1, which will then wait that long
|
||||
//! before toggling the LED.
|
||||
//! Core 0 will wait for Core 1 to complete this task and send an acknowledgement value.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! See the `Cargo.toml` file for Copyright and license details.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use hal::clocks::Clock;
|
||||
use hal::multicore::{Multicore, Stack};
|
||||
use hal::sio::Sio;
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::ToggleableOutputPin;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// Value to indicate that Core 1 has completed its task
|
||||
const CORE1_TASK_COMPLETE: u32 = 0xEE;
|
||||
|
||||
/// Stack for core 1
|
||||
///
|
||||
/// Core 0 gets its stack via the normal route - any memory not used by static values is
|
||||
/// reserved for stack and initialised by cortex-m-rt.
|
||||
/// To get the same for Core 1, we would need to compile everything seperately and
|
||||
/// modify the linker file for both programs, and that's quite annoying.
|
||||
/// So instead, core1.spawn takes a [usize] which gets used for the stack.
|
||||
/// NOTE: We use the `Stack` struct here to ensure that it has 32-byte alignment, which allows
|
||||
/// the stack guard to take up the least amount of usable RAM.
|
||||
static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||
|
||||
fn core1_task(sys_freq: u32) -> ! {
|
||||
let mut pac = unsafe { pac::Peripherals::steal() };
|
||||
let core = unsafe { pac::CorePeripherals::steal() };
|
||||
|
||||
let mut sio = Sio::new(pac.SIO);
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
let mut led_pin = pins.gpio25.into_push_pull_output();
|
||||
let mut delay = cortex_m::delay::Delay::new(core.SYST, sys_freq);
|
||||
loop {
|
||||
let input = sio.fifo.read();
|
||||
if let Some(word) = input {
|
||||
delay.delay_ms(word);
|
||||
led_pin.toggle().unwrap();
|
||||
sio.fifo.write_blocking(CORE1_TASK_COMPLETE);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 toggles a GPIO pin in
|
||||
/// an infinite loop. If there is an LED connected to that pin, it will blink.
|
||||
#[rp2040_hal::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::Watchdog::new(pac.WATCHDOG);
|
||||
|
||||
// Configure the clocks
|
||||
let clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let sys_freq = clocks.system_clock.freq().to_Hz();
|
||||
|
||||
// The single-cycle I/O block controls our GPIO pins
|
||||
let mut sio = hal::sio::Sio::new(pac.SIO);
|
||||
|
||||
let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
|
||||
let cores = mc.cores();
|
||||
let core1 = &mut cores[1];
|
||||
let _test = core1.spawn(unsafe { &mut CORE1_STACK.mem }, move || {
|
||||
core1_task(sys_freq)
|
||||
});
|
||||
|
||||
/// How much we adjust the LED period every cycle
|
||||
const LED_PERIOD_INCREMENT: i32 = 2;
|
||||
|
||||
/// The minimum LED toggle interval we allow for.
|
||||
const LED_PERIOD_MIN: i32 = 0;
|
||||
|
||||
/// The maximum LED toggle interval period we allow for. Keep it reasonably short so it's easy to see.
|
||||
const LED_PERIOD_MAX: i32 = 100;
|
||||
|
||||
// Our current LED period. It starts at the shortest period, which is the highest blink frequency
|
||||
let mut led_period: i32 = LED_PERIOD_MIN;
|
||||
|
||||
// The direction we're incrementing our LED period.
|
||||
// Since we start at the minimum value, start by counting up
|
||||
let mut count_up = true;
|
||||
|
||||
loop {
|
||||
if count_up {
|
||||
// Increment our period
|
||||
led_period += LED_PERIOD_INCREMENT;
|
||||
|
||||
// Change direction of increment if we hit the limit
|
||||
if led_period > LED_PERIOD_MAX {
|
||||
led_period = LED_PERIOD_MAX;
|
||||
count_up = false;
|
||||
}
|
||||
} else {
|
||||
// Decrement our period
|
||||
led_period -= LED_PERIOD_INCREMENT;
|
||||
|
||||
// Change direction of increment if we hit the limit
|
||||
if led_period < LED_PERIOD_MIN {
|
||||
led_period = LED_PERIOD_MIN;
|
||||
count_up = true;
|
||||
}
|
||||
}
|
||||
|
||||
// It should not be possible for led_period to go negative, but let's ensure that.
|
||||
if led_period < 0 {
|
||||
led_period = 0;
|
||||
}
|
||||
|
||||
// Send the new delay time to Core 1. We convert it
|
||||
sio.fifo.write(led_period as u32);
|
||||
|
||||
// Sleep until Core 1 sends a message to tell us it is done
|
||||
let ack = sio.fifo.read_blocking();
|
||||
if ack != CORE1_TASK_COMPLETE {
|
||||
// In a real application you might want to handle the case
|
||||
// where the CPU sent the wrong message - we're going to
|
||||
// ignore it here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,132 +0,0 @@
|
|||
//! # Multicore Blinking Example
|
||||
//!
|
||||
//! This application blinks two LEDs on GPIOs 2 and 3 at different rates (3Hz
|
||||
//! and 4Hz respectively.)
|
||||
//!
|
||||
//! See the `Cargo.toml` file for Copyright and licence details.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use cortex_m::delay::Delay;
|
||||
|
||||
use hal::clocks::Clock;
|
||||
use hal::gpio::Pins;
|
||||
use hal::multicore::{Multicore, Stack};
|
||||
use hal::sio::Sio;
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::ToggleableOutputPin;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// The frequency at which core 0 will blink its LED (Hz).
|
||||
const CORE0_FREQ: u32 = 3;
|
||||
/// The frequency at which core 1 will blink its LED (Hz).
|
||||
const CORE1_FREQ: u32 = 4;
|
||||
/// The delay between each toggle of core 0's LED (us).
|
||||
const CORE0_DELAY: u32 = 1_000_000 / CORE0_FREQ;
|
||||
/// The delay between each toggle of core 1's LED (us).
|
||||
const CORE1_DELAY: u32 = 1_000_000 / CORE1_FREQ;
|
||||
|
||||
/// Stack for core 1
|
||||
///
|
||||
/// Core 0 gets its stack via the normal route - any memory not used by static
|
||||
/// values is reserved for stack and initialised by cortex-m-rt.
|
||||
/// To get the same for Core 1, we would need to compile everything seperately
|
||||
/// and modify the linker file for both programs, and that's quite annoying.
|
||||
/// So instead, core1.spawn takes a [usize] which gets used for the stack.
|
||||
/// NOTE: We use the `Stack` struct here to ensure that it has 32-byte
|
||||
/// alignment, which allows the stack guard to take up the least amount of
|
||||
/// usable RAM.
|
||||
static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||
|
||||
/// 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.
|
||||
#[rp2040_hal::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::Watchdog::new(pac.WATCHDOG);
|
||||
|
||||
// Configure the clocks
|
||||
let clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
// Set up the GPIO pins
|
||||
let mut sio = Sio::new(pac.SIO);
|
||||
let pins = Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
let mut led1 = pins.gpio2.into_push_pull_output();
|
||||
let mut led2 = pins.gpio3.into_push_pull_output();
|
||||
|
||||
// Set up the delay for the first core.
|
||||
let sys_freq = clocks.system_clock.freq().to_Hz();
|
||||
let mut delay = Delay::new(core.SYST, sys_freq);
|
||||
|
||||
// Start up the second core to blink the second LED
|
||||
let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
|
||||
let cores = mc.cores();
|
||||
let core1 = &mut cores[1];
|
||||
core1
|
||||
.spawn(unsafe { &mut CORE1_STACK.mem }, move || {
|
||||
// Get the second core's copy of the `CorePeripherals`, which are per-core.
|
||||
// Unfortunately, `cortex-m` doesn't support this properly right now,
|
||||
// so we have to use `steal`.
|
||||
let core = unsafe { pac::CorePeripherals::steal() };
|
||||
// Set up the delay for the second core.
|
||||
let mut delay = Delay::new(core.SYST, sys_freq);
|
||||
// Blink the second LED.
|
||||
loop {
|
||||
led2.toggle().unwrap();
|
||||
delay.delay_us(CORE1_DELAY)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Blink the first LED.
|
||||
loop {
|
||||
led1.toggle().unwrap();
|
||||
delay.delay_us(CORE0_DELAY)
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,79 +0,0 @@
|
|||
//! This example toggles the GPIO25 pin, using a PIO program.
|
||||
//!
|
||||
//! If a LED is connected to that pin, like on a Pico board, the LED should blink.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use hal::gpio::{FunctionPio0, Pin};
|
||||
use hal::pac;
|
||||
use hal::pio::PIOExt;
|
||||
use hal::Sio;
|
||||
use panic_halt as _;
|
||||
use rp2040_hal as hal;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// 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 blinks an LED using the PIO peripheral.
|
||||
#[rp2040_hal::entry]
|
||||
fn main() -> ! {
|
||||
let mut pac = pac::Peripherals::take().unwrap();
|
||||
|
||||
let sio = Sio::new(pac.SIO);
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// configure LED pin for Pio0.
|
||||
let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode();
|
||||
// PIN id for use inside of PIO
|
||||
let led_pin_id = 25;
|
||||
|
||||
// Define some simple PIO program.
|
||||
const MAX_DELAY: u8 = 31;
|
||||
let mut a = pio::Assembler::<32>::new();
|
||||
let mut wrap_target = a.label();
|
||||
let mut wrap_source = a.label();
|
||||
// Set pin as Out
|
||||
a.set(pio::SetDestination::PINDIRS, 1);
|
||||
// Define begin of program loop
|
||||
a.bind(&mut wrap_target);
|
||||
// Set pin low
|
||||
a.set_with_delay(pio::SetDestination::PINS, 0, MAX_DELAY);
|
||||
// Set pin high
|
||||
a.set_with_delay(pio::SetDestination::PINS, 1, MAX_DELAY);
|
||||
// Define end of program loop
|
||||
a.bind(&mut wrap_source);
|
||||
// The labels wrap_target and wrap_source, as set above,
|
||||
// define a loop which is executed repeatedly by the PIO
|
||||
// state machine.
|
||||
let program = a.assemble_with_wrap(wrap_source, wrap_target);
|
||||
|
||||
// Initialize and start PIO
|
||||
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
|
||||
let installed = pio.install(&program).unwrap();
|
||||
let (int, frac) = (0, 0); // as slow as possible (0 is interpreted as 65536)
|
||||
let (sm, _, _) = rp2040_hal::pio::PIOBuilder::from_program(installed)
|
||||
.set_pins(led_pin_id, 1)
|
||||
.clock_divisor_fixed_point(int, frac)
|
||||
.build(sm0);
|
||||
sm.start();
|
||||
|
||||
// PIO runs in background, independently from CPU
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
//! This example toggles the GPIO25 pin, using a PIO program compiled via pio_proc::pio!().
|
||||
//!
|
||||
//! If a LED is connected to that pin, like on a Pico board, the LED should blink.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use hal::gpio::{FunctionPio0, Pin};
|
||||
use hal::pac;
|
||||
use hal::pio::PIOExt;
|
||||
use hal::Sio;
|
||||
use panic_halt as _;
|
||||
use rp2040_hal as hal;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// 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.
|
||||
#[rp2040_hal::entry]
|
||||
fn main() -> ! {
|
||||
let mut pac = pac::Peripherals::take().unwrap();
|
||||
|
||||
let sio = Sio::new(pac.SIO);
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// configure LED pin for Pio0.
|
||||
let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode();
|
||||
// PIN id for use inside of PIO
|
||||
let led_pin_id = 25;
|
||||
|
||||
// Define some simple PIO program.
|
||||
let program = pio_proc::pio_asm!(
|
||||
".wrap_target",
|
||||
"set pins, 1 [31]",
|
||||
"set pins, 0 [31]",
|
||||
".wrap"
|
||||
);
|
||||
|
||||
// Initialize and start PIO
|
||||
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
|
||||
let installed = pio.install(&program.program).unwrap();
|
||||
let (int, frac) = (0, 0); // as slow as possible (0 is interpreted as 65536)
|
||||
let (mut sm, _, _) = rp2040_hal::pio::PIOBuilder::from_program(installed)
|
||||
.set_pins(led_pin_id, 1)
|
||||
.clock_divisor_fixed_point(int, frac)
|
||||
.build(sm0);
|
||||
// The GPIO pin needs to be configured as an output.
|
||||
sm.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]);
|
||||
sm.start();
|
||||
|
||||
// PIO runs in background, independently from CPU
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
//! This example toggles the GPIO25 pin, using a PIO program compiled via pio_proc::pio!().
|
||||
//!
|
||||
//! If a LED is connected to that pin, like on a Pico board, the LED should blink.
|
||||
//!
|
||||
//! This example makes use of side setting.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use hal::gpio::{FunctionPio0, Pin};
|
||||
use hal::pac;
|
||||
use hal::pio::PIOExt;
|
||||
use hal::Sio;
|
||||
use panic_halt as _;
|
||||
use rp2040_hal as hal;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// 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.
|
||||
#[rp2040_hal::entry]
|
||||
fn main() -> ! {
|
||||
let mut pac = pac::Peripherals::take().unwrap();
|
||||
|
||||
let sio = Sio::new(pac.SIO);
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// configure LED pin for Pio0.
|
||||
let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode();
|
||||
// PIN id for use inside of PIO
|
||||
let led_pin_id = 25;
|
||||
|
||||
// Define some simple PIO program.
|
||||
let program = pio_proc::pio_asm!(
|
||||
".side_set 1", // each instruction may set 1 bit
|
||||
".wrap_target",
|
||||
" nop side 1",
|
||||
" nop side 0",
|
||||
".wrap",
|
||||
);
|
||||
|
||||
// Initialize and start PIO
|
||||
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
|
||||
let installed = pio.install(&program.program).unwrap();
|
||||
let (int, frac) = (0, 0); // as slow as possible (0 is interpreted as 65536)
|
||||
let (mut sm, _, _) = rp2040_hal::pio::PIOBuilder::from_program(installed)
|
||||
.side_set_pin_base(led_pin_id)
|
||||
.clock_divisor_fixed_point(int, frac)
|
||||
.build(sm0);
|
||||
// The GPIO pin needs to be configured as an output.
|
||||
sm.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]);
|
||||
sm.start();
|
||||
|
||||
// PIO runs in background, independently from CPU
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
//! This example toggles the GPIO0 and GPIO1 pins, with each controlled from a
|
||||
//! separate PIO state machine.
|
||||
//!
|
||||
//! Despite running in separate state machines, the clocks are sychronized at
|
||||
//! the rise and fall times will be simultaneous.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use hal::gpio::{FunctionPio0, Pin};
|
||||
use hal::pac;
|
||||
use hal::pio::PIOExt;
|
||||
use hal::Sio;
|
||||
use panic_halt as _;
|
||||
use rp2040_hal as hal;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// 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.
|
||||
#[rp2040_hal::entry]
|
||||
fn main() -> ! {
|
||||
let mut pac = pac::Peripherals::take().unwrap();
|
||||
|
||||
let sio = Sio::new(pac.SIO);
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// configure pins for Pio0.
|
||||
let _: Pin<_, FunctionPio0> = pins.gpio0.into_mode();
|
||||
let _: Pin<_, FunctionPio0> = pins.gpio1.into_mode();
|
||||
|
||||
// PIN id for use inside of PIO
|
||||
let pin0 = 0;
|
||||
let pin1 = 1;
|
||||
|
||||
// Define some simple PIO program.
|
||||
let program = pio_proc::pio_asm!(
|
||||
"
|
||||
.wrap_target
|
||||
set pins, 1 [31]
|
||||
set pins, 0 [31]
|
||||
.wrap
|
||||
"
|
||||
);
|
||||
|
||||
// Initialize and start PIO
|
||||
let (mut pio, sm0, sm1, _, _) = pac.PIO0.split(&mut pac.RESETS);
|
||||
// I'm "measuring" the phase offset between the two pins by connecting
|
||||
// then through a LED. If there is a clock offset, there will be a
|
||||
// short time with a voltage between the pins, so the LED will flash up.
|
||||
// With a slow clock this is not visible, so use a reasonably fast clock.
|
||||
let (int, frac) = (256, 0);
|
||||
|
||||
let installed = pio.install(&program.program).unwrap();
|
||||
let (mut sm0, _, _) = rp2040_hal::pio::PIOBuilder::from_program(
|
||||
// Safety: We won't uninstall the program, ever
|
||||
unsafe { installed.share() },
|
||||
)
|
||||
.set_pins(pin0, 1)
|
||||
.clock_divisor_fixed_point(int, frac)
|
||||
.build(sm0);
|
||||
// The GPIO pin needs to be configured as an output.
|
||||
sm0.set_pindirs([(pin0, hal::pio::PinDir::Output)]);
|
||||
|
||||
let (mut sm1, _, _) = rp2040_hal::pio::PIOBuilder::from_program(installed)
|
||||
.set_pins(pin1, 1)
|
||||
.clock_divisor_fixed_point(int, frac)
|
||||
.build(sm1);
|
||||
// The GPIO pin needs to be configured as an output.
|
||||
sm1.set_pindirs([(pin1, hal::pio::PinDir::Output)]);
|
||||
|
||||
// Start both SMs at the same time
|
||||
let group = sm0.with(sm1).sync().start();
|
||||
cortex_m::asm::delay(10_000_000);
|
||||
|
||||
// Stop both SMs at the same time
|
||||
let group = group.stop();
|
||||
cortex_m::asm::delay(10_000_000);
|
||||
|
||||
// Start them again and extract the individual state machines
|
||||
let (sm1, sm2) = group.start().free();
|
||||
cortex_m::asm::delay(10_000_000);
|
||||
|
||||
// Stop the two state machines separately
|
||||
let _sm1 = sm1.stop();
|
||||
cortex_m::asm::delay(10_000_000);
|
||||
let _sm2 = sm2.stop();
|
||||
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
//! # PWM Blink Example
|
||||
//!
|
||||
//! If you have an LED connected to pin 25, it will fade the LED using the PWM
|
||||
//! peripheral.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// Alias for our HAL crate
|
||||
use rp2040_hal as hal;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::PwmPin;
|
||||
use rp2040_hal::clocks::Clock;
|
||||
|
||||
// A shorter alias for the Peripheral Access Crate, which provides low-level
|
||||
// register access
|
||||
use hal::pac;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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();
|
||||
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(
|
||||
XTAL_FREQ_HZ,
|
||||
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 = hal::gpio::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 GPIO 25
|
||||
let channel = &mut pwm.channel_b;
|
||||
channel.output_to(pins.gpio25);
|
||||
|
||||
// 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
|
|
@ -1,193 +0,0 @@
|
|||
//! # 'ROM Functions' Example
|
||||
//!
|
||||
//! This application demonstrates how to call functions in the RP2040's boot ROM.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use core::fmt::Write;
|
||||
use fugit::RateExtU32;
|
||||
use hal::Clock;
|
||||
|
||||
// UART related types
|
||||
use hal::uart::{DataBits, StopBits, UartConfig};
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// Our Cortex-M systick goes from this value down to zero. For our timer maths
|
||||
/// to work, this value must be of the form `2**N - 1`.
|
||||
const SYSTICK_RELOAD: u32 = 0x00FF_FFFF;
|
||||
|
||||
/// 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 writes to the UART in
|
||||
/// an infinite loop.
|
||||
#[rp2040_hal::entry]
|
||||
fn main() -> ! {
|
||||
// Grab our singleton objects
|
||||
let mut pac = pac::Peripherals::take().unwrap();
|
||||
let mut 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(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::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(9600.Hz(), DataBits::Eight, None, StopBits::One),
|
||||
clocks.peripheral_clock.freq(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
writeln!(uart, "ROM Copyright: {}", hal::rom_data::copyright_string()).unwrap();
|
||||
writeln!(
|
||||
uart,
|
||||
"ROM Git Revision: 0x{:x}",
|
||||
hal::rom_data::git_revision()
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Some ROM functions are exported directly, so we can just call them
|
||||
writeln!(
|
||||
uart,
|
||||
"popcount32(0xF000_0001) = {}",
|
||||
hal::rom_data::popcount32(0xF000_0001)
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Try to hide the numbers from the compiler so it is forced to do the maths
|
||||
let x = hal::rom_data::popcount32(0xFF) as f32; // 8
|
||||
let y = hal::rom_data::popcount32(0xFFF) as f32; // 12
|
||||
|
||||
// Use systick as a count-down timer
|
||||
core.SYST.set_reload(SYSTICK_RELOAD);
|
||||
core.SYST.clear_current();
|
||||
core.SYST.enable_counter();
|
||||
|
||||
// Do some simple sums
|
||||
let start_soft = cortex_m::peripheral::SYST::get_current();
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
let soft_result = x * y;
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
let end_soft = cortex_m::peripheral::SYST::get_current();
|
||||
|
||||
writeln!(
|
||||
uart,
|
||||
"{} x {} = {} in {} systicks (doing soft-float maths)",
|
||||
x,
|
||||
y,
|
||||
soft_result,
|
||||
calc_delta(start_soft, end_soft)
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Some functions require a look-up in a table. First we do the lookup and
|
||||
// find the function pointer in ROM (you only want to do this once per
|
||||
// function).
|
||||
let fmul = hal::rom_data::float_funcs::fmul::ptr();
|
||||
|
||||
// Then we can call the function whenever we want
|
||||
let start_rom = cortex_m::peripheral::SYST::get_current();
|
||||
let rom_result = fmul(x, y);
|
||||
let end_rom = cortex_m::peripheral::SYST::get_current();
|
||||
|
||||
writeln!(
|
||||
uart,
|
||||
"{} x {} = {} in {} systicks (using the ROM)",
|
||||
x,
|
||||
y,
|
||||
rom_result,
|
||||
calc_delta(start_rom, end_rom)
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Now just spin (whilst the UART does its thing)
|
||||
for _ in 0..1_000_000 {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
|
||||
// Reboot back into USB mode (no activity, both interfaces enabled)
|
||||
rp2040_hal::rom_data::reset_to_usb_boot(0, 0);
|
||||
|
||||
// In case the reboot fails
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the number of systicks elapsed between two counter readings.
|
||||
///
|
||||
/// Note: SYSTICK starts at `SYSTICK_RELOAD` and counts down towards zero, so
|
||||
/// these comparisons might appear to be backwards.
|
||||
///
|
||||
/// ```
|
||||
/// assert_eq!(1, calc_delta(SYSTICK_RELOAD, SYSTICK_RELOAD - 1));
|
||||
/// assert_eq!(2, calc_delta(0, SYSTICK_RELOAD - 1));
|
||||
/// ```
|
||||
fn calc_delta(start: u32, end: u32) -> u32 {
|
||||
if start < end {
|
||||
(start.wrapping_sub(end)) & SYSTICK_RELOAD
|
||||
} else {
|
||||
start - end
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,128 +0,0 @@
|
|||
//! # SPI Example
|
||||
//!
|
||||
//! This application demonstrates how to use the SPI Driver to talk to a remote
|
||||
//! SPI device.
|
||||
//!
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin
|
||||
//! assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// Alias for our HAL crate
|
||||
use rp2040_hal as hal;
|
||||
|
||||
// Some traits we need
|
||||
use cortex_m::prelude::*;
|
||||
use fugit::RateExtU32;
|
||||
use rp2040_hal::clocks::Clock;
|
||||
|
||||
// A shorter alias for the Peripheral Access Crate, which provides low-level
|
||||
// register access
|
||||
use hal::pac;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 performs some example
|
||||
/// SPI transactions, then goes to sleep.
|
||||
#[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
|
||||
let clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
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 to their default state
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// These are implicitly used by the spi driver if they are in the correct mode
|
||||
let _spi_sclk = pins.gpio6.into_mode::<hal::gpio::FunctionSpi>();
|
||||
let _spi_mosi = pins.gpio7.into_mode::<hal::gpio::FunctionSpi>();
|
||||
let _spi_miso = pins.gpio4.into_mode::<hal::gpio::FunctionSpi>();
|
||||
let spi = hal::Spi::<_, _, 8>::new(pac.SPI0);
|
||||
|
||||
// Exchange the uninitialised SPI driver for an initialised one
|
||||
let mut spi = spi.init(
|
||||
&mut pac.RESETS,
|
||||
clocks.peripheral_clock.freq(),
|
||||
16.MHz(),
|
||||
&embedded_hal::spi::MODE_0,
|
||||
);
|
||||
|
||||
// Write out 0, ignore return value
|
||||
if spi.write(&[0]).is_ok() {
|
||||
// SPI write was succesful
|
||||
};
|
||||
|
||||
// write 50, then check the return
|
||||
let send_success = spi.send(50);
|
||||
match send_success {
|
||||
Ok(_) => {
|
||||
// We succeeded, check the read value
|
||||
if let Ok(_x) = spi.read() {
|
||||
// We got back `x` in exchange for the 0x50 we sent.
|
||||
};
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
|
||||
// Do a read+write at the same time. Data in `buffer` will be replaced with
|
||||
// the data read from the SPI device.
|
||||
let mut buffer: [u8; 4] = [1, 2, 3, 4];
|
||||
let transfer_success = spi.transfer(&mut buffer);
|
||||
#[allow(clippy::single_match)]
|
||||
match transfer_success {
|
||||
Ok(_) => {} // Handle success
|
||||
Err(_) => {} // handle errors
|
||||
};
|
||||
|
||||
loop {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,110 +0,0 @@
|
|||
//! # UART Example
|
||||
//!
|
||||
//! This application demonstrates how to use the UART Driver to talk to a serial
|
||||
//! connection.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin
|
||||
//! assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use core::fmt::Write;
|
||||
use fugit::RateExtU32;
|
||||
use rp2040_hal::clocks::Clock;
|
||||
|
||||
// UART related types
|
||||
use hal::uart::{DataBits, StopBits, UartConfig};
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 writes to the UART in
|
||||
/// an infinite loop.
|
||||
#[rp2040_hal::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(
|
||||
XTAL_FREQ_HZ,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
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 = hal::gpio::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(9600.Hz(), DataBits::Eight, None, StopBits::One),
|
||||
clocks.peripheral_clock.freq(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
uart.write_full_blocking(b"UART example\r\n");
|
||||
|
||||
let mut value = 0u32;
|
||||
loop {
|
||||
writeln!(uart, "value: {:02}\r", value).unwrap();
|
||||
delay.delay_ms(1000);
|
||||
value += 1
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,188 +0,0 @@
|
|||
//! # RAM Vector Table example
|
||||
//!
|
||||
//! This application demonstrates how to create a new Interrupt Vector Table in RAM.
|
||||
//! To demonstrate the extra utility of this, we also replace an entry in the Vector Table
|
||||
//! with a new one.
|
||||
//!
|
||||
//! See the `Cargo.toml` file for Copyright and license details.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// Ensure we halt the program on panic
|
||||
use panic_halt as _;
|
||||
|
||||
// Alias for our HAL crate
|
||||
use rp2040_hal as hal;
|
||||
|
||||
// A shorter alias for the Peripheral Access Crate
|
||||
use hal::pac;
|
||||
|
||||
// Some traits we need
|
||||
use core::cell::RefCell;
|
||||
use critical_section::Mutex;
|
||||
use embedded_hal::digital::v2::ToggleableOutputPin;
|
||||
use fugit::MicrosDurationU32;
|
||||
use pac::interrupt;
|
||||
use rp2040_hal::clocks::Clock;
|
||||
use rp2040_hal::timer::Alarm;
|
||||
use rp2040_hal::vector_table::VectorTable;
|
||||
|
||||
// Memory that will hold our vector table in RAM
|
||||
static mut RAM_VTABLE: VectorTable = VectorTable::new();
|
||||
|
||||
// Give our LED and Alarm a type alias to make it easier to refer to them
|
||||
type LedAndAlarm = (
|
||||
hal::gpio::Pin<hal::gpio::bank0::Gpio25, hal::gpio::PushPullOutput>,
|
||||
hal::timer::Alarm0,
|
||||
);
|
||||
|
||||
// Place our LED and Alarm type in a static variable, so we can access it from interrupts
|
||||
static mut LED_AND_ALARM: Mutex<RefCell<Option<LedAndAlarm>>> = Mutex::new(RefCell::new(None));
|
||||
|
||||
// Period that each of the alarms will be set for - 1 second and 300ms respectively
|
||||
const SLOW_BLINK_INTERVAL_US: MicrosDurationU32 = MicrosDurationU32::secs(1);
|
||||
const FAST_BLINK_INTERVAL_US: MicrosDurationU32 = MicrosDurationU32::millis(300);
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 toggles a GPIO pin in
|
||||
/// an infinite loop. If there is an LED connected to that pin, it will blink.
|
||||
#[rp2040_hal::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);
|
||||
// The single-cycle I/O block controls our GPIO pins
|
||||
let sio = hal::Sio::new(pac.SIO);
|
||||
|
||||
// Need to make a reference to the Peripheral Base at this scope to avoid confusing the borrow checker
|
||||
let ppb = &mut pac.PPB;
|
||||
unsafe {
|
||||
// Copy the vector table that cortex_m_rt produced into the RAM vector table
|
||||
RAM_VTABLE.init(ppb);
|
||||
// Replace the function that is called on Alarm0 interrupts with a new one
|
||||
RAM_VTABLE.register_handler(pac::Interrupt::TIMER_IRQ_0 as usize, timer_irq0_replacement);
|
||||
}
|
||||
|
||||
// Configure the clocks
|
||||
let clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
// Create simple delay
|
||||
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
||||
|
||||
// Set the pins to their default state
|
||||
let pins = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// Configure GPIO25 as an output
|
||||
let led_pin = pins.gpio25.into_push_pull_output();
|
||||
|
||||
let mut timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS);
|
||||
critical_section::with(|cs| {
|
||||
let mut alarm = timer.alarm_0().unwrap();
|
||||
// Schedule an alarm in 1 second
|
||||
let _ = alarm.schedule(SLOW_BLINK_INTERVAL_US);
|
||||
// Enable generating an interrupt on alarm
|
||||
alarm.enable_interrupt();
|
||||
// Move alarm into ALARM, so that it can be accessed from interrupts
|
||||
unsafe {
|
||||
LED_AND_ALARM.borrow(cs).replace(Some((led_pin, alarm)));
|
||||
}
|
||||
});
|
||||
// Unmask the timer0 IRQ so that it will generate an interrupt
|
||||
unsafe {
|
||||
pac::NVIC::unmask(pac::Interrupt::TIMER_IRQ_0);
|
||||
}
|
||||
|
||||
// After 5 seconds, switch to our modified vector rable
|
||||
delay.delay_ms(5000);
|
||||
unsafe {
|
||||
critical_section::with(|_| {
|
||||
RAM_VTABLE.activate(ppb);
|
||||
});
|
||||
}
|
||||
|
||||
loop {
|
||||
// Wait for an interrupt to fire before doing any more work
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
// Regular interrupt handler for Alarm0. The `interrupt` macro will perform some transformations to ensure
|
||||
// that this interrupt entry ends up in the vector table.
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_0() {
|
||||
critical_section::with(|cs| {
|
||||
// Temporarily take our LED_AND_ALARM
|
||||
let ledalarm = unsafe { LED_AND_ALARM.borrow(cs).take() };
|
||||
if let Some((mut led, mut alarm)) = ledalarm {
|
||||
// Clear the alarm interrupt or this interrupt service routine will keep firing
|
||||
alarm.clear_interrupt();
|
||||
// Schedule a new alarm after SLOW_BLINK_INTERVAL_US have passed (1 second)
|
||||
let _ = alarm.schedule(SLOW_BLINK_INTERVAL_US);
|
||||
// Blink the LED so we know we hit this interrupt
|
||||
led.toggle().unwrap();
|
||||
// Return LED_AND_ALARM into our static variable
|
||||
unsafe {
|
||||
LED_AND_ALARM
|
||||
.borrow(cs)
|
||||
.replace_with(|_| Some((led, alarm)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This is the function we will use to replace TIMER_IRQ_0 in our RAM Vector Table
|
||||
extern "C" fn timer_irq0_replacement() {
|
||||
critical_section::with(|cs| {
|
||||
let ledalarm = unsafe { LED_AND_ALARM.borrow(cs).take() };
|
||||
if let Some((mut led, mut alarm)) = ledalarm {
|
||||
// Clear the alarm interrupt or this interrupt service routine will keep firing
|
||||
alarm.clear_interrupt();
|
||||
// Schedule a new alarm after FAST_BLINK_INTERVAL_US have passed (300 milliseconds)
|
||||
let _ = alarm.schedule(FAST_BLINK_INTERVAL_US);
|
||||
led.toggle().unwrap();
|
||||
// Return LED_AND_ALARM into our static variable
|
||||
unsafe {
|
||||
LED_AND_ALARM
|
||||
.borrow(cs)
|
||||
.replace_with(|_| Some((led, alarm)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,113 +0,0 @@
|
|||
//! # Watchdog Example
|
||||
//!
|
||||
//! This application demonstrates how to use the RP2040 Watchdog.
|
||||
//!
|
||||
//! It may need to be adapted to your particular board layout and/or pin assignment.
|
||||
//!
|
||||
//! 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 _;
|
||||
|
||||
// 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;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal::watchdog::{Watchdog, WatchdogEnable};
|
||||
use fugit::ExtU32;
|
||||
use rp2040_hal::clocks::Clock;
|
||||
|
||||
/// 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.
|
||||
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
||||
/// as the BSPs already perform this step.
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
||||
|
||||
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
||||
/// if your board has a different frequency
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
||||
|
||||
/// 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 toggles a GPIO pin in
|
||||
/// an infinite loop. After a period of time, the watchdog will kick in to reset
|
||||
/// the CPU.
|
||||
#[rp2040_hal::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(
|
||||
XTAL_FREQ_HZ,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
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 = hal::gpio::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
// Configure an LED so we can show the current state of the watchdog
|
||||
let mut led_pin = pins.gpio25.into_push_pull_output();
|
||||
|
||||
// Set the LED high for 2 seconds so we know when we're about to start the watchdog
|
||||
led_pin.set_high().unwrap();
|
||||
delay.delay_ms(2000);
|
||||
|
||||
// Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it
|
||||
watchdog.start(1_050.millis());
|
||||
|
||||
// Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset
|
||||
for _ in 1..=5 {
|
||||
led_pin.set_low().unwrap();
|
||||
delay.delay_ms(500);
|
||||
led_pin.set_high().unwrap();
|
||||
delay.delay_ms(500);
|
||||
watchdog.feed();
|
||||
}
|
||||
|
||||
// Blink 10 times per second, not feeding the watchdog.
|
||||
// The processor should reset in 1.05 seconds, or 5 blinks time
|
||||
loop {
|
||||
led_pin.set_low().unwrap();
|
||||
delay.delay_ms(100);
|
||||
led_pin.set_high().unwrap();
|
||||
delay.delay_ms(100);
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
|
@ -1,155 +0,0 @@
|
|||
//! Analog-Digital Converter (ADC)
|
||||
//!
|
||||
//! See [Chapter 4 Section 9](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! Capture ADC reading from a pin
|
||||
//! ```no_run
|
||||
//! use embedded_hal::adc::OneShot;
|
||||
//! use rp2040_hal::{adc::Adc, gpio::Pins, pac, Sio};
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let sio = Sio::new(peripherals.SIO);
|
||||
//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//! // Enable adc
|
||||
//! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
|
||||
//! // Configure one of the pins as an ADC input
|
||||
//! let mut adc_pin_0 = pins.gpio26.into_floating_input();
|
||||
//! // Read the ADC counts from the ADC channel
|
||||
//! let pin_adc_counts: u16 = adc.read(&mut adc_pin_0).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Capture ADC reading from temperature sensor. Note that this needs conversion to be a real-world temperature.
|
||||
//! ```no_run
|
||||
//! use embedded_hal::adc::OneShot;
|
||||
//! use rp2040_hal::{adc::Adc, gpio::Pins, pac, Sio};
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let sio = Sio::new(peripherals.SIO);
|
||||
//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//! // Enable adc
|
||||
//! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
|
||||
//! // Enable the temperature sensor
|
||||
//! let mut temperature_sensor = adc.enable_temp_sensor();
|
||||
//! // Read the ADC counts from the ADC channel
|
||||
//! let temperature_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! See [examples/adc.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/adc.rs) and
|
||||
//! [pimoroni_pico_explorer_showcase.rs](https://github.com/rp-rs/rp-hal/tree/main/boards/pimoroni_pico_explorer/examples/pimoroni_pico_explorer_showcase.rs) for more complete examples
|
||||
|
||||
use hal::adc::{Channel, OneShot};
|
||||
use pac::{ADC, RESETS};
|
||||
|
||||
use crate::{
|
||||
gpio::Pin,
|
||||
gpio::{
|
||||
bank0::{Gpio26, Gpio27, Gpio28, Gpio29},
|
||||
FloatingInput,
|
||||
},
|
||||
resets::SubsystemReset,
|
||||
};
|
||||
|
||||
const TEMPERATURE_SENSOR_CHANNEL: u8 = 4;
|
||||
|
||||
/// Adc
|
||||
pub struct Adc {
|
||||
device: ADC,
|
||||
}
|
||||
|
||||
impl Adc {
|
||||
/// Create new adc struct and bring up adc
|
||||
pub fn new(device: ADC, resets: &mut RESETS) -> Self {
|
||||
device.reset_bring_down(resets);
|
||||
device.reset_bring_up(resets);
|
||||
|
||||
// Enable adc
|
||||
device.cs.write(|w| w.en().set_bit());
|
||||
|
||||
// Wait for adc ready
|
||||
while !device.cs.read().ready().bit_is_set() {}
|
||||
|
||||
Self { device }
|
||||
}
|
||||
|
||||
/// Free underlying register block
|
||||
pub fn free(self) -> ADC {
|
||||
self.device
|
||||
}
|
||||
|
||||
/// Read single
|
||||
pub fn read_single(&self) -> u16 {
|
||||
self.device.result.read().result().bits()
|
||||
}
|
||||
|
||||
/// Enable temperature sensor, returns a channel to use
|
||||
pub fn enable_temp_sensor(&mut self) -> TempSense {
|
||||
self.device.cs.modify(|_, w| w.ts_en().set_bit());
|
||||
|
||||
TempSense { __private: () }
|
||||
}
|
||||
|
||||
/// Disable temperature sensor, consumes channel
|
||||
pub fn disable_temp_sensor(&mut self, _: TempSense) {
|
||||
self.device.cs.modify(|_, w| w.ts_en().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! channel {
|
||||
($pin:ident, $channel:expr) => {
|
||||
impl Channel<Adc> for Pin<$pin, FloatingInput> {
|
||||
type ID = u8; // ADC channels are identified numerically
|
||||
|
||||
fn channel() -> u8 {
|
||||
$channel
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
channel!(Gpio26, 0);
|
||||
channel!(Gpio27, 1);
|
||||
channel!(Gpio28, 2);
|
||||
channel!(Gpio29, 3);
|
||||
|
||||
/// Internal temperature sensor type
|
||||
pub struct TempSense {
|
||||
__private: (),
|
||||
}
|
||||
|
||||
impl Channel<Adc> for TempSense {
|
||||
type ID = u8; // ADC channels are identified numerically
|
||||
|
||||
fn channel() -> u8 {
|
||||
TEMPERATURE_SENSOR_CHANNEL
|
||||
}
|
||||
}
|
||||
|
||||
impl<WORD, PIN> OneShot<Adc, WORD, PIN> for Adc
|
||||
where
|
||||
WORD: From<u16>,
|
||||
PIN: Channel<Adc, ID = u8>,
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
|
||||
let chan = PIN::channel();
|
||||
|
||||
if chan == 4 {
|
||||
self.device.cs.modify(|_, w| w.ts_en().set_bit())
|
||||
}
|
||||
|
||||
while !self.device.cs.read().ready().bit_is_set() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
|
||||
self.device
|
||||
.cs
|
||||
.modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() });
|
||||
|
||||
while !self.device.cs.read().ready().bit_is_set() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
|
||||
Ok(self.device.result.read().result().bits().into())
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
//! Provide atomic access to peripheral registers
|
||||
//!
|
||||
//! This feature is not available for all peripherals.
|
||||
//! See [section 2.1.2 of the RP2040 datasheet][section_2_1_2] for details.
|
||||
//!
|
||||
//! [section_2_1_2]: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#atomic-rwtype
|
||||
|
||||
use core::ptr::write_volatile;
|
||||
|
||||
/// Perform atomic bitmask set operation on register
|
||||
///
|
||||
/// See [section 2.1.2 of the RP2040 datasheet][section_2_1_2] for details.
|
||||
///
|
||||
/// [section_2_1_2]: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#atomic-rwtype
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// In addition to the requirements of [core::ptr::write_volatile],
|
||||
/// `register` must point to a register providing atomic aliases.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn write_bitmask_set(register: *mut u32, bits: u32) {
|
||||
let alias = (register as usize + 0x2000) as *mut u32;
|
||||
write_volatile(alias, bits);
|
||||
}
|
||||
|
||||
/// Perform atomic bitmask clear operation on register
|
||||
///
|
||||
/// See [section 2.1.2 of the RP2040 datasheet][section_2_1_2] for details.
|
||||
///
|
||||
/// [section_2_1_2]: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#atomic-rwtype
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// In addition to the requirements of [core::ptr::write_volatile],
|
||||
/// `register` must point to a register providing atomic aliases.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn write_bitmask_clear(register: *mut u32, bits: u32) {
|
||||
let alias = (register as usize + 0x3000) as *mut u32;
|
||||
write_volatile(alias, bits);
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
//! Available clocks
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
gpio::{
|
||||
bank0::{Gpio20, Gpio22},
|
||||
FunctionClock, Pin,
|
||||
},
|
||||
pll::{Locked, PhaseLockedLoop},
|
||||
rosc::{Enabled, RingOscillator},
|
||||
typelevel::Sealed,
|
||||
xosc::{CrystalOscillator, Stable},
|
||||
};
|
||||
use pac::{PLL_SYS, PLL_USB};
|
||||
|
||||
pub(crate) type PllSys = PhaseLockedLoop<Locked, PLL_SYS>;
|
||||
impl Sealed for PllSys {}
|
||||
impl ClockSource for PllSys {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type PllUsb = PhaseLockedLoop<Locked, PLL_USB>;
|
||||
impl Sealed for PllUsb {}
|
||||
impl ClockSource for PllUsb {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for UsbClock {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for AdcClock {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for RtcClock {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for SystemClock {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for ReferenceClock {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type Xosc = CrystalOscillator<Stable>;
|
||||
impl Sealed for Xosc {}
|
||||
impl ClockSource for Xosc {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type Rosc = RingOscillator<Enabled>;
|
||||
impl Sealed for Rosc {}
|
||||
// We are assuming the second output is never phase shifted (see 2.17.4)
|
||||
impl ClockSource for RingOscillator<Enabled> {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
// GPIN0
|
||||
pub(crate) type GPin0 = Pin<Gpio20, FunctionClock>;
|
||||
impl ClockSource for GPin0 {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// GPIN1
|
||||
pub(crate) type GPin1 = Pin<Gpio22, FunctionClock>;
|
||||
impl ClockSource for Pin<Gpio22, FunctionClock> {
|
||||
fn get_freq(&self) -> HertzU32 {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -1,431 +0,0 @@
|
|||
macro_rules! clocks {
|
||||
(
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident {
|
||||
init_freq: $init_freq:expr,
|
||||
reg: $reg:ident,
|
||||
$(src: {$($src:ident: $src_variant:ident),*},)?
|
||||
auxsrc: {$($auxsrc:ident: $aux_variant:ident),*}
|
||||
$(, div: $div:tt)?
|
||||
}
|
||||
)*
|
||||
|
||||
) => {
|
||||
|
||||
$crate::paste::paste!{
|
||||
/// Abstraction layer providing Clock Management.
|
||||
pub struct ClocksManager {
|
||||
clocks: CLOCKS,
|
||||
$(
|
||||
#[doc = "`" $name "` field"]
|
||||
pub [<$name:snake>]: $name,
|
||||
)*
|
||||
}
|
||||
|
||||
impl ClocksManager {
|
||||
/// Exchanges CLOCKS block against Self.
|
||||
pub fn new(mut clocks_block: CLOCKS) -> Self {
|
||||
// Disable resus that may be enabled from previous software
|
||||
unsafe {
|
||||
clocks_block.clk_sys_resus_ctrl.write_with_zero(|w| w);
|
||||
}
|
||||
|
||||
let shared_clocks = ShareableClocks::new(&mut clocks_block);
|
||||
ClocksManager {
|
||||
clocks: clocks_block,
|
||||
$(
|
||||
[<$name:snake>]: $name {
|
||||
shared_dev: shared_clocks,
|
||||
frequency: $init_freq.Hz(),
|
||||
},
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
clock!(
|
||||
$(#[$attr])*
|
||||
struct $name {
|
||||
reg: $reg,
|
||||
$(src: {$($src: $src_variant),*},)?
|
||||
auxsrc: {$($auxsrc: $aux_variant),*}
|
||||
$(, div: $div )?
|
||||
}
|
||||
);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! clock {
|
||||
{
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident {
|
||||
reg: $reg:ident,
|
||||
src: {$($src:ident: $src_variant:ident),*},
|
||||
auxsrc: {$($auxsrc:ident: $aux_variant:ident),*}
|
||||
}
|
||||
} => {
|
||||
base_clock!{
|
||||
$(#[$attr])*
|
||||
($name, $reg, auxsrc={$($auxsrc: $aux_variant),*})
|
||||
}
|
||||
|
||||
divisable_clock!($name, $reg);
|
||||
|
||||
$crate::paste::paste!{
|
||||
$(impl ValidSrc<$name> for $src {
|
||||
fn is_aux(&self) -> bool{
|
||||
false
|
||||
}
|
||||
fn variant(&self) -> [<$reg:camel SrcType>] {
|
||||
[<$reg:camel SrcType>]::Src(pac::clocks::[<$reg _ctrl>]::SRC_A::$src_variant)
|
||||
}
|
||||
})*
|
||||
|
||||
impl GlitchlessClock for $name {
|
||||
type Clock = Self;
|
||||
|
||||
fn await_select(&self, clock_token: &ChangingClockToken<Self>) -> nb::Result<(), Infallible> {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
let selected = shared_dev.[<$reg _selected>].read().bits();
|
||||
if selected != 1 << clock_token.clock_nr {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Holds register value for ClockSource for `"$name"`"]
|
||||
pub enum [<$reg:camel SrcType>] {
|
||||
#[doc = "Contains a valid clock source register value that is to be used to set a clock as glitchless source for `"$name"`"]
|
||||
Src(pac::clocks::[<$reg _ctrl>]::SRC_A),
|
||||
#[doc = "Contains a valid clock source register value that is to be used to set a clock as aux source for `"$name"`"]
|
||||
Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A)
|
||||
}
|
||||
|
||||
impl [<$reg:camel SrcType>] {
|
||||
fn get_clock_id(&self) -> u8 {
|
||||
match self {
|
||||
Self::Src(v) => *v as u8,
|
||||
Self::Aux(v) => *v as u8,
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_src(&self) -> pac::clocks::[<$reg _ctrl>]::SRC_A{
|
||||
match self {
|
||||
Self::Src(v) => *v,
|
||||
Self::Aux(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_aux(&self) -> pac::clocks::[<$reg _ctrl>]::AUXSRC_A {
|
||||
match self {
|
||||
Self::Src(_) => panic!(),
|
||||
Self::Aux(v) => *v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// Reset clock back to its reset source
|
||||
pub fn reset_source_await(&mut self) -> nb::Result<(), Infallible> {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
shared_dev.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.src().variant(self.get_default_clock_source())
|
||||
});
|
||||
|
||||
use fugit::RateExtU32;
|
||||
self.frequency = 12.MHz(); //TODO Get actual clock source.. Most likely 12 MHz though
|
||||
|
||||
self.await_select(&ChangingClockToken{clock_nr:0, clock: PhantomData::<Self>})
|
||||
}
|
||||
|
||||
fn set_src<S: ValidSrc<$name>>(&mut self, src: &S)-> ChangingClockToken<$name> {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
shared_dev.[<$reg _ctrl>].modify(|_,w| {
|
||||
w.src().variant(src.variant().unwrap_src())
|
||||
});
|
||||
|
||||
ChangingClockToken {
|
||||
clock: PhantomData::<$name>,
|
||||
clock_nr: src.variant().get_clock_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_self_aux_src(&mut self) -> ChangingClockToken<$name> {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.src().variant(self.get_aux_source())
|
||||
});
|
||||
|
||||
ChangingClockToken{
|
||||
clock: PhantomData::<$name>,
|
||||
clock_nr: pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock for $name {
|
||||
type Variant = [<$reg:camel SrcType>];
|
||||
|
||||
#[doc = "Get operating frequency for `"$name"`"]
|
||||
fn freq(&self) -> HertzU32 {
|
||||
self.frequency
|
||||
}
|
||||
|
||||
#[doc = "Configure `"$name"`"]
|
||||
fn configure_clock<S: ValidSrc<$name>>(&mut self, src: &S, freq: HertzU32) -> Result<(), ClockError>{
|
||||
let src_freq: HertzU32 = src.get_freq().into();
|
||||
|
||||
if freq.gt(&src_freq){
|
||||
return Err(ClockError::CantIncreaseFreq);
|
||||
}
|
||||
|
||||
let div = fractional_div(src_freq.to_Hz(), freq.to_Hz()).ok_or(ClockError::FrequencyTooLow)?;
|
||||
|
||||
// If increasing divisor, set divisor before source. Otherwise set source
|
||||
// before divisor. This avoids a momentary overspeed when e.g. switching
|
||||
// to a faster source and increasing divisor to compensate.
|
||||
if div > self.get_div() {
|
||||
self.set_div(div);
|
||||
}
|
||||
|
||||
// If switching a glitchless slice (ref or sys) to an aux source, switch
|
||||
// away from aux *first* to avoid passing glitches when changing aux mux.
|
||||
// Assume (!!!) glitchless source 0 is no faster than the aux source.
|
||||
nb::block!(self.reset_source_await()).unwrap();
|
||||
|
||||
|
||||
// Set aux mux first, and then glitchless mux if this self has one
|
||||
let token = if src.is_aux() {
|
||||
self.set_aux(src);
|
||||
self.set_self_aux_src()
|
||||
} else {
|
||||
self.set_src(src)
|
||||
};
|
||||
|
||||
nb::block!(self.await_select(&token)).unwrap();
|
||||
|
||||
|
||||
// Now that the source is configured, we can trust that the user-supplied
|
||||
// divisor is a safe value.
|
||||
self.set_div(div);
|
||||
|
||||
// Store the configured frequency
|
||||
use fugit::RateExtU32;
|
||||
self.frequency = fractional_div(src_freq.to_Hz(), div).ok_or(ClockError::FrequencyTooHigh)?.Hz();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
{
|
||||
$( #[$attr:meta])*
|
||||
struct $name:ident {
|
||||
reg: $reg:ident,
|
||||
auxsrc: {$($auxsrc:ident: $variant:ident),*},
|
||||
div: false
|
||||
}
|
||||
} => {
|
||||
base_clock!{
|
||||
$(#[$attr])*
|
||||
($name, $reg, auxsrc={$($auxsrc: $variant),*})
|
||||
}
|
||||
|
||||
// Just to match proper divisible clocks so we don't have to do something special in configure function
|
||||
impl ClockDivision for $name {
|
||||
fn set_div(&mut self, _: u32) {}
|
||||
fn get_div(&self) -> u32 {1}
|
||||
}
|
||||
|
||||
stoppable_clock!($name, $reg);
|
||||
};
|
||||
{
|
||||
$( #[$attr:meta])*
|
||||
struct $name:ident {
|
||||
reg: $reg:ident,
|
||||
auxsrc: {$($auxsrc:ident: $variant:ident),*}
|
||||
}
|
||||
} => {
|
||||
base_clock!{
|
||||
$(#[$attr])*
|
||||
($name, $reg, auxsrc={$($auxsrc: $variant),*})
|
||||
}
|
||||
|
||||
divisable_clock!($name, $reg);
|
||||
stoppable_clock!($name, $reg);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! divisable_clock {
|
||||
($name:ident, $reg:ident) => {
|
||||
$crate::paste::paste! {
|
||||
impl ClockDivision for $name {
|
||||
fn set_div(&mut self, div: u32) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _div>].modify(|_, w| unsafe {
|
||||
w.bits(div);
|
||||
w
|
||||
});
|
||||
}
|
||||
fn get_div(&self) -> u32 {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _div>].read().bits()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! stoppable_clock {
|
||||
($name:ident, $reg:ident) => {
|
||||
$crate::paste::paste!{
|
||||
#[doc = "Holds register value for ClockSource for `"$name"`"]
|
||||
pub enum [<$reg:camel SrcType>] {
|
||||
#[doc = "Contains a valid clock source register value that is to be used to set a clock as aux source for `"$name"`"]
|
||||
Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A)
|
||||
}
|
||||
|
||||
impl [<$reg:camel SrcType>] {
|
||||
fn unwrap_aux(&self) -> pac::clocks::[<$reg _ctrl>]::AUXSRC_A {
|
||||
match self {
|
||||
Self::Aux(v) => *v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StoppableClock for $name {
|
||||
/// Enable the clock
|
||||
fn enable(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.enable().set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
/// Disable the clock cleanly
|
||||
fn disable(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.enable().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
/// Disable the clock asynchronously
|
||||
fn kill(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.kill().set_bit()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock for $name {
|
||||
type Variant = [<$reg:camel SrcType>];
|
||||
|
||||
#[doc = "Get operating frequency for `"$name"`"]
|
||||
fn freq(&self) -> HertzU32 {
|
||||
self.frequency
|
||||
}
|
||||
|
||||
#[doc = "Configure `"$name"`"]
|
||||
fn configure_clock<S: ValidSrc<$name>>(&mut self, src: &S, freq: HertzU32) -> Result<(), ClockError>{
|
||||
let src_freq: HertzU32 = src.get_freq().into();
|
||||
|
||||
if freq.gt(&src_freq){
|
||||
return Err(ClockError::CantIncreaseFreq);
|
||||
}
|
||||
|
||||
let div = fractional_div(src_freq.to_Hz(), freq.to_Hz()).ok_or(ClockError::FrequencyTooLow)?;
|
||||
|
||||
// If increasing divisor, set divisor before source. Otherwise set source
|
||||
// before divisor. This avoids a momentary overspeed when e.g. switching
|
||||
// to a faster source and increasing divisor to compensate.
|
||||
if div > self.get_div() {
|
||||
self.set_div(div);
|
||||
}
|
||||
|
||||
// If no glitchless mux, cleanly stop the clock to avoid glitches
|
||||
// propagating when changing aux mux. Note it would be a really bad idea
|
||||
// to do this on one of the glitchless clocks (clk_sys, clk_ref).
|
||||
|
||||
// Disable clock. On clk_ref and clk_sys this does nothing,
|
||||
// all other clocks have the ENABLE bit in the same position.
|
||||
self.disable();
|
||||
if self.frequency > HertzU32::Hz(0) {
|
||||
// Delay for 3 cycles of the target clock, for ENABLE propagation.
|
||||
// Note XOSC_COUNT is not helpful here because XOSC is not
|
||||
// necessarily running, nor is timer... so, 3 cycles per loop:
|
||||
let sys_freq = 125_000_000; // TODO get actual sys_clk frequency
|
||||
let delay_cyc = sys_freq / self.frequency.to_Hz() + 1u32;
|
||||
cortex_m::asm::delay(delay_cyc);
|
||||
}
|
||||
|
||||
// Set aux mux first, and then glitchless mux if this self has one
|
||||
self.set_aux(src);
|
||||
|
||||
// Enable clock. On clk_ref and clk_sys this does nothing,
|
||||
// all other clocks have the ENABLE bit in the same posi
|
||||
self.enable();
|
||||
|
||||
// Now that the source is configured, we can trust that the user-supplied
|
||||
// divisor is a safe value.
|
||||
self.set_div(div);
|
||||
|
||||
// Store the configured frequency
|
||||
use fugit::RateExtU32;
|
||||
self.frequency = fractional_div(src_freq.to_Hz(), div).ok_or(ClockError::FrequencyTooHigh)?.Hz();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! base_clock {
|
||||
{
|
||||
$(#[$attr:meta])*
|
||||
($name:ident, $reg:ident, auxsrc={$($auxsrc:ident: $variant:ident),*})
|
||||
} => {
|
||||
$crate::paste::paste!{
|
||||
|
||||
$(impl ValidSrc<$name> for $auxsrc {
|
||||
|
||||
fn is_aux(&self) -> bool{
|
||||
true
|
||||
}
|
||||
fn variant(&self) -> [<$reg:camel SrcType>] {
|
||||
[<$reg:camel SrcType>]::Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A::$variant)
|
||||
}
|
||||
})*
|
||||
|
||||
$(#[$attr])*
|
||||
pub struct $name {
|
||||
shared_dev: ShareableClocks,
|
||||
frequency: HertzU32,
|
||||
}
|
||||
|
||||
impl $name {
|
||||
fn set_aux<S: ValidSrc<$name>>(&mut self, src: &S) {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
shared_dev.[<$reg _ctrl>].modify(|_,w| {
|
||||
w.auxsrc().variant(src.variant().unwrap_aux())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealed for $name {}
|
||||
|
||||
impl From<&$name> for HertzU32
|
||||
{
|
||||
fn from(value: &$name) -> HertzU32 {
|
||||
value.frequency
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,426 +0,0 @@
|
|||
//! Clocks (CLOCKS)
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! ## Usage simple
|
||||
//! ```no_run
|
||||
//! use rp2040_hal::{clocks::init_clocks_and_plls, watchdog::Watchdog, pac};
|
||||
//!
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
|
||||
//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||
//! let mut clocks = init_clocks_and_plls(XOSC_CRYSTAL_FREQ, peripherals.XOSC, peripherals.CLOCKS, peripherals.PLL_SYS, peripherals.PLL_USB, &mut peripherals.RESETS, &mut watchdog).ok().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! ## Usage extended
|
||||
//! ```no_run
|
||||
//! use fugit::RateExtU32;
|
||||
//! use rp2040_hal::{clocks::{Clock, ClocksManager, ClockSource, InitError}, gpio::Pins, pac, pll::{common_configs::{PLL_SYS_125MHZ, PLL_USB_48MHZ}, setup_pll_blocking}, Sio, watchdog::Watchdog, xosc::setup_xosc_blocking};
|
||||
//!
|
||||
//! # fn func() -> Result<(), InitError> {
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
|
||||
//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||
//!
|
||||
//! // Enable the xosc
|
||||
//! let xosc = setup_xosc_blocking(peripherals.XOSC, XOSC_CRYSTAL_FREQ.Hz()).map_err(InitError::XoscErr)?;
|
||||
//!
|
||||
//! // Start tick in watchdog
|
||||
//! watchdog.enable_tick_generation((XOSC_CRYSTAL_FREQ / 1_000_000) as u8);
|
||||
//!
|
||||
//! let mut clocks = ClocksManager::new(peripherals.CLOCKS);
|
||||
//!
|
||||
//! // Configure PLLs
|
||||
//! // REF FBDIV VCO POSTDIV
|
||||
//! // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
|
||||
//! // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
|
||||
//! let pll_sys = setup_pll_blocking(peripherals.PLL_SYS, xosc.operating_frequency().into(), PLL_SYS_125MHZ, &mut clocks, &mut peripherals.RESETS).map_err(InitError::PllError)?;
|
||||
//! let pll_usb = setup_pll_blocking(peripherals.PLL_USB, xosc.operating_frequency().into(), PLL_USB_48MHZ, &mut clocks, &mut peripherals.RESETS).map_err(InitError::PllError)?;
|
||||
//!
|
||||
//! // Configure clocks
|
||||
//! // CLK_REF = XOSC (12MHz) / 1 = 12MHz
|
||||
//! clocks.reference_clock.configure_clock(&xosc, xosc.get_freq()).map_err(InitError::ClockError)?;
|
||||
//!
|
||||
//! // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
|
||||
//! clocks.system_clock.configure_clock(&pll_sys, pll_sys.get_freq()).map_err(InitError::ClockError)?;
|
||||
//!
|
||||
//! // CLK USB = PLL USB (48MHz) / 1 = 48MHz
|
||||
//! clocks.usb_clock.configure_clock(&pll_usb, pll_usb.get_freq()).map_err(InitError::ClockError)?;
|
||||
//!
|
||||
//! // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
|
||||
//! clocks.adc_clock.configure_clock(&pll_usb, pll_usb.get_freq()).map_err(InitError::ClockError)?;
|
||||
//!
|
||||
//! // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
|
||||
//! clocks.rtc_clock.configure_clock(&pll_usb, 46875u32.Hz()).map_err(InitError::ClockError)?;
|
||||
//!
|
||||
//! // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
|
||||
//! // Normally choose clk_sys or clk_usb
|
||||
//! clocks.peripheral_clock.configure_clock(&clocks.system_clock, clocks.system_clock.freq()).map_err(InitError::ClockError)?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! See [Chapter 2 Section 15](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
|
||||
use crate::{
|
||||
pll::{
|
||||
common_configs::{PLL_SYS_125MHZ, PLL_USB_48MHZ},
|
||||
setup_pll_blocking, Error as PllError, Locked, PhaseLockedLoop,
|
||||
},
|
||||
typelevel::Sealed,
|
||||
watchdog::Watchdog,
|
||||
xosc::{setup_xosc_blocking, CrystalOscillator, Error as XoscError, Stable},
|
||||
};
|
||||
use core::{convert::Infallible, marker::PhantomData};
|
||||
use fugit::HertzU32;
|
||||
use fugit::RateExtU32;
|
||||
use pac::{CLOCKS, PLL_SYS, PLL_USB, RESETS, XOSC};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod clock_sources;
|
||||
|
||||
use clock_sources::PllSys;
|
||||
|
||||
use self::clock_sources::{GPin0, GPin1, PllUsb, Rosc, Xosc};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
/// Provides refs to the CLOCKS block.
|
||||
struct ShareableClocks {
|
||||
_internal: (),
|
||||
}
|
||||
|
||||
impl ShareableClocks {
|
||||
fn new(_clocks: &mut CLOCKS) -> Self {
|
||||
ShareableClocks { _internal: () }
|
||||
}
|
||||
|
||||
unsafe fn get(&self) -> &pac::clocks::RegisterBlock {
|
||||
&*CLOCKS::ptr()
|
||||
}
|
||||
}
|
||||
|
||||
/// Something when wrong setting up the clock
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ClockError {
|
||||
/// The frequency desired is higher than the source frequency
|
||||
CantIncreaseFreq,
|
||||
/// The desired frequency is to high (would overflow an u32)
|
||||
FrequencyTooHigh,
|
||||
/// The desired frequency is too low (divider can't reach the desired value)
|
||||
FrequencyTooLow,
|
||||
}
|
||||
|
||||
/// For clocks
|
||||
pub trait Clock: Sealed + Sized {
|
||||
/// Enum with valid source clocks register values for `Clock`
|
||||
type Variant;
|
||||
|
||||
/// Get operating frequency
|
||||
fn freq(&self) -> HertzU32;
|
||||
|
||||
/// Configure this clock based on a clock source and desired frequency
|
||||
fn configure_clock<S: ValidSrc<Self>>(
|
||||
&mut self,
|
||||
src: &S,
|
||||
freq: HertzU32,
|
||||
) -> Result<(), ClockError>;
|
||||
}
|
||||
|
||||
/// For clocks with a divider
|
||||
trait ClockDivision {
|
||||
/// Set integer divider value.
|
||||
fn set_div(&mut self, div: u32);
|
||||
/// Get integer diveder value.
|
||||
fn get_div(&self) -> u32;
|
||||
}
|
||||
|
||||
/// Clock with glitchless source
|
||||
trait GlitchlessClock {
|
||||
/// Self type to hand to ChangingClockToken
|
||||
type Clock: Clock;
|
||||
|
||||
/// Await switching clock sources without glitches. Needs a token that is returned when setting
|
||||
fn await_select(
|
||||
&self,
|
||||
clock_token: &ChangingClockToken<Self::Clock>,
|
||||
) -> nb::Result<(), Infallible>;
|
||||
}
|
||||
|
||||
/// Token which can be used to await the glitchless switch
|
||||
pub struct ChangingClockToken<G: Clock> {
|
||||
clock_nr: u8,
|
||||
clock: PhantomData<G>,
|
||||
}
|
||||
|
||||
/// For clocks that can be disabled
|
||||
pub trait StoppableClock {
|
||||
/// Enables the clock.
|
||||
fn enable(&mut self);
|
||||
|
||||
/// Disables the clock.
|
||||
fn disable(&mut self);
|
||||
|
||||
/// Kills the clock.
|
||||
fn kill(&mut self);
|
||||
}
|
||||
|
||||
/// Trait for things that can be used as clock source
|
||||
pub trait ClockSource: Sealed {
|
||||
/// Get the operating frequency for this source
|
||||
///
|
||||
/// Used to determine the divisor
|
||||
fn get_freq(&self) -> HertzU32;
|
||||
}
|
||||
|
||||
/// Trait to contrain which ClockSource is valid for which Clock
|
||||
pub trait ValidSrc<C: Clock>: Sealed + ClockSource {
|
||||
/// Is this a ClockSource for src or aux?
|
||||
fn is_aux(&self) -> bool;
|
||||
/// Get register value for this ClockSource
|
||||
fn variant(&self) -> C::Variant;
|
||||
}
|
||||
|
||||
clocks! {
|
||||
/// GPIO Output 0 Clock
|
||||
struct GpioOutput0Clock {
|
||||
init_freq: 0,
|
||||
reg: clk_gpout0,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
/// GPIO Output 1 Clock
|
||||
struct GpioOutput1Clock {
|
||||
init_freq: 0,
|
||||
reg: clk_gpout1,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
/// GPIO Output 2 Clock
|
||||
struct GpioOutput2Clock {
|
||||
init_freq: 0,
|
||||
reg: clk_gpout2,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
/// GPIO Output 3 Clock
|
||||
struct GpioOutput3Clock {
|
||||
init_freq: 0,
|
||||
reg: clk_gpout3,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
/// Reference Clock
|
||||
struct ReferenceClock {
|
||||
init_freq: 12_000_000, // Starts from ROSC which actually varies with input voltage etc, but 12 MHz seems to be a good value
|
||||
reg: clk_ref,
|
||||
src: {Rosc: ROSC_CLKSRC_PH, Xosc:XOSC_CLKSRC},
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
/// System Clock
|
||||
struct SystemClock {
|
||||
init_freq: 12_000_000, // ref_clk is 12 MHz
|
||||
reg: clk_sys,
|
||||
src: {ReferenceClock: CLK_REF},
|
||||
auxsrc: {PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
/// Peripheral Clock
|
||||
struct PeripheralClock {
|
||||
init_freq: 12_000_000, // sys_clk is 12 MHz
|
||||
reg: clk_peri,
|
||||
auxsrc: {SystemClock: CLK_SYS, PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1 },
|
||||
div: false
|
||||
}
|
||||
/// USB Clock
|
||||
struct UsbClock {
|
||||
init_freq: 0,
|
||||
reg: clk_usb,
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
/// Adc Clock
|
||||
struct AdcClock {
|
||||
init_freq: 0,
|
||||
reg: clk_adc,
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
/// RTC Clock
|
||||
struct RtcClock {
|
||||
init_freq: 0,
|
||||
reg: clk_rtc,
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemClock {
|
||||
fn get_default_clock_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
|
||||
pac::clocks::clk_sys_ctrl::SRC_A::CLK_REF
|
||||
}
|
||||
|
||||
fn get_aux_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
|
||||
pac::clocks::clk_sys_ctrl::SRC_A::CLKSRC_CLK_SYS_AUX
|
||||
}
|
||||
}
|
||||
|
||||
impl ReferenceClock {
|
||||
fn get_default_clock_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
|
||||
pac::clocks::clk_ref_ctrl::SRC_A::ROSC_CLKSRC_PH
|
||||
}
|
||||
|
||||
fn get_aux_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
|
||||
pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX
|
||||
}
|
||||
}
|
||||
|
||||
impl ClocksManager {
|
||||
/// Initialize the clocks to a sane default
|
||||
pub fn init_default(
|
||||
&mut self,
|
||||
xosc: &CrystalOscillator<Stable>,
|
||||
pll_sys: &PhaseLockedLoop<Locked, PLL_SYS>,
|
||||
pll_usb: &PhaseLockedLoop<Locked, PLL_USB>,
|
||||
) -> Result<(), ClockError> {
|
||||
// Configure clocks
|
||||
// CLK_REF = XOSC (12MHz) / 1 = 12MHz
|
||||
self.reference_clock
|
||||
.configure_clock(xosc, xosc.get_freq())?;
|
||||
|
||||
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
|
||||
self.system_clock
|
||||
.configure_clock(pll_sys, pll_sys.get_freq())?;
|
||||
|
||||
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
|
||||
self.usb_clock
|
||||
.configure_clock(pll_usb, pll_usb.get_freq())?;
|
||||
|
||||
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
|
||||
self.adc_clock
|
||||
.configure_clock(pll_usb, pll_usb.get_freq())?;
|
||||
|
||||
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
|
||||
self.rtc_clock.configure_clock(pll_usb, 46875u32.Hz())?;
|
||||
|
||||
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
|
||||
// Normally choose clk_sys or clk_usb
|
||||
self.peripheral_clock
|
||||
.configure_clock(&self.system_clock, self.system_clock.freq())
|
||||
}
|
||||
|
||||
/// Releases the CLOCKS block
|
||||
pub fn free(self) -> CLOCKS {
|
||||
self.clocks
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible init errors
|
||||
pub enum InitError {
|
||||
/// Something went wrong setting up the Xosc
|
||||
XoscErr(XoscError),
|
||||
/// Something went wrong setting up the Pll
|
||||
PllError(PllError),
|
||||
/// Something went wrong setting up the Clocks
|
||||
ClockError(ClockError),
|
||||
}
|
||||
|
||||
/// Initialize the clocks and plls according to the reference implementation
|
||||
pub fn init_clocks_and_plls(
|
||||
xosc_crystal_freq: u32,
|
||||
xosc_dev: XOSC,
|
||||
clocks_dev: CLOCKS,
|
||||
pll_sys_dev: PLL_SYS,
|
||||
pll_usb_dev: PLL_USB,
|
||||
resets: &mut RESETS,
|
||||
watchdog: &mut Watchdog,
|
||||
) -> Result<ClocksManager, InitError> {
|
||||
let xosc = setup_xosc_blocking(xosc_dev, xosc_crystal_freq.Hz()).map_err(InitError::XoscErr)?;
|
||||
|
||||
// Configure watchdog tick generation to tick over every microsecond
|
||||
watchdog.enable_tick_generation((xosc_crystal_freq / 1_000_000) as u8);
|
||||
|
||||
let mut clocks = ClocksManager::new(clocks_dev);
|
||||
|
||||
let pll_sys = setup_pll_blocking(
|
||||
pll_sys_dev,
|
||||
xosc.operating_frequency(),
|
||||
PLL_SYS_125MHZ,
|
||||
&mut clocks,
|
||||
resets,
|
||||
)
|
||||
.map_err(InitError::PllError)?;
|
||||
let pll_usb = setup_pll_blocking(
|
||||
pll_usb_dev,
|
||||
xosc.operating_frequency(),
|
||||
PLL_USB_48MHZ,
|
||||
&mut clocks,
|
||||
resets,
|
||||
)
|
||||
.map_err(InitError::PllError)?;
|
||||
|
||||
clocks
|
||||
.init_default(&xosc, &pll_sys, &pll_usb)
|
||||
.map_err(InitError::ClockError)?;
|
||||
Ok(clocks)
|
||||
}
|
||||
|
||||
// Calculates (numerator<<8)/denominator, avoiding 64bit division
|
||||
// Returns None if the result would not fit in 32 bit.
|
||||
fn fractional_div(numerator: u32, denominator: u32) -> Option<u32> {
|
||||
if denominator.eq(&numerator) {
|
||||
return Some(1 << 8);
|
||||
}
|
||||
|
||||
let div_int = numerator / denominator;
|
||||
if div_int >= 1 << 24 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let div_rem = numerator - (div_int * denominator);
|
||||
|
||||
let div_frac = if div_rem < 1 << 24 {
|
||||
// div_rem is small enough to shift it by 8 bits without overflow
|
||||
(div_rem << 8) / denominator
|
||||
} else {
|
||||
// div_rem is too large. Shift denominator right, instead.
|
||||
// As 1<<24 < div_rem < denominator, relative error caused by the
|
||||
// lost lower 8 bits of denominator is smaller than 2^-16
|
||||
(div_rem) / (denominator >> 8)
|
||||
};
|
||||
|
||||
Some((div_int << 8) + div_frac)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fractional_div() {
|
||||
// easy values
|
||||
assert_eq!(fractional_div(1, 1), Some(1 << 8));
|
||||
|
||||
// typical values
|
||||
assert_eq!(fractional_div(125_000_000, 48_000_000), Some(666));
|
||||
assert_eq!(fractional_div(48_000_000, 46875), Some(1024 << 8));
|
||||
|
||||
// resulting frequencies
|
||||
assert_eq!(
|
||||
fractional_div(
|
||||
125_000_000,
|
||||
fractional_div(125_000_000, 48_000_000).unwrap()
|
||||
),
|
||||
Some(48_048_048)
|
||||
);
|
||||
assert_eq!(
|
||||
fractional_div(48_000_000, fractional_div(48_000_000, 46875).unwrap()),
|
||||
Some(46875)
|
||||
);
|
||||
|
||||
// not allowed in src/clocks/mod.rs, but should still deliver correct results
|
||||
assert_eq!(fractional_div(1, 2), Some(128));
|
||||
assert_eq!(fractional_div(1, 256), Some(1));
|
||||
assert_eq!(fractional_div(1, 257), Some(0));
|
||||
|
||||
// borderline cases
|
||||
assert_eq!(fractional_div((1 << 24) - 1, 1), Some(((1 << 24) - 1) << 8));
|
||||
assert_eq!(fractional_div(1 << 24, 1), None);
|
||||
assert_eq!(fractional_div(1 << 24, 2), Some(1 << (23 + 8)));
|
||||
assert_eq!(fractional_div(1 << 24, (1 << 24) + 1), Some(1 << 8));
|
||||
assert_eq!(fractional_div(u32::MAX, u32::MAX), Some(1 << 8));
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
struct RpSpinlockCs;
|
||||
critical_section::set_impl!(RpSpinlockCs);
|
||||
|
||||
/// Marker value to indicate no-one has the lock.
|
||||
///
|
||||
/// Initialising `LOCK_OWNER` to 0 means cheaper static initialisation so it's the best choice
|
||||
const LOCK_UNOWNED: u8 = 0;
|
||||
|
||||
/// Indicates which core owns the lock so that we can call critical_section recursively.
|
||||
///
|
||||
/// 0 = no one has the lock, 1 = core0 has the lock, 2 = core1 has the lock
|
||||
static LOCK_OWNER: AtomicU8 = AtomicU8::new(LOCK_UNOWNED);
|
||||
|
||||
/// Marker value to indicate that we already owned the lock when we started the `critical_section`.
|
||||
///
|
||||
/// Since we can't take the spinlock when we already have it, we need some other way to keep track of `critical_section` ownership.
|
||||
/// `critical_section` provides a token for communicating between `acquire` and `release` so we use that.
|
||||
/// If we're the outermost call to `critical_section` we use the values 0 and 1 to indicate we should release the spinlock and set the interrupts back to disabled and enabled, respectively.
|
||||
/// The value 2 indicates that we aren't the outermost call, and should not release the spinlock or re-enable interrupts in `release`
|
||||
const LOCK_ALREADY_OWNED: u8 = 2;
|
||||
|
||||
unsafe impl critical_section::Impl for RpSpinlockCs {
|
||||
unsafe fn acquire() -> u8 {
|
||||
RpSpinlockCs::acquire()
|
||||
}
|
||||
|
||||
unsafe fn release(token: u8) {
|
||||
RpSpinlockCs::release(token);
|
||||
}
|
||||
}
|
||||
|
||||
impl RpSpinlockCs {
|
||||
unsafe fn acquire() -> u8 {
|
||||
// Store the initial interrupt state and current core id in stack variables
|
||||
let interrupts_active = cortex_m::register::primask::read().is_active();
|
||||
// We reserved 0 as our `LOCK_UNOWNED` value, so add 1 to core_id so we get 1 for core0, 2 for core1.
|
||||
let core = crate::Sio::core() + 1_u8;
|
||||
// Do we already own the spinlock?
|
||||
if LOCK_OWNER.load(Ordering::Acquire) == core {
|
||||
// We already own the lock, so we must have called acquire within a critical_section.
|
||||
// Return the magic inner-loop value so that we know not to re-enable interrupts in release()
|
||||
LOCK_ALREADY_OWNED
|
||||
} else {
|
||||
// Spin until we get the lock
|
||||
loop {
|
||||
// Need to disable interrupts to ensure that we will not deadlock
|
||||
// if an interrupt enters critical_section::Impl after we acquire the lock
|
||||
cortex_m::interrupt::disable();
|
||||
// Ensure the compiler doesn't re-order accesses and violate safety here
|
||||
core::sync::atomic::compiler_fence(Ordering::SeqCst);
|
||||
// Read the spinlock reserved for `critical_section`
|
||||
if let Some(lock) = crate::sio::Spinlock31::try_claim() {
|
||||
// We just acquired the lock.
|
||||
// 1. Forget it, so we don't immediately unlock
|
||||
core::mem::forget(lock);
|
||||
// 2. Store which core we are so we can tell if we're called recursively
|
||||
LOCK_OWNER.store(core, Ordering::Relaxed);
|
||||
break;
|
||||
}
|
||||
// We didn't get the lock, enable interrupts if they were enabled before we started
|
||||
if interrupts_active {
|
||||
cortex_m::interrupt::enable();
|
||||
}
|
||||
}
|
||||
// If we broke out of the loop we have just acquired the lock
|
||||
// As the outermost loop, we want to return the interrupt status to restore later
|
||||
interrupts_active as _
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn release(token: u8) {
|
||||
// Did we already own the lock at the start of the `critical_section`?
|
||||
if token != LOCK_ALREADY_OWNED {
|
||||
// No, it wasn't owned at the start of this `critical_section`, so this core no longer owns it.
|
||||
// Set `LOCK_OWNER` back to `LOCK_UNOWNED` to ensure the next critical section tries to obtain the spinlock instead
|
||||
LOCK_OWNER.store(LOCK_UNOWNED, Ordering::Relaxed);
|
||||
// Ensure the compiler doesn't re-order accesses and violate safety here
|
||||
core::sync::atomic::compiler_fence(Ordering::SeqCst);
|
||||
// Release the spinlock to allow others to enter critical_section again
|
||||
crate::sio::Spinlock31::release();
|
||||
// Re-enable interrupts if they were enabled when we first called acquire()
|
||||
// We only do this on the outermost `critical_section` to ensure interrupts stay disabled
|
||||
// for the whole time that we have the lock
|
||||
if token != 0 {
|
||||
cortex_m::interrupt::enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
//! # DMA
|
||||
//!
|
||||
//! This is the start of a DMA driver.
|
||||
|
||||
/// The DREQ value for PIO0's TX FIFO 0
|
||||
pub const DREQ_PIO0_TX0: u8 = 0;
|
||||
/// The DREQ value for PIO0's TX FIFO 1
|
||||
pub const DREQ_PIO0_TX1: u8 = 1;
|
||||
/// The DREQ value for PIO0's TX FIFO 2
|
||||
pub const DREQ_PIO0_TX2: u8 = 2;
|
||||
/// The DREQ value for PIO0's TX FIFO 3
|
||||
pub const DREQ_PIO0_TX3: u8 = 3;
|
||||
/// The DREQ value for PIO0's RX FIFO 0
|
||||
pub const DREQ_PIO0_RX0: u8 = 4;
|
||||
/// The DREQ value for PIO0's RX FIFO 1
|
||||
pub const DREQ_PIO0_RX1: u8 = 5;
|
||||
/// The DREQ value for PIO0's RX FIFO 2
|
||||
pub const DREQ_PIO0_RX2: u8 = 6;
|
||||
/// The DREQ value for PIO0's RX FIFO 3
|
||||
pub const DREQ_PIO0_RX3: u8 = 7;
|
||||
/// The DREQ value for PIO1's TX FIFO 0
|
||||
pub const DREQ_PIO1_TX0: u8 = 8;
|
||||
/// The DREQ value for PIO1's TX FIFO 1
|
||||
pub const DREQ_PIO1_TX1: u8 = 9;
|
||||
/// The DREQ value for PIO1's TX FIFO 2
|
||||
pub const DREQ_PIO1_TX2: u8 = 10;
|
||||
/// The DREQ value for PIO1's TX FIFO 3
|
||||
pub const DREQ_PIO1_TX3: u8 = 11;
|
||||
/// The DREQ value for PIO1's RX FIFO 0
|
||||
pub const DREQ_PIO1_RX0: u8 = 12;
|
||||
/// The DREQ value for PIO1's RX FIFO 1
|
||||
pub const DREQ_PIO1_RX1: u8 = 13;
|
||||
/// The DREQ value for PIO1's RX FIFO 2
|
||||
pub const DREQ_PIO1_RX2: u8 = 14;
|
||||
/// The DREQ value for PIO1's RX FIFO 3
|
||||
pub const DREQ_PIO1_RX3: u8 = 15;
|
||||
/// The DREQ value for SPI0's TX FIFO
|
||||
pub const DREQ_SPI0_TX: u8 = 16;
|
||||
/// The DREQ value for SPI0's RX FIFO
|
||||
pub const DREQ_SPI0_RX: u8 = 17;
|
||||
/// The DREQ value for SPI1's TX FIFO
|
||||
pub const DREQ_SPI1_TX: u8 = 18;
|
||||
/// The DREQ value for SPI1's RX FIFO
|
||||
pub const DREQ_SPI1_RX: u8 = 19;
|
||||
/// The DREQ value for UART0's TX FIFO
|
||||
pub const DREQ_UART0_TX: u8 = 20;
|
||||
/// The DREQ value for UART0's RX FIFO
|
||||
pub const DREQ_UART0_RX: u8 = 21;
|
||||
/// The DREQ value for UART1's TX FIFO
|
||||
pub const DREQ_UART1_TX: u8 = 22;
|
||||
/// The DREQ value for UART1's RX FIFO
|
||||
pub const DREQ_UART1_RX: u8 = 23;
|
||||
/// The DREQ value for PWM Counter 0's Wrap Value
|
||||
pub const DREQ_PWM_WRAP0: u8 = 24;
|
||||
/// The DREQ value for PWM Counter 1's Wrap Value
|
||||
pub const DREQ_PWM_WRAP1: u8 = 25;
|
||||
/// The DREQ value for PWM Counter 2's Wrap Value
|
||||
pub const DREQ_PWM_WRAP2: u8 = 26;
|
||||
/// The DREQ value for PWM Counter 3's Wrap Value
|
||||
pub const DREQ_PWM_WRAP3: u8 = 27;
|
||||
/// The DREQ value for PWM Counter 4's Wrap Value
|
||||
pub const DREQ_PWM_WRAP4: u8 = 28;
|
||||
/// The DREQ value for PWM Counter 5's Wrap Value
|
||||
pub const DREQ_PWM_WRAP5: u8 = 29;
|
||||
/// The DREQ value for PWM Counter 6's Wrap Value
|
||||
pub const DREQ_PWM_WRAP6: u8 = 30;
|
||||
/// The DREQ value for PWM Counter 7's Wrap Value
|
||||
pub const DREQ_PWM_WRAP7: u8 = 31;
|
||||
/// The DREQ value for I2C0's TX FIFO
|
||||
pub const DREQ_I2C0_TX: u8 = 32;
|
||||
/// The DREQ value for I2C0's RX FIFO
|
||||
pub const DREQ_I2C0_RX: u8 = 33;
|
||||
/// The DREQ value for I2C1's TX FIFO
|
||||
pub const DREQ_I2C1_TX: u8 = 34;
|
||||
/// The DREQ value for I2C1's RX FIFO
|
||||
pub const DREQ_I2C1_RX: u8 = 35;
|
||||
/// The DREQ value for the ADC
|
||||
pub const DREQ_ADC: u8 = 36;
|
||||
/// The DREQ value for the XIP Streaming FIFO
|
||||
pub const DREQ_XIP_STREAM: u8 = 37;
|
||||
/// The DREQ value for the XIP SSI TX FIFO
|
||||
pub const DREQ_XIP_SSITX: u8 = 38;
|
||||
/// The DREQ value for the XIP SSI RX FIFO
|
||||
pub const DREQ_XIP_SSIRX: u8 = 39;
|
|
@ -1,89 +0,0 @@
|
|||
use super::{Float, Int};
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMAdd {
|
||||
fn rom_add(self, b: Self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMAdd for f32 {
|
||||
fn rom_add(self, b: Self) -> Self {
|
||||
rom_data::float_funcs::fadd(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMAdd for f64 {
|
||||
fn rom_add(self, b: Self) -> Self {
|
||||
rom_data::double_funcs::dadd(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn add<F: Float + ROMAdd>(a: F, b: F) -> F {
|
||||
if a.is_not_finite() {
|
||||
if b.is_not_finite() {
|
||||
let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
|
||||
let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
|
||||
|
||||
if class_a == F::Int::ZERO && class_b == F::Int::ZERO {
|
||||
// inf + inf = inf
|
||||
return a;
|
||||
}
|
||||
if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK {
|
||||
// -inf + (-inf) = -inf
|
||||
return a;
|
||||
}
|
||||
|
||||
// Sign mismatch, or either is NaN already
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
// [-]inf/NaN + X = [-]inf/NaN
|
||||
return a;
|
||||
}
|
||||
|
||||
if b.is_not_finite() {
|
||||
// X + [-]inf/NaN = [-]inf/NaN
|
||||
return b;
|
||||
}
|
||||
|
||||
a.rom_add(b)
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[alias = __addsf3vfp]
|
||||
#[aeabi = __aeabi_fadd]
|
||||
extern "C" fn __addsf3(a: f32, b: f32) -> f32 {
|
||||
add(a, b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __adddf3vfp]
|
||||
#[aeabi = __aeabi_dadd]
|
||||
extern "C" fn __adddf3(a: f64, b: f64) -> f64 {
|
||||
add(a, b)
|
||||
}
|
||||
|
||||
// The ROM just implements subtraction the same way, so just do it here
|
||||
// and save the work of implementing more complicated NaN/inf handling.
|
||||
|
||||
#[alias = __subsf3vfp]
|
||||
#[aeabi = __aeabi_fsub]
|
||||
extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
|
||||
add(a, -b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __subdf3vfp]
|
||||
#[aeabi = __aeabi_dsub]
|
||||
extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
|
||||
add(a, -b)
|
||||
}
|
||||
|
||||
extern "aapcs" fn __aeabi_frsub(a: f32, b: f32) -> f32 {
|
||||
add(b, -a)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_drsub(a: f64, b: f64) -> f64 {
|
||||
add(b, -a)
|
||||
}
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMCmp {
|
||||
fn rom_cmp(self, b: Self) -> i32;
|
||||
}
|
||||
|
||||
impl ROMCmp for f32 {
|
||||
fn rom_cmp(self, b: Self) -> i32 {
|
||||
rom_data::float_funcs::fcmp(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMCmp for f64 {
|
||||
fn rom_cmp(self, b: Self) -> i32 {
|
||||
rom_data::double_funcs::dcmp(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn le_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
|
||||
if a.is_nan() || b.is_nan() {
|
||||
1
|
||||
} else {
|
||||
a.rom_cmp(b)
|
||||
}
|
||||
}
|
||||
|
||||
fn ge_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
|
||||
if a.is_nan() || b.is_nan() {
|
||||
-1
|
||||
} else {
|
||||
a.rom_cmp(b)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __eqsf2, __ltsf2, __nesf2]
|
||||
extern "C" fn __lesf2(a: f32, b: f32) -> i32 {
|
||||
le_abi(a, b)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __eqdf2, __ltdf2, __nedf2]
|
||||
extern "C" fn __ledf2(a: f64, b: f64) -> i32 {
|
||||
le_abi(a, b)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __gtsf2]
|
||||
extern "C" fn __gesf2(a: f32, b: f32) -> i32 {
|
||||
ge_abi(a, b)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __gtdf2]
|
||||
extern "C" fn __gedf2(a: f64, b: f64) -> i32 {
|
||||
ge_abi(a, b)
|
||||
}
|
||||
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gesf2vfp(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gedf2vfp(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gtsf2vfp(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gtdf2vfp(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __ltsf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __ltdf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __lesf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __ledf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __nesf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) != 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __nedf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) != 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __eqsf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __eqdf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
// Some of these are also not connected in the Pico SDK. This is probably
|
||||
// because the ROM version actually does a fixed point conversion, just with
|
||||
// the fractional width set to zero.
|
||||
|
||||
intrinsics! {
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_i2f]
|
||||
extern "C" fn __floatsisf(i: i32) -> f32 {
|
||||
rom_data::float_funcs::int_to_float(i)
|
||||
}
|
||||
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_i2d]
|
||||
extern "C" fn __floatsidf(i: i32) -> f64 {
|
||||
rom_data::double_funcs::int_to_double(i)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[aeabi = __aeabi_l2f]
|
||||
extern "C" fn __floatdisf(i: i64) -> f32 {
|
||||
rom_data::float_funcs::int64_to_float(i)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_l2d]
|
||||
extern "C" fn __floatdidf(i: i64) -> f64 {
|
||||
rom_data::double_funcs::int64_to_double(i)
|
||||
}
|
||||
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_ui2f]
|
||||
extern "C" fn __floatunsisf(i: u32) -> f32 {
|
||||
rom_data::float_funcs::uint_to_float(i)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_ui2d]
|
||||
extern "C" fn __floatunsidf(i: u32) -> f64 {
|
||||
rom_data::double_funcs::uint_to_double(i)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_ul2f]
|
||||
extern "C" fn __floatundisf(i: u64) -> f32 {
|
||||
rom_data::float_funcs::uint64_to_float(i)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_ul2d]
|
||||
extern "C" fn __floatundidf(i: u64) -> f64 {
|
||||
rom_data::double_funcs::uint64_to_double(i)
|
||||
}
|
||||
|
||||
|
||||
// The Pico SDK does some optimization here (e.x. fast paths for zero and
|
||||
// one), but we can just directly connect it.
|
||||
#[aeabi = __aeabi_f2iz]
|
||||
extern "C" fn __fixsfsi(f: f32) -> i32 {
|
||||
rom_data::float_funcs::float_to_int(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_f2lz]
|
||||
extern "C" fn __fixsfdi(f: f32) -> i64 {
|
||||
rom_data::float_funcs::float_to_int64(f)
|
||||
}
|
||||
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2iz]
|
||||
extern "C" fn __fixdfsi(f: f64) -> i32 {
|
||||
rom_data::double_funcs::double_to_int(f)
|
||||
}
|
||||
|
||||
// Like with the 32 bit version, there's optimization that we just
|
||||
// skip.
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2lz]
|
||||
extern "C" fn __fixdfdi(f: f64) -> i64 {
|
||||
rom_data::double_funcs::double_to_int64(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_f2uiz]
|
||||
extern "C" fn __fixunssfsi(f: f32) -> u32 {
|
||||
rom_data::float_funcs::float_to_uint(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_f2ulz]
|
||||
extern "C" fn __fixunssfdi(f: f32) -> u64 {
|
||||
rom_data::float_funcs::float_to_uint64(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2uiz]
|
||||
extern "C" fn __fixunsdfsi(f: f64) -> u32 {
|
||||
rom_data::double_funcs::double_to_uint(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2ulz]
|
||||
extern "C" fn __fixunsdfdi(f: f64) -> u64 {
|
||||
rom_data::double_funcs::double_to_uint64(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __extendsfdf2vfp]
|
||||
#[aeabi = __aeabi_f2d]
|
||||
extern "C" fn __extendsfdf2(f: f32) -> f64 {
|
||||
if f.is_not_finite() {
|
||||
return f64::from_repr(
|
||||
// Not finite
|
||||
f64::EXPONENT_MASK |
|
||||
// Preserve NaN or inf
|
||||
((f.repr() & f32::SIGNIFICAND_MASK) as u64) |
|
||||
// Preserve sign
|
||||
((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS)
|
||||
);
|
||||
}
|
||||
rom_data::float_funcs::float_to_double(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __truncdfsf2vfp]
|
||||
#[aeabi = __aeabi_d2f]
|
||||
extern "C" fn __truncdfsf2(f: f64) -> f32 {
|
||||
if f.is_not_finite() {
|
||||
let mut repr: u32 =
|
||||
// Not finite
|
||||
f32::EXPONENT_MASK |
|
||||
// Preserve sign
|
||||
((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32;
|
||||
// Set NaN
|
||||
if (f.repr() & f64::SIGNIFICAND_MASK) != 0 {
|
||||
repr |= 1;
|
||||
}
|
||||
return f32::from_repr(repr);
|
||||
}
|
||||
rom_data::double_funcs::double_to_float(f)
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
// Make sure this stays as a separate call, because when it's inlined the
|
||||
// compiler will move the save of the registers used to contain the divider
|
||||
// state into the function prologue. That save and restore (push/pop) takes
|
||||
// longer than the actual division, so doing it in the common case where
|
||||
// they are not required wastes a lot of time.
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn save_divider_and_call<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let sio = unsafe { &(*pac::SIO::ptr()) };
|
||||
|
||||
// Since we can't save the signed-ness of the calculation, we have to make
|
||||
// sure that there's at least an 8 cycle delay before we read the result.
|
||||
// The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads.
|
||||
// Since we can't be sure the Rust implementation will optimize to the same,
|
||||
// just use an explicit wait.
|
||||
while !sio.div_csr.read().ready().bit() {}
|
||||
|
||||
// Read the quotient last, since that's what clears the dirty flag
|
||||
let dividend = sio.div_udividend.read().bits();
|
||||
let divisor = sio.div_udivisor.read().bits();
|
||||
let remainder = sio.div_remainder.read().bits();
|
||||
let quotient = sio.div_quotient.read().bits();
|
||||
|
||||
// If we get interrupted here (before a write sets the DIRTY flag) its fine, since
|
||||
// we have the full state, so the interruptor doesn't have to restore it. Once the
|
||||
// write happens and the DIRTY flag is set, the interruptor becomes responsible for
|
||||
// restoring our state.
|
||||
let result = f();
|
||||
|
||||
// If we are interrupted here, then the interruptor will start an incorrect calculation
|
||||
// using a wrong divisor, but we'll restore the divisor and result ourselves correctly.
|
||||
// This sets DIRTY, so any interruptor will save the state.
|
||||
sio.div_udividend.write(|w| unsafe { w.bits(dividend) });
|
||||
// If we are interrupted here, the the interruptor may start the calculation using
|
||||
// incorrectly signed inputs, but we'll restore the result ourselves.
|
||||
// This sets DIRTY, so any interruptor will save the state.
|
||||
sio.div_udivisor.write(|w| unsafe { w.bits(divisor) });
|
||||
// If we are interrupted here, the interruptor will have restored everything but the
|
||||
// quotient may be wrongly signed. If the calculation started by the above writes is
|
||||
// still ongoing it is stopped, so it won't replace the result we're restoring.
|
||||
// DIRTY and READY set, but only DIRTY matters to make the interruptor save the state.
|
||||
sio.div_remainder.write(|w| unsafe { w.bits(remainder) });
|
||||
// State fully restored after the quotient write. This sets both DIRTY and READY, so
|
||||
// whatever we may have interrupted can read the result.
|
||||
sio.div_quotient.write(|w| unsafe { w.bits(quotient) });
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn save_divider<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let sio = unsafe { &(*pac::SIO::ptr()) };
|
||||
if !sio.div_csr.read().dirty().bit() {
|
||||
// Not dirty, so nothing is waiting for the calculation. So we can just
|
||||
// issue it directly without a save/restore.
|
||||
f()
|
||||
} else {
|
||||
save_divider_and_call(f)
|
||||
}
|
||||
}
|
||||
|
||||
trait ROMDiv {
|
||||
fn rom_div(self, b: Self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMDiv for f32 {
|
||||
fn rom_div(self, b: Self) -> Self {
|
||||
// ROM implementation uses the hardware divider, so we have to save it
|
||||
save_divider(|| rom_data::float_funcs::fdiv(self, b))
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMDiv for f64 {
|
||||
fn rom_div(self, b: Self) -> Self {
|
||||
// ROM implementation uses the hardware divider, so we have to save it
|
||||
save_divider(|| rom_data::double_funcs::ddiv(self, b))
|
||||
}
|
||||
}
|
||||
|
||||
fn div<F: Float + ROMDiv>(a: F, b: F) -> F {
|
||||
if a.is_not_finite() {
|
||||
if b.is_not_finite() {
|
||||
// inf/NaN / inf/NaN = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
if b.is_zero() {
|
||||
// inf/NaN / 0 = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
return if b.is_sign_negative() {
|
||||
// [+/-]inf/NaN / (-X) = [-/+]inf/NaN
|
||||
a.negate()
|
||||
} else {
|
||||
// [-]inf/NaN / X = [-]inf/NaN
|
||||
a
|
||||
};
|
||||
}
|
||||
|
||||
if b.is_nan() {
|
||||
// X / NaN = NaN
|
||||
return b;
|
||||
}
|
||||
|
||||
// ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only
|
||||
// need to catch 0 / 0
|
||||
if b.is_zero() && a.is_zero() {
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
a.rom_div(b)
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[alias = __divsf3vfp]
|
||||
#[aeabi = __aeabi_fdiv]
|
||||
extern "C" fn __divsf3(a: f32, b: f32) -> f32 {
|
||||
div(a, b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __divdf3vfp]
|
||||
#[aeabi = __aeabi_ddiv]
|
||||
extern "C" fn __divdf3(a: f64, b: f64) -> f64 {
|
||||
div(a, b)
|
||||
}
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
use crate::float::{Float, Int};
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMFunctions {
|
||||
fn sqrt(self) -> Self;
|
||||
fn ln(self) -> Self;
|
||||
fn exp(self) -> Self;
|
||||
fn sin(self) -> Self;
|
||||
fn cos(self) -> Self;
|
||||
fn tan(self) -> Self;
|
||||
fn atan2(self, y: Self) -> Self;
|
||||
|
||||
fn to_trig_range(self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMFunctions for f32 {
|
||||
fn sqrt(self) -> Self {
|
||||
rom_data::float_funcs::fsqrt(self)
|
||||
}
|
||||
|
||||
fn ln(self) -> Self {
|
||||
rom_data::float_funcs::fln(self)
|
||||
}
|
||||
|
||||
fn exp(self) -> Self {
|
||||
rom_data::float_funcs::fexp(self)
|
||||
}
|
||||
|
||||
fn sin(self) -> Self {
|
||||
rom_data::float_funcs::fsin(self)
|
||||
}
|
||||
|
||||
fn cos(self) -> Self {
|
||||
rom_data::float_funcs::fcos(self)
|
||||
}
|
||||
|
||||
fn tan(self) -> Self {
|
||||
rom_data::float_funcs::ftan(self)
|
||||
}
|
||||
|
||||
fn atan2(self, y: Self) -> Self {
|
||||
rom_data::float_funcs::fatan2(self, y)
|
||||
}
|
||||
|
||||
fn to_trig_range(self) -> Self {
|
||||
// -128 < X < 128, logic from the Pico SDK
|
||||
let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
|
||||
if exponent < 134 {
|
||||
self
|
||||
} else {
|
||||
self % (core::f32::consts::PI * 2.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMFunctions for f64 {
|
||||
fn sqrt(self) -> Self {
|
||||
rom_data::double_funcs::dsqrt(self)
|
||||
}
|
||||
|
||||
fn ln(self) -> Self {
|
||||
rom_data::double_funcs::dln(self)
|
||||
}
|
||||
|
||||
fn exp(self) -> Self {
|
||||
rom_data::double_funcs::dexp(self)
|
||||
}
|
||||
|
||||
fn sin(self) -> Self {
|
||||
rom_data::double_funcs::dsin(self)
|
||||
}
|
||||
|
||||
fn cos(self) -> Self {
|
||||
rom_data::double_funcs::dcos(self)
|
||||
}
|
||||
fn tan(self) -> Self {
|
||||
rom_data::double_funcs::dtan(self)
|
||||
}
|
||||
|
||||
fn atan2(self, y: Self) -> Self {
|
||||
rom_data::double_funcs::datan2(self, y)
|
||||
}
|
||||
|
||||
fn to_trig_range(self) -> Self {
|
||||
// -1024 < X < 1024, logic from the Pico SDK
|
||||
let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
|
||||
if exponent < 1033 {
|
||||
self
|
||||
} else {
|
||||
self % (core::f64::consts::PI * 2.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_negative_nonzero_or_nan<F: Float>(f: F) -> bool {
|
||||
let repr = f.repr();
|
||||
if (repr & F::SIGN_MASK) != F::Int::ZERO {
|
||||
// Negative, so anything other than exactly zero
|
||||
return (repr & (!F::SIGN_MASK)) != F::Int::ZERO;
|
||||
}
|
||||
// NaN
|
||||
(repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK
|
||||
}
|
||||
|
||||
fn sqrt<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if is_negative_nonzero_or_nan(f) {
|
||||
F::NAN
|
||||
} else {
|
||||
f.sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
fn ln<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if is_negative_nonzero_or_nan(f) {
|
||||
F::NAN
|
||||
} else {
|
||||
f.ln()
|
||||
}
|
||||
}
|
||||
|
||||
fn exp<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_nan() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.exp()
|
||||
}
|
||||
}
|
||||
|
||||
fn sin<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_not_finite() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.to_trig_range().sin()
|
||||
}
|
||||
}
|
||||
|
||||
fn cos<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_not_finite() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.to_trig_range().cos()
|
||||
}
|
||||
}
|
||||
|
||||
fn tan<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_not_finite() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.to_trig_range().tan()
|
||||
}
|
||||
}
|
||||
|
||||
fn atan2<F: Float + ROMFunctions>(x: F, y: F) -> F {
|
||||
if x.is_nan() || y.is_nan() {
|
||||
F::NAN
|
||||
} else {
|
||||
x.to_trig_range().atan2(y)
|
||||
}
|
||||
}
|
||||
|
||||
// Name collisions
|
||||
mod intrinsics {
|
||||
intrinsics! {
|
||||
extern "C" fn sqrtf(f: f32) -> f32 {
|
||||
super::sqrt(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "C" fn sqrt(f: f64) -> f64 {
|
||||
super::sqrt(f)
|
||||
}
|
||||
|
||||
extern "C" fn logf(f: f32) -> f32 {
|
||||
super::ln(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "C" fn log(f: f64) -> f64 {
|
||||
super::ln(f)
|
||||
}
|
||||
|
||||
extern "C" fn expf(f: f32) -> f32 {
|
||||
super::exp(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "C" fn exp(f: f64) -> f64 {
|
||||
super::exp(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
extern "C" fn sinf(f: f32) -> f32 {
|
||||
super::sin(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn sin(f: f64) -> f64 {
|
||||
super::sin(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
extern "C" fn cosf(f: f32) -> f32 {
|
||||
super::cos(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn cos(f: f64) -> f64 {
|
||||
super::cos(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
extern "C" fn tanf(f: f32) -> f32 {
|
||||
super::tan(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn tan(f: f64) -> f64 {
|
||||
super::tan(f)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
extern "C" fn atan2f(a: f32, b: f32) -> f32 {
|
||||
super::atan2(a, b)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
extern "C" fn atan2(a: f64, b: f64) -> f64 {
|
||||
super::atan2(a, b)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
use core::ops;
|
||||
|
||||
// Borrowed and simplified from compiler-builtins so we can use bit ops
|
||||
// on floating point without macro soup.
|
||||
pub trait Int:
|
||||
Copy
|
||||
+ core::fmt::Debug
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ ops::AddAssign
|
||||
+ ops::SubAssign
|
||||
+ ops::BitAndAssign
|
||||
+ ops::BitOrAssign
|
||||
+ ops::BitXorAssign
|
||||
+ ops::ShlAssign<i32>
|
||||
+ ops::ShrAssign<u32>
|
||||
+ ops::Add<Output = Self>
|
||||
+ ops::Sub<Output = Self>
|
||||
+ ops::Div<Output = Self>
|
||||
+ ops::Shl<u32, Output = Self>
|
||||
+ ops::Shr<u32, Output = Self>
|
||||
+ ops::BitOr<Output = Self>
|
||||
+ ops::BitXor<Output = Self>
|
||||
+ ops::BitAnd<Output = Self>
|
||||
+ ops::Not<Output = Self>
|
||||
{
|
||||
const ZERO: Self;
|
||||
}
|
||||
|
||||
macro_rules! int_impl {
|
||||
($ty:ty) => {
|
||||
impl Int for $ty {
|
||||
const ZERO: Self = 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_impl!(u32);
|
||||
int_impl!(u64);
|
||||
|
||||
pub trait Float:
|
||||
Copy
|
||||
+ core::fmt::Debug
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ ops::AddAssign
|
||||
+ ops::MulAssign
|
||||
+ ops::Add<Output = Self>
|
||||
+ ops::Sub<Output = Self>
|
||||
+ ops::Div<Output = Self>
|
||||
+ ops::Rem<Output = Self>
|
||||
{
|
||||
/// A uint of the same with as the float
|
||||
type Int: Int;
|
||||
|
||||
/// NaN representation for the float
|
||||
const NAN: Self;
|
||||
|
||||
/// The bitwidth of the float type
|
||||
const BITS: u32;
|
||||
|
||||
/// The bitwidth of the significand
|
||||
const SIGNIFICAND_BITS: u32;
|
||||
|
||||
/// A mask for the sign bit
|
||||
const SIGN_MASK: Self::Int;
|
||||
|
||||
/// A mask for the significand
|
||||
const SIGNIFICAND_MASK: Self::Int;
|
||||
|
||||
/// A mask for the exponent
|
||||
const EXPONENT_MASK: Self::Int;
|
||||
|
||||
/// Returns `self` transmuted to `Self::Int`
|
||||
fn repr(self) -> Self::Int;
|
||||
|
||||
/// Returns a `Self::Int` transmuted back to `Self`
|
||||
fn from_repr(a: Self::Int) -> Self;
|
||||
|
||||
/// Return a sign swapped `self`
|
||||
fn negate(self) -> Self;
|
||||
|
||||
/// Returns true if `self` is either NaN or infinity
|
||||
fn is_not_finite(self) -> bool {
|
||||
(self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK
|
||||
}
|
||||
|
||||
/// Returns true if `self` is infinity
|
||||
fn is_infinity(self) -> bool {
|
||||
(self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK
|
||||
}
|
||||
|
||||
/// Returns true if `self is NaN
|
||||
fn is_nan(self) -> bool {
|
||||
(self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK
|
||||
}
|
||||
|
||||
/// Returns true if `self` is negative
|
||||
fn is_sign_negative(self) -> bool {
|
||||
(self.repr() & Self::SIGN_MASK) != Self::Int::ZERO
|
||||
}
|
||||
|
||||
/// Returns true if `self` is zero (either sign)
|
||||
fn is_zero(self) -> bool {
|
||||
(self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! float_impl {
|
||||
($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => {
|
||||
impl Float for $ty {
|
||||
type Int = $ity;
|
||||
|
||||
const NAN: Self = <$ty>::NAN;
|
||||
|
||||
const BITS: u32 = $bits;
|
||||
const SIGNIFICAND_BITS: u32 = $significand_bits;
|
||||
|
||||
const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1);
|
||||
const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1;
|
||||
const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK);
|
||||
|
||||
fn repr(self) -> Self::Int {
|
||||
self.to_bits()
|
||||
}
|
||||
|
||||
fn from_repr(a: Self::Int) -> Self {
|
||||
Self::from_bits(a)
|
||||
}
|
||||
|
||||
fn negate(self) -> Self {
|
||||
-self
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
float_impl!(f32, u32, 32, 23);
|
||||
float_impl!(f64, u64, 64, 52);
|
||||
|
||||
mod add_sub;
|
||||
mod cmp;
|
||||
mod conv;
|
||||
mod div;
|
||||
mod functions;
|
||||
mod mul;
|
|
@ -1,67 +0,0 @@
|
|||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMMul {
|
||||
fn rom_mul(self, b: Self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMMul for f32 {
|
||||
fn rom_mul(self, b: Self) -> Self {
|
||||
rom_data::float_funcs::fmul(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMMul for f64 {
|
||||
fn rom_mul(self, b: Self) -> Self {
|
||||
rom_data::double_funcs::dmul(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn mul<F: Float + ROMMul>(a: F, b: F) -> F {
|
||||
if a.is_not_finite() {
|
||||
if b.is_zero() {
|
||||
// [-]inf/NaN * 0 = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
return if b.is_sign_negative() {
|
||||
// [+/-]inf/NaN * (-X) = [-/+]inf/NaN
|
||||
a.negate()
|
||||
} else {
|
||||
// [-]inf/NaN * X = [-]inf/NaN
|
||||
a
|
||||
};
|
||||
}
|
||||
|
||||
if b.is_not_finite() {
|
||||
if a.is_zero() {
|
||||
// 0 * [-]inf/NaN = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
return if b.is_sign_negative() {
|
||||
// (-X) * [+/-]inf/NaN = [-/+]inf/NaN
|
||||
b.negate()
|
||||
} else {
|
||||
// X * [-]inf/NaN = [-]inf/NaN
|
||||
b
|
||||
};
|
||||
}
|
||||
|
||||
a.rom_mul(b)
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[alias = __mulsf3vfp]
|
||||
#[aeabi = __aeabi_fmul]
|
||||
extern "C" fn __mulsf3(a: f32, b: f32) -> f32 {
|
||||
mul(a, b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __muldf3vfp]
|
||||
#[aeabi = __aeabi_dmul]
|
||||
extern "C" fn __muldf3(a: f64, b: f64) -> f64 {
|
||||
mul(a, b)
|
||||
}
|
||||
}
|
|
@ -1,600 +0,0 @@
|
|||
//! # Type-erased, value-level module for GPIO pins
|
||||
//!
|
||||
//! Based heavily on `atsamd-hal`.
|
||||
//!
|
||||
//! Although the type-level API is generally preferred, it is not suitable in
|
||||
//! all cases. Because each pin is represented by a distinct type, it is not
|
||||
//! possible to store multiple pins in a homogeneous data structure. The
|
||||
//! value-level API solves this problem by erasing the type information and
|
||||
//! tracking the pin at run-time.
|
||||
//!
|
||||
//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two
|
||||
//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`]
|
||||
//! respectively. The implementation of these types closely mirrors the
|
||||
//! type-level API.
|
||||
//!
|
||||
//! Instances of [`DynPin`] cannot be created directly. Rather, they must be
|
||||
//! created from their type-level equivalents using [`From`]/[`Into`].
|
||||
//!
|
||||
//! ```no_run
|
||||
//! // Move a pin out of the Pins struct and convert to a DynPin
|
||||
//! # use rp2040_hal::{pac, gpio::{DynPin, bank0::Gpio12, Pins}, Sio};
|
||||
//! # let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! # let sio = Sio::new(peripherals.SIO);
|
||||
//! # let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//! # use rp2040_hal::gpio::DYN_FLOATING_INPUT;
|
||||
//! let gpio12: DynPin = pins.gpio12.into();
|
||||
//! ```
|
||||
//!
|
||||
//! Conversions between pin modes use a value-level version of the type-level
|
||||
//! API.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use rp2040_hal::{pac, gpio::{DynPin, Pins}, Sio};
|
||||
//! # let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! # let sio = Sio::new(peripherals.SIO);
|
||||
//! # let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//! # use rp2040_hal::gpio::DYN_FLOATING_INPUT;
|
||||
//! # let mut gpio12: DynPin = pins.gpio12.into();
|
||||
//! // Use one of the literal function names
|
||||
//! gpio12.into_floating_input();
|
||||
//! // Use a method and a DynPinMode variant
|
||||
//! gpio12.try_into_mode(DYN_FLOATING_INPUT).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Because the pin state cannot be tracked at compile-time, many [`DynPin`]
|
||||
//! operations become fallible. Run-time checks are inserted to ensure that
|
||||
//! users don't try to, for example, set the output level of an input pin.
|
||||
//!
|
||||
//! Users may try to convert value-level pins back to their type-level
|
||||
//! equivalents. However, this option is fallible, because the compiler cannot
|
||||
//! guarantee the pin has the correct ID or is in the correct mode at
|
||||
//! compile-time. Use [`TryFrom`](core::convert::TryFrom)/
|
||||
//! [`TryInto`](core::convert::TryInto) for this conversion.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use core::convert::TryInto;
|
||||
//! # use rp2040_hal::{pac, gpio::{DynPin, bank0::Gpio12, Pin, Pins, FloatingInput}, Sio};
|
||||
//! # let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! # let sio = Sio::new(peripherals.SIO);
|
||||
//! # let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//! // Convert to a `DynPin`
|
||||
//! let mut gpio12: DynPin = pins.gpio12.into();
|
||||
//! // Change pin mode
|
||||
//! gpio12.into_floating_input();
|
||||
//! // Convert back to a `Pin`
|
||||
//! let gpio12: Pin<Gpio12, FloatingInput> = gpio12.try_into().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! # Embedded HAL traits
|
||||
//!
|
||||
//! This module implements all of the embedded HAL GPIO traits for [`DynPin`].
|
||||
//! However, whereas the type-level API uses
|
||||
//! `Error = core::convert::Infallible`, the value-level API can return a real
|
||||
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
|
||||
//! operation, the trait functions will return
|
||||
//! [`InvalidPinType`](Error::InvalidPinType).
|
||||
use super::pin::{Pin, PinId, PinMode, ValidPinMode};
|
||||
use super::reg::RegisterInterface;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
use eh1_0_alpha::digital as eh1;
|
||||
use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
|
||||
//==============================================================================
|
||||
// DynPinMode configurations
|
||||
//==============================================================================
|
||||
|
||||
/// Value-level `enum` for disabled configurations
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DynDisabled {
|
||||
Floating,
|
||||
PullDown,
|
||||
PullUp,
|
||||
BusKeep,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for input configurations
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DynInput {
|
||||
Floating,
|
||||
PullDown,
|
||||
PullUp,
|
||||
BusKeep,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for output configurations
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DynOutput {
|
||||
PushPull,
|
||||
Readable,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for output configurations
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DynFunction {
|
||||
Spi,
|
||||
Xip,
|
||||
Uart,
|
||||
I2C,
|
||||
Pwm,
|
||||
Pio0,
|
||||
Pio1,
|
||||
Clock,
|
||||
UsbAux,
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// DynPinMode
|
||||
//==============================================================================
|
||||
|
||||
/// Value-level `enum` representing pin modes
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DynPinMode {
|
||||
Disabled(DynDisabled),
|
||||
Input(DynInput),
|
||||
Output(DynOutput),
|
||||
Function(DynFunction),
|
||||
}
|
||||
|
||||
impl DynPinMode {
|
||||
#[inline]
|
||||
fn valid_for(&self, id: DynPinId) -> bool {
|
||||
use DynFunction::*;
|
||||
use DynGroup::*;
|
||||
use DynPinMode::*;
|
||||
match self {
|
||||
Disabled(_) => true,
|
||||
Input(_) => true,
|
||||
Output(_) => true,
|
||||
Function(alt) => match id.group {
|
||||
Bank0 => match alt {
|
||||
Spi | Uart | I2C | Pwm | Pio0 | Pio1 | UsbAux => true,
|
||||
Clock if id.num >= 20 && id.num <= 25 => true,
|
||||
_ => false,
|
||||
},
|
||||
#[allow(clippy::match_like_matches_macro)]
|
||||
Qspi => match alt {
|
||||
Xip => true,
|
||||
_ => false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Value-level variant of [`DynPinMode`] for floating disabled mode
|
||||
pub const DYN_FLOATING_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::Floating);
|
||||
/// Value-level variant of [`DynPinMode`] for pull-down disabled mode
|
||||
pub const DYN_PULL_DOWN_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullDown);
|
||||
/// Value-level variant of [`DynPinMode`] for pull-up disabled mode
|
||||
pub const DYN_PULL_UP_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullUp);
|
||||
|
||||
/// Value-level variant of [`DynPinMode`] for floating input mode
|
||||
pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
|
||||
/// Value-level variant of [`DynPinMode`] for pull-down input mode
|
||||
pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
|
||||
/// Value-level variant of [`DynPinMode`] for pull-up input mode
|
||||
pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
|
||||
|
||||
/// Value-level variant of [`DynPinMode`] for push-pull output mode
|
||||
pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
|
||||
/// Value-level variant of [`DynPinMode`] for readable push-pull output mode
|
||||
pub const DYN_READABLE_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::Readable);
|
||||
|
||||
macro_rules! dyn_function {
|
||||
( $($Func:ident),+ ) => {
|
||||
crate::paste::paste! {
|
||||
$(
|
||||
#[
|
||||
doc = "Value-level variant of [`DynPinMode`] for alternate "
|
||||
"peripheral function " $Func
|
||||
]
|
||||
pub const [<DYN_FUNCTION_ $Func:upper>]: DynPinMode =
|
||||
DynPinMode::Function(DynFunction::$Func);
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
dyn_function!(Spi, Xip, Uart, I2C, Pwm, Pio0, Pio1, Clock, UsbAux);
|
||||
|
||||
//==============================================================================
|
||||
// DynGroup & DynPinId
|
||||
//==============================================================================
|
||||
|
||||
/// Value-level `enum` for pin groups
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DynGroup {
|
||||
Bank0,
|
||||
Qspi,
|
||||
}
|
||||
|
||||
/// Value-level `struct` representing pin IDs
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct DynPinId {
|
||||
pub group: DynGroup,
|
||||
pub num: u8,
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// DynRegisters
|
||||
//==============================================================================
|
||||
|
||||
/// Provide a safe register interface for [`DynPin`]s
|
||||
///
|
||||
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||
/// access the corresponding regsiters.
|
||||
struct DynRegisters {
|
||||
id: DynPinId,
|
||||
}
|
||||
|
||||
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||
unsafe impl RegisterInterface for DynRegisters {
|
||||
#[inline]
|
||||
fn id(&self) -> DynPinId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl DynRegisters {
|
||||
/// Create a new instance of [`DynRegisters`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Users must never create two simultaneous instances of this `struct` with
|
||||
/// the same [`DynPinId`]
|
||||
#[inline]
|
||||
unsafe fn new(id: DynPinId) -> Self {
|
||||
DynRegisters { id }
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Error
|
||||
//==============================================================================
|
||||
|
||||
/// GPIO error type
|
||||
///
|
||||
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
|
||||
/// operations are fallible. This `enum` represents the corresponding errors.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// The pin did not have the correct ID or mode for the requested operation
|
||||
InvalidPinType,
|
||||
/// The pin does not support the requeted mode
|
||||
InvalidPinMode,
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// DynPin
|
||||
//==============================================================================
|
||||
|
||||
/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`]
|
||||
///
|
||||
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
|
||||
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||
pub struct DynPin {
|
||||
regs: DynRegisters,
|
||||
mode: DynPinMode,
|
||||
}
|
||||
|
||||
impl DynPin {
|
||||
/// Create a new [`DynPin`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
|
||||
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||
/// time. Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||
DynPin {
|
||||
regs: DynRegisters::new(id),
|
||||
mode,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a copy of the pin ID
|
||||
#[inline]
|
||||
pub fn id(&self) -> DynPinId {
|
||||
self.regs.id
|
||||
}
|
||||
|
||||
/// Return a copy of the pin mode
|
||||
#[inline]
|
||||
pub fn mode(&self) -> DynPinMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// Convert the pin to the requested [`DynPinMode`]
|
||||
#[inline]
|
||||
pub fn try_into_mode(&mut self, mode: DynPinMode) -> Result<(), Error> {
|
||||
// FIXME: check valid modes
|
||||
// Only modify registers if we are actually changing pin mode
|
||||
if mode.valid_for(self.regs.id) {
|
||||
if mode != self.mode {
|
||||
self.regs.do_change_mode(mode);
|
||||
self.mode = mode;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidPinMode)
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the pin and set it to float
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_floating_disabled(&mut self) {
|
||||
self.try_into_mode(DYN_FLOATING_DISABLED).unwrap(); // always valid
|
||||
}
|
||||
|
||||
/// Disable the pin and set it to pull down
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_pull_down_disabled(&mut self) {
|
||||
self.try_into_mode(DYN_PULL_DOWN_DISABLED).unwrap(); // always valid
|
||||
}
|
||||
|
||||
/// Disable the pin and set it to pull up
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_pull_up_disabled(&mut self) {
|
||||
self.try_into_mode(DYN_PULL_UP_DISABLED).unwrap(); // always valid
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a floating input
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_floating_input(&mut self) {
|
||||
self.try_into_mode(DYN_FLOATING_INPUT).unwrap(); // always valid
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled down input
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_pull_down_input(&mut self) {
|
||||
self.try_into_mode(DYN_PULL_DOWN_INPUT).unwrap(); // always valid
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled up input
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_pull_up_input(&mut self) {
|
||||
self.try_into_mode(DYN_PULL_UP_INPUT).unwrap(); // always valid
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a push-pull output
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_push_pull_output(&mut self) {
|
||||
self.try_into_mode(DYN_PUSH_PULL_OUTPUT).unwrap(); // always valid
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a readable push pull output
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)] // matches pin api
|
||||
pub fn into_readable_output(&mut self) {
|
||||
self.try_into_mode(DYN_READABLE_OUTPUT).unwrap(); // always valid
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _read(&self) -> Result<bool, Error> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DYN_READABLE_OUTPUT => Ok(self.regs.read_pin()),
|
||||
_ => Err(Error::InvalidPinType),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn _write(&mut self, bit: bool) -> Result<(), Error> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.write_pin(bit);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Error::InvalidPinType),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn _toggle(&mut self) -> Result<(), Error> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.toggle_pin();
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Error::InvalidPinType),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn _read_out(&self) -> Result<bool, Error> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => Ok(self.regs.read_out_pin()),
|
||||
_ => Err(Error::InvalidPinType),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
#[allow(clippy::bool_comparison)] // more explicit this way
|
||||
fn _is_low(&self) -> Result<bool, Error> {
|
||||
Ok(self._read()? == false)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(clippy::bool_comparison)] // more explicit this way
|
||||
fn _is_high(&self) -> Result<bool, Error> {
|
||||
Ok(self._read()? == true)
|
||||
}
|
||||
#[inline]
|
||||
fn _set_low(&mut self) -> Result<(), Error> {
|
||||
self._write(false)
|
||||
}
|
||||
#[inline]
|
||||
fn _set_high(&mut self) -> Result<(), Error> {
|
||||
self._write(true)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(clippy::bool_comparison)] // more explicit this way
|
||||
fn _is_set_low(&self) -> Result<bool, Error> {
|
||||
Ok(self._read_out()? == false)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(clippy::bool_comparison)] // more explicit this way
|
||||
fn _is_set_high(&self) -> Result<bool, Error> {
|
||||
Ok(self._read_out()? == true)
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Convert between Pin and DynPin
|
||||
//==============================================================================
|
||||
|
||||
impl<I, M> From<Pin<I, M>> for DynPin
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||
/// [`DynPin`]
|
||||
#[inline]
|
||||
fn from(_pin: Pin<I, M>) -> Self {
|
||||
// The `Pin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `DynPin`
|
||||
unsafe { DynPin::new(I::DYN, M::DYN) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, M> TryFrom<DynPin> for Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||
///
|
||||
/// There is no way for the compiler to know if the conversion will be
|
||||
/// successful at compile-time. We must verify the conversion at run-time
|
||||
/// or refuse to perform it.
|
||||
#[inline]
|
||||
fn try_from(pin: DynPin) -> Result<Self, Error> {
|
||||
if pin.regs.id == I::DYN && pin.mode == M::DYN {
|
||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `Pin`
|
||||
Ok(unsafe { Self::new() })
|
||||
} else {
|
||||
Err(Error::InvalidPinType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Embedded HAL traits
|
||||
//==============================================================================
|
||||
|
||||
impl OutputPin for DynPin {
|
||||
type Error = Error;
|
||||
#[inline]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_high()
|
||||
}
|
||||
#[inline]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_low()
|
||||
}
|
||||
}
|
||||
|
||||
impl InputPin for DynPin {
|
||||
type Error = Error;
|
||||
#[inline]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self._is_high()
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
self._is_low()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToggleableOutputPin for DynPin {
|
||||
type Error = Error;
|
||||
#[inline]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self._toggle()
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulOutputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
self._is_set_high()
|
||||
}
|
||||
#[inline]
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
self._is_set_low()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl eh1::ErrorType for DynPin {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl eh1::OutputPin for DynPin {
|
||||
#[inline]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_high()
|
||||
}
|
||||
#[inline]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self._set_low()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl eh1::InputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self._is_high()
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
self._is_low()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl eh1::ToggleableOutputPin for DynPin {
|
||||
#[inline]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self._toggle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl eh1::StatefulOutputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
self._is_set_high()
|
||||
}
|
||||
#[inline]
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
self._is_set_low()
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
//! General Purpose Input and Output (GPIO)
|
||||
//!
|
||||
//! See [`pin`](self::pin) for implementation details and in-depth documentation.
|
||||
//!
|
||||
//! ## Basic usage
|
||||
//! ```no_run
|
||||
//! use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||
//! use rp2040_hal::{clocks::init_clocks_and_plls, gpio::Pins, watchdog::Watchdog, pac, Sio};
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
|
||||
//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||
//! let mut clocks = init_clocks_and_plls(XOSC_CRYSTAL_FREQ, peripherals.XOSC, peripherals.CLOCKS, peripherals.PLL_SYS, peripherals.PLL_USB, &mut peripherals.RESETS, &mut watchdog).ok().unwrap();
|
||||
//!
|
||||
//! let mut pac = pac::Peripherals::take().unwrap();
|
||||
//! let sio = Sio::new(pac.SIO);
|
||||
//! let pins = rp2040_hal::gpio::Pins::new(pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS);
|
||||
//! // Set a pin to drive output
|
||||
//! let mut output_pin = pins.gpio25.into_push_pull_output();
|
||||
//! // Drive output to 3.3V
|
||||
//! output_pin.set_high().unwrap();
|
||||
//! // Drive output to 0V
|
||||
//! output_pin.set_low().unwrap();
|
||||
//! // Set a pin to input
|
||||
//! let input_pin = pins.gpio24.into_floating_input();
|
||||
//! // pinstate will be true if the pin is above 2V
|
||||
//! let pinstate = input_pin.is_high().unwrap();
|
||||
//! // pinstate_low will be true if the pin is below 1.15V
|
||||
//! let pinstate_low = input_pin.is_low().unwrap();
|
||||
//! // you'll want to pull-up or pull-down a switch if it's not done externally
|
||||
//! let button_pin = pins.gpio23.into_pull_down_input();
|
||||
//! let button2_pin = pins.gpio22.into_pull_up_input();
|
||||
//! ```
|
||||
//! See [examples/gpio_in_out.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/gpio_in_out.rs) for a more practical example
|
||||
|
||||
// Based heavily on and in some places copied from `atsamd-hal` gpio::v2
|
||||
pub mod pin;
|
||||
pub use pin::*;
|
||||
|
||||
pub mod dynpin;
|
||||
pub use dynpin::*;
|
||||
|
||||
mod reg;
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
/// The amount of current that a pin can drive when used as an output
|
||||
pub enum OutputDriveStrength {
|
||||
/// 2 mA
|
||||
TwoMilliAmps,
|
||||
/// 4 mA
|
||||
FourMilliAmps,
|
||||
/// 8 mA
|
||||
EightMilliAmps,
|
||||
/// 12 mA
|
||||
TwelveMilliAmps,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
/// The slew rate of a pin when used as an output
|
||||
pub enum OutputSlewRate {
|
||||
/// Slew slow
|
||||
Slow,
|
||||
/// Slew fast
|
||||
Fast,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
/// Interrupt kind
|
||||
pub enum Interrupt {
|
||||
/// While low
|
||||
LevelLow,
|
||||
/// While high
|
||||
LevelHigh,
|
||||
/// On falling edge
|
||||
EdgeLow,
|
||||
/// On rising edge
|
||||
EdgeHigh,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
/// Interrupt override state.
|
||||
pub enum InterruptOverride {
|
||||
/// Don't invert the interrupt.
|
||||
DontInvert = 0,
|
||||
/// Invert the interrupt.
|
||||
Invert = 1,
|
||||
/// Drive interrupt low.
|
||||
AlwaysLow = 2,
|
||||
/// Drive interrupt high.
|
||||
AlwaysHigh = 3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
/// Input override state.
|
||||
pub enum InputOverride {
|
||||
/// Don't invert the peripheral input.
|
||||
DontInvert = 0,
|
||||
/// Invert the peripheral input.
|
||||
Invert = 1,
|
||||
/// Drive peripheral input low.
|
||||
AlwaysLow = 2,
|
||||
/// Drive peripheral input high.
|
||||
AlwaysHigh = 3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
/// Output enable override state.
|
||||
pub enum OutputEnableOverride {
|
||||
/// Use the original output enable signal from selected peripheral.
|
||||
DontInvert = 0,
|
||||
/// Invert the output enable signal from selected peripheral.
|
||||
Invert = 1,
|
||||
/// Disable output.
|
||||
Disable = 2,
|
||||
/// Enable output.
|
||||
Enable = 3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
/// Output override state.
|
||||
pub enum OutputOverride {
|
||||
/// Use the original output signal from selected peripheral.
|
||||
DontInvert = 0,
|
||||
/// Invert the output signal from selected peripheral.
|
||||
Invert = 1,
|
||||
/// Drive output low.
|
||||
AlwaysLow = 2,
|
||||
/// Drive output high.
|
||||
AlwaysHigh = 3,
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,530 +0,0 @@
|
|||
// Based heavily on and in some places copied from `atsamd-hal` gpio::v2
|
||||
use super::dynpin::{DynGroup, DynPinId};
|
||||
use super::{
|
||||
InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride,
|
||||
OutputOverride, OutputSlewRate,
|
||||
};
|
||||
use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set};
|
||||
use crate::gpio::dynpin::{DynDisabled, DynFunction, DynInput, DynOutput, DynPinMode};
|
||||
use crate::pac;
|
||||
|
||||
//==============================================================================
|
||||
// ModeFields
|
||||
//==============================================================================
|
||||
|
||||
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||
#[derive(Default)]
|
||||
struct ModeFields {
|
||||
inen: bool,
|
||||
pue: bool,
|
||||
pde: bool,
|
||||
|
||||
sio_outen: bool,
|
||||
funcsel: u8,
|
||||
}
|
||||
|
||||
const SIO_FUNCSEL: u8 = 5;
|
||||
const NULL_FUNCSEL: u8 = 0x1f;
|
||||
|
||||
impl From<DynPinMode> for ModeFields {
|
||||
#[inline]
|
||||
fn from(mode: DynPinMode) -> Self {
|
||||
use DynPinMode::*;
|
||||
let mut fields = Self::default();
|
||||
match mode {
|
||||
Disabled(config) => {
|
||||
use DynDisabled::*;
|
||||
fields.funcsel = NULL_FUNCSEL;
|
||||
fields.sio_outen = false;
|
||||
fields.inen = false;
|
||||
|
||||
match config {
|
||||
Floating => (),
|
||||
PullDown => {
|
||||
fields.pde = true;
|
||||
}
|
||||
PullUp => {
|
||||
fields.pue = true;
|
||||
}
|
||||
BusKeep => {
|
||||
fields.pde = true;
|
||||
fields.pue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Input(config) => {
|
||||
use DynInput::*;
|
||||
fields.funcsel = SIO_FUNCSEL;
|
||||
fields.sio_outen = false;
|
||||
fields.inen = true;
|
||||
|
||||
match config {
|
||||
Floating => (),
|
||||
PullDown => {
|
||||
fields.pde = true;
|
||||
}
|
||||
PullUp => {
|
||||
fields.pue = true;
|
||||
}
|
||||
BusKeep => {
|
||||
fields.pde = true;
|
||||
fields.pue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Output(config) => {
|
||||
use DynOutput::*;
|
||||
fields.funcsel = SIO_FUNCSEL;
|
||||
fields.sio_outen = true;
|
||||
match config {
|
||||
PushPull => {
|
||||
fields.inen = false;
|
||||
}
|
||||
Readable => {
|
||||
fields.inen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Function(func) => {
|
||||
use DynFunction::*;
|
||||
fields.funcsel = match func {
|
||||
Xip => 0,
|
||||
Spi => 1,
|
||||
Uart => 2,
|
||||
I2C => 3,
|
||||
Pwm => 4,
|
||||
// SIO is 5, but isn't an alternate function but is instead for using the pin as GPIO
|
||||
Pio0 => 6,
|
||||
Pio1 => 7,
|
||||
Clock => 8,
|
||||
UsbAux => 9,
|
||||
};
|
||||
fields.inen = true;
|
||||
if func == I2C {
|
||||
fields.pue = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fields
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Users should only implement the [`id`] function. No default function
|
||||
/// implementations should be overridden. The implementing type must also have
|
||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that each
|
||||
/// pin ID is a singleton
|
||||
pub(super) unsafe trait RegisterInterface {
|
||||
/// Provide a [`DynPinId`] identifying the set of registers controlled by
|
||||
/// this type.
|
||||
fn id(&self) -> DynPinId;
|
||||
|
||||
#[inline]
|
||||
fn mask_32(&self) -> u32 {
|
||||
1 << self.id().num
|
||||
}
|
||||
|
||||
/// Read the logic level of an input put
|
||||
#[inline]
|
||||
fn read_pin(&self) -> bool {
|
||||
let mask = self.mask_32();
|
||||
(match self.id().group {
|
||||
DynGroup::Bank0 => unsafe { &(*pac::SIO::ptr()) }.gpio_in.read().bits(),
|
||||
DynGroup::Qspi => unsafe { &(*pac::SIO::ptr()) }.gpio_hi_in.read().bits(),
|
||||
}) & mask
|
||||
!= 0
|
||||
}
|
||||
|
||||
/// Write the logic level of an output pin
|
||||
#[inline]
|
||||
fn write_pin(&mut self, bit: bool) {
|
||||
let mask = self.mask_32();
|
||||
// This ordering to try and make the group match inline if bit can't be
|
||||
unsafe {
|
||||
match self.id().group {
|
||||
DynGroup::Bank0 => {
|
||||
if bit {
|
||||
(*pac::SIO::ptr()).gpio_out_set.write(|w| w.bits(mask));
|
||||
} else {
|
||||
(*pac::SIO::ptr()).gpio_out_clr.write(|w| w.bits(mask));
|
||||
}
|
||||
}
|
||||
DynGroup::Qspi => {
|
||||
if bit {
|
||||
(*pac::SIO::ptr()).gpio_hi_out_set.write(|w| w.bits(mask));
|
||||
} else {
|
||||
(*pac::SIO::ptr()).gpio_hi_out_clr.write(|w| w.bits(mask));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle the logic level of an output pin
|
||||
#[inline]
|
||||
fn toggle_pin(&mut self) {
|
||||
let mask = self.mask_32();
|
||||
match self.id().group {
|
||||
DynGroup::Bank0 => unsafe { (*pac::SIO::ptr()).gpio_out_xor.write(|w| w.bits(mask)) },
|
||||
DynGroup::Qspi => unsafe { (*pac::SIO::ptr()).gpio_hi_out_xor.write(|w| w.bits(mask)) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Read back the logic level of an output pin
|
||||
#[inline]
|
||||
fn read_out_pin(&self) -> bool {
|
||||
let mask = self.mask_32();
|
||||
(match self.id().group {
|
||||
DynGroup::Bank0 => unsafe { &(*pac::SIO::ptr()) }.gpio_out.read().bits(),
|
||||
DynGroup::Qspi => unsafe { &(*pac::SIO::ptr()) }.gpio_hi_out.read().bits(),
|
||||
}) & mask
|
||||
!= 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_drive_strength(&self) -> OutputDriveStrength {
|
||||
use OutputDriveStrength::*;
|
||||
let num = self.id().num as usize;
|
||||
let strength = match self.id().group {
|
||||
DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num]
|
||||
.read()
|
||||
.drive()
|
||||
.bits(),
|
||||
DynGroup::Qspi => qspi_read_drive(num),
|
||||
};
|
||||
match strength {
|
||||
0x0 => TwoMilliAmps,
|
||||
0x1 => FourMilliAmps,
|
||||
0x2 => EightMilliAmps,
|
||||
0x3 => TwelveMilliAmps,
|
||||
_ => unreachable!("invalid drive strength"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_drive_strength(&self, strength: OutputDriveStrength) {
|
||||
use OutputDriveStrength::*;
|
||||
let num = self.id().num as usize;
|
||||
let strength = match strength {
|
||||
TwoMilliAmps => 0x0,
|
||||
FourMilliAmps => 0x1,
|
||||
EightMilliAmps => 0x2,
|
||||
TwelveMilliAmps => 0x3,
|
||||
};
|
||||
|
||||
match self.id().group {
|
||||
DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num]
|
||||
.modify(|_, w| w.drive().bits(strength)),
|
||||
DynGroup::Qspi => qspi_write_drive(num, strength),
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_slew_rate(&self) -> OutputSlewRate {
|
||||
let num = self.id().num as usize;
|
||||
let slew_fast = match self.id().group {
|
||||
DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num]
|
||||
.read()
|
||||
.slewfast()
|
||||
.bit_is_set(),
|
||||
DynGroup::Qspi => qspi_read_slew(num),
|
||||
};
|
||||
if slew_fast {
|
||||
OutputSlewRate::Fast
|
||||
} else {
|
||||
OutputSlewRate::Slow
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_slew_rate(&self, rate: OutputSlewRate) {
|
||||
let num = self.id().num as usize;
|
||||
let slewfast = match rate {
|
||||
OutputSlewRate::Fast => true,
|
||||
OutputSlewRate::Slow => false,
|
||||
};
|
||||
|
||||
match self.id().group {
|
||||
DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num]
|
||||
.modify(|_, w| w.slewfast().bit(slewfast)),
|
||||
DynGroup::Qspi => qspi_write_slew(num, slewfast),
|
||||
};
|
||||
}
|
||||
|
||||
// We have to duplicate code, maybe a fix in the HAL layer can prevent this
|
||||
#[inline]
|
||||
fn do_change_mode(&self, mode: DynPinMode) {
|
||||
let num = self.id().num as usize;
|
||||
match self.id().group {
|
||||
DynGroup::Bank0 => gpio_change_mode(num, mode),
|
||||
DynGroup::Qspi => qspi_change_mode(num, mode),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear interrupt.
|
||||
#[inline]
|
||||
fn clear_interrupt(&self, interrupt: Interrupt) {
|
||||
let num = self.id().num as usize;
|
||||
unsafe {
|
||||
let io = &(*pac::IO_BANK0::ptr());
|
||||
// There are four bits for each GPIO pin (one for each enumerator
|
||||
// in the `Interrupt` enum). There are therefore eight pins per
|
||||
// 32-bit register, and four registers in total.
|
||||
let bit_in_reg = num % 8 * 4 + interrupt as usize;
|
||||
io.intr[num >> 3].write(|w| w.bits(1 << bit_in_reg));
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt status.
|
||||
#[inline]
|
||||
fn interrupt_status(&self, interrupt: Interrupt) -> bool {
|
||||
let num = self.id().num as usize;
|
||||
unsafe {
|
||||
let io = &(*pac::IO_BANK0::ptr());
|
||||
let cpuid = *(pac::SIO::ptr() as *const u32);
|
||||
// There are four bits for each GPIO pin (one for each enumerator
|
||||
// in the `Interrupt` enum). There are therefore eight pins per
|
||||
// 32-bit register, and four registers per CPU.
|
||||
let bit_in_reg = ((num % 8) * 4) + (interrupt as usize);
|
||||
if cpuid == 0 {
|
||||
(io.proc0_ints[num >> 3].read().bits() & (1 << bit_in_reg)) != 0
|
||||
} else {
|
||||
(io.proc1_ints[num >> 3].read().bits() & (1 << bit_in_reg)) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Is interrupt enabled.
|
||||
#[inline]
|
||||
fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool {
|
||||
let num = self.id().num as usize;
|
||||
unsafe {
|
||||
let io = &(*pac::IO_BANK0::ptr());
|
||||
let cpuid = *(pac::SIO::ptr() as *const u32);
|
||||
// There are four bits for each GPIO pin (one for each enumerator
|
||||
// in the `Interrupt` enum). There are therefore eight pins per
|
||||
// 32-bit register, and four registers per CPU.
|
||||
let bit_in_reg = num % 8 * 4 + interrupt as usize;
|
||||
if cpuid == 0 {
|
||||
(io.proc0_inte[num >> 3].read().bits() & (1 << bit_in_reg)) != 0
|
||||
} else {
|
||||
(io.proc1_inte[num >> 3].read().bits() & (1 << bit_in_reg)) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable or disable interrupt.
|
||||
#[inline]
|
||||
fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) {
|
||||
let num = self.id().num as usize;
|
||||
unsafe {
|
||||
let cpuid = *(pac::SIO::ptr() as *const u32);
|
||||
let io = &(*pac::IO_BANK0::ptr());
|
||||
// There are four bits for each GPIO pin (one for each enumerator
|
||||
// in the `Interrupt` enum). There are therefore eight pins per
|
||||
// 32-bit register, and four registers per CPU.
|
||||
let reg = if cpuid == 0 {
|
||||
io.proc0_inte[num >> 3].as_ptr()
|
||||
} else {
|
||||
io.proc1_inte[num >> 3].as_ptr()
|
||||
};
|
||||
let bit_in_reg = num % 8 * 4 + interrupt as usize;
|
||||
if enabled {
|
||||
write_bitmask_set(reg, 1 << bit_in_reg);
|
||||
} else {
|
||||
write_bitmask_clear(reg, 1 << bit_in_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Is interrupt forced.
|
||||
#[inline]
|
||||
fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool {
|
||||
let num = self.id().num as usize;
|
||||
unsafe {
|
||||
let cpuid = *(pac::SIO::ptr() as *const u32);
|
||||
let io = &(*pac::IO_BANK0::ptr());
|
||||
// There are four bits for each GPIO pin (one for each enumerator
|
||||
// in the `Interrupt` enum). There are therefore eight pins per
|
||||
// 32-bit register, and four registers per CPU.
|
||||
let bit_in_reg = num % 8 * 4 + interrupt as usize;
|
||||
if cpuid == 0 {
|
||||
(io.proc0_intf[num >> 3].read().bits() & (1 << bit_in_reg)) != 0
|
||||
} else {
|
||||
(io.proc1_intf[num >> 3].read().bits() & (1 << bit_in_reg)) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Force or release interrupt.
|
||||
#[inline]
|
||||
fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) {
|
||||
let num = self.id().num as usize;
|
||||
unsafe {
|
||||
let cpuid = *(pac::SIO::ptr() as *const u32);
|
||||
let io = &(*pac::IO_BANK0::ptr());
|
||||
// There are four bits for each GPIO pin (one for each enumerator
|
||||
// in the `Interrupt` enum). There are therefore eight pins per
|
||||
// 32-bit register, and four registers per CPU.
|
||||
let reg = if cpuid == 0 {
|
||||
io.proc0_intf[num >> 3].as_ptr()
|
||||
} else {
|
||||
io.proc1_intf[num >> 3].as_ptr()
|
||||
};
|
||||
let bit_in_reg = num % 8 * 4 + interrupt as usize;
|
||||
if forced {
|
||||
write_bitmask_set(reg, 1 << bit_in_reg);
|
||||
} else {
|
||||
write_bitmask_clear(reg, 1 << bit_in_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the interrupt override.
|
||||
#[inline]
|
||||
fn set_interrupt_override(&self, override_value: InterruptOverride) {
|
||||
let num = self.id().num as usize;
|
||||
unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num]
|
||||
.gpio_ctrl
|
||||
.modify(|_, w| w.irqover().bits(override_value as u8));
|
||||
}
|
||||
|
||||
/// Set the input override.
|
||||
#[inline]
|
||||
fn set_input_override(&self, override_value: InputOverride) {
|
||||
let num = self.id().num as usize;
|
||||
unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num]
|
||||
.gpio_ctrl
|
||||
.modify(|_, w| w.inover().bits(override_value as u8));
|
||||
}
|
||||
|
||||
/// Set the output enable override.
|
||||
#[inline]
|
||||
fn set_output_enable_override(&self, override_value: OutputEnableOverride) {
|
||||
let num = self.id().num as usize;
|
||||
unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num]
|
||||
.gpio_ctrl
|
||||
.modify(|_, w| w.oeover().bits(override_value as u8));
|
||||
}
|
||||
|
||||
/// Set the output override.
|
||||
#[inline]
|
||||
fn set_output_override(&self, override_value: OutputOverride) {
|
||||
let num = self.id().num as usize;
|
||||
unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num]
|
||||
.gpio_ctrl
|
||||
.modify(|_, w| w.outover().bits(override_value as u8));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpio_change_mode(num: usize, mode: DynPinMode) {
|
||||
let fields: ModeFields = mode.into();
|
||||
let io = unsafe { &(*pac::IO_BANK0::ptr()).gpio[num] };
|
||||
let pads = unsafe { &(*pac::PADS_BANK0::ptr()).gpio[num] };
|
||||
|
||||
pads.write(|w| {
|
||||
w.pue().bit(fields.pue);
|
||||
w.pde().bit(fields.pde);
|
||||
w.ie().bit(fields.inen);
|
||||
w.od().bit(false) // the SIO oe bit will handle this instead
|
||||
});
|
||||
|
||||
io.gpio_ctrl
|
||||
.write(|w| unsafe { w.funcsel().bits(fields.funcsel) });
|
||||
|
||||
if fields.funcsel == SIO_FUNCSEL {
|
||||
if fields.sio_outen {
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_oe_set.write(|w| w.bits(1 << num));
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is really nasty, but there's no single type for the QSPI pins
|
||||
// I'm not sure if a svd change is even possible to fix this, as these do have
|
||||
// different reset values
|
||||
macro_rules! qspi_bits {
|
||||
( $( ($num : expr, $suffix : ident) ),+ ) => {
|
||||
$crate::paste::paste! {
|
||||
#[inline]
|
||||
fn qspi_read_drive(num: usize) -> u8 {
|
||||
match num {
|
||||
$($num => unsafe { &(*pac::PADS_QSPI::ptr()).[<gpio_qspi_ $suffix>] }.read().drive().bits(), )+
|
||||
_ => unreachable!("invalid ID for QSPI pin")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn qspi_write_drive(num: usize, val : u8) {
|
||||
match num {
|
||||
$($num => unsafe { &(*pac::PADS_QSPI::ptr()).[<gpio_qspi_ $suffix>] }.modify(|_,w| w.drive().bits(val) ), )+
|
||||
_ => unreachable!("invalid ID for QSPI pin")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn qspi_read_slew(num: usize) -> bool {
|
||||
match num {
|
||||
$($num => unsafe { &(*pac::PADS_QSPI::ptr()).[<gpio_qspi_ $suffix>] }.read().slewfast().bit_is_set(), )+
|
||||
_ => unreachable!("invalid ID for QSPI pin")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn qspi_write_slew(num: usize, slewfast : bool) {
|
||||
match num {
|
||||
$($num => unsafe { &(*pac::PADS_QSPI::ptr()).[<gpio_qspi_ $suffix>] }.modify(|_,w| w.slewfast().bit(slewfast) ), )+
|
||||
_ => unreachable!("invalid ID for QSPI pin")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn qspi_change_mode(num: usize, mode: DynPinMode) {
|
||||
let fields : ModeFields = mode.into();
|
||||
|
||||
match num {
|
||||
$($num => {
|
||||
let io = unsafe { &(*pac::IO_QSPI::ptr()).[<gpio_qspi $suffix>] };
|
||||
let pads = unsafe { &(*pac::PADS_QSPI::ptr()).[<gpio_qspi_ $suffix>] };
|
||||
|
||||
pads.write(|w| {
|
||||
w.pue().bit(fields.pue);
|
||||
w.pde().bit(fields.pde);
|
||||
w.ie().bit(fields.inen);
|
||||
w.od().bit(false)
|
||||
});
|
||||
|
||||
io.gpio_ctrl.write(|w| unsafe { w.funcsel().bits(fields.funcsel) } );
|
||||
}, )+
|
||||
_ => unreachable!("invalid ID for QSPI pin")
|
||||
}
|
||||
|
||||
|
||||
// outen is only on SIO
|
||||
if fields.funcsel == SIO_FUNCSEL {
|
||||
if fields.sio_outen {
|
||||
unsafe { (*pac::SIO::ptr()).gpio_hi_oe_set.write(|w| w.bits(1 << num)); }
|
||||
} else {
|
||||
unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); }
|
||||
}
|
||||
} else {
|
||||
unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qspi_bits!((0, sclk), (1, ss), (2, sd0), (3, sd1), (4, sd2), (5, sd3));
|
|
@ -1,311 +0,0 @@
|
|||
//! Inter-Integrated Circuit (I2C) bus
|
||||
//!
|
||||
//! See [Chapter 4 Section 3](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
//!
|
||||
//! ## Usage
|
||||
//! ```no_run
|
||||
//! use fugit::RateExtU32;
|
||||
//! use rp2040_hal::{i2c::I2C, gpio::Pins, pac, Sio};
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let sio = Sio::new(peripherals.SIO);
|
||||
//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//!
|
||||
//! let mut i2c = I2C::i2c1(
|
||||
//! peripherals.I2C1,
|
||||
//! pins.gpio18.into_mode(), // sda
|
||||
//! pins.gpio19.into_mode(), // scl
|
||||
//! 400.kHz(),
|
||||
//! &mut peripherals.RESETS,
|
||||
//! 125_000_000.Hz(),
|
||||
//! );
|
||||
//!
|
||||
//! // Scan for devices on the bus by attempting to read from them
|
||||
//! use embedded_hal::prelude::_embedded_hal_blocking_i2c_Read;
|
||||
//! for i in 0..=127 {
|
||||
//! let mut readbuf: [u8; 1] = [0; 1];
|
||||
//! let result = i2c.read(i, &mut readbuf);
|
||||
//! if let Ok(d) = result {
|
||||
//! // Do whatever work you want to do with found devices
|
||||
//! // writeln!(uart, "Device found at address{:?}", i).unwrap();
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Write some data to a device at 0x2c
|
||||
//! use embedded_hal::prelude::_embedded_hal_blocking_i2c_Write;
|
||||
//! i2c.write(0x2c, &[1, 2, 3]).unwrap();
|
||||
//!
|
||||
//! // Write and then read from a device at 0x3a
|
||||
//! use embedded_hal::prelude::_embedded_hal_blocking_i2c_WriteRead;
|
||||
//! let mut readbuf: [u8; 1] = [0; 1];
|
||||
//! i2c.write_read(0x2c, &[1, 2, 3], &mut readbuf).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! See [examples/i2c.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/i2c.rs)
|
||||
//! for a complete example
|
||||
|
||||
use core::{marker::PhantomData, ops::Deref};
|
||||
|
||||
use crate::{
|
||||
gpio::pin::bank0::{
|
||||
BankPinId, Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17,
|
||||
Gpio18, Gpio19, Gpio2, Gpio20, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27,
|
||||
Gpio28, Gpio29, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, Gpio9,
|
||||
},
|
||||
gpio::pin::{FunctionI2C, Pin, PinId},
|
||||
resets::SubsystemReset,
|
||||
typelevel::Sealed,
|
||||
};
|
||||
use fugit::HertzU32;
|
||||
use pac::{i2c0::RegisterBlock as I2CBlock, I2C0, I2C1, RESETS};
|
||||
|
||||
/// Controller implementaion
|
||||
pub mod controller;
|
||||
/// Peripheral implementation
|
||||
pub mod peripheral;
|
||||
|
||||
/// I2C error
|
||||
#[non_exhaustive]
|
||||
#[cfg_attr(not(feature = "eh1_0_alpha"), derive(Debug))]
|
||||
#[cfg_attr(
|
||||
all(feature = "defmt", not(feature = "eh1_0_alpha")),
|
||||
derive(defmt::Format)
|
||||
)]
|
||||
pub enum Error {
|
||||
/// I2C abort with error
|
||||
Abort(u32),
|
||||
/// User passed in a read buffer that was 0 length
|
||||
InvalidReadBufferLength,
|
||||
/// User passed in a write buffer that was 0 length
|
||||
InvalidWriteBufferLength,
|
||||
/// Target i2c address is out of range
|
||||
AddressOutOfRange(u16),
|
||||
/// Target i2c address is reserved
|
||||
AddressReserved(u16),
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl core::fmt::Debug for Error {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
use eh1_0_alpha::i2c::Error as _;
|
||||
match self {
|
||||
Error::InvalidReadBufferLength => write!(fmt, "InvalidReadBufferLength"),
|
||||
Error::InvalidWriteBufferLength => write!(fmt, "InvalidWriteBufferLength"),
|
||||
Error::AddressOutOfRange(addr) => write!(fmt, "AddressOutOfRange({:x})", addr),
|
||||
Error::AddressReserved(addr) => write!(fmt, "AddressReserved({:x})", addr),
|
||||
Error::Abort(_) => {
|
||||
write!(fmt, "{:?}", self.kind())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "eh1_0_alpha"))]
|
||||
impl defmt::Format for Error {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
use eh1_0_alpha::i2c::Error as _;
|
||||
match self {
|
||||
Error::InvalidReadBufferLength => defmt::write!(fmt, "InvalidReadBufferLength"),
|
||||
Error::InvalidWriteBufferLength => defmt::write!(fmt, "InvalidWriteBufferLength"),
|
||||
Error::AddressOutOfRange(addr) => defmt::write!(fmt, "AddressOutOfRange({:x})", addr),
|
||||
Error::AddressReserved(addr) => defmt::write!(fmt, "AddressReserved({:x})", addr),
|
||||
Error::Abort(_) => {
|
||||
defmt::write!(fmt, "{}", defmt::Debug2Format(&self.kind()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl eh1_0_alpha::i2c::Error for Error {
|
||||
fn kind(&self) -> eh1_0_alpha::i2c::ErrorKind {
|
||||
match &self {
|
||||
Error::Abort(v) if v & 1<<12 != 0 // ARB_LOST
|
||||
=> eh1_0_alpha::i2c::ErrorKind::ArbitrationLoss,
|
||||
Error::Abort(v) if v & 1<<7 != 0 // ABRT_SBYTE_ACKDET
|
||||
=> eh1_0_alpha::i2c::ErrorKind::Bus,
|
||||
Error::Abort(v) if v & 1<<6 != 0 // ABRT_HS_ACKDET
|
||||
=> eh1_0_alpha::i2c::ErrorKind::Bus,
|
||||
Error::Abort(v) if v & 1<<4 != 0 // ABRT_GCALL_NOACK
|
||||
=> eh1_0_alpha::i2c::ErrorKind::NoAcknowledge(eh1_0_alpha::i2c::NoAcknowledgeSource::Address),
|
||||
Error::Abort(v) if v & 1<<3 != 0 // ABRT_TXDATA_NOACK
|
||||
=> eh1_0_alpha::i2c::ErrorKind::NoAcknowledge(eh1_0_alpha::i2c::NoAcknowledgeSource::Data),
|
||||
Error::Abort(v) if v & 1<<2 != 0 // ABRT_10ADDR2_NOACK
|
||||
=> eh1_0_alpha::i2c::ErrorKind::NoAcknowledge(eh1_0_alpha::i2c::NoAcknowledgeSource::Address),
|
||||
Error::Abort(v) if v & 1<<1 != 0 // ABRT_10ADDR1_NOACK
|
||||
=> eh1_0_alpha::i2c::ErrorKind::NoAcknowledge(eh1_0_alpha::i2c::NoAcknowledgeSource::Address),
|
||||
Error::Abort(v) if v & 1<<0 != 0 // ABRT_7B_ADDR_NOACK
|
||||
=> eh1_0_alpha::i2c::ErrorKind::NoAcknowledge(eh1_0_alpha::i2c::NoAcknowledgeSource::Address),
|
||||
_ => eh1_0_alpha::i2c::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SCL pin
|
||||
pub trait SclPin<I2C>: Sealed {}
|
||||
|
||||
/// SDA pin
|
||||
pub trait SdaPin<I2C>: Sealed {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio0 {}
|
||||
impl SclPin<I2C0> for Gpio1 {}
|
||||
|
||||
impl SdaPin<I2C1> for Gpio2 {}
|
||||
impl SclPin<I2C1> for Gpio3 {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio4 {}
|
||||
impl SclPin<I2C0> for Gpio5 {}
|
||||
|
||||
impl SdaPin<I2C1> for Gpio6 {}
|
||||
impl SclPin<I2C1> for Gpio7 {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio8 {}
|
||||
impl SclPin<I2C0> for Gpio9 {}
|
||||
|
||||
impl SdaPin<I2C1> for Gpio10 {}
|
||||
impl SclPin<I2C1> for Gpio11 {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio12 {}
|
||||
impl SclPin<I2C0> for Gpio13 {}
|
||||
|
||||
impl SdaPin<I2C1> for Gpio14 {}
|
||||
impl SclPin<I2C1> for Gpio15 {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio16 {}
|
||||
impl SclPin<I2C0> for Gpio17 {}
|
||||
|
||||
impl SdaPin<I2C1> for Gpio18 {}
|
||||
impl SclPin<I2C1> for Gpio19 {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio20 {}
|
||||
impl SclPin<I2C0> for Gpio21 {}
|
||||
|
||||
impl SdaPin<I2C1> for Gpio22 {}
|
||||
impl SclPin<I2C1> for Gpio23 {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio24 {}
|
||||
impl SclPin<I2C0> for Gpio25 {}
|
||||
|
||||
impl SdaPin<I2C1> for Gpio26 {}
|
||||
impl SclPin<I2C1> for Gpio27 {}
|
||||
|
||||
impl SdaPin<I2C0> for Gpio28 {}
|
||||
impl SclPin<I2C0> for Gpio29 {}
|
||||
|
||||
/// Operational mode of the I2C peripheral.
|
||||
pub trait I2CMode: Sealed {
|
||||
/// Indicates whether this mode is Controller or Peripheral.
|
||||
const IS_CONTROLLER: bool;
|
||||
}
|
||||
/// Marker for an I2C peripheral operating as a controller.
|
||||
pub enum Controller {}
|
||||
impl Sealed for Controller {}
|
||||
impl I2CMode for Controller {
|
||||
const IS_CONTROLLER: bool = true;
|
||||
}
|
||||
/// Marker for an I2C peripheral operating as a peripehral.
|
||||
pub enum Peripheral {}
|
||||
impl Sealed for Peripheral {}
|
||||
impl I2CMode for Peripheral {
|
||||
const IS_CONTROLLER: bool = false;
|
||||
}
|
||||
|
||||
/// I2C peripheral
|
||||
pub struct I2C<I2C, Pins, Mode = Controller> {
|
||||
i2c: I2C,
|
||||
pins: Pins,
|
||||
mode: PhantomData<Mode>,
|
||||
}
|
||||
|
||||
const TX_FIFO_SIZE: u8 = 16;
|
||||
const RX_FIFO_SIZE: u8 = 16;
|
||||
|
||||
fn i2c_reserved_addr(addr: u16) -> bool {
|
||||
(addr & 0x78) == 0 || (addr & 0x78) == 0x78
|
||||
}
|
||||
|
||||
impl<Block, Sda, Scl, Mode> I2C<Block, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>), Mode>
|
||||
where
|
||||
Block: SubsystemReset + Deref<Target = I2CBlock>,
|
||||
Sda: PinId + BankPinId,
|
||||
Scl: PinId + BankPinId,
|
||||
Mode: I2CMode,
|
||||
{
|
||||
/// Releases the I2C peripheral and associated pins
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn free(
|
||||
self,
|
||||
resets: &mut RESETS,
|
||||
) -> (Block, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)) {
|
||||
self.i2c.reset_bring_down(resets);
|
||||
|
||||
(self.i2c, self.pins)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: Deref<Target = I2CBlock>, PINS, Mode> I2C<Block, PINS, Mode> {
|
||||
/// Number of bytes currently in the RX FIFO
|
||||
#[inline]
|
||||
pub fn rx_fifo_used(&self) -> u8 {
|
||||
self.i2c.ic_rxflr.read().rxflr().bits()
|
||||
}
|
||||
|
||||
/// Remaining capacity in the RX FIFO
|
||||
#[inline]
|
||||
pub fn rx_fifo_free(&self) -> u8 {
|
||||
RX_FIFO_SIZE - self.rx_fifo_used()
|
||||
}
|
||||
|
||||
/// RX FIFO is empty
|
||||
#[inline]
|
||||
pub fn rx_fifo_empty(&self) -> bool {
|
||||
self.rx_fifo_used() == 0
|
||||
}
|
||||
|
||||
/// Number of bytes currently in the TX FIFO
|
||||
#[inline]
|
||||
pub fn tx_fifo_used(&self) -> u8 {
|
||||
self.i2c.ic_txflr.read().txflr().bits()
|
||||
}
|
||||
|
||||
/// Remaining capacity in the TX FIFO
|
||||
#[inline]
|
||||
pub fn tx_fifo_free(&self) -> u8 {
|
||||
TX_FIFO_SIZE - self.tx_fifo_used()
|
||||
}
|
||||
|
||||
/// TX FIFO is at capacity
|
||||
#[inline]
|
||||
pub fn tx_fifo_full(&self) -> bool {
|
||||
self.tx_fifo_free() == 0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! hal {
|
||||
($($I2CX:ident: ($i2cX:ident),)+) => {
|
||||
$(
|
||||
impl<Sda: PinId + BankPinId, Scl: PinId + BankPinId>
|
||||
I2C<$I2CX, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)> {
|
||||
/// Configures the I2C peripheral to work in master mode
|
||||
pub fn $i2cX<F, SystemF>(
|
||||
i2c: $I2CX,
|
||||
sda_pin: Pin<Sda, FunctionI2C>,
|
||||
scl_pin: Pin<Scl, FunctionI2C>,
|
||||
freq: F,
|
||||
resets: &mut RESETS,
|
||||
system_clock: SystemF) -> Self
|
||||
where
|
||||
F: Into<HertzU32>,
|
||||
Sda: SdaPin<$I2CX>,
|
||||
Scl: SclPin<$I2CX>,
|
||||
SystemF: Into<HertzU32>,
|
||||
{
|
||||
Self::new_controller(i2c, sda_pin, scl_pin, freq.into(), resets, system_clock.into())
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
hal! {
|
||||
I2C0: (i2c0),
|
||||
I2C1: (i2c1),
|
||||
}
|
|
@ -1,363 +0,0 @@
|
|||
use core::{marker::PhantomData, ops::Deref};
|
||||
|
||||
use crate::{
|
||||
gpio::pin::bank0::BankPinId,
|
||||
gpio::pin::{FunctionI2C, Pin, PinId},
|
||||
resets::SubsystemReset,
|
||||
};
|
||||
use fugit::HertzU32;
|
||||
use hal::blocking::i2c::{Read, Write, WriteRead};
|
||||
use pac::{i2c0::RegisterBlock as Block, RESETS};
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
use eh1_0_alpha::i2c as eh1;
|
||||
|
||||
use super::{i2c_reserved_addr, Controller, Error, SclPin, SdaPin, I2C};
|
||||
|
||||
impl<T: SubsystemReset + Deref<Target = Block>, Sda: PinId + BankPinId, Scl: PinId + BankPinId>
|
||||
I2C<T, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>), Controller>
|
||||
{
|
||||
/// Configures the I2C peripheral to work in controller mode
|
||||
pub fn new_controller(
|
||||
i2c: T,
|
||||
sda_pin: Pin<Sda, FunctionI2C>,
|
||||
scl_pin: Pin<Scl, FunctionI2C>,
|
||||
freq: HertzU32,
|
||||
resets: &mut RESETS,
|
||||
system_clock: HertzU32,
|
||||
) -> Self
|
||||
where
|
||||
Sda: SdaPin<T>,
|
||||
Scl: SclPin<T>,
|
||||
{
|
||||
let freq = freq.to_Hz();
|
||||
assert!(freq <= 1_000_000);
|
||||
assert!(freq > 0);
|
||||
|
||||
i2c.reset_bring_down(resets);
|
||||
i2c.reset_bring_up(resets);
|
||||
|
||||
i2c.ic_enable.write(|w| w.enable().disabled());
|
||||
|
||||
// select controller mode & speed
|
||||
i2c.ic_con.modify(|_, w| {
|
||||
w.speed().fast();
|
||||
w.master_mode().enabled();
|
||||
w.ic_slave_disable().slave_disabled();
|
||||
w.ic_restart_en().enabled();
|
||||
w.tx_empty_ctrl().enabled()
|
||||
});
|
||||
|
||||
// Clear FIFO threshold
|
||||
i2c.ic_tx_tl.write(|w| unsafe { w.tx_tl().bits(0) });
|
||||
i2c.ic_rx_tl.write(|w| unsafe { w.rx_tl().bits(0) });
|
||||
|
||||
let freq_in = system_clock.to_Hz();
|
||||
|
||||
// There are some subtleties to I2C timing which we are completely ignoring here
|
||||
// See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69
|
||||
let period = (freq_in + freq / 2) / freq;
|
||||
let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low
|
||||
let hcnt = period - lcnt; // and 2/5 (40%) of the period high
|
||||
|
||||
// Check for out-of-range divisors:
|
||||
assert!(hcnt <= 0xffff);
|
||||
assert!(lcnt <= 0xffff);
|
||||
assert!(hcnt >= 8);
|
||||
assert!(lcnt >= 8);
|
||||
|
||||
// Per I2C-bus specification a device in standard or fast mode must
|
||||
// internally provide a hold time of at least 300ns for the SDA signal to
|
||||
// bridge the undefined region of the falling edge of SCL. A smaller hold
|
||||
// time of 120ns is used for fast mode plus.
|
||||
let sda_tx_hold_count = if freq < 1000000 {
|
||||
// sda_tx_hold_count = freq_in [cycles/s] * 300ns * (1s / 1e9ns)
|
||||
// Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint.
|
||||
// Add 1 to avoid division truncation.
|
||||
((freq_in * 3) / 10000000) + 1
|
||||
} else {
|
||||
// fast mode plus requires a clk_in > 32MHz
|
||||
assert!(freq_in >= 32_000_000);
|
||||
|
||||
// sda_tx_hold_count = freq_in [cycles/s] * 120ns * (1s / 1e9ns)
|
||||
// Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint.
|
||||
// Add 1 to avoid division truncation.
|
||||
((freq_in * 3) / 25000000) + 1
|
||||
};
|
||||
assert!(sda_tx_hold_count <= lcnt - 2);
|
||||
|
||||
unsafe {
|
||||
i2c.ic_fs_scl_hcnt
|
||||
.write(|w| w.ic_fs_scl_hcnt().bits(hcnt as u16));
|
||||
i2c.ic_fs_scl_lcnt
|
||||
.write(|w| w.ic_fs_scl_lcnt().bits(lcnt as u16));
|
||||
i2c.ic_fs_spklen.write(|w| {
|
||||
w.ic_fs_spklen()
|
||||
.bits(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })
|
||||
});
|
||||
i2c.ic_sda_hold
|
||||
.modify(|_r, w| w.ic_sda_tx_hold().bits(sda_tx_hold_count as u16));
|
||||
}
|
||||
|
||||
// Enable I2C block
|
||||
i2c.ic_enable.write(|w| w.enable().enabled());
|
||||
|
||||
Self {
|
||||
i2c,
|
||||
pins: (sda_pin, scl_pin),
|
||||
mode: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: Deref<Target = Block>, PINS> I2C<T, PINS, Controller> {
|
||||
fn validate(
|
||||
addr: u16,
|
||||
opt_tx_empty: Option<bool>,
|
||||
opt_rx_empty: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
// validate tx parameters if present
|
||||
if opt_tx_empty.unwrap_or(false) {
|
||||
return Err(Error::InvalidWriteBufferLength);
|
||||
}
|
||||
|
||||
// validate rx parameters if present
|
||||
if opt_rx_empty.unwrap_or(false) {
|
||||
return Err(Error::InvalidReadBufferLength);
|
||||
}
|
||||
|
||||
// validate address
|
||||
if addr >= 0x80 {
|
||||
Err(Error::AddressOutOfRange(addr))
|
||||
} else if i2c_reserved_addr(addr) {
|
||||
Err(Error::AddressReserved(addr))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(&mut self, addr: u16) {
|
||||
self.i2c.ic_enable.write(|w| w.enable().disabled());
|
||||
self.i2c.ic_tar.write(|w| unsafe { w.ic_tar().bits(addr) });
|
||||
self.i2c.ic_enable.write(|w| w.enable().enabled());
|
||||
}
|
||||
|
||||
fn read_and_clear_abort_reason(&mut self) -> Option<u32> {
|
||||
let abort_reason = self.i2c.ic_tx_abrt_source.read().bits();
|
||||
if abort_reason != 0 {
|
||||
// Note clearing the abort flag also clears the reason, and
|
||||
// this instance of flag is clear-on-read! Note also the
|
||||
// IC_CLR_TX_ABRT register always reads as 0.
|
||||
self.i2c.ic_clr_tx_abrt.read();
|
||||
Some(abort_reason)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn read_internal(
|
||||
&mut self,
|
||||
buffer: &mut [u8],
|
||||
force_restart: bool,
|
||||
do_stop: bool,
|
||||
) -> Result<(), Error> {
|
||||
let lastindex = buffer.len() - 1;
|
||||
for (i, byte) in buffer.iter_mut().enumerate() {
|
||||
let first = i == 0;
|
||||
let last = i == lastindex;
|
||||
|
||||
// wait until there is space in the FIFO to write the next byte
|
||||
while self.tx_fifo_full() {}
|
||||
|
||||
self.i2c.ic_data_cmd.write(|w| {
|
||||
if force_restart && first {
|
||||
w.restart().enable();
|
||||
} else {
|
||||
w.restart().disable();
|
||||
}
|
||||
|
||||
if do_stop && last {
|
||||
w.stop().enable();
|
||||
} else {
|
||||
w.stop().disable();
|
||||
}
|
||||
|
||||
w.cmd().read()
|
||||
});
|
||||
|
||||
while self.i2c.ic_rxflr.read().bits() == 0 {
|
||||
if let Some(abort_reason) = self.read_and_clear_abort_reason() {
|
||||
return Err(Error::Abort(abort_reason));
|
||||
}
|
||||
}
|
||||
|
||||
*byte = self.i2c.ic_data_cmd.read().dat().bits();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_internal(&mut self, bytes: &[u8], do_stop: bool) -> Result<(), Error> {
|
||||
for (i, byte) in bytes.iter().enumerate() {
|
||||
let last = i == bytes.len() - 1;
|
||||
|
||||
self.i2c.ic_data_cmd.write(|w| {
|
||||
if do_stop && last {
|
||||
w.stop().enable();
|
||||
} else {
|
||||
w.stop().disable();
|
||||
}
|
||||
unsafe { w.dat().bits(*byte) }
|
||||
});
|
||||
|
||||
// Wait until the transmission of the address/data from the internal
|
||||
// shift register has completed. For this to function correctly, the
|
||||
// TX_EMPTY_CTRL flag in IC_CON must be set. The TX_EMPTY_CTRL flag
|
||||
// was set in i2c_init.
|
||||
while self.i2c.ic_raw_intr_stat.read().tx_empty().is_inactive() {}
|
||||
|
||||
let abort_reason = self.read_and_clear_abort_reason();
|
||||
|
||||
if abort_reason.is_some() || (do_stop && last) {
|
||||
// If the transaction was aborted or if it completed
|
||||
// successfully wait until the STOP condition has occured.
|
||||
|
||||
while self.i2c.ic_raw_intr_stat.read().stop_det().is_inactive() {}
|
||||
|
||||
self.i2c.ic_clr_stop_det.read().clr_stop_det();
|
||||
}
|
||||
|
||||
// Note the hardware issues a STOP automatically on an abort condition.
|
||||
// Note also the hardware clears RX FIFO as well as TX on abort,
|
||||
// ecause we set hwparam IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0.
|
||||
if let Some(abort_reason) = abort_reason {
|
||||
return Err(Error::Abort(abort_reason));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl<T: Deref<Target = Block>, PINS> Read for I2C<T, PINS, Controller> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
let addr: u16 = addr.into();
|
||||
|
||||
Self::validate(addr, None, Some(buffer.is_empty()))?;
|
||||
|
||||
self.setup(addr);
|
||||
self.read_internal(buffer, true, true)
|
||||
}
|
||||
}
|
||||
impl<T: Deref<Target = Block>, PINS> WriteRead for I2C<T, PINS, Controller> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, addr: u8, tx: &[u8], rx: &mut [u8]) -> Result<(), Error> {
|
||||
let addr: u16 = addr.into();
|
||||
|
||||
Self::validate(addr, Some(tx.is_empty()), Some(rx.is_empty()))?;
|
||||
self.setup(addr);
|
||||
|
||||
self.write_internal(tx, false)?;
|
||||
self.read_internal(rx, true, true)
|
||||
}
|
||||
}
|
||||
impl<T: Deref<Target = Block>, PINS> Write for I2C<T, PINS, Controller> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, addr: u8, tx: &[u8]) -> Result<(), Error> {
|
||||
let addr: u16 = addr.into();
|
||||
Self::validate(addr, Some(tx.is_empty()), None)?;
|
||||
self.setup(addr);
|
||||
|
||||
self.write_internal(tx, true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<T: Deref<Target = Block>, PINS> eh1::ErrorType for I2C<T, PINS, Controller> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<T: Deref<Target = Block>, PINS> eh1::I2c for I2C<T, PINS, Controller> {
|
||||
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
Write::write(self, addr, bytes)
|
||||
}
|
||||
|
||||
fn write_iter<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
let mut peekable = bytes.into_iter().peekable();
|
||||
let addr: u16 = address.into();
|
||||
Self::validate(addr, Some(peekable.peek().is_none()), None)?;
|
||||
self.setup(addr);
|
||||
|
||||
while let Some(tx) = peekable.next() {
|
||||
self.write_internal(&[tx], peekable.peek().is_none())?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
WriteRead::write_read(self, addr, bytes, buffer)
|
||||
}
|
||||
|
||||
fn write_iter_read<B>(
|
||||
&mut self,
|
||||
address: u8,
|
||||
bytes: B,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
let mut peekable = bytes.into_iter().peekable();
|
||||
let addr: u16 = address.into();
|
||||
Self::validate(addr, Some(peekable.peek().is_none()), None)?;
|
||||
self.setup(addr);
|
||||
|
||||
for tx in peekable {
|
||||
self.write_internal(&[tx], false)?
|
||||
}
|
||||
self.read_internal(buffer, true, true)
|
||||
}
|
||||
|
||||
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
Read::read(self, addr, buffer)
|
||||
}
|
||||
|
||||
fn transaction<'a>(
|
||||
&mut self,
|
||||
address: u8,
|
||||
operations: &mut [eh1::Operation<'a>],
|
||||
) -> Result<(), Self::Error> {
|
||||
let addr: u16 = address.into();
|
||||
self.setup(addr);
|
||||
for i in 0..operations.len() {
|
||||
let last = i == operations.len() - 1;
|
||||
match &mut operations[i] {
|
||||
eh1::Operation::Read(buf) => self.read_internal(buf, false, last)?,
|
||||
eh1::Operation::Write(buf) => self.write_internal(buf, last)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
|
||||
where
|
||||
O: IntoIterator<Item = eh1::Operation<'a>>,
|
||||
{
|
||||
let addr: u16 = address.into();
|
||||
self.setup(addr);
|
||||
let mut peekable = operations.into_iter().peekable();
|
||||
while let Some(operation) = peekable.next() {
|
||||
let last = peekable.peek().is_none();
|
||||
match operation {
|
||||
eh1::Operation::Read(buf) => self.read_internal(buf, false, last)?,
|
||||
eh1::Operation::Write(buf) => self.write_internal(buf, last)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
use core::{marker::PhantomData, ops::Deref};
|
||||
|
||||
use crate::{
|
||||
gpio::pin::bank0::BankPinId,
|
||||
gpio::pin::{FunctionI2C, Pin, PinId},
|
||||
resets::SubsystemReset,
|
||||
};
|
||||
use pac::{i2c0::RegisterBlock as I2CBlock, RESETS};
|
||||
|
||||
use super::{Peripheral, SclPin, SdaPin, I2C};
|
||||
|
||||
/// I2C bus events
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum I2CEvent {
|
||||
/// Start condition has been detected.
|
||||
Start,
|
||||
/// Restart condition has been detected.
|
||||
Restart,
|
||||
/// The controller requests data.
|
||||
TransferRead,
|
||||
/// The controller sends data.
|
||||
TransferWrite,
|
||||
/// Stop condition detected.
|
||||
Stop,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum State {
|
||||
Idle,
|
||||
Active,
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
/// Provides Async features to I2C peripheral.
|
||||
pub struct I2CPeripheralEventIterator<Block, Pins> {
|
||||
i2c: I2C<Block, Pins, Peripheral>,
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl<T, Sda, Scl> I2C<T, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>), Peripheral>
|
||||
where
|
||||
T: SubsystemReset + Deref<Target = I2CBlock>,
|
||||
Sda: PinId + BankPinId,
|
||||
Scl: PinId + BankPinId,
|
||||
{
|
||||
/// Configures the I2C peripheral to work in peripheral mode
|
||||
///
|
||||
/// The bus *MUST* be idle when this method is called.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn new_peripheral_event_iterator(
|
||||
i2c: T,
|
||||
sda_pin: Pin<Sda, FunctionI2C>,
|
||||
scl_pin: Pin<Scl, FunctionI2C>,
|
||||
resets: &mut RESETS,
|
||||
addr: u16,
|
||||
) -> I2CPeripheralEventIterator<T, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)>
|
||||
where
|
||||
Sda: SdaPin<T>,
|
||||
Scl: SclPin<T>,
|
||||
{
|
||||
i2c.reset_bring_down(resets);
|
||||
i2c.reset_bring_up(resets);
|
||||
|
||||
i2c.ic_enable.write(|w| w.enable().disabled());
|
||||
|
||||
// TODO: rp2040 supports 10bits addressing
|
||||
//i2c_reserved_addr(addr)
|
||||
i2c.ic_sar.write(|w| unsafe { w.ic_sar().bits(addr) });
|
||||
// select peripheral mode & speed
|
||||
i2c.ic_con.modify(|_, w| {
|
||||
// run in fast mode
|
||||
w.speed().fast();
|
||||
// setup slave mode
|
||||
w.master_mode().disabled();
|
||||
w.ic_slave_disable().slave_enabled();
|
||||
// hold scl when fifo's full
|
||||
w.rx_fifo_full_hld_ctrl().enabled();
|
||||
w.ic_restart_en().enabled();
|
||||
w.tx_empty_ctrl().enabled()
|
||||
});
|
||||
|
||||
// Clear FIFO threshold
|
||||
i2c.ic_tx_tl.write(|w| unsafe { w.tx_tl().bits(0) });
|
||||
i2c.ic_rx_tl.write(|w| unsafe { w.rx_tl().bits(0) });
|
||||
|
||||
// Enable I2C block
|
||||
i2c.ic_enable.write(|w| w.enable().enabled());
|
||||
|
||||
I2CPeripheralEventIterator {
|
||||
i2c: Self {
|
||||
i2c,
|
||||
pins: (sda_pin, scl_pin),
|
||||
mode: PhantomData,
|
||||
},
|
||||
state: State::Idle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Deref<Target = I2CBlock>, PINS> I2CPeripheralEventIterator<T, PINS> {
|
||||
/// Push up to `usize::min(TX_FIFO_SIZE, buf.len())` bytes to the TX FIFO.
|
||||
/// Returns the number of bytes pushed to the FIFO. Note this does *not* reflect how many bytes
|
||||
/// are effectively received by the controller.
|
||||
pub fn write(&mut self, buf: &[u8]) -> usize {
|
||||
// just in case, clears previous tx abort.
|
||||
self.i2c.i2c.ic_clr_tx_abrt.read();
|
||||
|
||||
let mut sent = 0;
|
||||
for &b in buf.iter() {
|
||||
if self.i2c.tx_fifo_full() {
|
||||
break;
|
||||
}
|
||||
|
||||
self.i2c
|
||||
.i2c
|
||||
.ic_data_cmd
|
||||
.write(|w| unsafe { w.dat().bits(b) });
|
||||
sent += 1;
|
||||
}
|
||||
// serve a pending read request
|
||||
self.i2c.i2c.ic_clr_rd_req.read();
|
||||
sent
|
||||
}
|
||||
|
||||
/// Pull up to `usize::min(RX_FIFO_SIZE, buf.len())` bytes from the RX FIFO.
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> usize {
|
||||
let mut read = 0;
|
||||
|
||||
for b in buf.iter_mut() {
|
||||
if self.i2c.rx_fifo_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
*b = self.i2c.i2c.ic_data_cmd.read().dat().bits();
|
||||
read += 1;
|
||||
}
|
||||
read
|
||||
}
|
||||
}
|
||||
impl<T: Deref<Target = I2CBlock>, PINS> Iterator for I2CPeripheralEventIterator<T, PINS> {
|
||||
type Item = I2CEvent;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let stat = self.i2c.i2c.ic_raw_intr_stat.read();
|
||||
self.i2c.i2c.ic_clr_activity.read();
|
||||
|
||||
match self.state {
|
||||
State::Idle if stat.start_det().bit_is_set() => {
|
||||
self.i2c.i2c.ic_clr_start_det.read();
|
||||
self.state = State::Active;
|
||||
Some(I2CEvent::Start)
|
||||
}
|
||||
State::Active if !self.i2c.rx_fifo_empty() => {
|
||||
self.state = State::Write;
|
||||
Some(I2CEvent::TransferWrite)
|
||||
}
|
||||
State::Active if stat.rd_req().bit_is_set() => {
|
||||
// Clearing `rd_req` is used by the hardware to detect when the I2C block can stop
|
||||
// stretching the clock and start process the data pushed to the FIFO (if any).
|
||||
// This is done in `Self::write`.
|
||||
self.state = State::Read;
|
||||
Some(I2CEvent::TransferRead)
|
||||
}
|
||||
State::Read if stat.rd_req().bit_is_set() => Some(I2CEvent::TransferRead),
|
||||
State::Read if stat.restart_det().bit_is_set() => {
|
||||
self.i2c.i2c.ic_clr_restart_det.read();
|
||||
self.state = State::Active;
|
||||
Some(I2CEvent::Restart)
|
||||
}
|
||||
State::Write if !self.i2c.rx_fifo_empty() => Some(I2CEvent::TransferWrite),
|
||||
State::Write if stat.restart_det().bit_is_set() => {
|
||||
self.i2c.i2c.ic_clr_restart_det.read();
|
||||
self.state = State::Active;
|
||||
Some(I2CEvent::Restart)
|
||||
}
|
||||
_ if stat.stop_det().bit_is_set() => {
|
||||
self.i2c.i2c.ic_clr_stop_det.read();
|
||||
self.state = State::Idle;
|
||||
Some(I2CEvent::Stop)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, Sda, Scl>
|
||||
I2CPeripheralEventIterator<Block, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)>
|
||||
where
|
||||
Block: SubsystemReset + Deref<Target = I2CBlock>,
|
||||
Sda: PinId + BankPinId,
|
||||
Scl: PinId + BankPinId,
|
||||
{
|
||||
/// Releases the I2C peripheral and associated pins
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn free(
|
||||
self,
|
||||
resets: &mut RESETS,
|
||||
) -> (Block, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)) {
|
||||
self.i2c.free(resets)
|
||||
}
|
||||
}
|
|
@ -1,274 +0,0 @@
|
|||
/// Generate a series of aliases for an intrinsic function.
|
||||
macro_rules! intrinsics_aliases {
|
||||
(
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
) => {};
|
||||
(
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
) => {};
|
||||
|
||||
(
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
mod $alias {
|
||||
#[no_mangle]
|
||||
pub extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
mod $alias {
|
||||
#[no_mangle]
|
||||
unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The macro used to define overridden intrinsics.
|
||||
///
|
||||
/// This is heavily inspired by the macro used by compiler-builtins. The idea
|
||||
/// is to abstract anything special that needs to be done to override an
|
||||
/// intrinsic function. Intrinsic generation is disabled for non-ARM targets
|
||||
/// so things like CI and docs generation do not have problems. Additionally
|
||||
/// they can be disabled with the crate feature `disable-intrinsics` for
|
||||
/// testing or comparing performance.
|
||||
///
|
||||
/// Like the compiler-builtins macro, it accepts a series of functions that
|
||||
/// looks like normal Rust code:
|
||||
///
|
||||
/// ```ignore
|
||||
/// intrinsics! {
|
||||
/// extern "C" fn foo(a: i32) -> u32 {
|
||||
/// // ...
|
||||
/// }
|
||||
/// #[nonstandard_attribute]
|
||||
/// extern "C" fn bar(a: i32) -> u32 {
|
||||
/// // ...
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Each function can also be decorated with nonstandard attributes to control
|
||||
/// additional behaviour:
|
||||
///
|
||||
/// * `slower_than_default` - indicates that the override is slower than the
|
||||
/// default implementation. Currently this just disables the override
|
||||
/// entirely.
|
||||
/// * `bootrom_v2` - indicates that the override is only available
|
||||
/// on a V2 bootrom or higher. Only enabled when the feature
|
||||
/// `rom-v2-intrinsics` is set.
|
||||
/// * `alias` - accepts a list of names to alias the intrinsic to.
|
||||
/// * `aeabi` - accepts a list of ARM EABI names to alias to.
|
||||
///
|
||||
macro_rules! intrinsics {
|
||||
() => {};
|
||||
|
||||
(
|
||||
#[slower_than_default]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[bootrom_v2]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(feature = "rom-v2-intrinsics"))]
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(feature = "rom-v2-intrinsics")]
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[alias = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[alias = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[aeabi = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))]
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$($attr:tt)*])*
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
$(#[$($attr)*])*
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))]
|
||||
#[allow(dead_code)]
|
||||
unsafe fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
//! HAL for the RP2040 microcontroller
|
||||
//!
|
||||
//! This is an implementation of the [`embedded-hal`] traits for the RP2040 microcontroller
|
||||
//! NOTE This HAL is still under active development. This API will remain volatile until 1.0.0
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![no_std]
|
||||
|
||||
extern crate cortex_m;
|
||||
extern crate embedded_hal as hal;
|
||||
extern crate nb;
|
||||
pub use paste;
|
||||
|
||||
pub extern crate rp2040_pac as pac;
|
||||
|
||||
#[macro_use]
|
||||
mod intrinsics;
|
||||
|
||||
pub mod adc;
|
||||
pub(crate) mod atomic_register_access;
|
||||
pub mod clocks;
|
||||
#[cfg(feature = "critical-section-impl")]
|
||||
mod critical_section_impl;
|
||||
pub mod dma;
|
||||
mod float;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod multicore;
|
||||
pub mod pio;
|
||||
pub mod pll;
|
||||
pub mod prelude;
|
||||
pub mod pwm;
|
||||
pub mod resets;
|
||||
pub mod rom_data;
|
||||
pub mod rosc;
|
||||
pub mod rtc;
|
||||
pub mod sio;
|
||||
pub mod spi;
|
||||
pub mod ssi;
|
||||
pub mod timer;
|
||||
pub mod typelevel;
|
||||
pub mod uart;
|
||||
pub mod usb;
|
||||
pub mod vector_table;
|
||||
pub mod watchdog;
|
||||
pub mod xosc;
|
||||
|
||||
// Provide access to common datastructures to avoid repeating ourselves
|
||||
pub use adc::Adc;
|
||||
pub use clocks::Clock;
|
||||
pub use i2c::I2C;
|
||||
/// Attribute to declare the entry point of the program
|
||||
///
|
||||
/// This is based on and can be used like the [entry attribute from
|
||||
/// cortex-m-rt](https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.entry.html).
|
||||
///
|
||||
/// It extends that macro with code to unlock all spinlocks at the beginning
|
||||
/// of `main`. As spinlocks are not automatically unlocked on software resets,
|
||||
/// this can prevent unexpected deadlocks when running from a debugger.
|
||||
pub use rp2040_hal_macros::entry;
|
||||
pub use sio::Sio;
|
||||
pub use spi::Spi;
|
||||
pub use timer::Timer;
|
||||
pub use watchdog::Watchdog;
|
|
@ -1,272 +0,0 @@
|
|||
//! Multicore support
|
||||
//!
|
||||
//! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1.
|
||||
//! It provides functionality for setting up the stack, and starting core1.
|
||||
//!
|
||||
//! The entrypoint for core1 can be any function that never returns, including closures.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use rp2040_hal::{pac, gpio::Pins, sio::Sio, multicore::{Multicore, Stack}};
|
||||
//!
|
||||
//! static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||
//!
|
||||
//! fn core1_task() -> ! {
|
||||
//! loop {}
|
||||
//! }
|
||||
//!
|
||||
//! fn main() -> ! {
|
||||
//! let mut pac = pac::Peripherals::take().unwrap();
|
||||
//! let mut sio = Sio::new(pac.SIO);
|
||||
//! // Other init code above this line
|
||||
//! let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
|
||||
//! let cores = mc.cores();
|
||||
//! let core1 = &mut cores[1];
|
||||
//! let _test = core1.spawn(unsafe { &mut CORE1_STACK.mem }, core1_task);
|
||||
//! // The rest of your application below this line
|
||||
//! # loop {}
|
||||
//! }
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! For inter-processor communications, see [`crate::sio::SioFifo`] and [`crate::sio::Spinlock0`]
|
||||
//!
|
||||
//! For a detailed example, see [examples/multicore_fifo_blink.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/multicore_fifo_blink.rs)
|
||||
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::sync::atomic::compiler_fence;
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use crate::pac;
|
||||
use crate::Sio;
|
||||
|
||||
/// Errors for multicore operations.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// Operation is invalid on this core.
|
||||
InvalidCore,
|
||||
/// Core was unresponsive to commands.
|
||||
Unresponsive,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn install_stack_guard(stack_bottom: *mut usize) {
|
||||
let core = unsafe { pac::CorePeripherals::steal() };
|
||||
|
||||
// Trap if MPU is already configured
|
||||
if core.MPU.ctrl.read() != 0 {
|
||||
cortex_m::asm::udf();
|
||||
}
|
||||
|
||||
// The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will
|
||||
// just shorten the valid stack range a tad.
|
||||
let addr = (stack_bottom as u32 + 31) & !31;
|
||||
// Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want
|
||||
let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7));
|
||||
unsafe {
|
||||
core.MPU.ctrl.write(5); // enable mpu with background default map
|
||||
core.MPU.rbar.write((addr & !0xff) | 0x8);
|
||||
core.MPU.rasr.write(
|
||||
1 // enable region
|
||||
| (0x7 << 1) // size 2^(7 + 1) = 256
|
||||
| (subregion_select << 8)
|
||||
| 0x10000000, // XN = disable instruction fetch; no other bits means no permissions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn core1_setup(stack_bottom: *mut usize) {
|
||||
install_stack_guard(stack_bottom);
|
||||
// TODO: irq priorities
|
||||
}
|
||||
|
||||
/// Multicore execution management.
|
||||
pub struct Multicore<'p> {
|
||||
cores: [Core<'p>; 2],
|
||||
}
|
||||
|
||||
/// Data type for a properly aligned stack of N 32-bit (usize) words
|
||||
#[repr(C, align(32))]
|
||||
pub struct Stack<const SIZE: usize> {
|
||||
/// Memory to be used for the stack
|
||||
pub mem: [usize; SIZE],
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Stack<SIZE> {
|
||||
/// Construct a stack of length SIZE, initialized to 0
|
||||
pub const fn new() -> Stack<SIZE> {
|
||||
Stack { mem: [0; SIZE] }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p> Multicore<'p> {
|
||||
/// Create a new |Multicore| instance.
|
||||
pub fn new(
|
||||
psm: &'p mut pac::PSM,
|
||||
ppb: &'p mut pac::PPB,
|
||||
sio: &'p mut crate::sio::SioFifo,
|
||||
) -> Self {
|
||||
Self {
|
||||
cores: [
|
||||
Core { inner: None },
|
||||
Core {
|
||||
inner: Some((psm, ppb, sio)),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the available |Core| instances.
|
||||
pub fn cores(&mut self) -> &'p mut [Core] {
|
||||
&mut self.cores
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle for controlling a logical core.
|
||||
pub struct Core<'p> {
|
||||
inner: Option<(
|
||||
&'p mut pac::PSM,
|
||||
&'p mut pac::PPB,
|
||||
&'p mut crate::sio::SioFifo,
|
||||
)>,
|
||||
}
|
||||
|
||||
impl<'p> Core<'p> {
|
||||
/// Get the id of this core.
|
||||
pub fn id(&self) -> u8 {
|
||||
match self.inner {
|
||||
None => 0,
|
||||
Some(..) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a function on this core.
|
||||
pub fn spawn<F>(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error>
|
||||
where
|
||||
F: FnOnce() -> bad::Never + Send + 'static,
|
||||
{
|
||||
if let Some((psm, ppb, fifo)) = self.inner.as_mut() {
|
||||
// The first two ignored `u64` parameters are there to take up all of the registers,
|
||||
// which means that the rest of the arguments are taken from the stack,
|
||||
// where we're able to put them from core 0.
|
||||
extern "C" fn core1_startup<F: FnOnce() -> bad::Never>(
|
||||
_: u64,
|
||||
_: u64,
|
||||
entry: &mut ManuallyDrop<F>,
|
||||
stack_bottom: *mut usize,
|
||||
) -> ! {
|
||||
core1_setup(stack_bottom);
|
||||
|
||||
let entry = unsafe { ManuallyDrop::take(entry) };
|
||||
|
||||
// Signal that it's safe for core 0 to get rid of the original value now.
|
||||
//
|
||||
// We don't have any way to get at core 1's SIO without using `Peripherals::steal` right now,
|
||||
// since svd2rust doesn't really support multiple cores properly.
|
||||
let peripherals = unsafe { pac::Peripherals::steal() };
|
||||
let mut sio = Sio::new(peripherals.SIO);
|
||||
sio.fifo.write_blocking(1);
|
||||
|
||||
entry()
|
||||
}
|
||||
|
||||
// Reset the core
|
||||
psm.frce_off.modify(|_, w| w.proc1().set_bit());
|
||||
while !psm.frce_off.read().proc1().bit_is_set() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
psm.frce_off.modify(|_, w| w.proc1().clear_bit());
|
||||
|
||||
// Set up the stack
|
||||
let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) };
|
||||
|
||||
// We don't want to drop this, since it's getting moved to the other core.
|
||||
let mut entry = ManuallyDrop::new(entry);
|
||||
|
||||
// Push the arguments to `core1_startup` onto the stack.
|
||||
unsafe {
|
||||
// Push `stack_bottom`.
|
||||
stack_ptr = stack_ptr.sub(1);
|
||||
stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr());
|
||||
|
||||
// Push `entry`.
|
||||
stack_ptr = stack_ptr.sub(1);
|
||||
stack_ptr.cast::<&mut ManuallyDrop<F>>().write(&mut entry);
|
||||
}
|
||||
|
||||
// Make sure the compiler does not reorder the stack writes after to after the
|
||||
// below FIFO writes, which would result in them not being seen by the second
|
||||
// core.
|
||||
//
|
||||
// From the compiler perspective, this doesn't guarantee that the second core
|
||||
// actually sees those writes. However, we know that the RP2040 doesn't have
|
||||
// memory caches, and writes happen in-order.
|
||||
compiler_fence(Ordering::Release);
|
||||
|
||||
let vector_table = ppb.vtor.read().bits();
|
||||
|
||||
// After reset, core 1 is waiting to receive commands over FIFO.
|
||||
// This is the sequence to have it jump to some code.
|
||||
let cmd_seq = [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
vector_table as usize,
|
||||
stack_ptr as usize,
|
||||
core1_startup::<F> as usize,
|
||||
];
|
||||
|
||||
let mut seq = 0;
|
||||
let mut fails = 0;
|
||||
loop {
|
||||
let cmd = cmd_seq[seq] as u32;
|
||||
if cmd == 0 {
|
||||
fifo.drain();
|
||||
cortex_m::asm::sev();
|
||||
}
|
||||
fifo.write_blocking(cmd);
|
||||
let response = fifo.read_blocking();
|
||||
if cmd == response {
|
||||
seq += 1;
|
||||
} else {
|
||||
seq = 0;
|
||||
fails += 1;
|
||||
if fails > 16 {
|
||||
// The second core isn't responding, and isn't going to take the entrypoint,
|
||||
// so we have to drop it ourselves.
|
||||
drop(ManuallyDrop::into_inner(entry));
|
||||
return Err(Error::Unresponsive);
|
||||
}
|
||||
}
|
||||
if seq >= cmd_seq.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the other core has copied `entry` before returning.
|
||||
fifo.read_blocking();
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidCore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/nvzqz/bad-rs/blob/master/src/never.rs
|
||||
mod bad {
|
||||
pub(crate) type Never = <F as HasOutput>::Output;
|
||||
|
||||
pub trait HasOutput {
|
||||
type Output;
|
||||
}
|
||||
|
||||
impl<O> HasOutput for fn() -> O {
|
||||
type Output = O;
|
||||
}
|
||||
|
||||
type F = fn() -> !;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,298 +0,0 @@
|
|||
//! Phase-Locked Loops (PLL)
|
||||
// See [Chapter 2 Section 18](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
|
||||
use core::{
|
||||
convert::{Infallible, TryInto},
|
||||
marker::PhantomData,
|
||||
ops::{Deref, Range, RangeInclusive},
|
||||
};
|
||||
|
||||
use fugit::{HertzU32, RateExtU32};
|
||||
|
||||
use nb::Error::WouldBlock;
|
||||
use pac::RESETS;
|
||||
|
||||
use crate::{clocks::ClocksManager, resets::SubsystemReset};
|
||||
|
||||
/// State of the PLL
|
||||
pub trait State {}
|
||||
|
||||
/// PLL is disabled.
|
||||
pub struct Disabled {
|
||||
refdiv: u8,
|
||||
fbdiv: u16,
|
||||
post_div1: u8,
|
||||
post_div2: u8,
|
||||
frequency: HertzU32,
|
||||
}
|
||||
|
||||
/// PLL is configured, started and locking into its designated frequency.
|
||||
pub struct Locking {
|
||||
post_div1: u8,
|
||||
post_div2: u8,
|
||||
frequency: HertzU32,
|
||||
}
|
||||
|
||||
/// PLL is locked : it delivers a steady frequency.
|
||||
pub struct Locked {
|
||||
frequency: HertzU32,
|
||||
}
|
||||
|
||||
impl State for Disabled {}
|
||||
impl State for Locked {}
|
||||
impl State for Locking {}
|
||||
|
||||
/// Trait to handle both underlying devices from the PAC (PLL_SYS & PLL_USB)
|
||||
pub trait PhaseLockedLoopDevice:
|
||||
Deref<Target = rp2040_pac::pll_sys::RegisterBlock> + SubsystemReset
|
||||
{
|
||||
}
|
||||
|
||||
impl PhaseLockedLoopDevice for rp2040_pac::PLL_SYS {}
|
||||
impl PhaseLockedLoopDevice for rp2040_pac::PLL_USB {}
|
||||
|
||||
/// A PLL.
|
||||
pub struct PhaseLockedLoop<S: State, D: PhaseLockedLoopDevice> {
|
||||
device: D,
|
||||
state: S,
|
||||
}
|
||||
|
||||
impl<S: State, D: PhaseLockedLoopDevice> PhaseLockedLoop<S, D> {
|
||||
fn transition<To: State>(self, state: To) -> PhaseLockedLoop<To, D> {
|
||||
PhaseLockedLoop {
|
||||
device: self.device,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases the underlying device.
|
||||
pub fn free(self) -> D {
|
||||
self.device
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type for the PLL module.
|
||||
/// See Chapter 2, Section 18 §2 for details on constraints triggering these errors.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// Proposed VCO frequency is out of range.
|
||||
VcoFreqOutOfRange,
|
||||
|
||||
/// Feedback Divider value is out of range.
|
||||
FeedbackDivOutOfRange,
|
||||
|
||||
/// Post Divider value is out of range.
|
||||
PostDivOutOfRage,
|
||||
|
||||
/// Reference Frequency is out of range.
|
||||
RefFreqOutOfRange,
|
||||
|
||||
/// Bad argument : overflows, bad conversion, ...
|
||||
BadArgument,
|
||||
}
|
||||
|
||||
/// Parameters for a PLL.
|
||||
pub struct PLLConfig {
|
||||
/// Voltage Controlled Oscillator frequency.
|
||||
pub vco_freq: HertzU32,
|
||||
|
||||
/// Reference divider
|
||||
pub refdiv: u8,
|
||||
|
||||
/// Post Divider 1
|
||||
pub post_div1: u8,
|
||||
|
||||
/// Post Divider 2
|
||||
pub post_div2: u8,
|
||||
}
|
||||
|
||||
/// Common configs for the two PLLs. Both assume the XOSC is cadenced at 12MHz !
|
||||
/// See Chapter 2, Section 18, §2
|
||||
pub mod common_configs {
|
||||
use super::PLLConfig;
|
||||
use fugit::HertzU32;
|
||||
|
||||
/// Default, nominal configuration for PLL_SYS
|
||||
pub const PLL_SYS_125MHZ: PLLConfig = PLLConfig {
|
||||
vco_freq: HertzU32::MHz(1500),
|
||||
refdiv: 1,
|
||||
post_div1: 6,
|
||||
post_div2: 2,
|
||||
};
|
||||
|
||||
/// Default, nominal configuration for PLL_USB.
|
||||
pub const PLL_USB_48MHZ: PLLConfig = PLLConfig {
|
||||
vco_freq: HertzU32::MHz(480),
|
||||
refdiv: 1,
|
||||
post_div1: 5,
|
||||
post_div2: 2,
|
||||
};
|
||||
}
|
||||
|
||||
impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
|
||||
/// Instantiates a new Phase-Locked-Loop device.
|
||||
pub fn new(
|
||||
dev: D,
|
||||
xosc_frequency: HertzU32,
|
||||
config: PLLConfig,
|
||||
) -> Result<PhaseLockedLoop<Disabled, D>, Error> {
|
||||
const VCO_FREQ_RANGE: RangeInclusive<HertzU32> = HertzU32::MHz(400)..=HertzU32::MHz(1_600);
|
||||
const POSTDIV_RANGE: Range<u8> = 1..7;
|
||||
const FBDIV_RANGE: Range<u16> = 16..320;
|
||||
|
||||
let vco_freq = config.vco_freq;
|
||||
|
||||
if !VCO_FREQ_RANGE.contains(&vco_freq) {
|
||||
return Err(Error::VcoFreqOutOfRange);
|
||||
}
|
||||
|
||||
if !POSTDIV_RANGE.contains(&config.post_div1) || !POSTDIV_RANGE.contains(&config.post_div2)
|
||||
{
|
||||
return Err(Error::PostDivOutOfRage);
|
||||
}
|
||||
|
||||
let ref_freq_max_vco = (vco_freq.to_Hz() / 16).Hz();
|
||||
let ref_freq_range: Range<HertzU32> = HertzU32::MHz(5)..ref_freq_max_vco;
|
||||
|
||||
let ref_freq_hz: HertzU32 = xosc_frequency
|
||||
.to_Hz()
|
||||
.checked_div(u32::from(config.refdiv))
|
||||
.ok_or(Error::BadArgument)?
|
||||
.Hz();
|
||||
|
||||
if !ref_freq_range.contains(&ref_freq_hz) {
|
||||
return Err(Error::RefFreqOutOfRange);
|
||||
}
|
||||
|
||||
let fbdiv = vco_freq
|
||||
.to_Hz()
|
||||
.checked_div(ref_freq_hz.to_Hz())
|
||||
.ok_or(Error::BadArgument)?;
|
||||
|
||||
let fbdiv: u16 = fbdiv.try_into().map_err(|_| Error::BadArgument)?;
|
||||
|
||||
if !FBDIV_RANGE.contains(&fbdiv) {
|
||||
return Err(Error::FeedbackDivOutOfRange);
|
||||
}
|
||||
|
||||
let refdiv = config.refdiv;
|
||||
let post_div1 = config.post_div1;
|
||||
let post_div2 = config.post_div2;
|
||||
let frequency: HertzU32 = ((ref_freq_hz / u32::from(refdiv)) * u32::from(fbdiv))
|
||||
/ (u32::from(post_div1) * u32::from(post_div2));
|
||||
|
||||
Ok(PhaseLockedLoop {
|
||||
state: Disabled {
|
||||
refdiv,
|
||||
fbdiv,
|
||||
post_div1,
|
||||
post_div2,
|
||||
frequency,
|
||||
},
|
||||
device: dev,
|
||||
})
|
||||
}
|
||||
|
||||
/// Configures and starts the PLL : it switches to Locking state.
|
||||
pub fn initialize(self, resets: &mut rp2040_pac::RESETS) -> PhaseLockedLoop<Locking, D> {
|
||||
self.device.reset_bring_up(resets);
|
||||
|
||||
// Turn off PLL in case it is already running
|
||||
self.device.pwr.reset();
|
||||
self.device.fbdiv_int.reset();
|
||||
|
||||
self.device.cs.write(|w| unsafe {
|
||||
w.refdiv().bits(self.state.refdiv);
|
||||
w
|
||||
});
|
||||
|
||||
self.device.fbdiv_int.write(|w| unsafe {
|
||||
w.fbdiv_int().bits(self.state.fbdiv);
|
||||
w
|
||||
});
|
||||
|
||||
// Turn on PLL
|
||||
self.device.pwr.modify(|_, w| {
|
||||
w.pd().clear_bit();
|
||||
w.vcopd().clear_bit();
|
||||
w
|
||||
});
|
||||
|
||||
let post_div1 = self.state.post_div1;
|
||||
let post_div2 = self.state.post_div2;
|
||||
let frequency = self.state.frequency;
|
||||
|
||||
self.transition(Locking {
|
||||
post_div1,
|
||||
post_div2,
|
||||
frequency,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A token that's given when the PLL is properly locked, so we can safely transition to the next state.
|
||||
pub struct LockedPLLToken<D> {
|
||||
_private: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Locking, D> {
|
||||
/// Awaits locking of the PLL.
|
||||
pub fn await_lock(&self) -> nb::Result<LockedPLLToken<D>, Infallible> {
|
||||
if self.device.cs.read().lock().bit_is_clear() {
|
||||
return Err(WouldBlock);
|
||||
}
|
||||
|
||||
Ok(LockedPLLToken {
|
||||
_private: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Exchanges a token for a Locked PLL.
|
||||
pub fn get_locked(self, _token: LockedPLLToken<D>) -> PhaseLockedLoop<Locked, D> {
|
||||
// Set up post dividers
|
||||
self.device.prim.write(|w| unsafe {
|
||||
w.postdiv1().bits(self.state.post_div1);
|
||||
w.postdiv2().bits(self.state.post_div2);
|
||||
w
|
||||
});
|
||||
|
||||
// Turn on post divider
|
||||
self.device.pwr.modify(|_, w| {
|
||||
w.postdivpd().clear_bit();
|
||||
w
|
||||
});
|
||||
|
||||
let frequency = self.state.frequency;
|
||||
|
||||
self.transition(Locked { frequency })
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Locked, D> {
|
||||
/// Get the operating frequency for the PLL
|
||||
pub fn operating_frequency(&self) -> HertzU32 {
|
||||
self.state.frequency
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocking helper method to setup the PLL without going through all the steps.
|
||||
pub fn setup_pll_blocking<D: PhaseLockedLoopDevice>(
|
||||
dev: D,
|
||||
xosc_frequency: HertzU32,
|
||||
config: PLLConfig,
|
||||
clocks: &mut ClocksManager,
|
||||
resets: &mut RESETS,
|
||||
) -> Result<PhaseLockedLoop<Locked, D>, Error> {
|
||||
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
|
||||
nb::block!(clocks.system_clock.reset_source_await()).unwrap();
|
||||
|
||||
nb::block!(clocks.reference_clock.reset_source_await()).unwrap();
|
||||
|
||||
let initialized_pll =
|
||||
PhaseLockedLoop::new(dev, xosc_frequency.convert(), config)?.initialize(resets);
|
||||
|
||||
let locked_pll_token = nb::block!(initialized_pll.await_lock()).unwrap();
|
||||
|
||||
Ok(initialized_pll.get_locked(locked_pll_token))
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
//! Prelude
|
||||
pub use crate::clocks::Clock as _rphal_clocks_Clock;
|
||||
pub use crate::pio::PIOExt as _rphal_pio_PIOExt;
|
|
@ -1,30 +0,0 @@
|
|||
//! Semi-internal enums mostly used in typelevel magic
|
||||
|
||||
/// Value-level `struct` representing slice IDs
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct DynSliceId {
|
||||
/// Slice id
|
||||
pub num: u8,
|
||||
}
|
||||
|
||||
/// Slice modes
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum DynSliceMode {
|
||||
/// Count continuously whenever the slice is enabled
|
||||
FreeRunning,
|
||||
/// Count continuously when a high level is detected on the B pin
|
||||
InputHighRunning,
|
||||
/// Count once with each rising edge detected on the B pin
|
||||
CountRisingEdge,
|
||||
/// Count once with each falling edge detected on the B pin
|
||||
CountFallingEdge,
|
||||
}
|
||||
|
||||
/// Channel ids
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum DynChannelId {
|
||||
/// Channel A
|
||||
A,
|
||||
/// Channel B
|
||||
B,
|
||||
}
|
|
@ -1,765 +0,0 @@
|
|||
//! Pulse Width Modulation (PWM)
|
||||
//!
|
||||
//! First you must create a Slices struct which contains all the pwm slices.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use rp2040_hal::{prelude::*, pwm::{InputHighRunning, Slices}};
|
||||
//!
|
||||
//!
|
||||
//! let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||
//!
|
||||
//! // Init PWMs
|
||||
//! let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
|
||||
//!
|
||||
//! // Configure PWM4
|
||||
//! let mut pwm = pwm_slices.pwm4;
|
||||
//! pwm.set_ph_correct();
|
||||
//! pwm.enable();
|
||||
//!
|
||||
//! // Set to run when b channel is high
|
||||
//! let pwm = pwm.into_mode::<InputHighRunning>();
|
||||
//! ```
|
||||
//!
|
||||
//! Once you have the PWM slice struct, you can add individual pins:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use rp2040_hal::{prelude::*, gpio::Pins, Sio, pwm::{InputHighRunning, Slices}};
|
||||
//! # let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||
//! # let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
|
||||
//! # let mut pwm = pwm_slices.pwm4.into_mode::<InputHighRunning>();
|
||||
//! # let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||
//! #
|
||||
//! # let sio = Sio::new(pac.SIO);
|
||||
//! # let pins = Pins::new(
|
||||
//! # pac.IO_BANK0,
|
||||
//! # pac.PADS_BANK0,
|
||||
//! # sio.gpio_bank0,
|
||||
//! # &mut pac.RESETS,
|
||||
//! # );
|
||||
//! #
|
||||
//! use embedded_hal::PwmPin;
|
||||
//!
|
||||
//! // Use B channel (which inputs from GPIO 25)
|
||||
//! let mut channel_b = pwm.channel_b;
|
||||
//! let channel_pin_b = channel_b.input_from(pins.gpio25);
|
||||
//!
|
||||
//! // Use A channel (which outputs to GPIO 24)
|
||||
//! let mut channel_a = pwm.channel_a;
|
||||
//! let channel_pin_a = channel_a.output_to(pins.gpio24);
|
||||
//!
|
||||
//! // Set duty cycle
|
||||
//! channel_a.set_duty(0x00ff);
|
||||
//! channel_a.get_duty();
|
||||
//! channel_a.set_inverted(); // Invert the output
|
||||
//! channel_a.clr_inverted(); // Don't invert the output
|
||||
//! ```
|
||||
//!
|
||||
//! The following configuration options are also available:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use rp2040_hal::{prelude::*, pwm::Slices};
|
||||
//! # let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||
//! # let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
|
||||
//! # let mut pwm = pwm_slices.pwm4;
|
||||
//! pwm.set_ph_correct(); // Run in phase correct mode
|
||||
//! pwm.clr_ph_correct(); // Don't run in phase correct mode
|
||||
//!
|
||||
//! pwm.set_div_int(1u8); // To set integer part of clock divider
|
||||
//! pwm.set_div_frac(0u8); // To set fractional part of clock divider
|
||||
//!
|
||||
//! pwm.get_top(); // To get the TOP register
|
||||
//! pwm.set_top(u16::MAX); // To set the TOP register
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! default_config() sets ph_correct to false, the clock divider to 1, does not invert the output, sets top to 65535, and resets the counter.
|
||||
//! min_config() leaves those registers in the state they were before it was called (Careful, this can lead to unexpected behavior)
|
||||
//! It's recommended to only call min_config() after calling default_config() on a pin that shares a PWM block.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
gpio::{
|
||||
bank0::*, Disabled, DisabledConfig, FunctionClock, FunctionI2C, FunctionPio0, FunctionPio1,
|
||||
FunctionPwm, FunctionSpi, FunctionUart, FunctionUsbAux, FunctionXip, Input, InputConfig,
|
||||
Output, OutputConfig, Pin, PinId, PinMode, ValidPinMode,
|
||||
},
|
||||
resets::SubsystemReset,
|
||||
typelevel::Sealed,
|
||||
};
|
||||
use embedded_hal::PwmPin;
|
||||
use pac::PWM;
|
||||
|
||||
use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set};
|
||||
|
||||
pub mod dyn_slice;
|
||||
pub use dyn_slice::*;
|
||||
|
||||
mod reg;
|
||||
|
||||
use reg::RegisterInterface;
|
||||
|
||||
/// Used to pin traits to a specific channel (A or B)
|
||||
pub trait ChannelId: Sealed {
|
||||
/// Corresponding [`DynChannelId`](dyn_slice::DynChannelId)
|
||||
const DYN: DynChannelId;
|
||||
}
|
||||
|
||||
/// Channel A
|
||||
///
|
||||
/// These are attached to the even gpio pins and can only do PWM output
|
||||
pub enum A {}
|
||||
|
||||
/// Channel B
|
||||
///
|
||||
/// These are attached to the odd gpio pins and can do PWM output and edge counting for input
|
||||
pub enum B {}
|
||||
|
||||
impl ChannelId for A {
|
||||
const DYN: DynChannelId = DynChannelId::A;
|
||||
}
|
||||
impl ChannelId for B {
|
||||
const DYN: DynChannelId = DynChannelId::B;
|
||||
}
|
||||
impl Sealed for A {}
|
||||
impl Sealed for B {}
|
||||
|
||||
/// Counter is free-running, and will count continuously whenever the slice is enabled
|
||||
pub struct FreeRunning;
|
||||
/// Count continuously when a high level is detected on the B pin
|
||||
pub struct InputHighRunning;
|
||||
/// Count once with each rising edge detected on the B pin
|
||||
pub struct CountRisingEdge;
|
||||
/// Count once with each falling edge detected on the B pin
|
||||
pub struct CountFallingEdge;
|
||||
|
||||
/// Type-level marker for tracking which slice modes are valid for which slices
|
||||
pub trait ValidSliceMode<I: SliceId>: Sealed {}
|
||||
|
||||
/// Type-level marker for tracking which slice modes are valid for which slices
|
||||
pub trait ValidSliceInputMode<I: SliceId>: Sealed + ValidSliceMode<I> {}
|
||||
|
||||
/// Mode for slice
|
||||
pub trait SliceMode: Sealed + Sized {
|
||||
/// Corresponding [`DynSliceMode`](dyn_slice::DynSliceMode)
|
||||
const DYN: DynSliceMode;
|
||||
}
|
||||
|
||||
impl Sealed for FreeRunning {}
|
||||
impl SliceMode for FreeRunning {
|
||||
const DYN: DynSliceMode = DynSliceMode::FreeRunning;
|
||||
}
|
||||
impl Sealed for InputHighRunning {}
|
||||
impl SliceMode for InputHighRunning {
|
||||
const DYN: DynSliceMode = DynSliceMode::InputHighRunning;
|
||||
}
|
||||
impl Sealed for CountRisingEdge {}
|
||||
impl SliceMode for CountRisingEdge {
|
||||
const DYN: DynSliceMode = DynSliceMode::CountRisingEdge;
|
||||
}
|
||||
impl Sealed for CountFallingEdge {}
|
||||
impl SliceMode for CountFallingEdge {
|
||||
const DYN: DynSliceMode = DynSliceMode::CountFallingEdge;
|
||||
}
|
||||
|
||||
impl<I: SliceId> ValidSliceMode<I> for FreeRunning {}
|
||||
impl<I: SliceId> ValidSliceMode<I> for InputHighRunning {}
|
||||
impl<I: SliceId> ValidSliceMode<I> for CountRisingEdge {}
|
||||
impl<I: SliceId> ValidSliceMode<I> for CountFallingEdge {}
|
||||
impl<I: SliceId> ValidSliceInputMode<I> for InputHighRunning {}
|
||||
impl<I: SliceId> ValidSliceInputMode<I> for CountRisingEdge {}
|
||||
impl<I: SliceId> ValidSliceInputMode<I> for CountFallingEdge {}
|
||||
|
||||
//==============================================================================
|
||||
// Slice IDs
|
||||
//==============================================================================
|
||||
|
||||
/// Type-level `enum` for slice IDs
|
||||
pub trait SliceId: Sealed {
|
||||
/// Corresponding [`DynSliceId`](dyn_slice::DynSliceId)
|
||||
const DYN: DynSliceId;
|
||||
/// [`SliceMode`] at reset
|
||||
type Reset;
|
||||
}
|
||||
|
||||
macro_rules! slice_id {
|
||||
($Id:ident, $NUM:literal, $reset : ident) => {
|
||||
$crate::paste::paste! {
|
||||
#[doc = "Slice ID representing slice " $NUM]
|
||||
pub enum $Id {}
|
||||
impl Sealed for $Id {}
|
||||
impl SliceId for $Id {
|
||||
type Reset = $reset;
|
||||
const DYN: DynSliceId = DynSliceId { num: $NUM };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Registers
|
||||
//==============================================================================
|
||||
|
||||
/// Provide a safe register interface for [`Slice`]s
|
||||
///
|
||||
/// This `struct` takes ownership of a [`SliceId`] and provides an API to
|
||||
/// access the corresponding registers.
|
||||
struct Registers<I: SliceId> {
|
||||
id: PhantomData<I>,
|
||||
}
|
||||
|
||||
// [`Registers`] takes ownership of the [`SliceId`], and [`Slice`] guarantees that
|
||||
// each slice is a singleton, so this implementation is safe.
|
||||
unsafe impl<I: SliceId> RegisterInterface for Registers<I> {
|
||||
#[inline]
|
||||
fn id(&self) -> DynSliceId {
|
||||
I::DYN
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: SliceId> Registers<I> {
|
||||
/// Create a new instance of [`Registers`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Users must never create two simultaneous instances of this `struct` with
|
||||
/// the same [`SliceId`]
|
||||
#[inline]
|
||||
unsafe fn new() -> Self {
|
||||
Registers { id: PhantomData }
|
||||
}
|
||||
|
||||
/// Provide a type-level equivalent for the
|
||||
/// [`RegisterInterface::change_mode`] method.
|
||||
#[inline]
|
||||
fn change_mode<M: SliceMode + ValidSliceMode<I>>(&mut self) {
|
||||
RegisterInterface::do_change_mode(self, M::DYN);
|
||||
}
|
||||
}
|
||||
|
||||
/// Pwm slice
|
||||
pub struct Slice<I, M>
|
||||
where
|
||||
I: SliceId,
|
||||
M: SliceMode + ValidSliceMode<I>,
|
||||
{
|
||||
regs: Registers<I>,
|
||||
mode: PhantomData<M>,
|
||||
/// Channel A (always output)
|
||||
pub channel_a: Channel<I, M, A>,
|
||||
/// Channel B (input or output)
|
||||
pub channel_b: Channel<I, M, B>,
|
||||
}
|
||||
|
||||
impl<I, M> Slice<I, M>
|
||||
where
|
||||
I: SliceId,
|
||||
M: SliceMode + ValidSliceMode<I>,
|
||||
{
|
||||
/// Create a new [`Slice`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each [`Slice`] must be a singleton. For a given [`SliceId`], there must be
|
||||
/// at most one corresponding [`Slice`] in existence at any given time.
|
||||
/// Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn new() -> Slice<I, M> {
|
||||
Slice {
|
||||
regs: Registers::new(),
|
||||
mode: PhantomData,
|
||||
channel_a: Channel::new(0),
|
||||
channel_b: Channel::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the slice to the requested [`SliceMode`]
|
||||
#[inline]
|
||||
pub fn into_mode<N: SliceMode + ValidSliceMode<I>>(mut self) -> Slice<I, N> {
|
||||
if N::DYN != M::DYN {
|
||||
self.regs.change_mode::<N>();
|
||||
}
|
||||
// Safe because we drop the existing slice
|
||||
unsafe { Slice::new() }
|
||||
}
|
||||
|
||||
/// Set a default config for the slice
|
||||
pub fn default_config(&mut self) {
|
||||
self.regs.write_ph_correct(false);
|
||||
self.regs.write_div_int(1); // No divisor
|
||||
self.regs.write_div_frac(0); // No divisor
|
||||
self.regs.write_inv_a(false); //Don't invert the channel
|
||||
self.regs.write_inv_b(false); //Don't invert the channel
|
||||
self.regs.write_top(0xffff); // Wrap at max
|
||||
self.regs.write_ctr(0x0000); //Reset the counter
|
||||
self.regs.write_cc_a(0); //Default duty cycle of 0%
|
||||
self.regs.write_cc_b(0); //Default duty cycle of 0%
|
||||
}
|
||||
|
||||
/// Advance the phase with one count
|
||||
///
|
||||
/// Counter must be running at less than full speed (div_int + div_frac / 16 > 1)
|
||||
#[inline]
|
||||
pub fn advance_phase(&mut self) {
|
||||
self.regs.advance_phase()
|
||||
}
|
||||
|
||||
/// Retard the phase with one count
|
||||
///
|
||||
/// Counter must be running at less than full speed (div_int + div_frac / 16 > 1)
|
||||
#[inline]
|
||||
pub fn retard_phase(&mut self) {
|
||||
self.regs.retard_phase()
|
||||
}
|
||||
|
||||
/// Enable phase correct mode
|
||||
#[inline]
|
||||
pub fn set_ph_correct(&mut self) {
|
||||
self.regs.write_ph_correct(true)
|
||||
}
|
||||
|
||||
/// Disables phase correct mode
|
||||
#[inline]
|
||||
pub fn clr_ph_correct(&mut self) {
|
||||
self.regs.write_ph_correct(false)
|
||||
}
|
||||
|
||||
/// Enable slice
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.regs.write_enable(true);
|
||||
}
|
||||
|
||||
/// Disable slice
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.regs.write_enable(false)
|
||||
}
|
||||
|
||||
/// Sets the integer part of the clock divider
|
||||
#[inline]
|
||||
pub fn set_div_int(&mut self, value: u8) {
|
||||
self.regs.write_div_int(value)
|
||||
}
|
||||
|
||||
/// Sets the fractional part of the clock divider
|
||||
#[inline]
|
||||
pub fn set_div_frac(&mut self, value: u8) {
|
||||
self.regs.write_div_frac(value)
|
||||
}
|
||||
|
||||
/// Get the counter register value
|
||||
#[inline]
|
||||
pub fn get_counter(&self) -> u16 {
|
||||
self.regs.read_ctr()
|
||||
}
|
||||
|
||||
/// Set the counter register value
|
||||
#[inline]
|
||||
pub fn set_counter(&mut self, value: u16) {
|
||||
self.regs.write_ctr(value)
|
||||
}
|
||||
|
||||
/// Get the top register value
|
||||
#[inline]
|
||||
pub fn get_top(&self) -> u16 {
|
||||
self.regs.read_top()
|
||||
}
|
||||
|
||||
/// Sets the top register value
|
||||
#[inline]
|
||||
pub fn set_top(&mut self, value: u16) {
|
||||
self.regs.write_top(value)
|
||||
}
|
||||
|
||||
/// Create the interrupt bitmask corresponding to this slice
|
||||
#[inline]
|
||||
fn bitmask(&self) -> u32 {
|
||||
1 << I::DYN.num
|
||||
}
|
||||
|
||||
/// Enable the PWM_IRQ_WRAP interrupt when this slice overflows.
|
||||
#[inline]
|
||||
pub fn enable_interrupt(&mut self) {
|
||||
unsafe {
|
||||
let pwm = &(*pac::PWM::ptr());
|
||||
let reg = pwm.inte.as_ptr();
|
||||
write_bitmask_set(reg, self.bitmask());
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the PWM_IRQ_WRAP interrupt for this slice.
|
||||
#[inline]
|
||||
pub fn disable_interrupt(&mut self) {
|
||||
unsafe {
|
||||
let pwm = &(*pac::PWM::ptr());
|
||||
let reg = pwm.inte.as_ptr();
|
||||
write_bitmask_clear(reg, self.bitmask());
|
||||
};
|
||||
}
|
||||
|
||||
/// Did this slice trigger an overflow interrupt?
|
||||
#[inline]
|
||||
pub fn has_overflown(&self) -> bool {
|
||||
let mask = self.bitmask();
|
||||
unsafe { (*pac::PWM::ptr()).ints.read().bits() & mask == mask }
|
||||
}
|
||||
|
||||
/// Mark the interrupt handled for this slice.
|
||||
#[inline]
|
||||
pub fn clear_interrupt(&mut self) {
|
||||
unsafe { (*pac::PWM::ptr()).intr.write(|w| w.bits(self.bitmask())) };
|
||||
}
|
||||
|
||||
/// Force the interrupt. This bit is not cleared by hardware and must be manually cleared to
|
||||
/// stop the interrupt from continuing to be asserted.
|
||||
#[inline]
|
||||
pub fn force_interrupt(&mut self) {
|
||||
unsafe {
|
||||
let pwm = &(*pac::PWM::ptr());
|
||||
let reg = pwm.intf.as_ptr();
|
||||
write_bitmask_set(reg, self.bitmask());
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear force interrupt. This bit is not cleared by hardware and must be manually cleared to
|
||||
/// stop the interrupt from continuing to be asserted.
|
||||
#[inline]
|
||||
pub fn clear_force_interrupt(&mut self) {
|
||||
unsafe {
|
||||
let pwm = &(*pac::PWM::ptr());
|
||||
let reg = pwm.intf.as_ptr();
|
||||
write_bitmask_clear(reg, self.bitmask());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! pwm {
|
||||
($PWMX:ident, [
|
||||
$($SXi:ident: ($slice:literal, [$($pin_a:ident, $pin_b:ident),*], $i:expr)),+
|
||||
]) => {
|
||||
$(
|
||||
slice_id!($SXi, $slice, FreeRunning);
|
||||
|
||||
$(
|
||||
impl ValidPwmOutputPin<$SXi, A> for $pin_a {}
|
||||
impl ValidPwmOutputPin<$SXi, B> for $pin_b {}
|
||||
impl ValidPwmInputPin<$SXi> for $pin_b {}
|
||||
)*
|
||||
)+
|
||||
|
||||
$crate::paste::paste!{
|
||||
|
||||
/// Collection of all the individual [`Slices`]s
|
||||
pub struct Slices {
|
||||
_pwm: $PWMX,
|
||||
$(
|
||||
#[doc = "Slice " $SXi]
|
||||
pub [<$SXi:lower>] : Slice<$SXi,<$SXi as SliceId>::Reset>,
|
||||
)+
|
||||
}
|
||||
|
||||
impl Slices {
|
||||
/// Take ownership of the PAC peripheral and split it into discrete [`Slice`]s
|
||||
pub fn new(pwm: $PWMX, reset : &mut pac::RESETS) -> Self {
|
||||
pwm.reset_bring_up(reset);
|
||||
unsafe {
|
||||
Self {
|
||||
_pwm: pwm,
|
||||
$(
|
||||
[<$SXi:lower>]: Slice::new(),
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pwm! {
|
||||
PWM, [
|
||||
Pwm0: (0, [Gpio0, Gpio1, Gpio16, Gpio17], 0),
|
||||
Pwm1: (1, [Gpio2, Gpio3, Gpio18, Gpio19], 1),
|
||||
Pwm2: (2, [Gpio4, Gpio5, Gpio20, Gpio21], 2),
|
||||
Pwm3: (3, [Gpio6, Gpio7, Gpio22, Gpio23], 3),
|
||||
Pwm4: (4, [Gpio8, Gpio9, Gpio24, Gpio25], 4),
|
||||
Pwm5: (5, [Gpio10, Gpio11, Gpio26, Gpio27], 5),
|
||||
Pwm6: (6, [Gpio12, Gpio13, Gpio28, Gpio29], 6),
|
||||
Pwm7: (7, [Gpio14, Gpio15], 7)
|
||||
]
|
||||
}
|
||||
|
||||
/// Marker trait for valid output pins
|
||||
pub trait ValidPwmInputPin<S: SliceId>: Sealed {}
|
||||
/// Marker trait for valid input pins (Channel B only)
|
||||
pub trait ValidPwmOutputPin<S: SliceId, C: ChannelId>: Sealed {}
|
||||
|
||||
/// Make sure we can't free an GPIO pin while still keeping it attached to pwm
|
||||
/// TODO: Maybe FunctionPWM should be private?
|
||||
pub trait NonPwmPinMode: Sealed {}
|
||||
|
||||
impl NonPwmPinMode for FunctionClock {}
|
||||
impl NonPwmPinMode for FunctionI2C {}
|
||||
impl NonPwmPinMode for FunctionPio0 {}
|
||||
impl NonPwmPinMode for FunctionPio1 {}
|
||||
impl NonPwmPinMode for FunctionSpi {}
|
||||
impl NonPwmPinMode for FunctionUart {}
|
||||
impl NonPwmPinMode for FunctionUsbAux {}
|
||||
impl NonPwmPinMode for FunctionXip {}
|
||||
impl<C: InputConfig> NonPwmPinMode for Input<C> {}
|
||||
impl<C: OutputConfig> NonPwmPinMode for Output<C> {}
|
||||
impl<C: DisabledConfig> NonPwmPinMode for Disabled<C> {}
|
||||
|
||||
/// Stores the attached gpio pin.
|
||||
///
|
||||
/// This value can be ignored/dropped or stored to retrieve the original pin struct
|
||||
pub struct PwmPinToken<G: PinId + BankPinId> {
|
||||
pin: Pin<G, FunctionPwm>,
|
||||
}
|
||||
|
||||
impl<G: PinId + BankPinId> PwmPinToken<G> {
|
||||
/// Retrieve the original pin while disconnecting it from the pwm
|
||||
pub fn into_mode<N: PinMode + ValidPinMode<G> + NonPwmPinMode>(self) -> Pin<G, N> {
|
||||
self.pin.into_mode::<N>()
|
||||
}
|
||||
}
|
||||
|
||||
impl Slices {
|
||||
/// Free the pwm registers from the pwm hal struct while consuming it.
|
||||
pub fn free(self) -> PWM {
|
||||
self._pwm
|
||||
}
|
||||
|
||||
// /// Enable multiple slices at the same time to make their counters sync up.
|
||||
// ///
|
||||
// /// You still need to call `slice` to get an actual slice
|
||||
// pub fn enable_simultaneous<S: SliceId>(&mut self, bits: u8) {
|
||||
// // Enable all slices at the same time
|
||||
// unsafe {
|
||||
// &(*pac::PWM::ptr())
|
||||
// .en
|
||||
// .modify(|r, w| w.bits(((r.bits() as u8) | bits) as u32));
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// Get pwm slice based on gpio pin
|
||||
// pub fn borrow_mut_from_pin<
|
||||
// S: SliceId,
|
||||
// C: ChannelId,
|
||||
// G: PinId + BankPinId + ValidPwmOutputPin<S, C>,
|
||||
// PM: PinMode + ValidPinMode<G>,
|
||||
// SM: SliceMode + ValidSliceMode<S>,
|
||||
// >(&mut self, _: &Pin<G, PM>) -> &mut Slice<S, SM>{
|
||||
// match S::DYN {
|
||||
// DynSliceId{num} if num == 0 => &mut self.pwm0,
|
||||
// DynSliceId{num} if num == 1 => &mut self.pwm1,
|
||||
// DynSliceId{num} if num == 2 => &mut self.pwm2,
|
||||
// DynSliceId{num} if num == 3 => &mut self.pwm3,
|
||||
// DynSliceId{num} if num == 4 => &mut self.pwm4,
|
||||
// DynSliceId{num} if num == 5 => &mut self.pwm5,
|
||||
// DynSliceId{num} if num == 6 => &mut self.pwm6,
|
||||
// DynSliceId{num} if num == 7 => &mut self.pwm7,
|
||||
// _ => unreachable!()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/// A Channel from the Pwm subsystem.
|
||||
///
|
||||
/// Its attached to one of the eight slices and can be an A or B side channel
|
||||
pub struct Channel<S: SliceId, M: SliceMode, C: ChannelId> {
|
||||
regs: Registers<S>,
|
||||
slice_mode: PhantomData<M>,
|
||||
channel_id: PhantomData<C>,
|
||||
duty_cycle: u16,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode, C: ChannelId> Channel<S, M, C> {
|
||||
pub(super) unsafe fn new(duty_cycle: u16) -> Self {
|
||||
Channel {
|
||||
regs: Registers::new(),
|
||||
slice_mode: PhantomData,
|
||||
channel_id: PhantomData,
|
||||
duty_cycle, // stores the duty cycle while the channel is disabled
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode, C: ChannelId> Sealed for Channel<S, M, C> {}
|
||||
|
||||
impl<S: SliceId, M: SliceMode> PwmPin for Channel<S, M, A> {
|
||||
type Duty = u16;
|
||||
|
||||
/// We cant disable the channel without disturbing the other channel.
|
||||
/// So this just sets the duty cycle to zero
|
||||
fn disable(&mut self) {
|
||||
if self.enabled {
|
||||
self.duty_cycle = self.regs.read_cc_a();
|
||||
}
|
||||
self.enabled = false;
|
||||
self.regs.write_cc_a(0)
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
if !self.enabled {
|
||||
self.enabled = true;
|
||||
self.regs.write_cc_a(self.duty_cycle)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_duty(&self) -> Self::Duty {
|
||||
if self.enabled {
|
||||
self.regs.read_cc_a()
|
||||
} else {
|
||||
self.duty_cycle
|
||||
}
|
||||
}
|
||||
|
||||
fn get_max_duty(&self) -> Self::Duty {
|
||||
self.regs.read_top()
|
||||
}
|
||||
|
||||
fn set_duty(&mut self, duty: Self::Duty) {
|
||||
self.duty_cycle = duty;
|
||||
if self.enabled {
|
||||
self.regs.write_cc_a(duty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode> PwmPin for Channel<S, M, B> {
|
||||
type Duty = u16;
|
||||
|
||||
/// We cant disable the channel without disturbing the other channel.
|
||||
/// So this just sets the duty cycle to zero
|
||||
fn disable(&mut self) {
|
||||
if self.enabled {
|
||||
self.duty_cycle = self.regs.read_cc_b();
|
||||
}
|
||||
self.enabled = false;
|
||||
self.regs.write_cc_b(0)
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
if !self.enabled {
|
||||
self.enabled = true;
|
||||
self.regs.write_cc_b(self.duty_cycle)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_duty(&self) -> Self::Duty {
|
||||
if self.enabled {
|
||||
self.regs.read_cc_b()
|
||||
} else {
|
||||
self.duty_cycle
|
||||
}
|
||||
}
|
||||
|
||||
fn get_max_duty(&self) -> Self::Duty {
|
||||
self.regs.read_top()
|
||||
}
|
||||
|
||||
fn set_duty(&mut self, duty: Self::Duty) {
|
||||
self.duty_cycle = duty;
|
||||
if self.enabled {
|
||||
self.regs.write_cc_b(duty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode + ValidSliceMode<S>> Channel<S, M, A> {
|
||||
/// Capture a gpio pin and use it as pwm output for channel A
|
||||
pub fn output_to<
|
||||
G: PinId + BankPinId + ValidPwmOutputPin<S, A>,
|
||||
PM: PinMode + ValidPinMode<G>,
|
||||
>(
|
||||
&mut self,
|
||||
pin: Pin<G, PM>,
|
||||
) -> PwmPinToken<G> {
|
||||
PwmPinToken {
|
||||
pin: pin.into_mode(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Invert channel output
|
||||
#[inline]
|
||||
pub fn set_inverted(&mut self) {
|
||||
self.regs.write_inv_a(true)
|
||||
}
|
||||
|
||||
/// Stop inverting channel output
|
||||
#[inline]
|
||||
pub fn clr_inverted(&mut self) {
|
||||
self.regs.write_inv_a(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode + ValidSliceMode<S>> Channel<S, M, B> {
|
||||
/// Capture a gpio pin and use it as pwm output for channel B
|
||||
pub fn output_to<
|
||||
G: PinId + BankPinId + ValidPwmOutputPin<S, B>,
|
||||
PM: PinMode + ValidPinMode<G>,
|
||||
>(
|
||||
&mut self,
|
||||
pin: Pin<G, PM>,
|
||||
) -> PwmPinToken<G> {
|
||||
PwmPinToken {
|
||||
pin: pin.into_mode(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Invert channel output
|
||||
#[inline]
|
||||
pub fn set_inverted(&mut self) {
|
||||
self.regs.write_inv_b(true)
|
||||
}
|
||||
|
||||
/// Stop inverting channel output
|
||||
#[inline]
|
||||
pub fn clr_inverted(&mut self) {
|
||||
self.regs.write_inv_b(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode + ValidSliceInputMode<S>> Channel<S, M, B> {
|
||||
/// Capture a gpio pin and use it as pwm input for channel B
|
||||
pub fn input_from<G: PinId + BankPinId + ValidPwmInputPin<S>, PM: PinMode + ValidPinMode<G>>(
|
||||
&mut self,
|
||||
pin: Pin<G, PM>,
|
||||
) -> PwmPinToken<G> {
|
||||
PwmPinToken {
|
||||
pin: pin.into_mode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode + ValidSliceMode<S>> Slice<S, M> {
|
||||
/// Capture a gpio pin and use it as pwm output
|
||||
pub fn output_to<
|
||||
G: PinId + BankPinId + ValidPwmOutputPin<S, C>,
|
||||
PM: PinMode + ValidPinMode<G>,
|
||||
C: ChannelId,
|
||||
>(
|
||||
&mut self,
|
||||
pin: Pin<G, PM>,
|
||||
) -> PwmPinToken<G> {
|
||||
PwmPinToken {
|
||||
pin: pin.into_mode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SliceId, M: SliceMode + ValidSliceInputMode<S>> Slice<S, M> {
|
||||
/// Capture a gpio pin and use it as pwm input for channel B
|
||||
pub fn input_from<G: PinId + BankPinId + ValidPwmInputPin<S>, PM: PinMode + ValidPinMode<G>>(
|
||||
&mut self,
|
||||
pin: Pin<G, PM>,
|
||||
) -> PwmPinToken<G> {
|
||||
PwmPinToken {
|
||||
pin: pin.into_mode(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
use super::dyn_slice::{DynSliceId, DynSliceMode};
|
||||
use pac::pwm::CH;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Users should only implement the [`id`] function. No default function
|
||||
/// implementations should be overridden. The implementing type must also have
|
||||
/// "control" over the corresponding slice ID, i.e. it must guarantee that each
|
||||
/// slice ID is a singleton
|
||||
pub(super) unsafe trait RegisterInterface {
|
||||
/// Provide a [`DynSliceId`] identifying the set of registers controlled by
|
||||
/// this type.
|
||||
fn id(&self) -> DynSliceId;
|
||||
|
||||
#[inline]
|
||||
fn ch(&self) -> &CH {
|
||||
let num = self.id().num as usize;
|
||||
unsafe { &(*pac::PWM::ptr()).ch[num] }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn advance_phase(&mut self) {
|
||||
self.ch().csr.modify(|_, w| w.ph_adv().set_bit())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn retard_phase(&mut self) {
|
||||
self.ch().csr.modify(|_, w| w.ph_ret().set_bit())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_change_mode(&mut self, mode: DynSliceMode) {
|
||||
self.ch().csr.modify(|_, w| match mode {
|
||||
DynSliceMode::FreeRunning => w.divmode().div(),
|
||||
DynSliceMode::InputHighRunning => w.divmode().level(),
|
||||
DynSliceMode::CountRisingEdge => w.divmode().rise(),
|
||||
DynSliceMode::CountFallingEdge => w.divmode().fall(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_inv_a(&mut self, value: bool) {
|
||||
self.ch().csr.modify(|_, w| w.a_inv().bit(value));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_inv_b(&mut self, value: bool) {
|
||||
self.ch().csr.modify(|_, w| w.b_inv().bit(value));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_ph_correct(&mut self, value: bool) {
|
||||
self.ch().csr.modify(|_, w| w.ph_correct().bit(value));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_enable(&mut self, value: bool) {
|
||||
self.ch().csr.modify(|_, w| w.en().bit(value));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_div_int(&mut self, value: u8) {
|
||||
self.ch().div.modify(|_, w| unsafe { w.int().bits(value) });
|
||||
}
|
||||
#[inline]
|
||||
fn write_div_frac(&mut self, value: u8) {
|
||||
self.ch().div.modify(|_, w| unsafe { w.frac().bits(value) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_ctr(&mut self, value: u16) {
|
||||
self.ch().ctr.write(|w| unsafe { w.ctr().bits(value) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_ctr(&self) -> u16 {
|
||||
self.ch().ctr.read().ctr().bits()
|
||||
}
|
||||
#[inline]
|
||||
fn write_cc_a(&mut self, value: u16) {
|
||||
self.ch().cc.modify(|_, w| unsafe { w.a().bits(value) });
|
||||
}
|
||||
#[inline]
|
||||
fn read_cc_a(&self) -> u16 {
|
||||
self.ch().cc.read().a().bits()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_cc_b(&mut self, value: u16) {
|
||||
self.ch().cc.modify(|_, w| unsafe { w.b().bits(value) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_cc_b(&self) -> u16 {
|
||||
self.ch().cc.read().b().bits()
|
||||
}
|
||||
#[inline]
|
||||
fn write_top(&mut self, value: u16) {
|
||||
self.ch().top.write(|w| unsafe { w.top().bits(value) });
|
||||
}
|
||||
#[inline]
|
||||
fn read_top(&self) -> u16 {
|
||||
self.ch().top.read().top().bits()
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
//! Subsystem Resets
|
||||
// See [Chapter 2 Section 14](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
use rp2040_pac as pac;
|
||||
|
||||
mod private {
|
||||
pub trait SubsystemReset {
|
||||
fn reset_bring_up(&self, resets: &mut pac::RESETS);
|
||||
fn reset_bring_down(&self, resets: &mut pac::RESETS);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use private::SubsystemReset;
|
||||
|
||||
macro_rules! generate_reset {
|
||||
($MODULE:ident, $module:ident) => {
|
||||
impl SubsystemReset for pac::$MODULE {
|
||||
fn reset_bring_up(&self, resets: &mut pac::RESETS) {
|
||||
resets.reset.modify(|_, w| w.$module().clear_bit());
|
||||
while resets.reset_done.read().$module().bit_is_clear() {}
|
||||
}
|
||||
fn reset_bring_down(&self, resets: &mut pac::RESETS) {
|
||||
resets.reset.modify(|_, w| w.$module().set_bit());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// In datasheet order
|
||||
generate_reset!(USBCTRL_REGS, usbctrl);
|
||||
generate_reset!(UART1, uart1);
|
||||
generate_reset!(UART0, uart0);
|
||||
generate_reset!(TIMER, timer);
|
||||
generate_reset!(TBMAN, tbman);
|
||||
generate_reset!(SYSINFO, sysinfo);
|
||||
generate_reset!(SYSCFG, syscfg);
|
||||
generate_reset!(SPI1, spi1);
|
||||
generate_reset!(SPI0, spi0);
|
||||
generate_reset!(RTC, rtc);
|
||||
generate_reset!(PWM, pwm);
|
||||
generate_reset!(PLL_USB, pll_usb);
|
||||
generate_reset!(PLL_SYS, pll_sys);
|
||||
generate_reset!(PIO1, pio1);
|
||||
generate_reset!(PIO0, pio0);
|
||||
generate_reset!(PADS_QSPI, pads_qspi);
|
||||
generate_reset!(PADS_BANK0, pads_bank0);
|
||||
//generate_reset!(JTAG,jtag); // This doesn't seem to have an item in the pac
|
||||
generate_reset!(IO_QSPI, io_qspi);
|
||||
generate_reset!(IO_BANK0, io_bank0);
|
||||
generate_reset!(I2C1, i2c1);
|
||||
generate_reset!(I2C0, i2c0);
|
||||
generate_reset!(DMA, dma);
|
||||
generate_reset!(BUSCTRL, busctrl);
|
||||
generate_reset!(ADC, adc);
|
|
@ -1,733 +0,0 @@
|
|||
//! Functions and data from the RPI Bootrom.
|
||||
//!
|
||||
//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
|
||||
//!
|
||||
//! > The Bootrom contains a number of public functions that provide useful
|
||||
//! > RP2040 functionality that might be needed in the absence of any other code
|
||||
//! > on the device, as well as highly optimized versions of certain key
|
||||
//! > functionality that would otherwise have to take up space in most user
|
||||
//! > binaries.
|
||||
|
||||
/// A bootrom function table code.
|
||||
pub type RomFnTableCode = [u8; 2];
|
||||
|
||||
/// This function searches for (table)
|
||||
type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T;
|
||||
|
||||
/// The following addresses are described at `2.8.2. Bootrom Contents`
|
||||
/// Pointer to the lookup table function supplied by the rom.
|
||||
const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _;
|
||||
|
||||
/// Pointer to helper functions lookup table.
|
||||
const FUNC_TABLE: *const u16 = 0x0000_0014 as _;
|
||||
|
||||
/// Pointer to the public data lookup table.
|
||||
const DATA_TABLE: *const u16 = 0x0000_0016 as _;
|
||||
|
||||
/// Address of the version number of the ROM.
|
||||
const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
|
||||
|
||||
/// Retrive rom content from a table using a code.
|
||||
fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
|
||||
unsafe {
|
||||
let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
|
||||
let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);
|
||||
rom_table_lookup(
|
||||
rom_hword_as_ptr(table) as *const u16,
|
||||
u16::from_le_bytes(tag) as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// To save space, the ROM likes to store memory pointers (which are 32-bit on
|
||||
/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the
|
||||
/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped
|
||||
/// to address `0x0000_0000` and so 16-bits are always sufficient.
|
||||
///
|
||||
/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer.
|
||||
unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {
|
||||
let ptr: u16 = *rom_address;
|
||||
ptr as *const u32
|
||||
}
|
||||
|
||||
macro_rules! declare_rom_function {
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
#[doc = r"` ROM function."]
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
#[doc = r"` ROM function."]
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! rom_functions {
|
||||
() => {};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
$c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
|
||||
}
|
||||
}
|
||||
|
||||
rom_functions!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
$c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
unsafe fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
|
||||
}
|
||||
}
|
||||
|
||||
rom_functions!($($rest)*);
|
||||
};
|
||||
}
|
||||
|
||||
rom_functions! {
|
||||
/// Return a count of the number of 1 bits in value.
|
||||
b"P3" fn popcount32(value: u32) -> u32;
|
||||
|
||||
/// Return the bits of value in the reverse order.
|
||||
b"R3" fn reverse32(value: u32) -> u32;
|
||||
|
||||
/// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32.
|
||||
b"L3" fn clz32(value: u32) -> u32;
|
||||
|
||||
/// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32.
|
||||
b"T3" fn ctz32(value: u32) -> u32;
|
||||
|
||||
/// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:
|
||||
/// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED
|
||||
/// for the USB Mass Storage Device:
|
||||
/// * 0 No pins are used as per cold boot.
|
||||
/// * Otherwise a single bit set indicating which GPIO pin should be set to output and
|
||||
/// raised whenever there is mass storage activity from the host.
|
||||
/// * disable_interface_mask may be used to control the exposed USB interfaces:
|
||||
/// * 0 To enable both interfaces (as per cold boot).
|
||||
/// * 1 To disable the USB Mass Storage Interface.
|
||||
/// * 2 to Disable the USB PICOBOOT Interface.
|
||||
b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();
|
||||
|
||||
/// Sets n bytes start at ptr to the value c and returns ptr
|
||||
b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8;
|
||||
|
||||
/// Sets n bytes start at ptr to the value c and returns ptr.
|
||||
///
|
||||
/// Note this is a slightly more efficient variant of _memset that may only
|
||||
/// be used if ptr is word aligned.
|
||||
// Note the datasheet does not match the actual ROM for the code here, see
|
||||
// https://github.com/raspberrypi/pico-feedback/issues/217
|
||||
b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;
|
||||
|
||||
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
|
||||
/// regions overlap.
|
||||
b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8;
|
||||
|
||||
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
|
||||
/// regions overlap.
|
||||
///
|
||||
/// Note this is a slightly more efficient variant of _memcpy that may only be
|
||||
/// used if dest and src are word aligned.
|
||||
b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8;
|
||||
|
||||
/// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads.
|
||||
b"IF" unsafe fn connect_internal_flash() -> ();
|
||||
|
||||
/// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence.
|
||||
///
|
||||
/// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be
|
||||
/// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This
|
||||
/// function configures the SSI with a fixed SCK clock divisor of /6.
|
||||
b"EX" unsafe fn flash_exit_xip() -> ();
|
||||
|
||||
/// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a
|
||||
/// block erase command e.g. D8h block erase, and the size of the block erased by this
|
||||
/// command — this function will use the larger block erase where possible, for much higher
|
||||
/// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of
|
||||
/// 4096 bytes.
|
||||
b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();
|
||||
|
||||
/// Program data to a range of flash addresses starting at `addr` (and
|
||||
/// offset from the start of flash) and `count` bytes in size. The value
|
||||
/// `addr` must be aligned to a 256-byte boundary, and `count` must be a
|
||||
/// multiple of 256.
|
||||
b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> ();
|
||||
|
||||
/// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can
|
||||
/// drive the flashchip select as normal.
|
||||
b"FC" unsafe fn flash_flush_cache() -> ();
|
||||
|
||||
/// Configure the SSI to generate a standard 03h serial read command, with 24 address bits,
|
||||
/// upon each XIP access. This is a very slow XIP configuration, but is very widely supported.
|
||||
/// The debugger calls this function after performing a flash erase/programming operation, so
|
||||
/// that the freshly-programmed code and data is visible to the debug host, without having to
|
||||
/// know exactly what kind of flash device is connected.
|
||||
b"CX" unsafe fn flash_enter_cmd_xip() -> ();
|
||||
|
||||
/// This is the method that is entered by core 1 on reset to wait to be launched by core 0.
|
||||
/// There are few cases where you should call this method (resetting core 1 is much better).
|
||||
/// This method does not return and should only ever be called on core 1.
|
||||
b"WV" unsafe fn wait_for_vector() -> !;
|
||||
}
|
||||
|
||||
// Various C intrinsics in the ROM
|
||||
intrinsics! {
|
||||
#[alias = __popcountdi2]
|
||||
extern "C" fn __popcountsi2(x: u32) -> u32 {
|
||||
popcount32(x)
|
||||
}
|
||||
|
||||
#[alias = __clzdi2]
|
||||
extern "C" fn __clzsi2(x: u32) -> u32 {
|
||||
clz32(x)
|
||||
}
|
||||
|
||||
#[alias = __ctzdi2]
|
||||
extern "C" fn __ctzsi2(x: u32) -> u32 {
|
||||
ctz32(x)
|
||||
}
|
||||
|
||||
// __rbit is only unofficial, but it show up in the ARM documentation,
|
||||
// so may as well hook it up.
|
||||
#[alias = __rbitl]
|
||||
extern "C" fn __rbit(x: u32) -> u32 {
|
||||
reverse32(x)
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () {
|
||||
// Different argument order
|
||||
memset(dest, c as u8, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memset8]
|
||||
unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () {
|
||||
// Different argument order
|
||||
memset4(dest as *mut u32, c as u8, n as u32);
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () {
|
||||
memset(dest, 0, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memclr8]
|
||||
unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () {
|
||||
memset4(dest as *mut u32, 0, n as u32);
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () {
|
||||
memcpy(dest, src, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memcpy8]
|
||||
unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () {
|
||||
memcpy44(dest as *mut u32, src as *const u32, n as u32);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn convert_str(s: *const u8) -> &'static str {
|
||||
let mut end = s;
|
||||
while *end != 0 {
|
||||
end = end.add(1);
|
||||
}
|
||||
let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);
|
||||
core::str::from_utf8_unchecked(s)
|
||||
}
|
||||
|
||||
/// The version number of the rom.
|
||||
pub fn rom_version_number() -> u8 {
|
||||
unsafe { *VERSION_NUMBER }
|
||||
}
|
||||
|
||||
/// The Raspberry Pi Trading Ltd copyright string.
|
||||
pub fn copyright_string() -> &'static str {
|
||||
let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR");
|
||||
unsafe { convert_str(s) }
|
||||
}
|
||||
|
||||
/// The 8 most significant hex digits of the Bootrom git revision.
|
||||
pub fn git_revision() -> u32 {
|
||||
let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR");
|
||||
unsafe { *s }
|
||||
}
|
||||
|
||||
/// The start address of the floating point library code and data.
|
||||
///
|
||||
/// This and fplib_end along with the individual function pointers in
|
||||
/// soft_float_table can be used to copy the floating point implementation into
|
||||
/// RAM if desired.
|
||||
pub fn fplib_start() -> *const u8 {
|
||||
rom_table_lookup(DATA_TABLE, *b"FS")
|
||||
}
|
||||
|
||||
/// See Table 180 in the RP2040 datasheet for the contents of this table.
|
||||
pub fn soft_float_table() -> *const usize {
|
||||
rom_table_lookup(DATA_TABLE, *b"SF")
|
||||
}
|
||||
|
||||
/// The end address of the floating point library code and data.
|
||||
pub fn fplib_end() -> *const u8 {
|
||||
rom_table_lookup(DATA_TABLE, *b"FE")
|
||||
}
|
||||
|
||||
/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
|
||||
pub fn soft_double_table() -> *const usize {
|
||||
if rom_version_number() < 2 {
|
||||
panic!(
|
||||
"Double precision operations require V2 bootrom (found: V{})",
|
||||
rom_version_number()
|
||||
);
|
||||
}
|
||||
rom_table_lookup(DATA_TABLE, *b"SD")
|
||||
}
|
||||
|
||||
/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms)
|
||||
pub mod float_funcs {
|
||||
|
||||
macro_rules! make_functions {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
let table: *const usize = $crate::rom_data::soft_float_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
make_functions! {
|
||||
/// Calculates `a + b`
|
||||
0x00 fadd(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a - b`
|
||||
0x04 fsub(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a * b`
|
||||
0x08 fmul(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a / b`
|
||||
0x0c fdiv(a: f32, b: f32) -> f32;
|
||||
|
||||
// 0x10 and 0x14 are deprecated
|
||||
|
||||
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
|
||||
0x18 fsqrt(v: f32) -> f32;
|
||||
/// Converts an f32 to a signed integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `-0x80000000` to `0x7FFFFFFF`
|
||||
0x1c float_to_int(v: f32) -> i32;
|
||||
/// Converts an f32 to an signed fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x20 float_to_fix(v: f32, n: i32) -> i32;
|
||||
/// Converts an f32 to an unsigned integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `0x00000000` to `0xFFFFFFFF`
|
||||
0x24 float_to_uint(v: f32) -> u32;
|
||||
/// Converts an f32 to an unsigned fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x28 float_to_ufix(v: f32, n: i32) -> u32;
|
||||
/// Converts a signed integer to the nearest
|
||||
/// f32 value, rounding to even on tie
|
||||
0x2c int_to_float(v: i32) -> f32;
|
||||
/// Converts a signed fixed point integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x30 fix_to_float(v: i32, n: i32) -> f32;
|
||||
/// Converts an unsigned integer to the nearest
|
||||
/// f32 value, rounding to even on tie
|
||||
0x34 uint_to_float(v: u32) -> f32;
|
||||
/// Converts an unsigned fixed point integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x38 ufix_to_float(v: u32, n: i32) -> f32;
|
||||
/// Calculates the cosine of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x3c fcos(angle: f32) -> f32;
|
||||
/// Calculates the sine of `angle`. The value of
|
||||
/// `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x40 fsin(angle: f32) -> f32;
|
||||
/// Calculates the tangent of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x44 ftan(angle: f32) -> f32;
|
||||
|
||||
// 0x48 is deprecated
|
||||
|
||||
/// Calculates the exponential value of `v`,
|
||||
/// i.e. `e ** v`
|
||||
0x4c fexp(v: f32) -> f32;
|
||||
/// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity
|
||||
0x50 fln(v: f32) -> f32;
|
||||
}
|
||||
|
||||
macro_rules! make_functions_v2 {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
if $crate::rom_data::rom_version_number() < 2 {
|
||||
panic!(
|
||||
"Floating point function requires V2 bootrom (found: V{})",
|
||||
$crate::rom_data::rom_version_number()
|
||||
);
|
||||
}
|
||||
let table: *const usize = $crate::rom_data::soft_float_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
// These are only on BootROM v2 or higher
|
||||
make_functions_v2! {
|
||||
/// Compares two floating point numbers, returning:
|
||||
/// • 0 if a == b
|
||||
/// • -1 if a < b
|
||||
/// • 1 if a > b
|
||||
0x54 fcmp(a: f32, b: f32) -> i32;
|
||||
/// Computes the arc tangent of `y/x` using the
|
||||
/// signs of arguments to determine the correct quadrant
|
||||
0x58 fatan2(y: f32, x: f32) -> f32;
|
||||
/// Converts a signed 64-bit integer to the
|
||||
/// nearest f32 value, rounding to even on tie
|
||||
0x5c int64_to_float(v: i64) -> f32;
|
||||
/// Converts a signed fixed point 64-bit integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x60 fix64_to_float(v: i64, n: i32) -> f32;
|
||||
/// Converts an unsigned 64-bit integer to the
|
||||
/// nearest f32 value, rounding to even on tie
|
||||
0x64 uint64_to_float(v: u64) -> f32;
|
||||
/// Converts an unsigned fixed point 64-bit
|
||||
/// integer representation to the nearest f32 value, rounding to even on
|
||||
/// tie. `n` specifies the position of the binary point in fixed point, so
|
||||
/// `f = nearest(v/(2^n))`
|
||||
0x68 ufix64_to_float(v: u64, n: i32) -> f32;
|
||||
/// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity,
|
||||
/// and clamping the result to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x6c float_to_int64(v: f32) -> i64;
|
||||
/// Converts an f32 to a signed fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x70 float_to_fix64(v: f32, n: i32) -> f32;
|
||||
/// Converts an f32 to an unsigned 64-bit
|
||||
/// integer, rounding towards -Infinity, and clamping the result to lie
|
||||
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
|
||||
0x74 float_to_uint64(v: f32) -> u64;
|
||||
/// Converts an f32 to an unsigned fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `0x0000000000000000` to
|
||||
/// `0xFFFFFFFFFFFFFFFF`
|
||||
0x78 float_to_ufix64(v: f32, n: i32) -> u64;
|
||||
/// Converts an f32 to an f64.
|
||||
0x7c float_to_double(v: f32) -> f64;
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms)
|
||||
pub mod double_funcs {
|
||||
|
||||
macro_rules! make_double_funcs {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
let table: *const usize = $crate::rom_data::soft_double_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
make_double_funcs! {
|
||||
/// Calculates `a + b`
|
||||
0x00 dadd(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a - b`
|
||||
0x04 dsub(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a * b`
|
||||
0x08 dmul(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a / b`
|
||||
0x0c ddiv(a: f64, b: f64) -> f64;
|
||||
|
||||
// 0x10 and 0x14 are deprecated
|
||||
|
||||
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
|
||||
0x18 dsqrt(v: f64) -> f64;
|
||||
/// Converts an f64 to a signed integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `-0x80000000` to `0x7FFFFFFF`
|
||||
0x1c double_to_int(v: f64) -> i32;
|
||||
/// Converts an f64 to an signed fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x20 double_to_fix(v: f64, n: i32) -> i32;
|
||||
/// Converts an f64 to an unsigned integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `0x00000000` to `0xFFFFFFFF`
|
||||
0x24 double_to_uint(v: f64) -> u32;
|
||||
/// Converts an f64 to an unsigned fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x28 double_to_ufix(v: f64, n: i32) -> u32;
|
||||
/// Converts a signed integer to the nearest
|
||||
/// double value, rounding to even on tie
|
||||
0x2c int_to_double(v: i32) -> f64;
|
||||
/// Converts a signed fixed point integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x30 fix_to_double(v: i32, n: i32) -> f64;
|
||||
/// Converts an unsigned integer to the nearest
|
||||
/// double value, rounding to even on tie
|
||||
0x34 uint_to_double(v: u32) -> f64;
|
||||
/// Converts an unsigned fixed point integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so f =
|
||||
/// nearest(v/(2^n))
|
||||
0x38 ufix_to_double(v: u32, n: i32) -> f64;
|
||||
/// Calculates the cosine of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x3c dcos(angle: f64) -> f64;
|
||||
/// Calculates the sine of `angle`. The value of
|
||||
/// `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x40 dsin(angle: f64) -> f64;
|
||||
/// Calculates the tangent of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x44 dtan(angle: f64) -> f64;
|
||||
|
||||
// 0x48 is deprecated
|
||||
|
||||
/// Calculates the exponential value of `v`,
|
||||
/// i.e. `e ** v`
|
||||
0x4c dexp(v: f64) -> f64;
|
||||
/// Calculates the natural logarithm of v. If v <= 0 return -Infinity
|
||||
0x50 dln(v: f64) -> f64;
|
||||
|
||||
// These are only on BootROM v2 or higher
|
||||
|
||||
/// Compares two floating point numbers, returning:
|
||||
/// • 0 if a == b
|
||||
/// • -1 if a < b
|
||||
/// • 1 if a > b
|
||||
0x54 dcmp(a: f64, b: f64) -> i32;
|
||||
/// Computes the arc tangent of `y/x` using the
|
||||
/// signs of arguments to determine the correct quadrant
|
||||
0x58 datan2(y: f64, x: f64) -> f64;
|
||||
/// Converts a signed 64-bit integer to the
|
||||
/// nearest double value, rounding to even on tie
|
||||
0x5c int64_to_double(v: i64) -> f64;
|
||||
/// Converts a signed fixed point 64-bit integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x60 fix64_to_doubl(v: i64, n: i32) -> f64;
|
||||
/// Converts an unsigned 64-bit integer to the
|
||||
/// nearest double value, rounding to even on tie
|
||||
0x64 uint64_to_double(v: u64) -> f64;
|
||||
/// Converts an unsigned fixed point 64-bit
|
||||
/// integer representation to the nearest double value, rounding to even on
|
||||
/// tie. `n` specifies the position of the binary point in fixed point, so
|
||||
/// `f = nearest(v/(2^n))`
|
||||
0x68 ufix64_to_double(v: u64, n: i32) -> f64;
|
||||
/// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity,
|
||||
/// and clamping the result to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x6c double_to_int64(v: f64) -> i64;
|
||||
/// Converts an f64 to a signed fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x70 double_to_fix64(v: f64, n: i32) -> i64;
|
||||
/// Converts an f64 to an unsigned 64-bit
|
||||
/// integer, rounding towards -Infinity, and clamping the result to lie
|
||||
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
|
||||
0x74 double_to_uint64(v: f64) -> u64;
|
||||
/// Converts an f64 to an unsigned fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `0x0000000000000000` to
|
||||
/// `0xFFFFFFFFFFFFFFFF`
|
||||
0x78 double_to_ufix64(v: f64, n: i32) -> u64;
|
||||
/// Converts an f64 to an f32
|
||||
0x7c double_to_float(v: f64) -> f32;
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
//! Ring Oscillator (ROSC)
|
||||
// See [Chapter 2 Section 17](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
|
||||
use fugit::HertzU32;
|
||||
|
||||
/// State of the Ring Oscillator (typestate trait)
|
||||
pub trait State {}
|
||||
|
||||
/// ROSC is disabled (typestate)
|
||||
pub struct Disabled;
|
||||
|
||||
/// ROSC is initialized, ie we've given parameters (typestate)
|
||||
pub struct Enabled {
|
||||
freq_hz: HertzU32,
|
||||
}
|
||||
|
||||
/// ROSC is in dormant mode (see Chapter 2, Section 17, §7)
|
||||
pub struct Dormant;
|
||||
|
||||
impl State for Disabled {}
|
||||
impl State for Enabled {}
|
||||
impl State for Dormant {}
|
||||
|
||||
/// A Ring Oscillator.
|
||||
pub struct RingOscillator<S: State> {
|
||||
device: rp2040_pac::ROSC,
|
||||
state: S,
|
||||
}
|
||||
|
||||
impl<S: State> RingOscillator<S> {
|
||||
/// Transitions the oscillator to another state.
|
||||
fn transition<To: State>(self, state: To) -> RingOscillator<To> {
|
||||
RingOscillator {
|
||||
device: self.device,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases the underlying device.
|
||||
pub fn free(self) -> rp2040_pac::ROSC {
|
||||
self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl RingOscillator<Disabled> {
|
||||
/// Creates a new RingOscillator from the underlying device.
|
||||
pub fn new(dev: rp2040_pac::ROSC) -> Self {
|
||||
RingOscillator {
|
||||
device: dev,
|
||||
state: Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the ROSC : frequency range is set, startup delay is calculated and set.
|
||||
pub fn initialize(self) -> RingOscillator<Enabled> {
|
||||
self.device.ctrl.write(|w| w.enable().enable());
|
||||
|
||||
use fugit::RateExtU32;
|
||||
self.transition(Enabled {
|
||||
freq_hz: 6_500_000u32.Hz(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RingOscillator<Enabled> {
|
||||
/// Approx operating frequency of the ROSC in hertz
|
||||
pub fn operating_frequency(&self) -> HertzU32 {
|
||||
self.state.freq_hz
|
||||
}
|
||||
|
||||
/// Disables the ROSC
|
||||
pub fn disable(self) -> RingOscillator<Disabled> {
|
||||
self.device.ctrl.modify(|_r, w| w.enable().disable());
|
||||
|
||||
self.transition(Disabled)
|
||||
}
|
||||
|
||||
/// Generate random bit based on the Ring oscillator
|
||||
/// This is not suited for security purposes
|
||||
pub fn get_random_bit(&self) -> bool {
|
||||
self.device.randombit.read().randombit().bit()
|
||||
}
|
||||
|
||||
/// Put the ROSC in DORMANT state.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method is marked unsafe because prior to switch the ROSC into DORMANT state,
|
||||
/// PLLs must be stopped and IRQs have to be properly configured.
|
||||
/// This method does not do any of that, it merely switches the ROSC to DORMANT state.
|
||||
/// See Chapter 2, Section 16, §5) for details.
|
||||
pub unsafe fn dormant(self) -> RingOscillator<Dormant> {
|
||||
//taken from the C SDK
|
||||
const ROSC_DORMANT_VALUE: u32 = 0x636f6d61;
|
||||
|
||||
self.device.dormant.write(|w| w.bits(ROSC_DORMANT_VALUE));
|
||||
|
||||
self.transition(Dormant)
|
||||
}
|
||||
}
|
||||
|
||||
impl rand_core::RngCore for RingOscillator<Enabled> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
rand_core::impls::next_u32_via_fill(self)
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
rand_core::impls::next_u64_via_fill(self)
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
for chunk in dest.iter_mut() {
|
||||
*chunk = 0_u8;
|
||||
for _ in 0..8 {
|
||||
*chunk <<= 1;
|
||||
*chunk ^= self.get_random_bit() as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
self.fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
use chrono::{Datelike, Timelike};
|
||||
use rp2040_pac::rtc::{rtc_0, rtc_1, setup_0, setup_1};
|
||||
|
||||
/// Alias for [`chrono::NaiveDateTime`]
|
||||
pub type DateTime = chrono::NaiveDateTime;
|
||||
/// Alias for [`chrono::Weekday`]
|
||||
pub type DayOfWeek = chrono::Weekday;
|
||||
|
||||
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||
///
|
||||
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [DateTime] has an invalid year. The year must be between 0 and 4095.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid date.
|
||||
InvalidDate,
|
||||
/// The [DateTime] contains an invalid time.
|
||||
InvalidTime,
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw.num_days_from_sunday() as u8
|
||||
}
|
||||
|
||||
pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year() < 0 || dt.year() > 4095 {
|
||||
// rp2040 can't hold these years
|
||||
Err(Error::InvalidYear)
|
||||
} else {
|
||||
// The rest of the chrono date is assumed to be valid
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_0(dt: &DateTime, w: &mut setup_0::W) {
|
||||
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||
unsafe {
|
||||
w.year().bits(dt.year() as u16);
|
||||
w.month().bits(dt.month() as u8);
|
||||
w.day().bits(dt.day() as u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_1(dt: &DateTime, w: &mut setup_1::W) {
|
||||
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||
unsafe {
|
||||
w.dotw().bits(dt.weekday().num_days_from_sunday() as u8);
|
||||
w.hour().bits(dt.hour() as u8);
|
||||
w.min().bits(dt.minute() as u8);
|
||||
w.sec().bits(dt.second() as u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn datetime_from_registers(rtc_0: rtc_0::R, rtc_1: rtc_1::R) -> Result<DateTime, Error> {
|
||||
let year = rtc_1.year().bits() as i32;
|
||||
let month = rtc_1.month().bits() as u32;
|
||||
let day = rtc_1.day().bits() as u32;
|
||||
|
||||
let hour = rtc_0.hour().bits() as u32;
|
||||
let minute = rtc_0.min().bits() as u32;
|
||||
let second = rtc_0.sec().bits() as u32;
|
||||
|
||||
let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?;
|
||||
let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?;
|
||||
Ok(DateTime::new(date, time))
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
use rp2040_pac::rtc::{rtc_0, rtc_1, setup_0, setup_1};
|
||||
|
||||
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||
///
|
||||
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid month value. Must be between `1..=12`.
|
||||
InvalidMonth,
|
||||
/// The [DateTime] contains an invalid day value. Must be between `1..=31`.
|
||||
InvalidDay,
|
||||
/// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
|
||||
InvalidDayOfWeek(
|
||||
/// The value of the DayOfWeek that was given.
|
||||
u8,
|
||||
),
|
||||
/// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
|
||||
InvalidHour,
|
||||
/// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
|
||||
InvalidMinute,
|
||||
/// The [DateTime] contains an invalid second value. Must be between `0..=59`.
|
||||
InvalidSecond,
|
||||
}
|
||||
|
||||
/// Structure containing date and time information
|
||||
pub struct DateTime {
|
||||
/// 0..4095
|
||||
pub year: u16,
|
||||
/// 1..12, 1 is January
|
||||
pub month: u8,
|
||||
/// 1..28,29,30,31 depending on month
|
||||
pub day: u8,
|
||||
///
|
||||
pub day_of_week: DayOfWeek,
|
||||
/// 0..23
|
||||
pub hour: u8,
|
||||
/// 0..59
|
||||
pub minute: u8,
|
||||
/// 0..59
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
/// A day of the week
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DayOfWeek {
|
||||
Sunday = 0,
|
||||
Monday = 1,
|
||||
Tuesday = 2,
|
||||
Wednesday = 3,
|
||||
Thursday = 4,
|
||||
Friday = 5,
|
||||
Saturday = 6,
|
||||
}
|
||||
|
||||
fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
|
||||
Ok(match v {
|
||||
0 => DayOfWeek::Sunday,
|
||||
1 => DayOfWeek::Monday,
|
||||
2 => DayOfWeek::Tuesday,
|
||||
3 => DayOfWeek::Wednesday,
|
||||
4 => DayOfWeek::Thursday,
|
||||
5 => DayOfWeek::Friday,
|
||||
6 => DayOfWeek::Saturday,
|
||||
x => return Err(Error::InvalidDayOfWeek(x)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw as u8
|
||||
}
|
||||
|
||||
pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year > 4095 {
|
||||
Err(Error::InvalidYear)
|
||||
} else if dt.month < 1 || dt.month > 12 {
|
||||
Err(Error::InvalidMonth)
|
||||
} else if dt.day < 1 || dt.day > 31 {
|
||||
Err(Error::InvalidDay)
|
||||
} else if dt.hour > 23 {
|
||||
Err(Error::InvalidHour)
|
||||
} else if dt.minute > 59 {
|
||||
Err(Error::InvalidMinute)
|
||||
} else if dt.second > 59 {
|
||||
Err(Error::InvalidSecond)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_0(dt: &DateTime, w: &mut setup_0::W) {
|
||||
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||
unsafe {
|
||||
w.year().bits(dt.year);
|
||||
w.month().bits(dt.month);
|
||||
w.day().bits(dt.day);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_1(dt: &DateTime, w: &mut setup_1::W) {
|
||||
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||
unsafe {
|
||||
w.dotw().bits(dt.day_of_week as u8);
|
||||
w.hour().bits(dt.hour);
|
||||
w.min().bits(dt.minute);
|
||||
w.sec().bits(dt.second);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn datetime_from_registers(rtc_0: rtc_0::R, rtc_1: rtc_1::R) -> Result<DateTime, Error> {
|
||||
let year = rtc_1.year().bits();
|
||||
let month = rtc_1.month().bits();
|
||||
let day = rtc_1.day().bits();
|
||||
|
||||
let day_of_week = rtc_0.dotw().bits();
|
||||
let hour = rtc_0.hour().bits();
|
||||
let minute = rtc_0.min().bits();
|
||||
let second = rtc_0.sec().bits();
|
||||
|
||||
let day_of_week = day_of_week_from_u8(day_of_week)?;
|
||||
Ok(DateTime {
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
day_of_week,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
})
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
use super::DayOfWeek;
|
||||
use rp2040_pac::rtc::{irq_setup_0, irq_setup_1};
|
||||
|
||||
/// A filter used for [`RealTimeClock::schedule_alarm`].
|
||||
///
|
||||
/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm
|
||||
#[derive(Default)]
|
||||
pub struct DateTimeFilter {
|
||||
/// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value.
|
||||
pub year: Option<u16>,
|
||||
/// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value.
|
||||
pub month: Option<u8>,
|
||||
/// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value.
|
||||
pub day: Option<u8>,
|
||||
/// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value.
|
||||
pub day_of_week: Option<DayOfWeek>,
|
||||
/// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value.
|
||||
pub hour: Option<u8>,
|
||||
/// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value.
|
||||
pub minute: Option<u8>,
|
||||
/// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value.
|
||||
pub second: Option<u8>,
|
||||
}
|
||||
|
||||
impl DateTimeFilter {
|
||||
/// Set a filter on the given year
|
||||
pub fn year(mut self, year: u16) -> Self {
|
||||
self.year = Some(year);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given month
|
||||
pub fn month(mut self, month: u8) -> Self {
|
||||
self.month = Some(month);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given day
|
||||
pub fn day(mut self, day: u8) -> Self {
|
||||
self.day = Some(day);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given day of the week
|
||||
pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self {
|
||||
self.day_of_week = Some(day_of_week);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given hour
|
||||
pub fn hour(mut self, hour: u8) -> Self {
|
||||
self.hour = Some(hour);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given minute
|
||||
pub fn minute(mut self, minute: u8) -> Self {
|
||||
self.minute = Some(minute);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given second
|
||||
pub fn second(mut self, second: u8) -> Self {
|
||||
self.second = Some(second);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// register helper functions
|
||||
impl DateTimeFilter {
|
||||
pub(super) fn write_setup_0(&self, w: &mut irq_setup_0::W) {
|
||||
// Safety: setting .bits() is considered unsafe because
|
||||
// svd2rust doesn't know what the valid values are.
|
||||
// But all values in these bitmasks are safe
|
||||
if let Some(year) = self.year {
|
||||
w.year_ena().set_bit();
|
||||
|
||||
unsafe {
|
||||
w.year().bits(year);
|
||||
}
|
||||
}
|
||||
if let Some(month) = self.month {
|
||||
w.month_ena().set_bit();
|
||||
unsafe {
|
||||
w.month().bits(month);
|
||||
}
|
||||
}
|
||||
if let Some(day) = self.day {
|
||||
w.day_ena().set_bit();
|
||||
unsafe {
|
||||
w.day().bits(day);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(super) fn write_setup_1(&self, w: &mut irq_setup_1::W) {
|
||||
// Safety: setting .bits() is considered unsafe because
|
||||
// svd2rust doesn't know what the valid values are.
|
||||
// But all values in these bitmasks are safe
|
||||
if let Some(day_of_week) = self.day_of_week {
|
||||
w.dotw_ena().set_bit();
|
||||
let bits = super::datetime::day_of_week_to_u8(day_of_week);
|
||||
|
||||
unsafe {
|
||||
w.dotw().bits(bits);
|
||||
}
|
||||
}
|
||||
if let Some(hour) = self.hour {
|
||||
w.hour_ena().set_bit();
|
||||
unsafe {
|
||||
w.hour().bits(hour);
|
||||
}
|
||||
}
|
||||
if let Some(minute) = self.minute {
|
||||
w.min_ena().set_bit();
|
||||
unsafe {
|
||||
w.min().bits(minute);
|
||||
}
|
||||
}
|
||||
if let Some(second) = self.second {
|
||||
w.sec_ena().set_bit();
|
||||
unsafe {
|
||||
w.sec().bits(second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
//! Real time clock functionality
|
||||
//!
|
||||
//! A [`RealTimeClock`] can be configured with an initial [`DateTime`]. Afterwards the clock will track time automatically. The current `DateTime` can be retrieved by [`RealTimeClock::now()`].
|
||||
//!
|
||||
//! With the **chrono** feature enabled, the following types will be alias for chrono types:
|
||||
//! - `DateTime`: `chrono::NaiveDateTime`
|
||||
//! - `DayOfWeek`: `chrono::Weekday`
|
||||
//!
|
||||
//! # Notes
|
||||
//!
|
||||
//! There are some things to take into account. As per the datasheet:
|
||||
//!
|
||||
//! - **Day of week**: The RTC will not compute the correct day of the week; it will only increment the existing value.
|
||||
//! - With the `chrono` feature, the day of week is calculated by chrono and should be correct. The value from the rp2040 itself is not used.
|
||||
//! - **Leap year**: If the current year is evenly divisible by 4, a leap year is detected, then Feb 28th is followed by Feb 29th instead of March 1st.
|
||||
//! - There are cases where this is incorrect, e.g. century years have no leap day, but the chip will still add a Feb 29th.
|
||||
//! - To disable leap year checking and never have a Feb 29th, call `RealTimeClock::set_leap_year_check(false)`.
|
||||
//!
|
||||
//! Other limitations:
|
||||
//!
|
||||
//! - **Leap seconds**: The rp2040 will not take leap seconds into account
|
||||
//! - With the `chrono` feature, leap seconds will be silently handled by `chrono`. This means there might be a slight difference between the value of [`RealTimeClock::now()`] and adding 2 times together in code.
|
||||
|
||||
use crate::clocks::Clock;
|
||||
use crate::clocks::RtcClock;
|
||||
use rp2040_pac::{RESETS, RTC};
|
||||
|
||||
mod filter;
|
||||
|
||||
pub use self::filter::DateTimeFilter;
|
||||
|
||||
#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
|
||||
#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
|
||||
mod datetime;
|
||||
|
||||
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
||||
|
||||
/// A reference to the real time clock of the system
|
||||
pub struct RealTimeClock {
|
||||
rtc: RTC,
|
||||
}
|
||||
|
||||
impl RealTimeClock {
|
||||
/// Create a new instance of the real time clock, with the given date as an initial value.
|
||||
///
|
||||
/// Note that the [`ClocksManager`] should be enabled first. See the [`clocks`] module for more information.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
///
|
||||
/// [`ClocksManager`]: ../clocks/struct.ClocksManager.html
|
||||
/// [`clocks`]: ../clocks/index.html
|
||||
pub fn new(
|
||||
rtc: RTC,
|
||||
clock: RtcClock,
|
||||
resets: &mut RESETS,
|
||||
initial_date: DateTime,
|
||||
) -> Result<Self, RtcError> {
|
||||
// Toggle the RTC reset
|
||||
resets.reset.modify(|_, w| w.rtc().set_bit());
|
||||
resets.reset.modify(|_, w| w.rtc().clear_bit());
|
||||
while resets.reset_done.read().rtc().bit_is_clear() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
// Set the RTC divider
|
||||
let freq = clock.freq().to_Hz() - 1;
|
||||
rtc.clkdiv_m1.write(|w| unsafe { w.bits(freq) });
|
||||
|
||||
let mut result = Self { rtc };
|
||||
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
|
||||
result.set_datetime(initial_date)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
|
||||
///
|
||||
/// Leap year checking is enabled by default.
|
||||
pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {
|
||||
self.rtc
|
||||
.ctrl
|
||||
.modify(|_, w| w.force_notleapyear().bit(!leap_year_check_enabled));
|
||||
}
|
||||
|
||||
/// Checks to see if this RealTimeClock is running
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.rtc.ctrl.read().rtc_active().bit_is_set()
|
||||
}
|
||||
|
||||
/// Set the datetime to a new value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
|
||||
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
|
||||
|
||||
// disable RTC while we configure it
|
||||
self.rtc.ctrl.modify(|_, w| w.rtc_enable().clear_bit());
|
||||
while self.rtc.ctrl.read().rtc_active().bit_is_set() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.rtc.setup_0.write(|w| {
|
||||
self::datetime::write_setup_0(&t, w);
|
||||
w
|
||||
});
|
||||
self.rtc.setup_1.write(|w| {
|
||||
self::datetime::write_setup_1(&t, w);
|
||||
w
|
||||
});
|
||||
|
||||
// Load the new datetime and re-enable RTC
|
||||
self.rtc.ctrl.write(|w| w.load().set_bit());
|
||||
self.rtc.ctrl.write(|w| w.rtc_enable().set_bit());
|
||||
while self.rtc.ctrl.read().rtc_active().bit_is_clear() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the current datetime.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
|
||||
pub fn now(&self) -> Result<DateTime, RtcError> {
|
||||
if !self.is_running() {
|
||||
return Err(RtcError::NotRunning);
|
||||
}
|
||||
|
||||
let rtc_0 = self.rtc.rtc_0.read();
|
||||
let rtc_1 = self.rtc.rtc_1.read();
|
||||
|
||||
self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime)
|
||||
}
|
||||
|
||||
/// Disable the alarm that was scheduled with [`schedule_alarm`].
|
||||
///
|
||||
/// [`schedule_alarm`]: #method.schedule_alarm
|
||||
pub fn disable_alarm(&mut self) {
|
||||
self.rtc
|
||||
.irq_setup_0
|
||||
.modify(|_, s| s.match_ena().clear_bit());
|
||||
|
||||
while self.rtc.irq_setup_0.read().match_active().bit() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule an alarm. The `filter` determines at which point in time this alarm is set.
|
||||
///
|
||||
/// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call:
|
||||
/// ```no_run
|
||||
/// # #[cfg(feature = "chrono")]
|
||||
/// # fn main() { }
|
||||
/// # #[cfg(not(feature = "chrono"))]
|
||||
/// # fn main() {
|
||||
/// # use rp2040_hal::rtc::{RealTimeClock, DateTimeFilter};
|
||||
/// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() };
|
||||
/// let now = real_time_clock.now().unwrap();
|
||||
/// real_time_clock.schedule_alarm(
|
||||
/// DateTimeFilter::default()
|
||||
/// .minute(if now.minute == 59 { 0 } else { now.minute + 1 })
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {
|
||||
self.disable_alarm();
|
||||
|
||||
self.rtc.irq_setup_0.write(|w| {
|
||||
filter.write_setup_0(w);
|
||||
w
|
||||
});
|
||||
self.rtc.irq_setup_1.write(|w| {
|
||||
filter.write_setup_1(w);
|
||||
w
|
||||
});
|
||||
|
||||
// Set the enable bit and check if it is set
|
||||
self.rtc.irq_setup_0.modify(|_, w| w.match_ena().set_bit());
|
||||
while self.rtc.irq_setup_0.read().match_active().bit_is_clear() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered,
|
||||
/// or the next [`schedule_alarm`] will never fire.
|
||||
///
|
||||
/// [`schedule_alarm`]: #method.schedule_alarm
|
||||
pub fn clear_interrupt(&mut self) {
|
||||
self.disable_alarm();
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur on methods on [RtcClock]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RtcError {
|
||||
/// An invalid DateTime was given or stored on the hardware.
|
||||
InvalidDateTime(DateTimeError),
|
||||
|
||||
/// The RTC clock is not running
|
||||
NotRunning,
|
||||
}
|
|
@ -1,919 +0,0 @@
|
|||
//! Single Cycle Input and Output (SIO)
|
||||
//!
|
||||
//! To be able to partition parts of the SIO block to other modules:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use rp2040_hal::{gpio::Pins, pac, sio::Sio};
|
||||
//!
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let sio = Sio::new(peripherals.SIO);
|
||||
//! ```
|
||||
//!
|
||||
//! And then for example
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use rp2040_hal::{gpio::Pins, pac, sio::Sio};
|
||||
//! # let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! # let sio = Sio::new(peripherals.SIO);
|
||||
//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//! ```
|
||||
|
||||
use super::*;
|
||||
use core::convert::Infallible;
|
||||
|
||||
/// Marker struct for ownership of SIO gpio bank0
|
||||
pub struct SioGpioBank0 {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
/// Marker struct for ownership of SIO FIFO
|
||||
pub struct SioFifo {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
/// Marker struct for ownership of SIO gpio qspi
|
||||
pub struct SioGpioQspi {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
/// Marker struct for ownership of divide/modulo module
|
||||
pub struct HwDivider {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
/// Result of divide/modulo operation
|
||||
pub struct DivResult<T> {
|
||||
/// The quotient of divide/modulo operation
|
||||
pub quotient: T,
|
||||
/// The remainder of divide/modulo operation
|
||||
pub remainder: T,
|
||||
}
|
||||
|
||||
/// Struct containing ownership markers for managing ownership of the SIO registers.
|
||||
pub struct Sio {
|
||||
_sio: pac::SIO,
|
||||
/// GPIO Bank 0 registers
|
||||
pub gpio_bank0: SioGpioBank0,
|
||||
/// GPIO QSPI registers
|
||||
pub gpio_qspi: SioGpioQspi,
|
||||
/// 8-cycle hardware divide/modulo module
|
||||
pub hwdivider: HwDivider,
|
||||
/// Inter-core FIFO
|
||||
pub fifo: SioFifo,
|
||||
/// Interpolator 0
|
||||
pub interp0: Interp0,
|
||||
/// Interpolator 1
|
||||
pub interp1: Interp1,
|
||||
}
|
||||
|
||||
impl Sio {
|
||||
/// Create `Sio` from the PAC.
|
||||
pub fn new(sio: pac::SIO) -> Self {
|
||||
Self {
|
||||
_sio: sio,
|
||||
gpio_bank0: SioGpioBank0 { _private: () },
|
||||
gpio_qspi: SioGpioQspi { _private: () },
|
||||
fifo: SioFifo { _private: () },
|
||||
hwdivider: HwDivider { _private: () },
|
||||
interp0: Interp0 {
|
||||
lane0: Interp0Lane0 { _private: () },
|
||||
lane1: Interp0Lane1 { _private: () },
|
||||
},
|
||||
interp1: Interp1 {
|
||||
lane0: Interp1Lane0 { _private: () },
|
||||
lane1: Interp1Lane1 { _private: () },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether we are running on Core 0 (`0`) or Core 1 (`1`).
|
||||
pub fn core() -> u8 {
|
||||
// Safety: it is always safe to read this read-only register
|
||||
unsafe { (*pac::SIO::ptr()).cpuid.read().bits() as u8 }
|
||||
}
|
||||
}
|
||||
|
||||
impl SioFifo {
|
||||
/// Check if the inter-core FIFO has valid data for reading.
|
||||
///
|
||||
/// Returning `true` means there is valid data, `false` means it is empty
|
||||
/// and you must not read from it.
|
||||
pub fn is_read_ready(&mut self) -> bool {
|
||||
let sio = unsafe { &(*pac::SIO::ptr()) };
|
||||
sio.fifo_st.read().vld().bit_is_set()
|
||||
}
|
||||
|
||||
/// Check if the inter-core FIFO is ready to receive data.
|
||||
///
|
||||
/// Returning `true` means there is room, `false` means it is full and you
|
||||
/// must not write to it.
|
||||
pub fn is_write_ready(&mut self) -> bool {
|
||||
let sio = unsafe { &(*pac::SIO::ptr()) };
|
||||
sio.fifo_st.read().rdy().bit_is_set()
|
||||
}
|
||||
|
||||
/// Return the FIFO status, as an integer.
|
||||
pub fn status(&self) -> u32 {
|
||||
let sio = unsafe { &(*pac::SIO::ptr()) };
|
||||
sio.fifo_st.read().bits()
|
||||
}
|
||||
|
||||
/// Write to the inter-core FIFO.
|
||||
///
|
||||
/// You must ensure the FIFO has space by calling `is_write_ready`
|
||||
pub fn write(&mut self, value: u32) {
|
||||
let sio = unsafe { &(*pac::SIO::ptr()) };
|
||||
sio.fifo_wr.write(|w| unsafe { w.bits(value) });
|
||||
// Fire off an event to the other core.
|
||||
// This is required as the other core may be `wfe` (waiting for event)
|
||||
cortex_m::asm::sev();
|
||||
}
|
||||
|
||||
/// Read from the inter-core FIFO.
|
||||
///
|
||||
/// Will return `Some(data)`, or `None` if the FIFO is empty.
|
||||
pub fn read(&mut self) -> Option<u32> {
|
||||
if self.is_read_ready() {
|
||||
let sio = unsafe { &(*pac::SIO::ptr()) };
|
||||
Some(sio.fifo_rd.read().bits())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Read from the FIFO until it is empty, throwing the contents away.
|
||||
pub fn drain(&mut self) {
|
||||
while self.read().is_some() {
|
||||
// Retry until FIFO empty
|
||||
}
|
||||
}
|
||||
|
||||
/// Push to the FIFO, spinning if there's no space.
|
||||
pub fn write_blocking(&mut self, value: u32) {
|
||||
// We busy-wait for the FIFO to have some space
|
||||
while !self.is_write_ready() {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
|
||||
// Write the value to the FIFO - the other core will now be able to
|
||||
// pop it off its end of the FIFO.
|
||||
self.write(value);
|
||||
|
||||
// Fire off an event to the other core
|
||||
cortex_m::asm::sev();
|
||||
}
|
||||
|
||||
/// Pop from the FIFO, spinning if there's currently no data.
|
||||
pub fn read_blocking(&mut self) -> u32 {
|
||||
// Keep trying until FIFO has data
|
||||
loop {
|
||||
// Have we got something?
|
||||
if let Some(data) = self.read() {
|
||||
// Yes, return it right away
|
||||
return data;
|
||||
} else {
|
||||
// No, so sleep the CPU. We expect the sending core to `sev`
|
||||
// on write.
|
||||
cortex_m::asm::wfe();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This takes advantage of how AAPCS defines a 64-bit return on 32-bit registers
|
||||
// by packing it into r0[0:31] and r1[32:63]. So all we need to do is put
|
||||
// the remainder in the high order 32 bits of a 64 bit result. We can also
|
||||
// alias the division operators to these for a similar reason r0 is the
|
||||
// result either way and r1 a scratch register, so the caller can't assume it
|
||||
// retains the argument value.
|
||||
#[cfg(target_arch = "arm")]
|
||||
core::arch::global_asm!(
|
||||
".macro hwdivider_head",
|
||||
"ldr r2, =(0xd0000000)", // SIO_BASE
|
||||
// Check the DIRTY state of the divider by shifting it into the C
|
||||
// status bit.
|
||||
"ldr r3, [r2, #0x078]", // DIV_CSR
|
||||
"lsrs r3, #2", // DIRTY = 1, so shift 2 down
|
||||
// We only need to save the state when DIRTY, otherwise we can just do the
|
||||
// division directly.
|
||||
"bcs 2f",
|
||||
"1:",
|
||||
// Do the actual division now, we're either not DIRTY, or we've saved the
|
||||
// state and branched back here so it's safe now.
|
||||
".endm",
|
||||
".macro hwdivider_tail",
|
||||
// 8 cycle delay to wait for the result. Each branch takes two cycles
|
||||
// and fits into a 2-byte Thumb instruction, so this is smaller than
|
||||
// 8 NOPs.
|
||||
"b 3f",
|
||||
"3: b 3f",
|
||||
"3: b 3f",
|
||||
"3: b 3f",
|
||||
"3:",
|
||||
// Read the quotient last, since that's what clears the dirty flag.
|
||||
"ldr r1, [r2, #0x074]", // DIV_REMAINDER
|
||||
"ldr r0, [r2, #0x070]", // DIV_QUOTIENT
|
||||
// Either return to the caller or back to the state restore.
|
||||
"bx lr",
|
||||
"2:",
|
||||
// Since we can't save the signed-ness of the calculation, we have to make
|
||||
// sure that there's at least an 8 cycle delay before we read the result.
|
||||
// The push takes 5 cycles, and we've already spent at least 7 checking
|
||||
// the DIRTY state to get here.
|
||||
"push {{r4-r6, lr}}",
|
||||
// Read the quotient last, since that's what clears the dirty flag.
|
||||
"ldr r3, [r2, #0x060]", // DIV_UDIVIDEND
|
||||
"ldr r4, [r2, #0x064]", // DIV_UDIVISOR
|
||||
"ldr r5, [r2, #0x074]", // DIV_REMAINDER
|
||||
"ldr r6, [r2, #0x070]", // DIV_QUOTIENT
|
||||
// If we get interrupted here (before a write sets the DIRTY flag) it's
|
||||
// fine, since we have the full state, so the interruptor doesn't have to
|
||||
// restore it. Once the write happens and the DIRTY flag is set, the
|
||||
// interruptor becomes responsible for restoring our state.
|
||||
"bl 1b",
|
||||
// If we are interrupted here, then the interruptor will start an incorrect
|
||||
// calculation using a wrong divisor, but we'll restore the divisor and
|
||||
// result ourselves correctly. This sets DIRTY, so any interruptor will
|
||||
// save the state.
|
||||
"str r3, [r2, #0x060]", // DIV_UDIVIDEND
|
||||
// If we are interrupted here, the the interruptor may start the
|
||||
// calculation using incorrectly signed inputs, but we'll restore the
|
||||
// result ourselves. This sets DIRTY, so any interruptor will save the
|
||||
// state.
|
||||
"str r4, [r2, #0x064]", // DIV_UDIVISOR
|
||||
// If we are interrupted here, the interruptor will have restored
|
||||
// everything but the quotient may be wrongly signed. If the calculation
|
||||
// started by the above writes is still ongoing it is stopped, so it won't
|
||||
// replace the result we're restoring. DIRTY and READY set, but only
|
||||
// DIRTY matters to make the interruptor save the state.
|
||||
"str r5, [r2, #0x074]", // DIV_REMAINDER
|
||||
// State fully restored after the quotient write. This sets both DIRTY
|
||||
// and READY, so whatever we may have interrupted can read the result.
|
||||
"str r6, [r2, #0x070]", // DIV_QUOTIENT
|
||||
"pop {{r4-r6, pc}}",
|
||||
".endm",
|
||||
);
|
||||
|
||||
macro_rules! division_function {
|
||||
(
|
||||
$name:ident $($intrinsic:ident)* ( $argty:ty ) {
|
||||
$($begin:literal),+
|
||||
}
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
core::arch::global_asm!(
|
||||
// Mangle the name slightly, since this is a global symbol.
|
||||
concat!(".global _rphal_", stringify!($name)),
|
||||
concat!(".type _rphal_", stringify!($name), ", %function"),
|
||||
".align 2",
|
||||
concat!("_rphal_", stringify!($name), ":"),
|
||||
$(
|
||||
concat!(".global ", stringify!($intrinsic)),
|
||||
concat!(".type ", stringify!($intrinsic), ", %function"),
|
||||
concat!(stringify!($intrinsic), ":"),
|
||||
)*
|
||||
|
||||
"hwdivider_head",
|
||||
$($begin),+ ,
|
||||
"hwdivider_tail",
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "arm", feature = "disable-intrinsics"))]
|
||||
core::arch::global_asm!(
|
||||
// Mangle the name slightly, since this is a global symbol.
|
||||
concat!(".global _rphal_", stringify!($name)),
|
||||
concat!(".type _rphal_", stringify!($name), ", %function"),
|
||||
".align 2",
|
||||
concat!("_rphal_", stringify!($name), ":"),
|
||||
|
||||
"hwdivider_head",
|
||||
$($begin),+ ,
|
||||
"hwdivider_tail",
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
extern "aapcs" {
|
||||
// Connect a local name to global symbol above through FFI.
|
||||
#[link_name = concat!("_rphal_", stringify!($name)) ]
|
||||
fn $name(n: $argty, d: $argty) -> u64;
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "arm"))]
|
||||
#[allow(unused_variables)]
|
||||
unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 }
|
||||
};
|
||||
}
|
||||
|
||||
division_function! {
|
||||
unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) {
|
||||
"str r0, [r2, #0x060]", // DIV_UDIVIDEND
|
||||
"str r1, [r2, #0x064]" // DIV_UDIVISOR
|
||||
}
|
||||
}
|
||||
|
||||
division_function! {
|
||||
signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) {
|
||||
"str r0, [r2, #0x068]", // DIV_SDIVIDEND
|
||||
"str r1, [r2, #0x06c]" // DIV_SDIVISOR
|
||||
}
|
||||
}
|
||||
|
||||
fn divider_unsigned(n: u32, d: u32) -> DivResult<u32> {
|
||||
let packed = unsafe { unsigned_divmod(n, d) };
|
||||
DivResult {
|
||||
quotient: packed as u32,
|
||||
remainder: (packed >> 32) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
fn divider_signed(n: i32, d: i32) -> DivResult<i32> {
|
||||
let packed = unsafe { signed_divmod(n, d) };
|
||||
// Double casts to avoid sign extension
|
||||
DivResult {
|
||||
quotient: packed as u32 as i32,
|
||||
remainder: (packed >> 32) as u32 as i32,
|
||||
}
|
||||
}
|
||||
|
||||
impl HwDivider {
|
||||
/// Perform hardware unsigned divide/modulo operation
|
||||
pub fn unsigned(&self, dividend: u32, divisor: u32) -> DivResult<u32> {
|
||||
divider_unsigned(dividend, divisor)
|
||||
}
|
||||
|
||||
/// Perform hardware signed divide/modulo operation
|
||||
pub fn signed(&self, dividend: i32, divisor: i32) -> DivResult<i32> {
|
||||
divider_signed(dividend, divisor)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
|
||||
divider_unsigned(n, d).quotient
|
||||
}
|
||||
|
||||
extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
|
||||
divider_unsigned(n, d).remainder
|
||||
}
|
||||
|
||||
extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
|
||||
let quo_rem = divider_unsigned(n, d);
|
||||
if let Some(rem) = rem {
|
||||
*rem = quo_rem.remainder;
|
||||
}
|
||||
quo_rem.quotient
|
||||
}
|
||||
|
||||
extern "C" fn __divsi3(n: i32, d: i32) -> i32 {
|
||||
divider_signed(n, d).quotient
|
||||
}
|
||||
|
||||
extern "C" fn __modsi3(n: i32, d: i32) -> i32 {
|
||||
divider_signed(n, d).remainder
|
||||
}
|
||||
|
||||
extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 {
|
||||
let quo_rem = divider_signed(n, d);
|
||||
*rem = quo_rem.remainder;
|
||||
quo_rem.quotient
|
||||
}
|
||||
}
|
||||
|
||||
/// This type is just used to limit us to Spinlocks `0..=31`
|
||||
pub trait SpinlockValid {}
|
||||
|
||||
/// Hardware based spinlock.
|
||||
///
|
||||
/// You can claim this lock by calling either [`claim`], [`try_claim`] or
|
||||
/// [`claim_async`]. These spin-locks are hardware backed, so if you lock
|
||||
/// e.g. `Spinlock<6>`, then any other part of your application using
|
||||
/// `Spinlock<6>` will contend for the same lock, without them needing to
|
||||
/// share a reference or otherwise communicate with each other.
|
||||
///
|
||||
/// When the obtained spinlock goes out of scope, it is automatically unlocked.
|
||||
///
|
||||
///
|
||||
/// ```no_run
|
||||
/// use rp2040_hal::sio::Spinlock0;
|
||||
/// static mut SOME_GLOBAL_VAR: u32 = 0;
|
||||
///
|
||||
/// /// This function is safe to call from two different cores, but is not safe
|
||||
/// /// to call from an interrupt routine!
|
||||
/// fn update_global_var() {
|
||||
/// // Do not say `let _ = ` here - it will immediately unlock!
|
||||
/// let _lock = Spinlock0::claim();
|
||||
/// // Do your thing here that Core 0 and Core 1 might want to do at the
|
||||
/// // same time, like update this global variable:
|
||||
/// unsafe { SOME_GLOBAL_VAR += 1 };
|
||||
/// // The lock is dropped here.
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// **Warning**: These spinlocks are not re-entrant, meaning that the
|
||||
/// following code will cause a deadlock:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use rp2040_hal::sio::Spinlock0;
|
||||
/// let lock_1 = Spinlock0::claim();
|
||||
/// let lock_2 = Spinlock0::claim(); // deadlock here
|
||||
/// ```
|
||||
///
|
||||
/// **Note:** The `critical-section` implementation uses Spinlock 31.
|
||||
///
|
||||
/// [`claim`]: #method.claim
|
||||
/// [`try_claim`]: #method.try_claim
|
||||
/// [`claim_async`]: #method.claim_asyncs
|
||||
pub struct Spinlock<const N: usize>(core::marker::PhantomData<()>)
|
||||
where
|
||||
Spinlock<N>: SpinlockValid;
|
||||
|
||||
impl<const N: usize> Spinlock<N>
|
||||
where
|
||||
Spinlock<N>: SpinlockValid,
|
||||
{
|
||||
/// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is
|
||||
/// already in use somewhere else.
|
||||
pub fn try_claim() -> Option<Self> {
|
||||
// Safety: We're only reading from this register
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
let lock = sio.spinlock[N].read().bits();
|
||||
if lock > 0 {
|
||||
Some(Self(core::marker::PhantomData))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Claim the spinlock, will block the current thread until the lock is available.
|
||||
///
|
||||
/// Note that calling this multiple times in a row will cause a deadlock
|
||||
pub fn claim() -> Self {
|
||||
loop {
|
||||
if let Some(result) = Self::try_claim() {
|
||||
break result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to claim the spinlock. Will return `WouldBlock` until the spinlock is available.
|
||||
pub fn claim_async() -> nb::Result<Self, Infallible> {
|
||||
Self::try_claim().ok_or(nb::Error::WouldBlock)
|
||||
}
|
||||
|
||||
/// Clear a locked spin-lock.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only call this function if you hold the spin-lock.
|
||||
pub unsafe fn release() {
|
||||
let sio = &*pac::SIO::ptr();
|
||||
// Write (any value): release the lock
|
||||
sio.spinlock[N].write_with_zero(|b| b.bits(1));
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Drop for Spinlock<N>
|
||||
where
|
||||
Spinlock<N>: SpinlockValid,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
// This is safe because we own the object, and hence hold the lock.
|
||||
unsafe { Self::release() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Spinlock number 0
|
||||
pub type Spinlock0 = Spinlock<0>;
|
||||
|
||||
impl SpinlockValid for Spinlock<0> {}
|
||||
|
||||
/// Spinlock number 1
|
||||
pub type Spinlock1 = Spinlock<1>;
|
||||
|
||||
impl SpinlockValid for Spinlock<1> {}
|
||||
|
||||
/// Spinlock number 2
|
||||
pub type Spinlock2 = Spinlock<2>;
|
||||
|
||||
impl SpinlockValid for Spinlock<2> {}
|
||||
|
||||
/// Spinlock number 3
|
||||
pub type Spinlock3 = Spinlock<3>;
|
||||
|
||||
impl SpinlockValid for Spinlock<3> {}
|
||||
|
||||
/// Spinlock number 4
|
||||
pub type Spinlock4 = Spinlock<4>;
|
||||
|
||||
impl SpinlockValid for Spinlock<4> {}
|
||||
|
||||
/// Spinlock number 5
|
||||
pub type Spinlock5 = Spinlock<5>;
|
||||
|
||||
impl SpinlockValid for Spinlock<5> {}
|
||||
|
||||
/// Spinlock number 6
|
||||
pub type Spinlock6 = Spinlock<6>;
|
||||
|
||||
impl SpinlockValid for Spinlock<6> {}
|
||||
|
||||
/// Spinlock number 7
|
||||
pub type Spinlock7 = Spinlock<7>;
|
||||
|
||||
impl SpinlockValid for Spinlock<7> {}
|
||||
|
||||
/// Spinlock number 8
|
||||
pub type Spinlock8 = Spinlock<8>;
|
||||
|
||||
impl SpinlockValid for Spinlock<8> {}
|
||||
|
||||
/// Spinlock number 9
|
||||
pub type Spinlock9 = Spinlock<9>;
|
||||
|
||||
impl SpinlockValid for Spinlock<9> {}
|
||||
|
||||
/// Spinlock number 10
|
||||
pub type Spinlock10 = Spinlock<10>;
|
||||
|
||||
impl SpinlockValid for Spinlock<10> {}
|
||||
|
||||
/// Spinlock number 11
|
||||
pub type Spinlock11 = Spinlock<11>;
|
||||
|
||||
impl SpinlockValid for Spinlock<11> {}
|
||||
|
||||
/// Spinlock number 12
|
||||
pub type Spinlock12 = Spinlock<12>;
|
||||
|
||||
impl SpinlockValid for Spinlock<12> {}
|
||||
|
||||
/// Spinlock number 13
|
||||
pub type Spinlock13 = Spinlock<13>;
|
||||
|
||||
impl SpinlockValid for Spinlock<13> {}
|
||||
|
||||
/// Spinlock number 14
|
||||
pub type Spinlock14 = Spinlock<14>;
|
||||
|
||||
impl SpinlockValid for Spinlock<14> {}
|
||||
|
||||
/// Spinlock number 15
|
||||
pub type Spinlock15 = Spinlock<15>;
|
||||
|
||||
impl SpinlockValid for Spinlock<15> {}
|
||||
|
||||
/// Spinlock number 16
|
||||
pub type Spinlock16 = Spinlock<16>;
|
||||
|
||||
impl SpinlockValid for Spinlock<16> {}
|
||||
|
||||
/// Spinlock number 17
|
||||
pub type Spinlock17 = Spinlock<17>;
|
||||
|
||||
impl SpinlockValid for Spinlock<17> {}
|
||||
|
||||
/// Spinlock number 18
|
||||
pub type Spinlock18 = Spinlock<18>;
|
||||
|
||||
impl SpinlockValid for Spinlock<18> {}
|
||||
|
||||
/// Spinlock number 19
|
||||
pub type Spinlock19 = Spinlock<19>;
|
||||
|
||||
impl SpinlockValid for Spinlock<19> {}
|
||||
|
||||
/// Spinlock number 20
|
||||
pub type Spinlock20 = Spinlock<20>;
|
||||
|
||||
impl SpinlockValid for Spinlock<20> {}
|
||||
|
||||
/// Spinlock number 21
|
||||
pub type Spinlock21 = Spinlock<21>;
|
||||
|
||||
impl SpinlockValid for Spinlock<21> {}
|
||||
|
||||
/// Spinlock number 22
|
||||
pub type Spinlock22 = Spinlock<22>;
|
||||
|
||||
impl SpinlockValid for Spinlock<22> {}
|
||||
|
||||
/// Spinlock number 23
|
||||
pub type Spinlock23 = Spinlock<23>;
|
||||
|
||||
impl SpinlockValid for Spinlock<23> {}
|
||||
|
||||
/// Spinlock number 24
|
||||
pub type Spinlock24 = Spinlock<24>;
|
||||
|
||||
impl SpinlockValid for Spinlock<24> {}
|
||||
|
||||
/// Spinlock number 25
|
||||
pub type Spinlock25 = Spinlock<25>;
|
||||
|
||||
impl SpinlockValid for Spinlock<25> {}
|
||||
|
||||
/// Spinlock number 26
|
||||
pub type Spinlock26 = Spinlock<26>;
|
||||
|
||||
impl SpinlockValid for Spinlock<26> {}
|
||||
|
||||
/// Spinlock number 27
|
||||
pub type Spinlock27 = Spinlock<27>;
|
||||
|
||||
impl SpinlockValid for Spinlock<27> {}
|
||||
|
||||
/// Spinlock number 28
|
||||
pub type Spinlock28 = Spinlock<28>;
|
||||
|
||||
impl SpinlockValid for Spinlock<28> {}
|
||||
|
||||
/// Spinlock number 29
|
||||
pub type Spinlock29 = Spinlock<29>;
|
||||
|
||||
impl SpinlockValid for Spinlock<29> {}
|
||||
|
||||
/// Spinlock number 30
|
||||
pub type Spinlock30 = Spinlock<30>;
|
||||
|
||||
impl SpinlockValid for Spinlock<30> {}
|
||||
|
||||
/// Spinlock number 31 - used by critical section implementation
|
||||
#[cfg(feature = "critical-section-impl")]
|
||||
pub(crate) type Spinlock31 = Spinlock<31>;
|
||||
|
||||
/// Spinlock number 31 - only public if critical-section-impl is not enabled
|
||||
#[cfg(not(feature = "critical-section-impl"))]
|
||||
pub type Spinlock31 = Spinlock<31>;
|
||||
|
||||
impl SpinlockValid for Spinlock<31> {}
|
||||
|
||||
/// Returns the current state of the spinlocks. Each index corresponds to the associated spinlock, e.g. if index `5` is set to `true`, it means that [`Spinlock5`] is currently locked.
|
||||
///
|
||||
/// Note that spinlocks can be claimed or released at any point, so this function cannot guarantee the spinlock is actually available right after calling this function. This function is mainly intended for debugging.
|
||||
pub fn spinlock_state() -> [bool; 32] {
|
||||
// Safety: we're only reading from a register
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
// A bitmap containing the state of all 32 spinlocks (1=locked).
|
||||
let register = sio.spinlock_st.read().bits();
|
||||
let mut result = [false; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
result[i] = (register & (1 << i)) > 0;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Free all spinlocks, regardless of their current status
|
||||
///
|
||||
/// RP2040 does not release all spinlocks on reset.
|
||||
/// The C SDK clears these all during entry, and so do we if you call hal::entry!
|
||||
/// But if someone is using the default cortex-m entry they risk hitting deadlocks so provide *something* to help out
|
||||
///
|
||||
/// # Safety
|
||||
/// Where possible, you should use the hal::entry macro attribute on main instead of this.
|
||||
/// You should call this as soon as possible after reset - preferably as the first entry in fn main(), before *ANY* use of spinlocks, atomics, or critical_section
|
||||
pub unsafe fn spinlock_reset() {
|
||||
// Using raw pointers to avoid taking peripherals accidently at startup
|
||||
const SIO_BASE: u32 = 0xd0000000;
|
||||
const SPINLOCK0_PTR: *mut u32 = (SIO_BASE + 0x100) as *mut u32;
|
||||
const SPINLOCK_COUNT: usize = 32;
|
||||
for i in 0..SPINLOCK_COUNT {
|
||||
SPINLOCK0_PTR.wrapping_add(i).write_volatile(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration struct for one lane of the interpolator
|
||||
pub struct LaneCtrl {
|
||||
/// Bit 22 - Only present on INTERP1 on each core. If CLAMP mode is enabled:
|
||||
/// - LANE0 result is shifted and masked ACCUM0, clamped by a lower bound of
|
||||
/// BASE0 and an upper bound of BASE1.
|
||||
/// - Signedness of these comparisons is determined by LANE0_CTRL_SIGNED
|
||||
pub clamp: bool,
|
||||
/// Bit 21 - Only present on INTERP0 on each core. If BLEND mode is enabled:
|
||||
/// - LANE1 result is a linear interpolation between BASE0 and BASE1, controlled
|
||||
/// by the 8 LSBs of lane 1 shift and mask value (a fractional number between
|
||||
/// 0 and 255/256ths)
|
||||
/// - LANE0 result does not have BASE0 added (yields only
|
||||
/// the 8 LSBs of lane 1 shift+mask value)
|
||||
/// - FULL result does not have lane 1 shift+mask value added (BASE2 + lane 0 shift+mask)
|
||||
/// LANE1 SIGNED flag controls whether the interpolation is signed or unsigned.
|
||||
pub blend: bool,
|
||||
/// Bits 19:20 - ORed into bits 29:28 of the lane result presented to the processor on the bus.
|
||||
/// No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence
|
||||
/// of pointers into flash or SRAM.
|
||||
pub force_msb: u8,
|
||||
/// Bit 18 - If 1, mask + shift is bypassed for LANE0 result. This does not affect FULL result.
|
||||
pub add_raw: bool,
|
||||
/// Bit 17 - If 1, feed the opposite lane's result into this lane's accumulator on POP.
|
||||
pub cross_result: bool,
|
||||
/// Bit 16 - If 1, feed the opposite lane's accumulator into this lane's shift + mask hardware.
|
||||
/// Takes effect even if ADD_RAW is set (the CROSS_INPUT mux is before the shift+mask bypass)
|
||||
pub cross_input: bool,
|
||||
/// Bit 15 - If SIGNED is set, the shifted and masked accumulator value is sign-extended to 32 bits
|
||||
/// before adding to BASE0, and LANE0 PEEK/POP appear extended to 32 bits when read by processor.
|
||||
pub signed: bool,
|
||||
/// Bits 10:14 - The most-significant bit allowed to pass by the mask (inclusive)
|
||||
/// Setting MSB < LSB may cause chip to turn inside-out
|
||||
pub mask_msb: u8,
|
||||
/// Bits 5:9 - The least-significant bit allowed to pass by the mask (inclusive)
|
||||
pub mask_lsb: u8,
|
||||
/// Bits 0:4 - Logical right-shift applied to accumulator before masking
|
||||
pub shift: u8,
|
||||
}
|
||||
|
||||
impl LaneCtrl {
|
||||
/// Default configuration. Normal operation, unsigned, mask keeps all bits, no shift.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
clamp: false,
|
||||
blend: false,
|
||||
force_msb: 0,
|
||||
add_raw: false,
|
||||
cross_result: false,
|
||||
cross_input: false,
|
||||
signed: false,
|
||||
mask_msb: 31,
|
||||
mask_lsb: 0,
|
||||
shift: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// encode the configuration to be loaded in the ctrl register of one lane of an interpolator
|
||||
pub const fn encode(&self) -> u32 {
|
||||
assert!(!(self.blend && self.clamp));
|
||||
assert!(self.force_msb < 0b100);
|
||||
assert!(self.mask_msb < 0b100000);
|
||||
assert!(self.mask_lsb < 0b100000);
|
||||
assert!(self.mask_msb >= self.mask_lsb);
|
||||
assert!(self.shift < 0b100000);
|
||||
((self.clamp as u32) << 22)
|
||||
| ((self.blend as u32) << 21)
|
||||
| ((self.force_msb as u32) << 19)
|
||||
| ((self.add_raw as u32) << 18)
|
||||
| ((self.cross_result as u32) << 17)
|
||||
| ((self.cross_input as u32) << 16)
|
||||
| ((self.signed as u32) << 15)
|
||||
| ((self.mask_msb as u32) << 10)
|
||||
| ((self.mask_lsb as u32) << 5)
|
||||
| (self.shift as u32)
|
||||
}
|
||||
}
|
||||
|
||||
///Trait representing the functionnality of a single lane of an interpolator.
|
||||
pub trait Lane {
|
||||
///Read the lane result, and simultaneously write lane results to both accumulators.
|
||||
fn pop(&mut self) -> u32;
|
||||
///Read the lane result without altering any internal state
|
||||
fn peek(&self) -> u32;
|
||||
///Write a value to the accumulator
|
||||
fn set_accum(&mut self, v: u32);
|
||||
///Read the value from the accumulator
|
||||
fn get_accum(&self) -> u32;
|
||||
///Write a value to the base register
|
||||
fn set_base(&mut self, v: u32);
|
||||
///Read the value from the base register
|
||||
fn get_base(&self) -> u32;
|
||||
///Write to the control register
|
||||
fn set_ctrl(&mut self, v: u32);
|
||||
///Read from the control register
|
||||
fn get_ctrl(&self) -> u32;
|
||||
///Add the value to the accumulator register
|
||||
fn add_accum(&mut self, v: u32);
|
||||
///Read the raw shift and mask value (BASE register not added)
|
||||
fn read_raw(&self) -> u32;
|
||||
}
|
||||
|
||||
///Trait representing the functionnality of an interpolator.
|
||||
/// ```no_run
|
||||
/// use rp2040_hal::sio::{Sio,LaneCtrl,Lane};
|
||||
/// use rp2040_hal::pac;
|
||||
/// let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
/// let mut sio = Sio::new(peripherals.SIO);
|
||||
///
|
||||
/// // by having the configuration const, the validity is checked during compilation.
|
||||
/// const config: u32 = LaneCtrl {
|
||||
/// mask_msb: 4, // Most significant bit of the mask is bit 4
|
||||
/// // By default the least significant bit is bit 0
|
||||
/// // this will keep only the 5 least significant bits.
|
||||
/// // this is equivalent to %32
|
||||
/// ..LaneCtrl::new()
|
||||
/// }.encode();
|
||||
/// sio.interp0.get_lane0().set_ctrl(config);
|
||||
/// sio.interp0.get_lane0().set_accum(0);
|
||||
/// sio.interp0.get_lane0().set_base(1); // will increment the value by 1 on each call to pop
|
||||
///
|
||||
/// sio.interp0.get_lane0().peek(); // returns 1
|
||||
/// sio.interp0.get_lane0().pop(); // returns 1
|
||||
/// sio.interp0.get_lane0().pop(); // returns 2
|
||||
/// sio.interp0.get_lane0().pop(); // returns 3
|
||||
/// ```
|
||||
pub trait Interp {
|
||||
///Read the interpolator result (Result 2 in the datasheet), and simultaneously write lane results to both accumulators.
|
||||
fn pop(&mut self) -> u32;
|
||||
///Read the interpolator result (Result 2 in the datasheet) without altering any internal state
|
||||
fn peek(&self) -> u32;
|
||||
///Write to the interpolator Base register (Base2 in the datasheet)
|
||||
fn set_base(&mut self, v: u32);
|
||||
///Read the interpolator Base register (Base2 in the datasheet)
|
||||
fn get_base(&self) -> u32;
|
||||
}
|
||||
|
||||
macro_rules! interpolators {
|
||||
(
|
||||
$($interp:ident : ( $( [ $lane:ident,$lane_id:expr ] ),+ ) ),+
|
||||
) => {
|
||||
$crate::paste::paste! {
|
||||
|
||||
|
||||
$(
|
||||
$(
|
||||
#[doc = "The lane " $lane_id " of " $interp]
|
||||
pub struct [<$interp $lane>]{
|
||||
_private: (),
|
||||
}
|
||||
impl Lane for [<$interp $lane>]{
|
||||
fn pop(&mut self) ->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _pop_ $lane:lower>].read().bits()
|
||||
}
|
||||
fn peek(&self) ->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _peek_ $lane:lower>].read().bits()
|
||||
}
|
||||
fn set_accum(&mut self,v:u32){
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _accum $lane_id>].write(|w| unsafe { w.bits(v) });
|
||||
}
|
||||
fn get_accum(&self)->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _accum $lane_id>].read().bits()
|
||||
}
|
||||
fn set_base(&mut self, v:u32){
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _base $lane_id>].write(|w| unsafe { w.bits(v) });
|
||||
}
|
||||
fn get_base(&self)->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _base $lane_id>].read().bits()
|
||||
}
|
||||
fn set_ctrl(&mut self, v:u32){
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _ctrl_lane $lane_id>].write(|w| unsafe { w.bits(v) });
|
||||
}
|
||||
fn get_ctrl(&self)->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _ctrl_lane $lane_id>].read().bits()
|
||||
}
|
||||
fn add_accum(&mut self, v:u32){
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _accum $lane_id _add>].write(|w| unsafe { w.bits(v) });
|
||||
}
|
||||
fn read_raw(&self)->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _accum $lane_id _add>].read().bits()
|
||||
}
|
||||
}
|
||||
)+
|
||||
#[doc = "Interpolator " $interp]
|
||||
pub struct $interp {
|
||||
$(
|
||||
[<$lane:lower>]: [<$interp $lane>],
|
||||
)+
|
||||
}
|
||||
impl $interp{
|
||||
$(
|
||||
/// Lane accessor function
|
||||
pub fn [<get_ $lane:lower>](&mut self)->&mut [<$interp $lane>]{
|
||||
&mut self.[<$lane:lower>]
|
||||
}
|
||||
)+
|
||||
}
|
||||
impl Interp for $interp{
|
||||
fn pop(&mut self) ->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _pop_full>].read().bits()
|
||||
}
|
||||
fn peek(&self) ->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _peek_full>].read().bits()
|
||||
}
|
||||
fn set_base(&mut self, v:u32){
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _base2>].write(|w| unsafe { w.bits(v)});
|
||||
}
|
||||
fn get_base(&self)->u32{
|
||||
let sio = unsafe { &*pac::SIO::ptr() };
|
||||
sio.[<$interp:lower _base2>].read().bits()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interpolators!(
|
||||
Interp0 : ([Lane0,0],[Lane1,1]),
|
||||
Interp1 : ([Lane0,0],[Lane1,1])
|
||||
);
|
|
@ -1,349 +0,0 @@
|
|||
//! Serial Peripheral Interface (SPI)
|
||||
//!
|
||||
//! See [Chapter 4 Section 4](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use embedded_hal::spi::MODE_0;
|
||||
//! use fugit::RateExtU32;
|
||||
//! use rp2040_hal::{spi::Spi, gpio::{Pins, FunctionSpi}, pac, Sio};
|
||||
//!
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let sio = Sio::new(peripherals.SIO);
|
||||
//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//!
|
||||
//! let _ = pins.gpio2.into_mode::<FunctionSpi>();
|
||||
//! let _ = pins.gpio3.into_mode::<FunctionSpi>();
|
||||
//!
|
||||
//! let spi = Spi::<_, _, 8>::new(peripherals.SPI0).init(&mut peripherals.RESETS, 125_000_000u32.Hz(), 16_000_000u32.Hz(), &MODE_0);
|
||||
//! ```
|
||||
|
||||
use crate::resets::SubsystemReset;
|
||||
use core::{convert::Infallible, marker::PhantomData, ops::Deref};
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
use eh1_0_alpha::spi as eh1;
|
||||
use embedded_hal::blocking::spi;
|
||||
use embedded_hal::spi::{FullDuplex, Mode, Phase, Polarity};
|
||||
use fugit::HertzU32;
|
||||
use pac::RESETS;
|
||||
|
||||
/// State of the SPI
|
||||
pub trait State {}
|
||||
|
||||
/// Spi is disabled
|
||||
pub struct Disabled {
|
||||
__private: (),
|
||||
}
|
||||
|
||||
/// Spi is enabled
|
||||
pub struct Enabled {
|
||||
__private: (),
|
||||
}
|
||||
|
||||
impl State for Disabled {}
|
||||
impl State for Enabled {}
|
||||
|
||||
/// Pac SPI device
|
||||
pub trait SpiDevice: Deref<Target = pac::spi0::RegisterBlock> + SubsystemReset {}
|
||||
|
||||
impl SpiDevice for pac::SPI0 {}
|
||||
impl SpiDevice for pac::SPI1 {}
|
||||
|
||||
/// Data size used in spi
|
||||
pub trait DataSize {}
|
||||
|
||||
impl DataSize for u8 {}
|
||||
impl DataSize for u16 {}
|
||||
|
||||
/// Spi
|
||||
pub struct Spi<S: State, D: SpiDevice, const DS: u8> {
|
||||
device: D,
|
||||
state: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S: State, D: SpiDevice, const DS: u8> Spi<S, D, DS> {
|
||||
fn transition<To: State>(self, _: To) -> Spi<To, D, DS> {
|
||||
Spi {
|
||||
device: self.device,
|
||||
state: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases the underlying device.
|
||||
pub fn free(self) -> D {
|
||||
self.device
|
||||
}
|
||||
|
||||
/// Set baudrate based on peripheral clock
|
||||
///
|
||||
/// Typically the peripheral clock is set to 125_000_000
|
||||
pub fn set_baudrate<F: Into<HertzU32>, B: Into<HertzU32>>(
|
||||
&mut self,
|
||||
peri_frequency: F,
|
||||
baudrate: B,
|
||||
) -> HertzU32 {
|
||||
let freq_in = peri_frequency.into().to_Hz();
|
||||
let baudrate = baudrate.into().to_Hz();
|
||||
let mut prescale: u8 = u8::MAX;
|
||||
let mut postdiv: u8 = 0;
|
||||
|
||||
// Find smallest prescale value which puts output frequency in range of
|
||||
// post-divide. Prescale is an even number from 2 to 254 inclusive.
|
||||
for prescale_option in (2u32..=254).step_by(2) {
|
||||
// We need to use an saturating_mul here because with a high baudrate certain invalid prescale
|
||||
// values might not fit in u32. However we can be sure those values exeed the max sys_clk frequency
|
||||
// So clamping a u32::MAX is fine here...
|
||||
if freq_in < ((prescale_option + 2) * 256).saturating_mul(baudrate) {
|
||||
prescale = prescale_option as u8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We might not find a prescale value that lowers the clock freq enough, so we leave it at max
|
||||
debug_assert_ne!(prescale, u8::MAX);
|
||||
|
||||
// Find largest post-divide which makes output <= baudrate. Post-divide is
|
||||
// an integer in the range 0 to 255 inclusive.
|
||||
for postdiv_option in (1..=255u8).rev() {
|
||||
if freq_in / (prescale as u32 * postdiv_option as u32) > baudrate {
|
||||
postdiv = postdiv_option;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.device
|
||||
.sspcpsr
|
||||
.write(|w| unsafe { w.cpsdvsr().bits(prescale) });
|
||||
self.device
|
||||
.sspcr0
|
||||
.modify(|_, w| unsafe { w.scr().bits(postdiv) });
|
||||
|
||||
// Return the frequency we were able to achieve
|
||||
use fugit::RateExtU32;
|
||||
(freq_in / (prescale as u32 * (1 + postdiv as u32))).Hz()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SpiDevice, const DS: u8> Spi<Disabled, D, DS> {
|
||||
/// Create new spi device
|
||||
pub fn new(device: D) -> Spi<Disabled, D, DS> {
|
||||
Spi {
|
||||
device,
|
||||
state: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set format and datasize
|
||||
fn set_format(&mut self, data_bits: u8, mode: &Mode) {
|
||||
self.device.sspcr0.modify(|_, w| unsafe {
|
||||
w.dss()
|
||||
.bits(data_bits - 1)
|
||||
.spo()
|
||||
.bit(mode.polarity == Polarity::IdleHigh)
|
||||
.sph()
|
||||
.bit(mode.phase == Phase::CaptureOnSecondTransition)
|
||||
});
|
||||
}
|
||||
|
||||
/// Initialize the SPI
|
||||
pub fn init<F: Into<HertzU32>, B: Into<HertzU32>>(
|
||||
mut self,
|
||||
resets: &mut RESETS,
|
||||
peri_frequency: F,
|
||||
baudrate: B,
|
||||
mode: &Mode,
|
||||
) -> Spi<Enabled, D, DS> {
|
||||
self.device.reset_bring_down(resets);
|
||||
self.device.reset_bring_up(resets);
|
||||
|
||||
self.set_baudrate(peri_frequency, baudrate);
|
||||
self.set_format(DS, mode);
|
||||
// Always enable DREQ signals -- harmless if DMA is not listening
|
||||
self.device
|
||||
.sspdmacr
|
||||
.modify(|_, w| w.txdmae().set_bit().rxdmae().set_bit());
|
||||
|
||||
// Finally enable the SPI
|
||||
self.device.sspcr1.modify(|_, w| w.sse().set_bit());
|
||||
|
||||
self.transition(Enabled { __private: () })
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SpiDevice, const DS: u8> Spi<Enabled, D, DS> {
|
||||
fn is_writable(&self) -> bool {
|
||||
self.device.sspsr.read().tnf().bit_is_set()
|
||||
}
|
||||
fn is_readable(&self) -> bool {
|
||||
self.device.sspsr.read().rne().bit_is_set()
|
||||
}
|
||||
|
||||
/// Check if spi is busy transmitting and/or receiving
|
||||
pub fn is_busy(&self) -> bool {
|
||||
self.device.sspsr.read().bsy().bit_is_set()
|
||||
}
|
||||
|
||||
/// Disable the spi to reset its configuration
|
||||
pub fn disable(self) -> Spi<Disabled, D, DS> {
|
||||
self.device.sspcr1.modify(|_, w| w.sse().clear_bit());
|
||||
|
||||
self.transition(Disabled { __private: () })
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_write {
|
||||
($type:ident, [$($nr:expr),+]) => {
|
||||
|
||||
$(
|
||||
impl<D: SpiDevice> FullDuplex<$type> for Spi<Enabled, D, $nr> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn read(&mut self) -> Result<$type, nb::Error<Infallible>> {
|
||||
if !self.is_readable() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
Ok(self.device.sspdr.read().data().bits() as $type)
|
||||
}
|
||||
fn send(&mut self, word: $type) -> Result<(), nb::Error<Infallible>> {
|
||||
// Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX
|
||||
// is full, PL022 inhibits RX pushes, and sets a sticky flag on
|
||||
// push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set.
|
||||
if !self.is_writable() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
self.device
|
||||
.sspdr
|
||||
.write(|w| unsafe { w.data().bits(word as u16) });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SpiDevice> spi::write::Default<$type> for Spi<Enabled, D, $nr> {}
|
||||
impl<D: SpiDevice> spi::transfer::Default<$type> for Spi<Enabled, D, $nr> {}
|
||||
impl<D: SpiDevice> spi::write_iter::Default<$type> for Spi<Enabled, D, $nr> {}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: SpiDevice> eh1::ErrorType for Spi<Enabled, D, $nr> {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: SpiDevice> eh1::SpiBusFlush for Spi<Enabled, D, $nr> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
while self.is_busy() {}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: SpiDevice> eh1::SpiBusRead<$type> for Spi<Enabled, D, $nr> {
|
||||
fn read(&mut self, words: &mut [$type]) -> Result<(), Self::Error> {
|
||||
for word in words.iter_mut() {
|
||||
// write empty word
|
||||
while !self.is_writable() {}
|
||||
self.device
|
||||
.sspdr
|
||||
.write(|w| unsafe { w.data().bits(0) });
|
||||
|
||||
// read one word
|
||||
while !self.is_readable() {}
|
||||
*word = self.device.sspdr.read().data().bits() as $type;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: SpiDevice> eh1::SpiBusWrite<$type> for Spi<Enabled, D, $nr> {
|
||||
fn write(&mut self, words: &[$type]) -> Result<(), Self::Error> {
|
||||
for word in words.iter() {
|
||||
// write one word
|
||||
while !self.is_writable() {}
|
||||
self.device
|
||||
.sspdr
|
||||
.write(|w| unsafe { w.data().bits(*word as u16) });
|
||||
|
||||
// drop read wordd
|
||||
while !self.is_readable() {}
|
||||
let _ = self.device.sspdr.read().data().bits();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: SpiDevice> eh1::SpiBus<$type> for Spi<Enabled, D, $nr> {
|
||||
fn transfer(&mut self, read: &mut [$type], write: &[$type]) -> Result<(), Self::Error>{
|
||||
let len = read.len().max(write.len());
|
||||
for i in 0..len {
|
||||
// write one word. Send empty word if buffer is empty.
|
||||
let wb = write.get(i).copied().unwrap_or(0);
|
||||
while !self.is_writable() {}
|
||||
self.device
|
||||
.sspdr
|
||||
.write(|w| unsafe { w.data().bits(wb as u16) });
|
||||
|
||||
// read one word. Drop extra words if buffer is full.
|
||||
while !self.is_readable() {}
|
||||
let rb = self.device.sspdr.read().data().bits() as $type;
|
||||
if let Some(r) = read.get_mut(i) {
|
||||
*r = rb;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transfer_in_place(&mut self, words: &mut [$type]) -> Result<(), Self::Error>{
|
||||
for word in words.iter_mut() {
|
||||
// write one word
|
||||
while !self.is_writable() {}
|
||||
self.device
|
||||
.sspdr
|
||||
.write(|w| unsafe { w.data().bits(*word as u16) });
|
||||
|
||||
// read one word
|
||||
while !self.is_readable() {}
|
||||
*word = self.device.sspdr.read().data().bits() as $type;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/* disabled for now - nb was migrated to separate crate
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: SpiDevice> eh1::nb::FullDuplex<$type> for Spi<Enabled, D, $nr> {
|
||||
fn read(&mut self) -> Result<$type, nb::Error<Infallible>> {
|
||||
if !self.is_readable() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
Ok(self.device.sspdr.read().data().bits() as $type)
|
||||
}
|
||||
fn write(&mut self, word: $type) -> Result<(), nb::Error<Infallible>> {
|
||||
// Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX
|
||||
// is full, PL022 inhibits RX pushes, and sets a sticky flag on
|
||||
// push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set.
|
||||
if !self.is_writable() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
self.device
|
||||
.sspdr
|
||||
.write(|w| unsafe { w.data().bits(word as u16) });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
)+
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
impl_write!(u8, [4, 5, 6, 7, 8]);
|
||||
impl_write!(u16, [9, 10, 11, 22, 13, 14, 15, 16]);
|
|
@ -1,3 +0,0 @@
|
|||
//! Synchronous Serial Interface (SSI)
|
||||
// See [Chapter 4 Section 10](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
// TODO
|
|
@ -1,434 +0,0 @@
|
|||
//! Timer Peripheral
|
||||
//!
|
||||
//! The Timer peripheral on RP2040 consists of a 64-bit counter and 4 alarms.
|
||||
//! The Counter is incremented once per microsecond. It obtains its clock source from the watchdog peripheral, you must enable the watchdog before using this peripheral.
|
||||
//! Since it would take thousands of years for this counter to overflow you do not need to write logic for dealing with this if using get_counter.
|
||||
//!
|
||||
//! Each of the 4 alarms can match on the lower 32 bits of Counter and trigger an interrupt.
|
||||
//!
|
||||
//! See [Chapter 4 Section 6](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details.
|
||||
|
||||
use fugit::{MicrosDurationU32, MicrosDurationU64, TimerInstantU64};
|
||||
|
||||
use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set};
|
||||
use crate::pac::{RESETS, TIMER};
|
||||
use crate::resets::SubsystemReset;
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
/// Instant type used by the Timer & Alarm methods.
|
||||
pub type Instant = TimerInstantU64<1_000_000>;
|
||||
|
||||
static ALARMS: AtomicU8 = AtomicU8::new(0x0F);
|
||||
fn take_alarm(mask: u8) -> bool {
|
||||
critical_section::with(|_| {
|
||||
let alarms = ALARMS.load(Ordering::Relaxed);
|
||||
ALARMS.store(alarms & !mask, Ordering::Relaxed);
|
||||
(alarms & mask) != 0
|
||||
})
|
||||
}
|
||||
fn release_alarm(mask: u8) {
|
||||
critical_section::with(|_| {
|
||||
let alarms = ALARMS.load(Ordering::Relaxed);
|
||||
ALARMS.store(alarms | mask, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
fn get_counter(timer: &crate::pac::timer::RegisterBlock) -> Instant {
|
||||
let mut hi0 = timer.timerawh.read().bits();
|
||||
let timestamp = loop {
|
||||
let low = timer.timerawl.read().bits();
|
||||
let hi1 = timer.timerawh.read().bits();
|
||||
if hi0 == hi1 {
|
||||
break (u64::from(hi0) << 32) | u64::from(low);
|
||||
}
|
||||
hi0 = hi1;
|
||||
};
|
||||
TimerInstantU64::from_ticks(timestamp)
|
||||
}
|
||||
/// Timer peripheral
|
||||
pub struct Timer {
|
||||
timer: TIMER,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
/// Create a new [`Timer`]
|
||||
pub fn new(timer: TIMER, resets: &mut RESETS) -> Self {
|
||||
timer.reset_bring_down(resets);
|
||||
timer.reset_bring_up(resets);
|
||||
Self { timer }
|
||||
}
|
||||
|
||||
/// Get the current counter value.
|
||||
pub fn get_counter(&self) -> Instant {
|
||||
get_counter(&self.timer)
|
||||
}
|
||||
|
||||
/// Get the value of the least significant word of the counter.
|
||||
pub fn get_counter_low(&self) -> u32 {
|
||||
self.timer.timerawl.read().bits()
|
||||
}
|
||||
|
||||
/// Initialized a Count Down instance without starting it.
|
||||
pub fn count_down(&self) -> CountDown<'_> {
|
||||
CountDown {
|
||||
timer: self,
|
||||
period: MicrosDurationU64::nanos(0),
|
||||
next_end: None,
|
||||
}
|
||||
}
|
||||
/// Retrieve a reference to alarm 0. Will only return a value the first time this is called
|
||||
pub fn alarm_0(&mut self) -> Option<Alarm0> {
|
||||
take_alarm(1 << 0).then_some(Alarm0(PhantomData))
|
||||
}
|
||||
|
||||
/// Retrieve a reference to alarm 1. Will only return a value the first time this is called
|
||||
pub fn alarm_1(&mut self) -> Option<Alarm1> {
|
||||
take_alarm(1 << 1).then_some(Alarm1(PhantomData))
|
||||
}
|
||||
|
||||
/// Retrieve a reference to alarm 2. Will only return a value the first time this is called
|
||||
pub fn alarm_2(&mut self) -> Option<Alarm2> {
|
||||
take_alarm(1 << 2).then_some(Alarm2(PhantomData))
|
||||
}
|
||||
|
||||
/// Retrieve a reference to alarm 3. Will only return a value the first time this is called
|
||||
pub fn alarm_3(&mut self) -> Option<Alarm3> {
|
||||
take_alarm(1 << 3).then_some(Alarm3(PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
// safety: all write operations are synchronised and all reads are atomic
|
||||
unsafe impl Sync for Timer {}
|
||||
|
||||
/// Implementation of the embedded_hal::Timer traits using rp2040_hal::timer counter
|
||||
///
|
||||
/// ## Usage
|
||||
/// ```no_run
|
||||
/// use embedded_hal::timer::{CountDown, Cancel};
|
||||
/// use fugit::ExtU32;
|
||||
/// use rp2040_hal;
|
||||
/// let mut pac = rp2040_hal::pac::Peripherals::take().unwrap();
|
||||
/// // Configure the Timer peripheral in count-down mode
|
||||
/// let timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS);
|
||||
/// let mut count_down = timer.count_down();
|
||||
/// // Create a count_down timer for 500 milliseconds
|
||||
/// count_down.start(500.millis());
|
||||
/// // Block until timer has elapsed
|
||||
/// let _ = nb::block!(count_down.wait());
|
||||
/// // Restart the count_down timer with a period of 100 milliseconds
|
||||
/// count_down.start(100.millis());
|
||||
/// // Cancel it immediately
|
||||
/// count_down.cancel();
|
||||
/// ```
|
||||
pub struct CountDown<'timer> {
|
||||
timer: &'timer Timer,
|
||||
period: MicrosDurationU64,
|
||||
next_end: Option<u64>,
|
||||
}
|
||||
|
||||
impl embedded_hal::timer::CountDown for CountDown<'_> {
|
||||
type Time = MicrosDurationU64;
|
||||
|
||||
fn start<T>(&mut self, count: T)
|
||||
where
|
||||
T: Into<Self::Time>,
|
||||
{
|
||||
self.period = count.into();
|
||||
self.next_end = Some(
|
||||
self.timer
|
||||
.get_counter()
|
||||
.ticks()
|
||||
.wrapping_add(self.period.to_micros()),
|
||||
);
|
||||
}
|
||||
|
||||
fn wait(&mut self) -> nb::Result<(), void::Void> {
|
||||
if let Some(end) = self.next_end {
|
||||
let ts = self.timer.get_counter().ticks();
|
||||
if ts >= end {
|
||||
self.next_end = Some(end.wrapping_add(self.period.to_micros()));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
} else {
|
||||
panic!("CountDown is not running!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::timer::Periodic for CountDown<'_> {}
|
||||
|
||||
impl embedded_hal::timer::Cancel for CountDown<'_> {
|
||||
type Error = &'static str;
|
||||
|
||||
fn cancel(&mut self) -> Result<(), Self::Error> {
|
||||
if self.next_end.is_none() {
|
||||
Err("CountDown is not running.")
|
||||
} else {
|
||||
self.next_end = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Alarm abstraction.
|
||||
pub trait Alarm {
|
||||
/// Clear the interrupt flag.
|
||||
///
|
||||
/// The interrupt is unable to trigger a 2nd time until this interrupt is cleared.
|
||||
fn clear_interrupt(&mut self);
|
||||
|
||||
/// Enable this alarm to trigger an interrupt.
|
||||
///
|
||||
/// After this interrupt is triggered, make sure to clear the interrupt with [clear_interrupt].
|
||||
///
|
||||
/// [clear_interrupt]: #method.clear_interrupt
|
||||
fn enable_interrupt(&mut self);
|
||||
|
||||
/// Disable this alarm, preventing it from triggering an interrupt.
|
||||
fn disable_interrupt(&mut self);
|
||||
|
||||
/// Schedule the alarm to be finished after `countdown`. If [enable_interrupt] is called,
|
||||
/// this will trigger interrupt whenever this time elapses.
|
||||
///
|
||||
/// [enable_interrupt]: #method.enable_interrupt
|
||||
fn schedule(&mut self, countdown: MicrosDurationU32) -> Result<(), ScheduleAlarmError>;
|
||||
|
||||
/// Schedule the alarm to be finished at the given timestamp. If [enable_interrupt] is
|
||||
/// called, this will trigger interrupt whenever this timestamp is reached.
|
||||
///
|
||||
/// The RP2040 is unable to schedule an event taking place in more than
|
||||
/// `u32::max_value()` microseconds.
|
||||
///
|
||||
/// [enable_interrupt]: #method.enable_interrupt
|
||||
fn schedule_at(&mut self, timestamp: Instant) -> Result<(), ScheduleAlarmError>;
|
||||
|
||||
/// Return true if this alarm is finished.
|
||||
fn finished(&self) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! impl_alarm {
|
||||
($name:ident { rb: $timer_alarm:ident, int: $int_alarm:ident, int_name: $int_name:tt, armed_bit_mask: $armed_bit_mask: expr }) => {
|
||||
/// An alarm that can be used to schedule events in the future. Alarms can also be configured to trigger interrupts.
|
||||
pub struct $name(PhantomData<()>);
|
||||
impl $name {
|
||||
fn schedule_internal(
|
||||
&mut self,
|
||||
timer: &crate::pac::timer::RegisterBlock,
|
||||
timestamp: Instant,
|
||||
) -> Result<(), ScheduleAlarmError> {
|
||||
let timestamp_low = (timestamp.ticks() & 0xFFFF_FFFF) as u32;
|
||||
|
||||
// This lock is for time-criticality
|
||||
cortex_m::interrupt::free(|_| {
|
||||
let alarm = &timer.$timer_alarm;
|
||||
|
||||
// safety: This is the only code in the codebase that accesses memory address $timer_alarm
|
||||
alarm.write(|w| unsafe { w.bits(timestamp_low) });
|
||||
|
||||
// If it is not set, it has already triggered.
|
||||
let now = get_counter(timer);
|
||||
if now > timestamp && (timer.armed.read().bits() & $armed_bit_mask) != 0 {
|
||||
// timestamp was set in the past
|
||||
|
||||
// safety: TIMER.armed is a write-clear register, and there can only be
|
||||
// 1 instance of AlarmN so we can safely atomically clear this bit.
|
||||
unsafe {
|
||||
timer.armed.write_with_zero(|w| w.bits($armed_bit_mask));
|
||||
crate::atomic_register_access::write_bitmask_set(
|
||||
timer.intf.as_ptr(),
|
||||
$armed_bit_mask,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Alarm for $name {
|
||||
/// Clear the interrupt flag. This should be called after interrupt `
|
||||
#[doc = $int_name]
|
||||
/// ` is called.
|
||||
///
|
||||
/// The interrupt is unable to trigger a 2nd time until this interrupt is cleared.
|
||||
fn clear_interrupt(&mut self) {
|
||||
// safety: TIMER.intr is a write-clear register, so we can atomically clear our interrupt
|
||||
// by writing its value to this field
|
||||
// Only one instance of this alarm index can exist, and only this alarm interacts with this bit
|
||||
// of the TIMER.inte register
|
||||
unsafe {
|
||||
let timer = &(*pac::TIMER::ptr());
|
||||
crate::atomic_register_access::write_bitmask_clear(
|
||||
timer.intf.as_ptr(),
|
||||
$armed_bit_mask,
|
||||
);
|
||||
timer.intr.write_with_zero(|w| w.$int_alarm().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable this alarm to trigger an interrupt. This alarm will trigger `
|
||||
#[doc = $int_name]
|
||||
/// `.
|
||||
///
|
||||
/// After this interrupt is triggered, make sure to clear the interrupt with [clear_interrupt].
|
||||
///
|
||||
/// [clear_interrupt]: #method.clear_interrupt
|
||||
fn enable_interrupt(&mut self) {
|
||||
// safety: using the atomic set alias means we can atomically set our interrupt enable bit.
|
||||
// Only one instance of this alarm can exist, and only this alarm interacts with this bit
|
||||
// of the TIMER.inte register
|
||||
unsafe {
|
||||
let timer = &(*pac::TIMER::ptr());
|
||||
let reg = (&timer.inte).as_ptr();
|
||||
write_bitmask_set(reg, $armed_bit_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable this alarm, preventing it from triggering an interrupt.
|
||||
fn disable_interrupt(&mut self) {
|
||||
// safety: using the atomic set alias means we can atomically clear our interrupt enable bit.
|
||||
// Only one instance of this alarm can exist, and only this alarm interacts with this bit
|
||||
// of the TIMER.inte register
|
||||
unsafe {
|
||||
let timer = &(*pac::TIMER::ptr());
|
||||
let reg = (&timer.inte).as_ptr();
|
||||
write_bitmask_clear(reg, $armed_bit_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule the alarm to be finished after `countdown`. If [enable_interrupt] is called,
|
||||
/// this will trigger interrupt `
|
||||
#[doc = $int_name]
|
||||
/// ` whenever this time elapses.
|
||||
///
|
||||
/// [enable_interrupt]: #method.enable_interrupt
|
||||
fn schedule(&mut self, countdown: MicrosDurationU32) -> Result<(), ScheduleAlarmError> {
|
||||
// safety: Only read operations are made on the timer and they should not have any UB
|
||||
let timer = unsafe { &*TIMER::ptr() };
|
||||
let timestamp = get_counter(timer) + countdown;
|
||||
|
||||
self.schedule_internal(timer, timestamp)
|
||||
}
|
||||
|
||||
/// Schedule the alarm to be finished at the given timestamp. If [enable_interrupt] is
|
||||
/// called, this will trigger interrupt `
|
||||
#[doc = $int_name]
|
||||
/// ` whenever this timestamp is reached.
|
||||
///
|
||||
/// The RP2040 is unable to schedule an event taking place in more than
|
||||
/// `u32::max_value()` microseconds.
|
||||
///
|
||||
/// [enable_interrupt]: #method.enable_interrupt
|
||||
fn schedule_at(&mut self, timestamp: Instant) -> Result<(), ScheduleAlarmError> {
|
||||
// safety: Only read operations are made on the timer and they should not have any UB
|
||||
let timer = unsafe { &*TIMER::ptr() };
|
||||
let now = get_counter(timer);
|
||||
let duration = timestamp.ticks().saturating_sub(now.ticks());
|
||||
if duration > u32::max_value().into() {
|
||||
return Err(ScheduleAlarmError::AlarmTooLate);
|
||||
}
|
||||
|
||||
self.schedule_internal(timer, timestamp)
|
||||
}
|
||||
|
||||
/// Return true if this alarm is finished.
|
||||
fn finished(&self) -> bool {
|
||||
// safety: This is a read action and should not have any UB
|
||||
let bits: u32 = unsafe { &*TIMER::ptr() }.armed.read().bits();
|
||||
(bits & $armed_bit_mask) == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for $name {
|
||||
fn drop(&mut self) {
|
||||
self.disable_interrupt();
|
||||
release_alarm($armed_bit_mask)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Errors that can be returned from any of the `AlarmX::schedule` methods.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ScheduleAlarmError {
|
||||
/// Alarm time is too high. Should not be more than `u32::max_value()` in the future.
|
||||
AlarmTooLate,
|
||||
}
|
||||
|
||||
impl_alarm!(Alarm0 {
|
||||
rb: alarm0,
|
||||
int: alarm_0,
|
||||
int_name: "TIMER_IRQ_0",
|
||||
armed_bit_mask: 0b0001
|
||||
});
|
||||
|
||||
impl_alarm!(Alarm1 {
|
||||
rb: alarm1,
|
||||
int: alarm_1,
|
||||
int_name: "TIMER_IRQ_1",
|
||||
armed_bit_mask: 0b0010
|
||||
});
|
||||
|
||||
impl_alarm!(Alarm2 {
|
||||
rb: alarm2,
|
||||
int: alarm_2,
|
||||
int_name: "TIMER_IRQ_2",
|
||||
armed_bit_mask: 0b0100
|
||||
});
|
||||
|
||||
impl_alarm!(Alarm3 {
|
||||
rb: alarm3,
|
||||
int: alarm_3,
|
||||
int_name: "TIMER_IRQ_3",
|
||||
armed_bit_mask: 0b1000
|
||||
});
|
||||
|
||||
/// Support for RTIC monotonic trait.
|
||||
#[cfg(feature = "rtic-monotonic")]
|
||||
pub mod monotonic {
|
||||
use super::{Alarm, Instant, Timer};
|
||||
use fugit::ExtU32;
|
||||
|
||||
/// RTIC Monotonic Implementation
|
||||
pub struct Monotonic<A>(pub Timer, A);
|
||||
impl<A: Alarm> Monotonic<A> {
|
||||
/// Creates a new monotonic.
|
||||
pub const fn new(timer: Timer, alarm: A) -> Self {
|
||||
Self(timer, alarm)
|
||||
}
|
||||
}
|
||||
impl<A: Alarm> rtic_monotonic::Monotonic for Monotonic<A> {
|
||||
type Instant = Instant;
|
||||
type Duration = fugit::MicrosDurationU64;
|
||||
|
||||
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false;
|
||||
|
||||
fn now(&mut self) -> Instant {
|
||||
self.0.get_counter()
|
||||
}
|
||||
|
||||
fn set_compare(&mut self, instant: Instant) {
|
||||
// The alarm can only trigger up to 2^32 - 1 ticks in the future.
|
||||
// So, if `instant` is more than 2^32 - 2 in the future, we use `max_instant` instead.
|
||||
let max_instant = self.0.get_counter() + 0xFFFF_FFFE.micros();
|
||||
let wake_at = core::cmp::min(instant, max_instant);
|
||||
|
||||
// Cannot fail
|
||||
let _ = self.1.schedule_at(wake_at);
|
||||
self.1.enable_interrupt();
|
||||
}
|
||||
|
||||
fn clear_compare_flag(&mut self) {
|
||||
self.1.clear_interrupt();
|
||||
}
|
||||
|
||||
fn zero() -> Self::Instant {
|
||||
Instant::from_ticks(0)
|
||||
}
|
||||
|
||||
unsafe fn reset(&mut self) {}
|
||||
}
|
||||
}
|
|
@ -1,697 +0,0 @@
|
|||
//! Module supporting type-level programming
|
||||
//!
|
||||
//! Copied from [atsamd-hal](https://github.com/atsamd-rs/atsamd).
|
||||
//!
|
||||
//! # Introduction
|
||||
//!
|
||||
//! Embedded software is often difficult to debug, so there is a strong
|
||||
//! motivation to catch as many bugs as possible at compile-time. However, the
|
||||
//! performance requirements of embedded software also make it difficult to
|
||||
//! justify changes that impose additional overhead in terms of size or speed.
|
||||
//! Ideally, we would like to add as many compile-time checks as possible, while
|
||||
//! also producing the fewest possible assembly instructions.
|
||||
//!
|
||||
//! The Rust type system can help accomplish this goal. By expressing software
|
||||
//! constraints within the type system, developers can enforce invariants at
|
||||
//! compile-time.
|
||||
//!
|
||||
//! Sometimes this is done using Rust macros. However, that approach can produce
|
||||
//! code that is difficult to read and understand. Moreover, macro-generated
|
||||
//! code can only be extended by *more* macros, which further spreads the
|
||||
//! problem. In `atsamd-hal` specifically, issue
|
||||
//! [#214](https://github.com/atsamd-rs/atsamd/issues/214) discussed the extent
|
||||
//! to which macros were once used in the repository.
|
||||
//!
|
||||
//! Alternatively, many of the same goals can be accomplished with the Rust
|
||||
//! type & trait system directly, which is quite powerful. In fact, it is
|
||||
//! [turing complete](https://sdleffler.github.io/RustTypeSystemTuringComplete/).
|
||||
//! By expressing our invariants entirely within the type system, we can encode
|
||||
//! the desired compile-time checks in a form that is easier to read, understand
|
||||
//! and document.
|
||||
//!
|
||||
//! This module documents some of the type-level programming techniques used
|
||||
//! throughout this HAL, and it contains a few items used to implement them.
|
||||
//!
|
||||
//! ## Contents
|
||||
//!
|
||||
//! - [Basics of type-level programming](#basics-of-type-level-programming)
|
||||
//! - [Type-level enums](#type-level-enums)
|
||||
//! - [Type classes](#type-classes)
|
||||
//! - [Type-level containers](#type-level-containers)
|
||||
//! - [Type-level functions](#type-level-functions)
|
||||
//! - [`OptionalKind` trait pattern](#optionalkind-trait-pattern)
|
||||
//! - [`AnyKind` trait pattern](#anykind-trait-pattern)
|
||||
//! - [Defining an `AnyKind` trait](#defining-an-anykind-trait)
|
||||
//! - [Using an `AnyKind` trait](#using-an-anykind-trait)
|
||||
//!
|
||||
//! # Basics of type-level programming
|
||||
//!
|
||||
//! Type-level programming aims to execute a form of compile-time computation.
|
||||
//! But to perform such computation, we need to map our traditional notions of
|
||||
//! programming to the Rust type system.
|
||||
//!
|
||||
//! In normal Rust, individual values are grouped or categorized into types. For
|
||||
//! example, `0`, `1`, `2`, etc. are all members of the `usize` type. Similarly,
|
||||
//! `Enum::A` and `Enum::B` are members of the `Enum` type, defined as
|
||||
//!
|
||||
//! ```ignore
|
||||
//! enum Enum { A, B }
|
||||
//! ```
|
||||
//!
|
||||
//! We use composite types and containers to create more complex data
|
||||
//! structures, and we use functions to map between values.
|
||||
//!
|
||||
//! All of these concepts can also be expressed within the Rust type system.
|
||||
//! However, in this case, types are grouped and categorized into traits. For
|
||||
//! instance, the [`typenum`](https://docs.rs/typenum/1.13.0/typenum/index.html)
|
||||
//! crate provides the types `U0`, `U1`, `U2`, etc., which are all members of
|
||||
//! the `Unsigned` trait. Similarly, the following sections will illustrate how
|
||||
//! to define type-level enums, containers and functions.
|
||||
//!
|
||||
//! ## Type-level enums
|
||||
//!
|
||||
//! Type-level enums are one of the foundational concepts of type-level
|
||||
//! programming used in this HAL.
|
||||
//!
|
||||
//! At the value-level, a typical Rust enum represents some set of variants that
|
||||
//! can be assigned to a particular variable. Similarly, a type-level enum
|
||||
//! represents some set of types that can be assigned to a particular type
|
||||
//! parameter.
|
||||
//!
|
||||
//! To lift an enum from the value level to the type level, you typically map
|
||||
//! the enum variants to types and the enum itself to a trait. For instance, the
|
||||
//! value-level enum
|
||||
//!
|
||||
//! ```ignore
|
||||
//! enum Enum {
|
||||
//! A,
|
||||
//! B,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! would be mapped to the type level like so.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait Enum {}
|
||||
//!
|
||||
//! enum A {}
|
||||
//! enum B {}
|
||||
//!
|
||||
//! impl Enum for A {}
|
||||
//! impl Enum for B {}
|
||||
//! ```
|
||||
//!
|
||||
//! At the value level, the variants `A` and `B` are grouped by the `Enum` type,
|
||||
//! while at the type level, the types `A` and `B` are grouped by the `Enum`
|
||||
//! trait.
|
||||
//!
|
||||
//! ## Type classes
|
||||
//!
|
||||
//! At the value-level, a type restricts the possible values that can be taken
|
||||
//! by some free variable. While at the type-level, a trait bound restricts the
|
||||
//! possible types that can be taken by some free type parameter. In effect,
|
||||
//! trait bounds can be used to create a kind of meta-type, or type class. The
|
||||
//! type-level enums in the previous section represent the most primitive
|
||||
//! application of the concept, but type classes can take other forms. The
|
||||
//! `OptionalKind` and `AnyKind` trait patterns discussed below are more
|
||||
//! advanced applications of the same concept.
|
||||
//!
|
||||
//! ## Type-level containers
|
||||
//!
|
||||
//! To represent more complex relationships, we need a way to form composite
|
||||
//! data structures at the type level.
|
||||
//!
|
||||
//! At the value level, a container holds an instance of a particular type. The
|
||||
//! exact value of that instance is usually not known to the author, it is only
|
||||
//! known at run-time.
|
||||
//!
|
||||
//! At the type level, we don't have the same notion of "run-time", but we do
|
||||
//! have two different notions of "compile-time" that form a similar
|
||||
//! relationship. There is compile time for the HAL authors, and there is a
|
||||
//! separate compile-time for the HAL users. We want to create a type-level
|
||||
//! container where the exact type is not known at author-time, but it is known
|
||||
//! at user-time.
|
||||
//!
|
||||
//! For example, take the following, value-level container struct. It contains
|
||||
//! two fields, `a` and `b`, of different types, `EnumOne` and `EnumTwo`.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct Container {
|
||||
//! a: EnumOne,
|
||||
//! b: EnumTwo,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! We can create an instance of this container with specific values.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! let x = Container { a: EnumOne::VariantX, b: EnumTwo::VariantY };
|
||||
//! ```
|
||||
//!
|
||||
//! Next, suppose we had already translated `EnumOne` and `EnumTwo` to the type
|
||||
//! level using the technique in the previous section. If we wanted to create a
|
||||
//! similar, composite data structure at the type level, we could use type
|
||||
//! parameters in place of struct fields to represent the unknown types.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct Container<A, B>
|
||||
//! where
|
||||
//! A: EnumOne,
|
||||
//! B: EnumTwo,
|
||||
//! {
|
||||
//! a: PhantomData<A>,
|
||||
//! b: PhantomData<B>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And we could create an instance of this container with specific types.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! type X = Container<VariantX, VariantY>;
|
||||
//! ```
|
||||
//!
|
||||
//! You might notice the use of `PhantomData` in the definition of the
|
||||
//! type-level container. Because it is geared more toward value-level
|
||||
//! programming, Rust requires all type parameters actually be used by the
|
||||
//! corresponding type. However, we don't need to "store" a type in the same way
|
||||
//! we store values. The compiler is responsible for tracking the concrete type
|
||||
//! for each type parameter. But the language still requires us to act as if we
|
||||
//! used each type parameter. `PhantomData` is the solution here, because it
|
||||
//! lets us make use of the type parameters without actually storing any values.
|
||||
//!
|
||||
//! Separately, `PhantomData` also allows us to create "instances" of types that
|
||||
//! normally can't be instantiated, like empty enums. For example, instances of
|
||||
//! `Enum` below can never exist directly.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! enum Enum {}
|
||||
//! ```
|
||||
//!
|
||||
//! But instances of `PhantomData<Enum>` are perfectly valid. In this way,
|
||||
//! library authors can create types that only exist at the type level, which
|
||||
//! can sometimes simplify a design.
|
||||
//!
|
||||
//! ## Type-level functions
|
||||
//!
|
||||
//! To perform type-level computations, we need some way to map or transform
|
||||
//! types into other types.
|
||||
//!
|
||||
//! At the value level, functions and methods map values of the input types to
|
||||
//! values of the output types. The same can be accomplished at the type level
|
||||
//! using traits and associated types. Type-level functions are implemented as
|
||||
//! traits, where the implementing type and any type parameters are the inputs,
|
||||
//! and associated types are the outputs.
|
||||
//!
|
||||
//! For example, consider the value level `not` method below.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! enum Bool {
|
||||
//! False,
|
||||
//! True,
|
||||
//! }
|
||||
//!
|
||||
//! impl Bool {
|
||||
//! fn not(self) -> Self {
|
||||
//! use Bool::*;
|
||||
//! match self {
|
||||
//! True => False,
|
||||
//! False => True,
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! We can translate this example to the type level like so.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait Bool {}
|
||||
//!
|
||||
//! enum True {}
|
||||
//! enum False {}
|
||||
//!
|
||||
//! impl Bool for True {}
|
||||
//! impl Bool for False {}
|
||||
//!
|
||||
//! trait Not: Bool {
|
||||
//! type Result: Bool;
|
||||
//! }
|
||||
//!
|
||||
//! impl Not for True {
|
||||
//! type Result = False;
|
||||
//! }
|
||||
//!
|
||||
//! impl Not for False {
|
||||
//! type Result = True;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! We can use the `Not` trait bound to transform one type to another. For
|
||||
//! instance, we can create a container that accepts one type parameter but
|
||||
//! stores a different one.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct Container<B: Not> {
|
||||
//! not: PhantomData<B::Result>;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Alternatively, we could redefine the trait and declar a corresponding type
|
||||
//! alias as
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait NotFunction: Bool {
|
||||
//! type Result: Bool;
|
||||
//! }
|
||||
//!
|
||||
//! type Not<B> = <B as NotFunction>::Result;
|
||||
//! ```
|
||||
//!
|
||||
//! Doing so would allow us to us reframe the last example as
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct Container<B: NotFunction> {
|
||||
//! not: PhantomData<Not<B>>;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Type-level functions can be more complicated than this example, but they
|
||||
//! ultimately represent a mapping from a set of input types (the implementing
|
||||
//! type and any type parameters) to a set of output types (the associated
|
||||
//! types).
|
||||
//!
|
||||
//! # `OptionalKind` trait pattern
|
||||
//!
|
||||
//! As mentioned above, traits can be used to define a kind of meta-type or type
|
||||
//! class, essentially forming a set of valid types for a given type parameter.
|
||||
//! They also represent the concept of types lifted from the value level to the
|
||||
//! type level.
|
||||
//!
|
||||
//! What if we want to define a type class representing either a set of useful
|
||||
//! types or some useless, null type? Essentially, how do we take the notion of
|
||||
//! an [`Option`] type and raise it to the type level?
|
||||
//!
|
||||
//! Suppose we have some existing type class, defined by the `Class` trait, that
|
||||
//! we want to make optional. We can define a new type class that includes all
|
||||
//! instances of `Class` as well as some null type. For the latter we use
|
||||
//! [`NoneT`], defined in this module.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait OptionalClass {}
|
||||
//!
|
||||
//! impl OptionalClass for NoneT {}
|
||||
//! impl<C: Class> OptionalClass for C {}
|
||||
//! ```
|
||||
//!
|
||||
//! We can use this new type class to store an optional instance of a `Class`
|
||||
//! type in a struct.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct Container<C: OptionalClass> {
|
||||
//! class: PhantomData<C>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And we can restrict some of its methods to only operate on instances with a
|
||||
//! valid `Class`.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<C: Class> Container<C> {
|
||||
//! fn method(self) { ... }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Although it is not strictly necessary, we can also introduce a new type
|
||||
//! class to differentiate the bare usage of `Class` from instances of some
|
||||
//! `Class` where an `OptionalClass` is accepted.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait SomeClass: OptionalClass + Class {}
|
||||
//!
|
||||
//! impl<C: Class> SomeClass for C {}
|
||||
//! ```
|
||||
//!
|
||||
//! This new trait doesn't add any new information, but it can still help
|
||||
//! readers understand that a particular type parameter is restricted to an
|
||||
//! instances of `Class` when an `OptionalClass` could be accepted.
|
||||
//!
|
||||
//! Note that when `Class` and `OptionalClass` contain associated types, name
|
||||
//! clashes may occur when using `SomeClass` as a trait bound. This can be
|
||||
//! avoided by removing the `OptionalClass` super trait from `SomeClass`.
|
||||
//! Ultimately, it is redundant anyway, because any implementer of `Class` also
|
||||
//! implements `OptionalClass`.
|
||||
//!
|
||||
//! # `AnyKind` trait pattern
|
||||
//!
|
||||
//! The `AnyKind` trait pattern allows you to encapsulate types with multiple
|
||||
//! type parameters and represent them with only a single type parameter. It
|
||||
//! lets you introduce a layer of abstraction, which can simplify interfaces and
|
||||
//! make them more readable. But most of all, it does so without sacrificing any
|
||||
//! of our normal, type-level abilities.
|
||||
//!
|
||||
//! ## Defining an `AnyKind` trait
|
||||
//!
|
||||
//! Suppose you had a composite, type-level data structure. For example, the
|
||||
//! GPIO `Pin` struct contains instances of two type-level enums, a `PinId` and
|
||||
//! a `PinMode`. It looks something like this.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct Pin<I: PinId, M: PinMode> {
|
||||
//! // ...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Rust does not provide any way to speak about a `Pin` generally. Any mention
|
||||
//! of the `Pin` type must also include its type parameters, i.e. `Pin<I, M>`.
|
||||
//! This is not a deal-breaker, but it is less than ideal for type-level
|
||||
//! programming. It would be nice if there were a way to succinctly refer to any
|
||||
//! `Pin`, regardless of its type parameters.
|
||||
//!
|
||||
//! We've seen above that we can use traits to form a type class. What if we
|
||||
//! were to introduce a new trait to label all instances of `Pin`? It would look
|
||||
//! something like this.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait AnyPin {}
|
||||
//!
|
||||
//! impl<I: PinId, M: PinMode> AnyPin for Pin<I, M> {}
|
||||
//! ```
|
||||
//!
|
||||
//! Now, instead of refering to `Pin<I, M>`, we can refer to instances of the
|
||||
//! `AnyPin` type class.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! fn example<P: AnyPin>(pin: P) { ... }
|
||||
//! ```
|
||||
//!
|
||||
//! Unfortunately, while this is more ergonomic, it is not very useful. As
|
||||
//! authors of the code, we know that `AnyPin` is only implemented for `Pin`
|
||||
//! types. But the compiler doesn't know that. Traits in Rust are open, so the
|
||||
//! compiler must consider that `AnyPin` could be implemented for other types.
|
||||
//!
|
||||
//! As a consequence, the compiler knows very little about the type `P` in the
|
||||
//! function above. In fact, because the `AnyPin` trait is completely empty, the
|
||||
//! compiler knows *absolutely nothing* about the type `P`.
|
||||
//!
|
||||
//! Is there a way to make the `AnyPin` trait more useful? We can see from the
|
||||
//! current implementation that we are throwing away information.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<I: PinId, M: PinMode> AnyPin for Pin<I, M> {}
|
||||
//! ```
|
||||
//!
|
||||
//! The implementation of `AnyPin` is identical for every `Pin`, regardless of
|
||||
//! the type parameters `I` and `M`, which erases that information. Instead, we
|
||||
//! could choose to save that information in the form of associated types.
|
||||
//!
|
||||
//! Let's redesign the `AnyPin` trait to record the `PinId` and `PinMode`.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait AnyPin {
|
||||
//! type Id: PinId;
|
||||
//! type Mode: PinMode;
|
||||
//! }
|
||||
//!
|
||||
//! impl<I: PinId, M: PinMode> AnyPin for Pin<I, M> {
|
||||
//! type Id = I;
|
||||
//! type Mode = M;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This is better. When `P` implements `AnyPin`, we can at least recover the
|
||||
//! corresponding `PinId` and `PinMode` types. However, `AnyPin` still doesn't
|
||||
//! include any trait methods nor any super traits, so the compiler won't allow
|
||||
//! us to do anything useful with an instances of `P`.
|
||||
//!
|
||||
//! We need some way to tell the compiler that when `P` implements `AnyPin`,
|
||||
//! it is equivalent to saying `P` is exactly `Pin<P::Id, P::Mode>`.
|
||||
//! Essentially, we want to take a generic type parameter `P` and treat it as if
|
||||
//! it were an instance of a specific `Pin` type.
|
||||
//!
|
||||
//! We can start by defining a trait alias to recover the specific `Pin` type.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
|
||||
//! ```
|
||||
//!
|
||||
//! With this new definition, we can rephrase our statement above. We need some
|
||||
//! way to tell the compiler that when `P` implements `AnyPin`,
|
||||
//! `P == SpecificPin<P>`. There's no way to do that exactly, but we can come
|
||||
//! close with some useful trait bounds: [`From`], [`Into`], [`AsRef`] and
|
||||
//! [`AsMut`].
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait AnyPin
|
||||
//! where
|
||||
//! Self: From<SpecificPin<Self>>,
|
||||
//! Self: Into<SpecificPin<Self>>,
|
||||
//! Self: AsRef<SpecificPin<Self>>,
|
||||
//! Self: AsMut<SpecificPin<Self>>,
|
||||
//! {
|
||||
//! type Id: PinId;
|
||||
//! type Mode: PinMode;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Now we've given the compiler some useful information. When a type implements
|
||||
//! `AnyPin`, it can be converted from and into instances of `Pin`. And
|
||||
//! references to types that implement `AnyPin` can be converted into references
|
||||
//! to `Pin`s.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! fn example<P: AnyPin>(mut any_pin: P) {
|
||||
//! // None of the type annotations here are necessary
|
||||
//! // Everything can be inferred
|
||||
//! // Remember that SpecificPin<P> is Pin<P::Id, P::Mode>
|
||||
//! let pin_mut: &mut SpecificPin<P> = any_pin.as_mut();
|
||||
//! let pin_ref: &SpecificPin<P> = any_pin.as_ref();
|
||||
//! let pin: SpecificPin<P> = any_pin.into();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Finally, to simplify this pattern, we can gather all of the super trait
|
||||
//! bounds into a single, reusable trait.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait Is
|
||||
//! where
|
||||
//! Self: From<IsType<Self>>,
|
||||
//! Self: Into<IsType<Self>>,
|
||||
//! Self: AsRef<IsType<Self>>,
|
||||
//! Self: AsMut<IsType<Self>>,
|
||||
//! {
|
||||
//! type Type;
|
||||
//! }
|
||||
//!
|
||||
//! type IsType<T> = <T as Is>::Type;
|
||||
//!
|
||||
//! impl<T: AsRef<T> + AsMut<T>> Is for T {
|
||||
//! type Type = T;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And we can rewrite our `AnyPin` trait as
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait AnyPin: Is<Type = SpecificPin<Self>> {
|
||||
//! type Id: PinId;
|
||||
//! type Mode: PinMode;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Using an `AnyKind` trait
|
||||
//!
|
||||
//! If a type takes multiple type parameters, storing it within a container
|
||||
//! requires repeating all of the corresponding type parameters. For instance,
|
||||
//! imagine a container that stores two completely generic `Pin` types.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct TwoPins<I1, I2, M1, M2>
|
||||
//! where
|
||||
//! I1: PinId,
|
||||
//! I2: PinId,
|
||||
//! M1: PinMode,
|
||||
//! M2: PinMode,
|
||||
//! {
|
||||
//! pin1: Pin<I1, M1>,
|
||||
//! pin2: Pin<I2, M2>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This struct has already ballooned to four type parameters, without even
|
||||
//! doing much useful work. Given its heavy use of type parameters, this
|
||||
//! limitation can make type-level programming tedious, cumbersome and
|
||||
//! error-prone.
|
||||
//!
|
||||
//! Instead, we can use the `AnyKind` trait pattern to encapsulate each `Pin`
|
||||
//! with a single type parameter.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct TwoPins<P1, P2>
|
||||
//! where
|
||||
//! P1: AnyPin,
|
||||
//! P2: AnyPin,
|
||||
//! {
|
||||
//! pin1: P1,
|
||||
//! pin2: P2,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The result is far more readable and generally more comprehensible. Moreover,
|
||||
//! although we no longer have direct access to the `PinId` and `PinMode` type
|
||||
//! parameters, we haven't actually lost any expressive power.
|
||||
//!
|
||||
//! In the first version of `TwoPins`, suppose we wanted to implement a method
|
||||
//! for pins in `FloatingInput` mode while simultaneously restricting the
|
||||
//! possible `PinId`s based on some type class. The result might look like
|
||||
//! this.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<I1, I2> for TwoPins<I1, I2, FloatingInput, FloatingInput>
|
||||
//! where
|
||||
//! I1: PinId + Class,
|
||||
//! I2: PinId + Class,
|
||||
//! {
|
||||
//! fn method(&self) {
|
||||
//! // ...
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The same method could be expressed with the `AnyPin` approach like so
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<P1, P2> for TwoPins<P1, P2>
|
||||
//! where
|
||||
//! P1: AnyPin<Mode = FloatingInput>,
|
||||
//! P2: AnyPin<Mode = FloatingInput>,
|
||||
//! P1::Id: Class,
|
||||
//! P2::Id: Class,
|
||||
//! {
|
||||
//! fn method(&self) {
|
||||
//! // ...
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This example demonstrates the simultaneous readability and expressive power
|
||||
//! of the `AnyKind` pattern.
|
||||
//!
|
||||
//! However, remember that when working with a type `P` that implements
|
||||
//! `AnyPin`, the compiler can only use what it knows about the `AnyPin` trait.
|
||||
//! But all of the functionality for GPIO pins is defined on the `Pin` type. To
|
||||
//! make use of a generic type `P` implementing `AnyPin`, you must first convert
|
||||
//! it to its corresponding `SpecificPin` using [`Into`], [`AsRef`] or
|
||||
//! [`AsMut`]. And, in some instances, you may also need to convert back to the
|
||||
//! type `P`.
|
||||
//!
|
||||
//! Suppose you wanted to store a completely generic `Pin` within a struct.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! pub struct Example<P: AnyPin> {
|
||||
//! pin: P,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Next, suppose you want to create a method that would take the `Pin` out of
|
||||
//! the struct, perform some operations in different `PinMode`s, and put it back
|
||||
//! into the struct before returning. The `elided` method below shows such an
|
||||
//! example. However, it can be a bit tricky to follow all of the type
|
||||
//! conversions here. For clarity, the `expanded` method shows the same behavior
|
||||
//! with each transformation given its proper type annotation.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<P: AnyPin> Example<P> {
|
||||
//! pub fn elided(mut self) -> Self {
|
||||
//! let pin = self.pin.into();
|
||||
//! let mut pin = pin.into_push_pull_output();
|
||||
//! pin.set_high().ok();
|
||||
//! let pin = pin.into_floating_input();
|
||||
//! let _bit = pin.is_low().unwrap();
|
||||
//! let pin = pin.into_mode();
|
||||
//! self.pin = pin.into();
|
||||
//! self
|
||||
//! }
|
||||
//! pub fn expanded(mut self) -> Self {
|
||||
//! let pin: SpecificPin<P> = self.pin.into();
|
||||
//! let mut pin: Pin<P::Id, PushPullOutput> = pin.into_push_pull_output();
|
||||
//! pin.set_high().ok();
|
||||
//! let pin: Pin<P::Id, FloatingInput> = pin.into_floating_input();
|
||||
//! let _bit = pin.is_low().unwrap();
|
||||
//! let pin: SpecificPin<P> = pin.into_mode::<P::Mode>();
|
||||
//! self.pin = pin.into();
|
||||
//! self
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Notice that it is not enough to simply put back the correct `SpecificPin`.
|
||||
//! Even though the `SpecificPin` implements
|
||||
//! `AnyPin<Id = P::Id, Mode = P::Mode>` the compiler doesn't understand that
|
||||
//! `SpecificPin<P> == P` for all `P`. As far as the compiler is concerned,
|
||||
//! there could be several different types that implement
|
||||
//! `AnyPin<Id = P::Id, Mode = P::Mode>`. Instead, the compiler requires that
|
||||
//! you put back an instance of `P` exactly. The final use of [`Into`] is key
|
||||
//! here. It transforms the `SpecificPin` back into `P` itself.
|
||||
|
||||
mod private {
|
||||
/// Super trait used to mark traits with an exhaustive set of
|
||||
/// implementations
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
pub(crate) use private::Sealed;
|
||||
|
||||
/// Type-level version of the [None] variant
|
||||
#[derive(Default)]
|
||||
pub struct NoneT;
|
||||
impl Sealed for NoneT {}
|
||||
|
||||
/// Marker trait for type identity
|
||||
///
|
||||
/// This trait is used as part of the [`AnyKind`] trait pattern. It represents
|
||||
/// the concept of type identity, because all implementors have
|
||||
/// `<Self as Is>::Type == Self`. When used as a trait bound with a specific
|
||||
/// type, it guarantees that the corresponding type parameter is exactly the
|
||||
/// specific type. Stated differently, it guarantees that `T == Specific` in
|
||||
/// the following example.
|
||||
///
|
||||
/// ```ignore
|
||||
/// where T: Is<Type = Specific>
|
||||
/// ```
|
||||
///
|
||||
/// Moreover, the super traits guarantee that any instance of or reference to a
|
||||
/// type `T` can be converted into the `Specific` type.
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn example<T>(mut any: T)
|
||||
/// where
|
||||
/// T: Is<Type = Specific>,
|
||||
/// {
|
||||
/// let specific_mut: &mut Specific = any.as_mut();
|
||||
/// let specific_ref: &Specific = any.as_ref();
|
||||
/// let specific: Specific = any.into();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`AnyKind`]: #anykind-trait-pattern
|
||||
pub trait Is
|
||||
where
|
||||
Self: Sealed,
|
||||
Self: From<IsType<Self>>,
|
||||
Self: Into<IsType<Self>>,
|
||||
Self: AsRef<IsType<Self>>,
|
||||
Self: AsMut<IsType<Self>>,
|
||||
{
|
||||
#[allow(missing_docs)]
|
||||
type Type;
|
||||
}
|
||||
|
||||
/// Type alias for [`Is::Type`]
|
||||
pub type IsType<T> = <T as Is>::Type;
|
||||
|
||||
impl<T> Is for T
|
||||
where
|
||||
T: Sealed + AsRef<T> + AsMut<T>,
|
||||
{
|
||||
type Type = T;
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
use fugit::HertzU32;
|
||||
|
||||
use super::{DataBits, StopBits, UartConfig};
|
||||
|
||||
/// 9600 baud, 8 data bits, no parity, 1 stop bit
|
||||
pub const _9600_8_N_1: UartConfig = UartConfig {
|
||||
baudrate: HertzU32::from_raw(9600),
|
||||
data_bits: DataBits::Eight,
|
||||
stop_bits: StopBits::One,
|
||||
parity: None,
|
||||
};
|
||||
|
||||
/// 19200 baud, 8 data bits, no parity, 1 stop bit
|
||||
pub const _19200_8_N_1: UartConfig = UartConfig {
|
||||
baudrate: HertzU32::from_raw(19200),
|
||||
data_bits: DataBits::Eight,
|
||||
stop_bits: StopBits::One,
|
||||
parity: None,
|
||||
};
|
||||
|
||||
/// 38400 baud, 8 data bits, no parity, 1 stop bit
|
||||
pub const _38400_8_N_1: UartConfig = UartConfig {
|
||||
baudrate: HertzU32::from_raw(38400),
|
||||
data_bits: DataBits::Eight,
|
||||
stop_bits: StopBits::One,
|
||||
parity: None,
|
||||
};
|
||||
|
||||
/// 57600 baud, 8 data bits, no parity, 1 stop bit
|
||||
pub const _57600_8_N_1: UartConfig = UartConfig {
|
||||
baudrate: HertzU32::from_raw(57600),
|
||||
data_bits: DataBits::Eight,
|
||||
stop_bits: StopBits::One,
|
||||
parity: None,
|
||||
};
|
||||
|
||||
/// 115200 baud, 8 data bits, no parity, 1 stop bit
|
||||
pub const _115200_8_N_1: UartConfig = UartConfig {
|
||||
baudrate: HertzU32::from_raw(115200),
|
||||
data_bits: DataBits::Eight,
|
||||
stop_bits: StopBits::One,
|
||||
parity: None,
|
||||
};
|
|
@ -1,49 +0,0 @@
|
|||
//! Universal Asynchronous Receiver Transmitter (UART)
|
||||
//!
|
||||
//! See [Chapter 4 Section 2](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! See [examples/uart.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/uart.rs) for a more complete example
|
||||
//! ```no_run
|
||||
//! use rp2040_hal::{Clock, clocks::init_clocks_and_plls, gpio::{Pins, FunctionUart}, pac, sio::Sio, uart::{self, DataBits, StopBits, UartConfig, UartPeripheral}, watchdog::Watchdog};
|
||||
//! use fugit::RateExtU32;
|
||||
//!
|
||||
//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||
//!
|
||||
//! let mut peripherals = pac::Peripherals::take().unwrap();
|
||||
//! let sio = Sio::new(peripherals.SIO);
|
||||
//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
|
||||
//! let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
|
||||
//! let mut clocks = init_clocks_and_plls(XOSC_CRYSTAL_FREQ, peripherals.XOSC, peripherals.CLOCKS, peripherals.PLL_SYS, peripherals.PLL_USB, &mut peripherals.RESETS, &mut watchdog).ok().unwrap();
|
||||
//!
|
||||
//! // Set up UART on GP0 and GP1 (Pico pins 1 and 2)
|
||||
//! let pins = (
|
||||
//! pins.gpio0.into_mode::<FunctionUart>(),
|
||||
//! pins.gpio1.into_mode::<FunctionUart>(),
|
||||
//! );
|
||||
//! // Need to perform clock init before using UART or it will freeze.
|
||||
//! let uart = UartPeripheral::new(peripherals.UART0, pins, &mut peripherals.RESETS)
|
||||
//! .enable(
|
||||
//! UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
|
||||
//! clocks.peripheral_clock.freq(),
|
||||
//! ).unwrap();
|
||||
//!
|
||||
//! uart.write_full_blocking(b"Hello World!\r\n");
|
||||
//! ```
|
||||
|
||||
mod peripheral;
|
||||
mod pins;
|
||||
mod reader;
|
||||
mod utils;
|
||||
mod writer;
|
||||
|
||||
pub use self::peripheral::UartPeripheral;
|
||||
pub use self::pins::*;
|
||||
pub use self::reader::{ReadError, ReadErrorType, Reader};
|
||||
pub use self::utils::*;
|
||||
pub use self::writer::Writer;
|
||||
|
||||
/// Common configurations for UART.
|
||||
#[deprecated(note = "Use UartConfig::new(...) instead.")]
|
||||
pub mod common_configs;
|
|
@ -1,419 +0,0 @@
|
|||
//! Universal Asynchronous Receiver Transmitter - Bi-directional Peripheral Code
|
||||
//!
|
||||
//! This module brings together `uart::reader` and `uart::writer` to give a
|
||||
//! UartPeripheral object that can both read and write.
|
||||
|
||||
use super::*;
|
||||
use crate::pac::uart0::uartlcr_h::W as UART_LCR_H_Writer;
|
||||
use core::convert::Infallible;
|
||||
use core::fmt;
|
||||
use embedded_hal::serial::{Read, Write};
|
||||
use fugit::HertzU32;
|
||||
use nb::Error::{Other, WouldBlock};
|
||||
use rp2040_pac::{UART0, UART1};
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
use eh1_0_alpha::serial as eh1;
|
||||
use pac::Peripherals;
|
||||
|
||||
/// An UART Peripheral based on an underlying UART device.
|
||||
pub struct UartPeripheral<S: State, D: UartDevice, P: ValidUartPinout<D>> {
|
||||
device: D,
|
||||
_state: S,
|
||||
pins: P,
|
||||
}
|
||||
|
||||
impl<S: State, D: UartDevice, P: ValidUartPinout<D>> UartPeripheral<S, D, P> {
|
||||
fn transition<To: State>(self, state: To) -> UartPeripheral<To, D, P> {
|
||||
UartPeripheral {
|
||||
device: self.device,
|
||||
pins: self.pins,
|
||||
_state: state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases the underlying device and pins.
|
||||
pub fn free(self) -> (D, P) {
|
||||
(self.device, self.pins)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> UartPeripheral<Disabled, D, P> {
|
||||
/// Creates an UartPeripheral in Disabled state.
|
||||
pub fn new(device: D, pins: P, resets: &mut pac::RESETS) -> UartPeripheral<Disabled, D, P> {
|
||||
device.reset_bring_down(resets);
|
||||
device.reset_bring_up(resets);
|
||||
|
||||
UartPeripheral {
|
||||
device,
|
||||
_state: Disabled,
|
||||
pins,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables the provided UART device with the given configuration.
|
||||
pub fn enable(
|
||||
self,
|
||||
config: UartConfig,
|
||||
frequency: HertzU32,
|
||||
) -> Result<UartPeripheral<Enabled, D, P>, Error> {
|
||||
let (mut device, pins) = self.free();
|
||||
configure_baudrate(&mut device, config.baudrate, frequency)?;
|
||||
|
||||
device.uartlcr_h.write(|w| {
|
||||
// FIFOs are enabled
|
||||
w.fen().set_bit(); // Leaved here for backward compatibility
|
||||
set_format(w, &config.data_bits, &config.stop_bits, &config.parity);
|
||||
w
|
||||
});
|
||||
|
||||
// Enable the UART, and the TX,RC,CTS and RTS based on the pins
|
||||
device.uartcr.write(|w| {
|
||||
w.uarten().set_bit();
|
||||
w.txe().bit(P::TX_ENABLED);
|
||||
w.rxe().bit(P::RX_ENABLED);
|
||||
w.ctsen().bit(P::CTS_ENABLED);
|
||||
w.rtsen().bit(P::RTS_ENABLED);
|
||||
|
||||
w
|
||||
});
|
||||
|
||||
device.uartdmacr.write(|w| {
|
||||
w.txdmae().set_bit();
|
||||
w.rxdmae().set_bit();
|
||||
w
|
||||
});
|
||||
|
||||
Ok(UartPeripheral {
|
||||
device,
|
||||
pins,
|
||||
_state: Enabled,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> UartPeripheral<Enabled, D, P> {
|
||||
/// Disable this UART Peripheral, falling back to the Disabled state.
|
||||
pub fn disable(self) -> UartPeripheral<Disabled, D, P> {
|
||||
// Disable the UART, both TX and RX
|
||||
self.device.uartcr.write(|w| {
|
||||
w.uarten().clear_bit();
|
||||
w.txe().clear_bit();
|
||||
w.rxe().clear_bit();
|
||||
w.ctsen().clear_bit();
|
||||
w.rtsen().clear_bit();
|
||||
w
|
||||
});
|
||||
|
||||
self.transition(Disabled)
|
||||
}
|
||||
|
||||
/// Enable/disable the rx/tx FIFO
|
||||
///
|
||||
/// Unfortunately, it's not possible to enable/disable rx/tx
|
||||
/// independently on this chip
|
||||
/// Default is false
|
||||
pub fn set_fifos(&mut self, enable: bool) {
|
||||
super::reader::set_fifos(&self.device, enable)
|
||||
}
|
||||
|
||||
/// Set rx FIFO watermark
|
||||
///
|
||||
/// See DS: Table 423
|
||||
pub fn set_rx_watermark(&mut self, watermark: FifoWatermark) {
|
||||
super::reader::set_rx_watermark(&self.device, watermark)
|
||||
}
|
||||
|
||||
/// Set tx FIFO watermark
|
||||
///
|
||||
/// See DS: Table 423
|
||||
pub fn set_tx_watermark(&mut self, watermark: FifoWatermark) {
|
||||
super::writer::set_tx_watermark(&self.device, watermark)
|
||||
}
|
||||
|
||||
/// Enables the Receive Interrupt.
|
||||
///
|
||||
/// The relevant UARTx IRQ will fire when there is data in the receive register.
|
||||
pub fn enable_rx_interrupt(&mut self) {
|
||||
super::reader::enable_rx_interrupt(&self.device)
|
||||
}
|
||||
|
||||
/// Enables the Transmit Interrupt.
|
||||
///
|
||||
/// The relevant UARTx IRQ will fire when there is space in the transmit FIFO.
|
||||
pub fn enable_tx_interrupt(&mut self) {
|
||||
super::writer::enable_tx_interrupt(&self.device)
|
||||
}
|
||||
|
||||
/// Disables the Receive Interrupt.
|
||||
pub fn disable_rx_interrupt(&mut self) {
|
||||
super::reader::disable_rx_interrupt(&self.device)
|
||||
}
|
||||
|
||||
/// Disables the Transmit Interrupt.
|
||||
pub fn disable_tx_interrupt(&mut self) {
|
||||
super::writer::disable_tx_interrupt(&self.device)
|
||||
}
|
||||
|
||||
/// Is there space in the UART TX FIFO for new data to be written?
|
||||
pub fn uart_is_writable(&self) -> bool {
|
||||
super::writer::uart_is_writable(&self.device)
|
||||
}
|
||||
|
||||
/// Is there data in the UART RX FIFO ready to be read?
|
||||
pub fn uart_is_readable(&self) -> bool {
|
||||
super::reader::is_readable(&self.device)
|
||||
}
|
||||
|
||||
/// Writes bytes to the UART.
|
||||
/// This function writes as long as it can. As soon that the FIFO is full, if :
|
||||
/// - 0 bytes were written, a WouldBlock Error is returned
|
||||
/// - some bytes were written, it is deemed to be a success
|
||||
/// Upon success, the remaining slice is returned.
|
||||
pub fn write_raw<'d>(&self, data: &'d [u8]) -> nb::Result<&'d [u8], Infallible> {
|
||||
super::writer::write_raw(&self.device, data)
|
||||
}
|
||||
|
||||
/// Reads bytes from the UART.
|
||||
/// This function reads as long as it can. As soon that the FIFO is empty, if :
|
||||
/// - 0 bytes were read, a WouldBlock Error is returned
|
||||
/// - some bytes were read, it is deemed to be a success
|
||||
/// Upon success, it will return how many bytes were read.
|
||||
pub fn read_raw<'b>(&self, buffer: &'b mut [u8]) -> nb::Result<usize, ReadError<'b>> {
|
||||
super::reader::read_raw(&self.device, buffer)
|
||||
}
|
||||
|
||||
/// Writes bytes to the UART.
|
||||
/// This function blocks until the full buffer has been sent.
|
||||
pub fn write_full_blocking(&self, data: &[u8]) {
|
||||
super::writer::write_full_blocking(&self.device, data);
|
||||
}
|
||||
|
||||
/// Reads bytes from the UART.
|
||||
/// This function blocks until the full buffer has been received.
|
||||
pub fn read_full_blocking(&self, buffer: &mut [u8]) -> Result<(), ReadErrorType> {
|
||||
super::reader::read_full_blocking(&self.device, buffer)
|
||||
}
|
||||
|
||||
/// Join the reader and writer halves together back into the original Uart peripheral.
|
||||
///
|
||||
/// A reader/writer pair can be obtained by calling [`split`].
|
||||
///
|
||||
/// [`split`]: #method.split
|
||||
pub fn join(reader: Reader<D, P>, writer: Writer<D, P>) -> Self {
|
||||
let _ = writer;
|
||||
Self {
|
||||
device: reader.device,
|
||||
_state: Enabled,
|
||||
pins: reader.pins,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ValidUartPinout<UART0>> UartPeripheral<Enabled, UART0, P> {
|
||||
/// Split this peripheral into a separate reader and writer.
|
||||
pub fn split(self) -> (Reader<UART0, P>, Writer<UART0, P>) {
|
||||
let reader = Reader {
|
||||
device: self.device,
|
||||
pins: self.pins,
|
||||
};
|
||||
// Safety: reader and writer will never write to the same address
|
||||
let device_copy = unsafe { Peripherals::steal().UART0 };
|
||||
let writer = Writer {
|
||||
device: device_copy,
|
||||
device_marker: core::marker::PhantomData,
|
||||
pins: core::marker::PhantomData,
|
||||
};
|
||||
(reader, writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ValidUartPinout<UART1>> UartPeripheral<Enabled, UART1, P> {
|
||||
/// Split this peripheral into a separate reader and writer.
|
||||
pub fn split(self) -> (Reader<UART1, P>, Writer<UART1, P>) {
|
||||
let reader = Reader {
|
||||
device: self.device,
|
||||
pins: self.pins,
|
||||
};
|
||||
// Safety: reader and writer will never write to the same address
|
||||
let device_copy = unsafe { Peripherals::steal().UART1 };
|
||||
let writer = Writer {
|
||||
device: device_copy,
|
||||
device_marker: core::marker::PhantomData,
|
||||
pins: core::marker::PhantomData,
|
||||
};
|
||||
(reader, writer)
|
||||
}
|
||||
}
|
||||
|
||||
/// The PL011 (PrimeCell UART) supports a fractional baud rate divider
|
||||
/// From the wanted baudrate, we calculate the divider's two parts: integer and fractional parts.
|
||||
/// Code inspired from the C SDK.
|
||||
fn calculate_baudrate_dividers(
|
||||
wanted_baudrate: HertzU32,
|
||||
frequency: HertzU32,
|
||||
) -> Result<(u16, u16), Error> {
|
||||
// See Chapter 4, Section 2 §7.1 from the datasheet for an explanation of how baudrate is
|
||||
// calculated
|
||||
let baudrate_div = frequency
|
||||
.to_Hz()
|
||||
.checked_mul(8)
|
||||
.and_then(|r| r.checked_div(wanted_baudrate.to_Hz()))
|
||||
.ok_or(Error::BadArgument)?;
|
||||
|
||||
Ok(match (baudrate_div >> 7, ((baudrate_div & 0x7F) + 1) / 2) {
|
||||
(0, _) => (1, 0),
|
||||
|
||||
(int_part, _) if int_part >= 65535 => (65535, 0),
|
||||
|
||||
(int_part, frac_part) => (int_part as u16, frac_part as u16),
|
||||
})
|
||||
}
|
||||
|
||||
/// Baudrate configuration. Code loosely inspired from the C SDK.
|
||||
fn configure_baudrate(
|
||||
device: &mut dyn UartDevice,
|
||||
wanted_baudrate: HertzU32,
|
||||
frequency: HertzU32,
|
||||
) -> Result<HertzU32, Error> {
|
||||
let (baud_div_int, baud_div_frac) = calculate_baudrate_dividers(wanted_baudrate, frequency)?;
|
||||
|
||||
// First we load the integer part of the divider.
|
||||
device.uartibrd.write(|w| unsafe {
|
||||
w.baud_divint().bits(baud_div_int);
|
||||
w
|
||||
});
|
||||
|
||||
// Then we load the fractional part of the divider.
|
||||
device.uartfbrd.write(|w| unsafe {
|
||||
w.baud_divfrac().bits(baud_div_frac as u8);
|
||||
w
|
||||
});
|
||||
|
||||
// PL011 needs a (dummy) line control register write to latch in the
|
||||
// divisors. We don't want to actually change LCR contents here.
|
||||
device.uartlcr_h.modify(|_, w| w);
|
||||
|
||||
Ok(HertzU32::from_raw(
|
||||
(4 * frequency.to_Hz()) / (64 * baud_div_int + baud_div_frac) as u32,
|
||||
))
|
||||
}
|
||||
|
||||
/// Format configuration. Code loosely inspired from the C SDK.
|
||||
fn set_format<'w>(
|
||||
w: &'w mut UART_LCR_H_Writer,
|
||||
data_bits: &DataBits,
|
||||
stop_bits: &StopBits,
|
||||
parity: &Option<Parity>,
|
||||
) -> &'w mut UART_LCR_H_Writer {
|
||||
match parity {
|
||||
Some(p) => {
|
||||
w.pen().set_bit();
|
||||
match p {
|
||||
Parity::Odd => w.eps().clear_bit(),
|
||||
Parity::Even => w.eps().set_bit(),
|
||||
};
|
||||
}
|
||||
None => {
|
||||
w.pen().bit(false);
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
w.wlen().bits(match data_bits {
|
||||
DataBits::Five => 0b00,
|
||||
DataBits::Six => 0b01,
|
||||
DataBits::Seven => 0b10,
|
||||
DataBits::Eight => 0b11,
|
||||
})
|
||||
};
|
||||
|
||||
match stop_bits {
|
||||
StopBits::One => w.stp2().clear_bit(),
|
||||
StopBits::Two => w.stp2().set_bit(),
|
||||
};
|
||||
|
||||
w
|
||||
}
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> Read<u8> for UartPeripheral<Enabled, D, P> {
|
||||
type Error = ReadErrorType;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let byte: &mut [u8] = &mut [0; 1];
|
||||
|
||||
match self.read_raw(byte) {
|
||||
Ok(_) => Ok(byte[0]),
|
||||
Err(e) => match e {
|
||||
Other(inner) => Err(Other(inner.err_type)),
|
||||
WouldBlock => Err(WouldBlock),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::ErrorType for UartPeripheral<Enabled, D, P> {
|
||||
type Error = ReadErrorType;
|
||||
}
|
||||
|
||||
/* disabled for now - nb was migrated to separate crate
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::nb::Read<u8> for UartPeripheral<Enabled, D, P> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let byte: &mut [u8] = &mut [0; 1];
|
||||
|
||||
match self.read_raw(byte) {
|
||||
Ok(_) => Ok(byte[0]),
|
||||
Err(e) => match e {
|
||||
Other(inner) => Err(Other(inner.err_type)),
|
||||
WouldBlock => Err(WouldBlock),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> Write<u8> for UartPeripheral<Enabled, D, P> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
if self.write_raw(&[word]).is_err() {
|
||||
Err(WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
super::writer::transmit_flushed(&self.device)
|
||||
}
|
||||
}
|
||||
|
||||
/* disabled for now - nb was migrated to separate crate
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::nb::Write<u8> for UartPeripheral<Enabled, D, P> {
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
if self.write_raw(&[word]).is_err() {
|
||||
Err(WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
super::writer::transmit_flushed(&self.device).map_err(|e| match e {
|
||||
WouldBlock => WouldBlock,
|
||||
Other(v) => match v {},
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> fmt::Write for UartPeripheral<Enabled, D, P> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
s.bytes()
|
||||
.try_for_each(|c| nb::block!(self.write(c)))
|
||||
.map_err(|_| fmt::Error)
|
||||
}
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
use crate::gpio::{bank0, FunctionUart, Pin};
|
||||
use crate::pac::{UART0, UART1};
|
||||
|
||||
use super::UartDevice;
|
||||
|
||||
/// Declares a valid UART pinout.
|
||||
pub trait ValidUartPinout<UART: UartDevice> {
|
||||
/// Indicates TX should be enabled for this pinout
|
||||
const TX_ENABLED: bool;
|
||||
/// Indicates RX should be enabled for this pinout
|
||||
const RX_ENABLED: bool;
|
||||
/// Indicates CTS should be enabled for this pinout
|
||||
const CTS_ENABLED: bool;
|
||||
/// Indicates RTS should be enabled for this pinout
|
||||
const RTS_ENABLED: bool;
|
||||
}
|
||||
|
||||
impl<UART, TX, RX, CTS, RTS> ValidUartPinout<UART> for Pins<TX, RX, CTS, RTS>
|
||||
where
|
||||
UART: UartDevice,
|
||||
TX: Tx<UART>,
|
||||
RX: Rx<UART>,
|
||||
CTS: Cts<UART>,
|
||||
RTS: Rts<UART>,
|
||||
{
|
||||
const TX_ENABLED: bool = TX::ENABLED;
|
||||
const RX_ENABLED: bool = RX::ENABLED;
|
||||
const CTS_ENABLED: bool = CTS::ENABLED;
|
||||
const RTS_ENABLED: bool = RTS::ENABLED;
|
||||
}
|
||||
|
||||
impl<UART, TX, RX> ValidUartPinout<UART> for (TX, RX)
|
||||
where
|
||||
UART: UartDevice,
|
||||
TX: Tx<UART>,
|
||||
RX: Rx<UART>,
|
||||
{
|
||||
const TX_ENABLED: bool = TX::ENABLED;
|
||||
const RX_ENABLED: bool = RX::ENABLED;
|
||||
const CTS_ENABLED: bool = false;
|
||||
const RTS_ENABLED: bool = false;
|
||||
}
|
||||
|
||||
impl<UART, TX, RX, CTS, RTS> ValidUartPinout<UART> for (TX, RX, CTS, RTS)
|
||||
where
|
||||
UART: UartDevice,
|
||||
TX: Tx<UART>,
|
||||
RX: Rx<UART>,
|
||||
CTS: Cts<UART>,
|
||||
RTS: Rts<UART>,
|
||||
{
|
||||
const TX_ENABLED: bool = TX::ENABLED;
|
||||
const RX_ENABLED: bool = RX::ENABLED;
|
||||
const CTS_ENABLED: bool = CTS::ENABLED;
|
||||
const RTS_ENABLED: bool = RTS::ENABLED;
|
||||
}
|
||||
|
||||
/// Customizable Uart pinout, allowing you to set the pins individually.
|
||||
///
|
||||
/// The following pins are valid UART pins:
|
||||
///
|
||||
/// |UART | TX | RX | CTS | RTS |
|
||||
/// |-----|-------------|-------------|-------------|-------------|
|
||||
/// |UART0|0, 12, 16, 28|1, 13, 17, 29|2, 14, 18 |3, 15, 19 |
|
||||
/// |UART1|4, 8, 20, 24 |5, 9, 21, 25 |6, 10, 22, 26|7, 11, 23, 27|
|
||||
///
|
||||
/// Every field can be set to `()` to not configure them.
|
||||
///
|
||||
/// Note that you can also use tuples `(RX, TX)` or `(RX, TX, CTS, RTS)` instead of this type.
|
||||
///
|
||||
/// This struct can either be filled manually or with a builder pattern:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use rp2040_hal::uart::{Pins, ValidUartPinout};
|
||||
/// # use rp2040_hal::pac::UART0;
|
||||
/// # let gpio_pins: rp2040_hal::gpio::Pins = unsafe { core::mem::zeroed() };
|
||||
/// let pins = Pins::default()
|
||||
/// .tx(gpio_pins.gpio0.into_mode())
|
||||
/// .rx(gpio_pins.gpio1.into_mode());
|
||||
///
|
||||
/// fn assert_is_valid_uart0<T: ValidUartPinout<UART0>>(_: T) {}
|
||||
///
|
||||
/// assert_is_valid_uart0(pins);
|
||||
/// ```
|
||||
#[allow(missing_docs)]
|
||||
pub struct Pins<TX, RX, CTS, RTS> {
|
||||
pub tx: TX,
|
||||
pub rx: RX,
|
||||
pub rts: RTS,
|
||||
pub cts: CTS,
|
||||
}
|
||||
|
||||
impl Default for Pins<(), (), (), ()> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tx: (),
|
||||
rx: (),
|
||||
rts: (),
|
||||
cts: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX, RX, CTS, RTS> Pins<TX, RX, CTS, RTS> {
|
||||
/// Set the TX pin
|
||||
pub fn tx<NTX>(self, tx: NTX) -> Pins<NTX, RX, CTS, RTS> {
|
||||
Pins {
|
||||
tx,
|
||||
rx: self.rx,
|
||||
rts: self.rts,
|
||||
cts: self.cts,
|
||||
}
|
||||
}
|
||||
/// Set the RX pin
|
||||
pub fn rx<NRX>(self, rx: NRX) -> Pins<TX, NRX, CTS, RTS> {
|
||||
Pins {
|
||||
tx: self.tx,
|
||||
rx,
|
||||
rts: self.rts,
|
||||
cts: self.cts,
|
||||
}
|
||||
}
|
||||
/// Set the CTS pin
|
||||
pub fn cts<NCTS>(self, cts: NCTS) -> Pins<TX, RX, NCTS, RTS> {
|
||||
Pins {
|
||||
tx: self.tx,
|
||||
rx: self.rx,
|
||||
rts: self.rts,
|
||||
cts,
|
||||
}
|
||||
}
|
||||
/// Set the RTS pin
|
||||
pub fn rts<NRTS>(self, rts: NRTS) -> Pins<TX, RX, CTS, NRTS> {
|
||||
Pins {
|
||||
tx: self.tx,
|
||||
rx: self.rx,
|
||||
rts,
|
||||
cts: self.cts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates a valid TX pin for UART0 or UART1
|
||||
pub trait Tx<UART: UartDevice> {
|
||||
#[allow(missing_docs)]
|
||||
const ENABLED: bool;
|
||||
}
|
||||
/// Indicates a valid RX pin for UART0 or UART1
|
||||
pub trait Rx<UART: UartDevice> {
|
||||
#[allow(missing_docs)]
|
||||
const ENABLED: bool;
|
||||
}
|
||||
/// Indicates a valid CTS pin for UART0 or UART1
|
||||
pub trait Cts<UART: UartDevice> {
|
||||
#[allow(missing_docs)]
|
||||
const ENABLED: bool;
|
||||
}
|
||||
/// Indicates a valid RTS pin for UART0 or UART1
|
||||
pub trait Rts<UART: UartDevice> {
|
||||
#[allow(missing_docs)]
|
||||
const ENABLED: bool;
|
||||
}
|
||||
|
||||
impl<UART: UartDevice> Tx<UART> for () {
|
||||
const ENABLED: bool = false;
|
||||
}
|
||||
impl<UART: UartDevice> Rx<UART> for () {
|
||||
const ENABLED: bool = false;
|
||||
}
|
||||
impl<UART: UartDevice> Cts<UART> for () {
|
||||
const ENABLED: bool = false;
|
||||
}
|
||||
impl<UART: UartDevice> Rts<UART> for () {
|
||||
const ENABLED: bool = false;
|
||||
}
|
||||
|
||||
macro_rules! impl_valid_uart {
|
||||
($($uart:ident: {
|
||||
tx: [$($tx:ident),*],
|
||||
rx: [$($rx:ident),*],
|
||||
cts: [$($cts:ident),*],
|
||||
rts: [$($rts:ident),*],
|
||||
}),*) => {
|
||||
$(
|
||||
$(
|
||||
impl Tx<$uart> for Pin<bank0::$tx, FunctionUart> {
|
||||
const ENABLED: bool = true;
|
||||
}
|
||||
)*
|
||||
$(
|
||||
impl Rx<$uart> for Pin<bank0::$rx, FunctionUart> {
|
||||
const ENABLED: bool = true;
|
||||
}
|
||||
)*
|
||||
$(
|
||||
impl Cts<$uart> for Pin<bank0::$cts, FunctionUart> {
|
||||
const ENABLED: bool = true;
|
||||
}
|
||||
)*
|
||||
$(
|
||||
impl Rts<$uart> for Pin<bank0::$rts, FunctionUart> {
|
||||
const ENABLED: bool = true;
|
||||
}
|
||||
)*
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_valid_uart!(
|
||||
UART0: {
|
||||
tx: [Gpio0, Gpio12, Gpio16, Gpio28],
|
||||
rx: [Gpio1, Gpio13, Gpio17, Gpio29],
|
||||
cts: [Gpio2, Gpio14, Gpio18],
|
||||
rts: [Gpio3, Gpio15, Gpio19],
|
||||
},
|
||||
UART1: {
|
||||
tx: [Gpio4, Gpio8, Gpio20, Gpio24],
|
||||
rx: [Gpio5, Gpio9, Gpio21, Gpio25],
|
||||
cts: [Gpio6, Gpio10, Gpio22, Gpio26],
|
||||
rts: [Gpio7, Gpio11, Gpio23, Gpio27],
|
||||
}
|
||||
);
|
|
@ -1,257 +0,0 @@
|
|||
//! Universal Asynchronous Receiver Transmitter - Receiver Code
|
||||
//!
|
||||
//! This module is for receiving data with a UART.
|
||||
|
||||
use super::{FifoWatermark, UartDevice, ValidUartPinout};
|
||||
use rp2040_pac::uart0::RegisterBlock;
|
||||
|
||||
use embedded_hal::serial::Read;
|
||||
use nb::Error::*;
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
use eh1_0_alpha::serial as eh1;
|
||||
|
||||
/// When there's a read error.
|
||||
pub struct ReadError<'err> {
|
||||
/// The type of error
|
||||
pub err_type: ReadErrorType,
|
||||
|
||||
/// Reference to the data that was read but eventually discarded because of the error.
|
||||
pub discarded: &'err [u8],
|
||||
}
|
||||
|
||||
/// Possible types of read errors. See Chapter 4, Section 2 §8 - Table 436: "UARTDR Register"
|
||||
#[cfg_attr(feature = "eh1_0_alpha", derive(Debug))]
|
||||
pub enum ReadErrorType {
|
||||
/// Triggered when the FIFO (or shift-register) is overflowed.
|
||||
Overrun,
|
||||
|
||||
/// Triggered when a break is received
|
||||
Break,
|
||||
|
||||
/// Triggered when there is a parity mismatch between what's received and our settings.
|
||||
Parity,
|
||||
|
||||
/// Triggered when the received character didn't have a valid stop bit.
|
||||
Framing,
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl eh1_0_alpha::serial::Error for ReadErrorType {
|
||||
fn kind(&self) -> eh1_0_alpha::serial::ErrorKind {
|
||||
match self {
|
||||
ReadErrorType::Overrun => eh1_0_alpha::serial::ErrorKind::Overrun,
|
||||
ReadErrorType::Break => eh1_0_alpha::serial::ErrorKind::Other,
|
||||
ReadErrorType::Parity => eh1_0_alpha::serial::ErrorKind::Parity,
|
||||
ReadErrorType::Framing => eh1_0_alpha::serial::ErrorKind::FrameFormat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_readable<D: UartDevice>(device: &D) -> bool {
|
||||
device.uartfr.read().rxfe().bit_is_clear()
|
||||
}
|
||||
|
||||
/// Enable/disable the rx/tx FIFO
|
||||
///
|
||||
/// Unfortunately, it's not possible to enable/disable rx/tx
|
||||
/// independently on this chip
|
||||
/// Default is false
|
||||
pub fn set_fifos(rb: &RegisterBlock, enable: bool) {
|
||||
if enable {
|
||||
rb.uartlcr_h.modify(|_r, w| w.fen().set_bit())
|
||||
} else {
|
||||
rb.uartlcr_h.modify(|_r, w| w.fen().clear_bit())
|
||||
}
|
||||
}
|
||||
|
||||
/// Set rx FIFO watermark
|
||||
///
|
||||
/// See DS: Table 423
|
||||
pub fn set_rx_watermark(rb: &RegisterBlock, watermark: FifoWatermark) {
|
||||
let wm = match watermark {
|
||||
FifoWatermark::Bytes4 => 0,
|
||||
FifoWatermark::Bytes8 => 1,
|
||||
FifoWatermark::Bytes16 => 2,
|
||||
FifoWatermark::Bytes24 => 3,
|
||||
FifoWatermark::Bytes28 => 4,
|
||||
};
|
||||
rb.uartifls.modify(|_r, w| unsafe { w.rxiflsel().bits(wm) });
|
||||
}
|
||||
|
||||
/// Enables the Receive Interrupt.
|
||||
///
|
||||
/// The relevant UARTx IRQ will fire when there is data in the receive register.
|
||||
pub(crate) fn enable_rx_interrupt(rb: &RegisterBlock) {
|
||||
// Access the UART Interrupt Mask Set/Clear register. Setting a bit
|
||||
// high enables the interrupt.
|
||||
|
||||
// We set the RX interrupt, and the RX Timeout interrupt. This means
|
||||
// we will get an interrupt when the RX FIFO level is triggered, or
|
||||
// when the RX FIFO is non-empty, but 32-bit periods have passed with
|
||||
// no further data. This means we don't have to interrupt on every
|
||||
// single byte, but can make use of the hardware FIFO.
|
||||
rb.uartimsc.modify(|_r, w| {
|
||||
w.rxim().set_bit();
|
||||
w.rtim().set_bit();
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
/// Disables the Receive Interrupt.
|
||||
pub(crate) fn disable_rx_interrupt(rb: &RegisterBlock) {
|
||||
// Access the UART Interrupt Mask Set/Clear register. Setting a bit
|
||||
// low disables the interrupt.
|
||||
|
||||
rb.uartimsc.modify(|_r, w| {
|
||||
w.rxim().clear_bit();
|
||||
w.rtim().clear_bit();
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn read_raw<'b, D: UartDevice>(
|
||||
device: &D,
|
||||
buffer: &'b mut [u8],
|
||||
) -> nb::Result<usize, ReadError<'b>> {
|
||||
let mut bytes_read = 0;
|
||||
|
||||
Ok(loop {
|
||||
if !is_readable(device) {
|
||||
if bytes_read == 0 {
|
||||
return Err(WouldBlock);
|
||||
} else {
|
||||
break bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
if bytes_read < buffer.len() {
|
||||
let mut error: Option<ReadErrorType> = None;
|
||||
|
||||
let read = device.uartdr.read();
|
||||
|
||||
if read.oe().bit_is_set() {
|
||||
error = Some(ReadErrorType::Overrun);
|
||||
}
|
||||
|
||||
if read.be().bit_is_set() {
|
||||
error = Some(ReadErrorType::Break);
|
||||
}
|
||||
|
||||
if read.pe().bit_is_set() {
|
||||
error = Some(ReadErrorType::Parity);
|
||||
}
|
||||
|
||||
if read.fe().bit_is_set() {
|
||||
error = Some(ReadErrorType::Framing);
|
||||
}
|
||||
|
||||
if let Some(err_type) = error {
|
||||
return Err(Other(ReadError {
|
||||
err_type,
|
||||
discarded: &buffer[..bytes_read],
|
||||
}));
|
||||
}
|
||||
|
||||
buffer[bytes_read] = read.data().bits();
|
||||
bytes_read += 1;
|
||||
} else {
|
||||
break bytes_read;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn read_full_blocking<D: UartDevice>(
|
||||
device: &D,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), ReadErrorType> {
|
||||
let mut offset = 0;
|
||||
|
||||
while offset != buffer.len() {
|
||||
offset += match read_raw(device, &mut buffer[offset..]) {
|
||||
Ok(bytes_read) => bytes_read,
|
||||
Err(e) => match e {
|
||||
Other(inner) => return Err(inner.err_type),
|
||||
WouldBlock => continue,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Half of an [`UartPeripheral`] that is only capable of reading. Obtained by calling [`UartPeripheral::split()`]
|
||||
///
|
||||
/// [`UartPeripheral`]: struct.UartPeripheral.html
|
||||
/// [`UartPeripheral::split()`]: struct.UartPeripheral.html#method.split
|
||||
pub struct Reader<D: UartDevice, P: ValidUartPinout<D>> {
|
||||
pub(super) device: D,
|
||||
pub(super) pins: P,
|
||||
}
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> Reader<D, P> {
|
||||
/// Reads bytes from the UART.
|
||||
/// This function reads as long as it can. As soon that the FIFO is empty, if :
|
||||
/// - 0 bytes were read, a WouldBlock Error is returned
|
||||
/// - some bytes were read, it is deemed to be a success
|
||||
/// Upon success, it will return how many bytes were read.
|
||||
pub fn read_raw<'b>(&self, buffer: &'b mut [u8]) -> nb::Result<usize, ReadError<'b>> {
|
||||
read_raw(&self.device, buffer)
|
||||
}
|
||||
|
||||
/// Reads bytes from the UART.
|
||||
/// This function blocks until the full buffer has been received.
|
||||
pub fn read_full_blocking(&self, buffer: &mut [u8]) -> Result<(), ReadErrorType> {
|
||||
read_full_blocking(&self.device, buffer)
|
||||
}
|
||||
|
||||
/// Enables the Receive Interrupt.
|
||||
///
|
||||
/// The relevant UARTx IRQ will fire when there is data in the receive register.
|
||||
pub fn enable_rx_interrupt(&mut self) {
|
||||
enable_rx_interrupt(&self.device)
|
||||
}
|
||||
|
||||
/// Disables the Receive Interrupt.
|
||||
pub fn disable_rx_interrupt(&mut self) {
|
||||
disable_rx_interrupt(&self.device)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> Read<u8> for Reader<D, P> {
|
||||
type Error = ReadErrorType;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let byte: &mut [u8] = &mut [0; 1];
|
||||
|
||||
match self.read_raw(byte) {
|
||||
Ok(_) => Ok(byte[0]),
|
||||
Err(e) => match e {
|
||||
Other(inner) => Err(Other(inner.err_type)),
|
||||
WouldBlock => Err(WouldBlock),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::ErrorType for Reader<D, P> {
|
||||
type Error = ReadErrorType;
|
||||
}
|
||||
|
||||
/* disabled for now - nb was migrated to separate crate
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::nb::Read<u8> for Reader<D, P> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let byte: &mut [u8] = &mut [0; 1];
|
||||
|
||||
match self.read_raw(byte) {
|
||||
Ok(_) => Ok(byte[0]),
|
||||
Err(e) => match e {
|
||||
Other(inner) => Err(Other(inner.err_type)),
|
||||
WouldBlock => Err(WouldBlock),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,135 +0,0 @@
|
|||
use crate::pac::{uart0::RegisterBlock, UART0, UART1};
|
||||
use crate::resets::SubsystemReset;
|
||||
use core::ops::Deref;
|
||||
use fugit::HertzU32;
|
||||
|
||||
/// Error type for UART operations.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// Bad argument : when things overflow, ...
|
||||
BadArgument,
|
||||
}
|
||||
/// State of the UART Peripheral.
|
||||
pub trait State {}
|
||||
|
||||
/// Trait to handle both underlying devices (UART0 & UART1)
|
||||
pub trait UartDevice: Deref<Target = RegisterBlock> + SubsystemReset + 'static {}
|
||||
|
||||
impl UartDevice for UART0 {}
|
||||
impl UartDevice for UART1 {}
|
||||
|
||||
/// UART is enabled.
|
||||
pub struct Enabled;
|
||||
|
||||
/// UART is disabled.
|
||||
pub struct Disabled;
|
||||
|
||||
impl State for Enabled {}
|
||||
impl State for Disabled {}
|
||||
|
||||
/// Data bits
|
||||
pub enum DataBits {
|
||||
/// 5 bits
|
||||
Five,
|
||||
/// 6 bits
|
||||
Six,
|
||||
/// 7 bits
|
||||
Seven,
|
||||
/// 8 bits
|
||||
Eight,
|
||||
}
|
||||
|
||||
/// Stop bits
|
||||
pub enum StopBits {
|
||||
/// 1 bit
|
||||
One,
|
||||
/// 2 bits
|
||||
Two,
|
||||
}
|
||||
|
||||
/// Parity
|
||||
/// The "none" state of parity is represented with the Option type (None).
|
||||
pub enum Parity {
|
||||
/// Odd parity
|
||||
Odd,
|
||||
/// Even parity
|
||||
Even,
|
||||
}
|
||||
|
||||
/// A struct holding the configuration for an UART device.
|
||||
///
|
||||
/// The `Default` implementation implements the following values:
|
||||
/// ```ignore
|
||||
/// # // can't actually create this with the non_exhaustive attribute
|
||||
/// UartConfig {
|
||||
/// baudrate: Baud(115_200),
|
||||
/// data_bits: DataBits::Eight,
|
||||
/// stop_bits: StopBits::One,
|
||||
/// parity: None,
|
||||
///}
|
||||
/// ```
|
||||
#[non_exhaustive]
|
||||
pub struct UartConfig {
|
||||
/// The baudrate the uart will run at.
|
||||
pub baudrate: HertzU32,
|
||||
|
||||
/// The amount of data bits the uart should be configured to.
|
||||
pub data_bits: DataBits,
|
||||
|
||||
/// The amount of stop bits the uart should be configured to.
|
||||
pub stop_bits: StopBits,
|
||||
|
||||
/// The parity that this uart should have
|
||||
pub parity: Option<Parity>,
|
||||
}
|
||||
|
||||
impl UartConfig {
|
||||
/// Create a new instance of UartConfig
|
||||
pub const fn new(
|
||||
baudrate: HertzU32,
|
||||
data_bits: DataBits,
|
||||
parity: Option<Parity>,
|
||||
stop_bits: StopBits,
|
||||
) -> UartConfig {
|
||||
UartConfig {
|
||||
baudrate,
|
||||
data_bits,
|
||||
stop_bits,
|
||||
parity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rx/Tx FIFO Watermark
|
||||
///
|
||||
/// Determine the FIFO level that trigger DMA/Interrupt
|
||||
/// Default is Bytes16, see DS Table 423 and UARTIFLS Register
|
||||
/// Example of use:
|
||||
/// uart0.set_fifos(true); // Default is false
|
||||
/// uart0.set_rx_watermark(hal::uart::FifoWatermark::Bytes8);
|
||||
/// uart0.enable_rx_interrupt();
|
||||
///
|
||||
pub enum FifoWatermark {
|
||||
/// Trigger when 4 bytes are (Rx: filled / Tx: available)
|
||||
Bytes4,
|
||||
/// Trigger when 8 bytes are (Rx: filled / Tx: available)
|
||||
Bytes8,
|
||||
/// Trigger when 16 bytes are (Rx: filled / Tx: available)
|
||||
Bytes16,
|
||||
/// Trigger when 24 bytes are (Rx: filled / Tx: available)
|
||||
Bytes24,
|
||||
/// Trigger when 28 bytes are (Rx: filled / Tx: available)
|
||||
Bytes28,
|
||||
}
|
||||
|
||||
impl Default for UartConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
baudrate: HertzU32::from_raw(115_200),
|
||||
data_bits: DataBits::Eight,
|
||||
stop_bits: StopBits::One,
|
||||
parity: None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
//! Universal Asynchronous Receiver Transmitter - Transmitter Code
|
||||
//!
|
||||
//! This module is for transmitting data with a UART.
|
||||
|
||||
use super::{FifoWatermark, UartDevice, ValidUartPinout};
|
||||
use core::fmt;
|
||||
use core::{convert::Infallible, marker::PhantomData};
|
||||
use embedded_hal::serial::Write;
|
||||
use nb::Error::*;
|
||||
use rp2040_pac::uart0::RegisterBlock;
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
use eh1_0_alpha::serial as eh1;
|
||||
|
||||
/// Set tx FIFO watermark
|
||||
///
|
||||
/// See DS: Table 423
|
||||
pub fn set_tx_watermark(rb: &RegisterBlock, watermark: FifoWatermark) {
|
||||
let wm = match watermark {
|
||||
FifoWatermark::Bytes4 => 4,
|
||||
FifoWatermark::Bytes8 => 3,
|
||||
FifoWatermark::Bytes16 => 2,
|
||||
FifoWatermark::Bytes24 => 1,
|
||||
FifoWatermark::Bytes28 => 0,
|
||||
};
|
||||
rb.uartifls.modify(|_r, w| unsafe { w.txiflsel().bits(wm) });
|
||||
}
|
||||
|
||||
/// Returns `Err(WouldBlock)` if the UART TX FIFO still has data in it or
|
||||
/// `Ok(())` if the FIFO is empty.
|
||||
pub(crate) fn transmit_flushed(rb: &RegisterBlock) -> nb::Result<(), Infallible> {
|
||||
if rb.uartfr.read().txfe().bit_is_set() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(WouldBlock)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the TX FIFO has space, or false if it is full
|
||||
pub(crate) fn uart_is_writable(rb: &RegisterBlock) -> bool {
|
||||
rb.uartfr.read().txff().bit_is_clear()
|
||||
}
|
||||
|
||||
/// Writes bytes to the UART.
|
||||
///
|
||||
/// This function writes as long as it can. As soon that the FIFO is full,
|
||||
/// if:
|
||||
/// - 0 bytes were written, a WouldBlock Error is returned
|
||||
/// - some bytes were written, it is deemed to be a success
|
||||
///
|
||||
/// Upon success, the remaining (unwritten) slice is returned.
|
||||
pub(crate) fn write_raw<'d>(
|
||||
rb: &RegisterBlock,
|
||||
data: &'d [u8],
|
||||
) -> nb::Result<&'d [u8], Infallible> {
|
||||
let mut bytes_written = 0;
|
||||
|
||||
for c in data {
|
||||
if !uart_is_writable(rb) {
|
||||
if bytes_written == 0 {
|
||||
return Err(WouldBlock);
|
||||
} else {
|
||||
return Ok(&data[bytes_written..]);
|
||||
}
|
||||
}
|
||||
|
||||
rb.uartdr.write(|w| unsafe {
|
||||
w.data().bits(*c);
|
||||
w
|
||||
});
|
||||
|
||||
bytes_written += 1;
|
||||
}
|
||||
Ok(&data[bytes_written..])
|
||||
}
|
||||
|
||||
/// Writes bytes to the UART.
|
||||
///
|
||||
/// This function blocks until the full buffer has been sent.
|
||||
pub(crate) fn write_full_blocking(rb: &RegisterBlock, data: &[u8]) {
|
||||
let mut temp = data;
|
||||
|
||||
while !temp.is_empty() {
|
||||
temp = match write_raw(rb, temp) {
|
||||
Ok(remaining) => remaining,
|
||||
Err(WouldBlock) => continue,
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables the Transmit Interrupt.
|
||||
///
|
||||
/// The relevant UARTx IRQ will fire when there is space in the transmit FIFO.
|
||||
pub(crate) fn enable_tx_interrupt(rb: &RegisterBlock) {
|
||||
// Access the UART FIFO Level Select. We set the TX FIFO trip level
|
||||
// to be when it's half-empty..
|
||||
|
||||
// 2 means '<= 1/2 full'.
|
||||
rb.uartifls.modify(|_r, w| unsafe { w.txiflsel().bits(2) });
|
||||
|
||||
// Access the UART Interrupt Mask Set/Clear register. Setting a bit
|
||||
// high enables the interrupt.
|
||||
|
||||
// We set the TX interrupt. This means we will get an interrupt when
|
||||
// the TX FIFO level is triggered. This means we don't have to
|
||||
// interrupt on every single byte, but can make use of the hardware
|
||||
// FIFO.
|
||||
rb.uartimsc.modify(|_r, w| {
|
||||
w.txim().set_bit();
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
/// Disables the Transmit Interrupt.
|
||||
pub(crate) fn disable_tx_interrupt(rb: &RegisterBlock) {
|
||||
// Access the UART Interrupt Mask Set/Clear register. Setting a bit
|
||||
// low disables the interrupt.
|
||||
|
||||
rb.uartimsc.modify(|_r, w| {
|
||||
w.txim().clear_bit();
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
/// Half of an [`UartPeripheral`] that is only capable of writing. Obtained by calling [`UartPeripheral::split()`]
|
||||
///
|
||||
/// [`UartPeripheral`]: struct.UartPeripheral.html
|
||||
/// [`UartPeripheral::split()`]: struct.UartPeripheral.html#method.split
|
||||
pub struct Writer<D: UartDevice, P: ValidUartPinout<D>> {
|
||||
pub(super) device: D,
|
||||
pub(super) device_marker: PhantomData<D>,
|
||||
pub(super) pins: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> Writer<D, P> {
|
||||
/// Writes bytes to the UART.
|
||||
///
|
||||
/// This function writes as long as it can. As soon that the FIFO is full,
|
||||
/// if:
|
||||
/// - 0 bytes were written, a WouldBlock Error is returned
|
||||
/// - some bytes were written, it is deemed to be a success
|
||||
///
|
||||
/// Upon success, the remaining (unwritten) slice is returned.
|
||||
pub fn write_raw<'d>(&self, data: &'d [u8]) -> nb::Result<&'d [u8], Infallible> {
|
||||
write_raw(&self.device, data)
|
||||
}
|
||||
|
||||
/// Writes bytes to the UART.
|
||||
///
|
||||
/// This function blocks until the full buffer has been sent.
|
||||
pub fn write_full_blocking(&self, data: &[u8]) {
|
||||
write_full_blocking(&self.device, data);
|
||||
}
|
||||
|
||||
/// Enables the Transmit Interrupt.
|
||||
///
|
||||
/// The relevant UARTx IRQ will fire when there is space in the transmit FIFO.
|
||||
pub fn enable_tx_interrupt(&mut self) {
|
||||
enable_tx_interrupt(&self.device)
|
||||
}
|
||||
|
||||
/// Disables the Transmit Interrupt.
|
||||
pub fn disable_tx_interrupt(&mut self) {
|
||||
disable_tx_interrupt(&self.device)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> Write<u8> for Writer<D, P> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
if self.write_raw(&[word]).is_err() {
|
||||
Err(WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
transmit_flushed(&self.device)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::ErrorType for Writer<D, P> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
/* disabled for now - nb was migrated to separate crate
|
||||
#[cfg(feature = "eh1_0_alpha")]
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> eh1::nb::Write<u8> for Writer<D, P> {
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
if self.write_raw(&[word]).is_err() {
|
||||
Err(WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
transmit_flushed(&self.device).map_err(|e| match e {
|
||||
WouldBlock => WouldBlock,
|
||||
Other(v) => match v {},
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<D: UartDevice, P: ValidUartPinout<D>> fmt::Write for Writer<D, P> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
s.bytes()
|
||||
.try_for_each(|c| nb::block!(self.write(c)))
|
||||
.map_err(|_| fmt::Error)
|
||||
}
|
||||
}
|
|
@ -1,684 +0,0 @@
|
|||
//! Universal Serial Bus (USB)
|
||||
// See [Chapter 4 Section 1](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
//! ## Usage
|
||||
//!
|
||||
//! Initialize the Usb Bus forcing the VBUS detection.
|
||||
//! ```no_run
|
||||
//! use rp2040_hal::{clocks::init_clocks_and_plls, pac, Sio, usb::UsbBus, watchdog::Watchdog};
|
||||
//! use usb_device::class_prelude::UsbBusAllocator;
|
||||
//!
|
||||
//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||
//!
|
||||
//! let mut pac = pac::Peripherals::take().unwrap();
|
||||
//! let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||
//! let mut clocks = init_clocks_and_plls(
|
||||
//! XOSC_CRYSTAL_FREQ,
|
||||
//! pac.XOSC,
|
||||
//! pac.CLOCKS,
|
||||
//! pac.PLL_SYS,
|
||||
//! pac.PLL_USB,
|
||||
//! &mut pac.RESETS,
|
||||
//! &mut watchdog
|
||||
//! ).ok().unwrap();
|
||||
//!
|
||||
//! let usb_bus = UsbBusAllocator::new(UsbBus::new(
|
||||
//! pac.USBCTRL_REGS,
|
||||
//! pac.USBCTRL_DPRAM,
|
||||
//! clocks.usb_clock,
|
||||
//! true,
|
||||
//! &mut pac.RESETS,
|
||||
//! ));
|
||||
//! // Use the usb_bus as usual.
|
||||
//! ```
|
||||
//!
|
||||
//! See [pico_usb_serial.rs](https://github.com/rp-rs/rp-hal/tree/main/boards/pico/examples/pico_usb_serial.rs) for more complete examples
|
||||
//!
|
||||
//!
|
||||
//! ## Enumeration issue with small EP0 max packet size
|
||||
//!
|
||||
//! During enumeration Windows hosts send a `StatusOut` after the `DataIn` packet of the first
|
||||
//! `Get Descriptor` resquest even if the `DataIn` isn't completed (typically when the `max_packet_size_ep0`
|
||||
//! is less than 18bytes). The next request is a `Set Address` that expect a `StatusIn`.
|
||||
//!
|
||||
//! The issue is that by the time the previous `DataIn` packet is acknoledged and the `StatusOut`
|
||||
//! followed by `Setup` are received, the usb stack may have already prepared the next `DataIn` payload
|
||||
//! in the EP0 IN mailbox resulting in the payload being transmitted to the host instead of the
|
||||
//! `StatusIn` for the `Set Address` request as expected by the host.
|
||||
//!
|
||||
//! To avoid that issue, the EP0 In mailbox should be invalidated between the `Setup` packet and the
|
||||
//! next `StatusIn` initiated by the host. The workaround implemented clears the available bit of the
|
||||
//! EP0 In endpoint's buffer to stop the device from sending the data instead of the status packet.
|
||||
//! This workaround has the caveat that the poll function must be called between those two which
|
||||
//! are only separated by a few microseconds.
|
||||
//!
|
||||
//! If the required timing cannot be met, using an maximum packet size of the endpoint 0 above 18bytes
|
||||
//! (e.g. `.max_packet_size_ep0(64)`) should avoid that issue.
|
||||
//!
|
||||
//! ## Issue on RP2040B0 and RP2040B1: USB device fails to exit RESET state on busy USB bus.
|
||||
//!
|
||||
//! The feature `rp2040-e5`implements the workaround described by [RP2040-E5](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#%5B%7B%22num%22%3A630%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C115%2C158.848%2Cnull%5D).
|
||||
//!
|
||||
//! The workaround requires the GPIO block to be released from its reset and has for side effect
|
||||
//! that GPIO15 will be stolen for a few hundred microseconds each time a Reset is detected on the
|
||||
//! USB bus.
|
||||
//!
|
||||
//! The pin will be temporarily put in "bus keep" mode, weakly pulling the output towards its current
|
||||
//! logic level. In absence of external loads, the current logic level will be maintained.
|
||||
//! A user will lose control of the pin's output and reading from it may not reflect the actual state
|
||||
//! of the external pin.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use rp2040_hal::{clocks::init_clocks_and_plls, pac, usb::UsbBus, watchdog::Watchdog};
|
||||
//! # use usb_device::class_prelude::UsbBusAllocator;
|
||||
//! use rp2040_hal::{gpio::Pins, Sio};
|
||||
//!
|
||||
//! # const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||
//! #
|
||||
//! # let mut pac = pac::Peripherals::take().unwrap();
|
||||
//! # let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||
//! # let mut clocks = init_clocks_and_plls(
|
||||
//! # XOSC_CRYSTAL_FREQ,
|
||||
//! # pac.XOSC,
|
||||
//! # pac.CLOCKS,
|
||||
//! # pac.PLL_SYS,
|
||||
//! # pac.PLL_USB,
|
||||
//! # &mut pac.RESETS,
|
||||
//! # &mut watchdog
|
||||
//! # ).ok().unwrap();
|
||||
//! #
|
||||
//! // required for the errata 5's workaround to function properly.
|
||||
//! let sio = Sio::new(pac.SIO);
|
||||
//! let _pins = Pins::new(
|
||||
//! pac.IO_BANK0,
|
||||
//! pac.PADS_BANK0,
|
||||
//! sio.gpio_bank0,
|
||||
//! &mut pac.RESETS,
|
||||
//! );
|
||||
//! #
|
||||
//! # let usb_bus = UsbBusAllocator::new(UsbBus::new(
|
||||
//! # pac.USBCTRL_REGS,
|
||||
//! # pac.USBCTRL_DPRAM,
|
||||
//! # clocks.usb_clock,
|
||||
//! # true,
|
||||
//! # &mut pac.RESETS,
|
||||
//! # ));
|
||||
//! # // Use the usb_bus as usual.
|
||||
//! ```
|
||||
|
||||
use core::cell::RefCell;
|
||||
|
||||
use crate::clocks::UsbClock;
|
||||
use crate::pac::RESETS;
|
||||
use crate::pac::USBCTRL_DPRAM;
|
||||
use crate::pac::USBCTRL_REGS;
|
||||
use crate::resets::SubsystemReset;
|
||||
|
||||
use critical_section::{self, Mutex};
|
||||
|
||||
use usb_device::{
|
||||
bus::{PollResult, UsbBus as UsbBusTrait},
|
||||
endpoint::{EndpointAddress, EndpointType},
|
||||
Result as UsbResult, UsbDirection, UsbError,
|
||||
};
|
||||
|
||||
#[cfg(feature = "rp2040-e5")]
|
||||
mod errata5;
|
||||
|
||||
#[allow(clippy::bool_to_int_with_if)]
|
||||
fn ep_addr_to_ep_buf_ctrl_idx(ep_addr: EndpointAddress) -> usize {
|
||||
ep_addr.index() * 2 + (if ep_addr.is_in() { 0 } else { 1 })
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Endpoint {
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
buffer_offset: u16,
|
||||
}
|
||||
impl Endpoint {
|
||||
unsafe fn get_buf_parts(&self) -> (*mut u8, usize) {
|
||||
const DPRAM_BASE: *mut u8 = USBCTRL_DPRAM::ptr() as *mut u8;
|
||||
if self.ep_type == EndpointType::Control {
|
||||
(DPRAM_BASE.offset(0x100), self.max_packet_size as usize)
|
||||
} else {
|
||||
(
|
||||
DPRAM_BASE.offset(0x180 + (self.buffer_offset * 64) as isize),
|
||||
self.max_packet_size as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_buf(&self) -> &'static [u8] {
|
||||
unsafe {
|
||||
let (base, len) = self.get_buf_parts();
|
||||
core::slice::from_raw_parts(base as *const _, len)
|
||||
}
|
||||
}
|
||||
fn get_buf_mut(&self) -> &'static mut [u8] {
|
||||
unsafe {
|
||||
let (base, len) = self.get_buf_parts();
|
||||
core::slice::from_raw_parts_mut(base, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
ctrl_reg: USBCTRL_REGS,
|
||||
ctrl_dpram: USBCTRL_DPRAM,
|
||||
in_endpoints: [Option<Endpoint>; 16],
|
||||
out_endpoints: [Option<Endpoint>; 16],
|
||||
next_offset: u16,
|
||||
read_setup: bool,
|
||||
#[cfg(feature = "rp2040-e5")]
|
||||
errata5_state: Option<errata5::Errata5State>,
|
||||
}
|
||||
impl Inner {
|
||||
fn new(ctrl_reg: USBCTRL_REGS, ctrl_dpram: USBCTRL_DPRAM) -> Self {
|
||||
Self {
|
||||
ctrl_reg,
|
||||
ctrl_dpram,
|
||||
in_endpoints: Default::default(),
|
||||
out_endpoints: Default::default(),
|
||||
next_offset: 0,
|
||||
read_setup: false,
|
||||
#[cfg(feature = "rp2040-e5")]
|
||||
errata5_state: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn ep_allocate(
|
||||
&mut self,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_dir: UsbDirection,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
) -> UsbResult<EndpointAddress> {
|
||||
let ep_addr = ep_addr
|
||||
.or_else(|| {
|
||||
let eps = if ep_dir == UsbDirection::In {
|
||||
self.in_endpoints.iter()
|
||||
} else {
|
||||
self.out_endpoints.iter()
|
||||
};
|
||||
// find free end point
|
||||
let mut iter = eps.enumerate();
|
||||
// reserve ep0 for the control endpoint
|
||||
if ep_type != EndpointType::Control {
|
||||
iter.next();
|
||||
}
|
||||
iter.find(|(_, ep)| ep.is_none())
|
||||
.map(|(index, _)| EndpointAddress::from_parts(index, ep_dir))
|
||||
})
|
||||
.ok_or(UsbError::EndpointOverflow)?;
|
||||
|
||||
let is_ep0 = ep_addr.index() == 0;
|
||||
let is_ctrl_ep = ep_type == EndpointType::Control;
|
||||
if !(is_ep0 ^ !is_ctrl_ep) {
|
||||
return Err(UsbError::Unsupported);
|
||||
}
|
||||
|
||||
let eps = if ep_addr.is_in() {
|
||||
&mut self.in_endpoints
|
||||
} else {
|
||||
&mut self.out_endpoints
|
||||
};
|
||||
let maybe_ep = eps
|
||||
.get_mut(ep_addr.index())
|
||||
.ok_or(UsbError::EndpointOverflow)?;
|
||||
if maybe_ep.is_some() {
|
||||
return Err(UsbError::InvalidEndpoint);
|
||||
}
|
||||
|
||||
// Validate buffer size. From datasheet (4.1.2.5):
|
||||
// Data Buffers are typically 64 bytes long as this is the max normal packet size for most FS packets.
|
||||
// For Isochronous endpoints a maximum buffer size of 1023 bytes is supported.
|
||||
// For other packet types the maximum size is 64 bytes per buffer.
|
||||
if (ep_type != EndpointType::Isochronous && max_packet_size > 64) || max_packet_size > 1023
|
||||
{
|
||||
return Err(UsbError::Unsupported);
|
||||
}
|
||||
|
||||
if ep_addr.index() == 0 {
|
||||
*maybe_ep = Some(Endpoint {
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
buffer_offset: 0, // not used on CTRL ep
|
||||
});
|
||||
} else {
|
||||
// size in 64bytes units.
|
||||
// NOTE: the compiler is smart enough to recognize /64 as a 6bit right shift so let's
|
||||
// keep the division here for the sake of clarity
|
||||
let aligned_sized = (max_packet_size + 63) / 64;
|
||||
if (self.next_offset + aligned_sized) > (4096 / 64) {
|
||||
return Err(UsbError::EndpointMemoryOverflow);
|
||||
}
|
||||
|
||||
let buffer_offset = self.next_offset;
|
||||
self.next_offset += aligned_sized;
|
||||
|
||||
*maybe_ep = Some(Endpoint {
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
buffer_offset,
|
||||
});
|
||||
}
|
||||
Ok(ep_addr)
|
||||
}
|
||||
|
||||
fn ep_reset_all(&mut self) {
|
||||
self.ctrl_reg
|
||||
.sie_ctrl
|
||||
.modify(|_, w| w.ep0_int_1buf().set_bit());
|
||||
// expect ctrl ep to receive on DATA first
|
||||
self.ctrl_dpram.ep_buffer_control[0].write(|w| w.pid_0().set_bit());
|
||||
self.ctrl_dpram.ep_buffer_control[1].write(|w| w.pid_0().set_bit());
|
||||
cortex_m::asm::delay(12);
|
||||
self.ctrl_dpram.ep_buffer_control[1].write(|w| w.available_0().set_bit());
|
||||
|
||||
for (index, ep) in itertools::interleave(
|
||||
self.in_endpoints.iter().skip(1), // skip control endpoint
|
||||
self.out_endpoints.iter().skip(1), // skip control endpoint
|
||||
)
|
||||
.enumerate()
|
||||
.filter_map(|(i, ep)| ep.as_ref().map(|ep| (i, ep)))
|
||||
{
|
||||
use pac::usbctrl_dpram::ep_control::ENDPOINT_TYPE_A;
|
||||
let ep_type = match ep.ep_type {
|
||||
EndpointType::Bulk => ENDPOINT_TYPE_A::BULK,
|
||||
EndpointType::Isochronous => ENDPOINT_TYPE_A::ISOCHRONOUS,
|
||||
EndpointType::Control => ENDPOINT_TYPE_A::CONTROL,
|
||||
EndpointType::Interrupt => ENDPOINT_TYPE_A::INTERRUPT,
|
||||
};
|
||||
// configure
|
||||
// ep 0 in&out are not part of index (skipped before enumeration)
|
||||
self.ctrl_dpram.ep_control[index].modify(|_, w| unsafe {
|
||||
w.endpoint_type().variant(ep_type);
|
||||
w.interrupt_per_buff().set_bit();
|
||||
w.enable().set_bit();
|
||||
w.buffer_address().bits(0x180 + (ep.buffer_offset << 6))
|
||||
});
|
||||
// reset OUT ep and prepare IN ep to accept data
|
||||
let buf_control = &self.ctrl_dpram.ep_buffer_control[index + 2];
|
||||
if (index & 1) == 0 {
|
||||
// first write occur on DATA0 so prepare the pid bit to be flipped
|
||||
buf_control.write(|w| w.pid_0().set_bit());
|
||||
} else {
|
||||
buf_control.write(|w| unsafe {
|
||||
w.pid_0().clear_bit();
|
||||
w.length_0().bits(ep.max_packet_size)
|
||||
});
|
||||
cortex_m::asm::delay(12);
|
||||
buf_control.modify(|_, w| w.available_0().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ep_write(&mut self, ep_addr: EndpointAddress, buf: &[u8]) -> UsbResult<usize> {
|
||||
let index = ep_addr.index();
|
||||
let ep = self
|
||||
.in_endpoints
|
||||
.get_mut(index)
|
||||
.and_then(Option::as_mut)
|
||||
.ok_or(UsbError::InvalidEndpoint)?;
|
||||
|
||||
let buf_control = &self.ctrl_dpram.ep_buffer_control[index * 2];
|
||||
if buf_control.read().available_0().bit_is_set() {
|
||||
return Err(UsbError::WouldBlock);
|
||||
}
|
||||
|
||||
let ep_buf = ep.get_buf_mut();
|
||||
if ep_buf.len() < buf.len() {
|
||||
return Err(UsbError::BufferOverflow);
|
||||
}
|
||||
ep_buf[..buf.len()].copy_from_slice(buf);
|
||||
|
||||
buf_control.modify(|r, w| unsafe {
|
||||
w.length_0().bits(buf.len() as u16);
|
||||
w.full_0().set_bit();
|
||||
w.pid_0().bit(!r.pid_0().bit())
|
||||
});
|
||||
cortex_m::asm::delay(12);
|
||||
buf_control.modify(|_, w| w.available_0().set_bit());
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ep_read(&mut self, ep_addr: EndpointAddress, buf: &mut [u8]) -> UsbResult<usize> {
|
||||
let index = ep_addr.index();
|
||||
let ep = self
|
||||
.out_endpoints
|
||||
.get_mut(index)
|
||||
.and_then(Option::as_mut)
|
||||
.ok_or(UsbError::InvalidEndpoint)?;
|
||||
|
||||
let buf_control = &self.ctrl_dpram.ep_buffer_control[index * 2 + 1];
|
||||
let buf_control_val = buf_control.read();
|
||||
|
||||
let process_setup = index == 0 && self.read_setup;
|
||||
if process_setup {
|
||||
// assume we want to read the setup request
|
||||
//
|
||||
// the OUT packet will be either data or a status zlp
|
||||
let len = 8;
|
||||
let ep_buf =
|
||||
unsafe { core::slice::from_raw_parts(USBCTRL_DPRAM::ptr() as *const u8, len) };
|
||||
if len > buf.len() {
|
||||
return Err(UsbError::BufferOverflow);
|
||||
}
|
||||
|
||||
buf[..len].copy_from_slice(&ep_buf[..len]);
|
||||
|
||||
// Next packet will be on DATA1 so clear pid_0 so it gets flipped by next buf config
|
||||
self.ctrl_dpram.ep_buffer_control[0].modify(|_, w| w.pid_0().clear_bit());
|
||||
// clear setup request flag
|
||||
self.ctrl_reg.sie_status.write(|w| w.setup_rec().set_bit());
|
||||
|
||||
// clear any out standing out flag e.g. in case a zlp got discarded
|
||||
self.ctrl_reg.buff_status.write(|w| unsafe { w.bits(2) });
|
||||
|
||||
let is_in_request = (buf[0] & 0x80) == 0x80;
|
||||
let data_length = u16::from(buf[6]) | (u16::from(buf[7]) << 8);
|
||||
let expect_data_or_zlp = is_in_request || data_length != 0;
|
||||
|
||||
buf_control.modify(|_, w| unsafe {
|
||||
w.length_0().bits(ep.max_packet_size);
|
||||
w.full_0().clear_bit();
|
||||
w.pid_0().set_bit()
|
||||
});
|
||||
// enable if and only if a dataphase is expected.
|
||||
cortex_m::asm::delay(12);
|
||||
buf_control.modify(|_, w| w.available_0().bit(expect_data_or_zlp));
|
||||
|
||||
self.read_setup = false;
|
||||
Ok(len)
|
||||
} else {
|
||||
if buf_control_val.full_0().bit_is_clear() {
|
||||
return Err(UsbError::WouldBlock);
|
||||
}
|
||||
let len = buf_control_val.length_0().bits().into();
|
||||
if len > buf.len() {
|
||||
return Err(UsbError::BufferOverflow);
|
||||
}
|
||||
|
||||
buf[..len].copy_from_slice(&ep.get_buf()[..len]);
|
||||
// Clear OUT flag once it is read.
|
||||
self.ctrl_reg
|
||||
.buff_status
|
||||
.write(|w| unsafe { w.bits(1 << (index * 2 + 1)) });
|
||||
|
||||
buf_control.modify(|r, w| unsafe {
|
||||
w.length_0().bits(ep.max_packet_size);
|
||||
w.full_0().clear_bit();
|
||||
w.pid_0().bit(!r.pid_0().bit())
|
||||
});
|
||||
if index != 0 || len == ep.max_packet_size.into() {
|
||||
// only mark as available on the control endpoint if and only if the packet was
|
||||
// max_packet_size
|
||||
cortex_m::asm::delay(12);
|
||||
buf_control.modify(|_, w| w.available_0().set_bit());
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Usb bus
|
||||
pub struct UsbBus {
|
||||
inner: Mutex<RefCell<Inner>>,
|
||||
}
|
||||
|
||||
impl UsbBus {
|
||||
/// Create new usb bus struct and bring up usb as device.
|
||||
pub fn new(
|
||||
ctrl_reg: USBCTRL_REGS,
|
||||
ctrl_dpram: USBCTRL_DPRAM,
|
||||
_pll: UsbClock,
|
||||
force_vbus_detect_bit: bool,
|
||||
resets: &mut RESETS,
|
||||
) -> Self {
|
||||
ctrl_reg.reset_bring_down(resets);
|
||||
ctrl_reg.reset_bring_up(resets);
|
||||
|
||||
unsafe {
|
||||
let raw_ctrl_reg =
|
||||
core::slice::from_raw_parts_mut(USBCTRL_REGS::ptr() as *mut u32, 1 + 0x98 / 4);
|
||||
raw_ctrl_reg.fill(0);
|
||||
|
||||
let raw_ctrl_pdram =
|
||||
core::slice::from_raw_parts_mut(USBCTRL_DPRAM::ptr() as *mut u32, 1 + 0xfc / 4);
|
||||
raw_ctrl_pdram.fill(0);
|
||||
}
|
||||
|
||||
ctrl_reg.usb_muxing.modify(|_, w| {
|
||||
w.to_phy().set_bit();
|
||||
w.softcon().set_bit()
|
||||
});
|
||||
|
||||
if force_vbus_detect_bit {
|
||||
ctrl_reg.usb_pwr.modify(|_, w| {
|
||||
w.vbus_detect().set_bit();
|
||||
w.vbus_detect_override_en().set_bit()
|
||||
});
|
||||
}
|
||||
ctrl_reg.main_ctrl.modify(|_, w| {
|
||||
w.sim_timing().clear_bit();
|
||||
w.host_ndevice().clear_bit();
|
||||
w.controller_en().set_bit()
|
||||
});
|
||||
|
||||
Self {
|
||||
inner: Mutex::new(RefCell::new(Inner::new(ctrl_reg, ctrl_dpram))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a resume request on the bus.
|
||||
pub fn remote_wakeup(&self) {
|
||||
critical_section::with(|cs| {
|
||||
let inner = self.inner.borrow(cs).borrow_mut();
|
||||
inner.ctrl_reg.sie_ctrl.modify(|_, w| w.resume().set_bit());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbBusTrait for UsbBus {
|
||||
fn alloc_ep(
|
||||
&mut self,
|
||||
ep_dir: UsbDirection,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
_interval: u8,
|
||||
) -> UsbResult<EndpointAddress> {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.inner.borrow(cs).borrow_mut();
|
||||
|
||||
inner.ep_allocate(ep_addr, ep_dir, ep_type, max_packet_size)
|
||||
})
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
critical_section::with(|cs| {
|
||||
let inner = self.inner.borrow(cs).borrow_mut();
|
||||
// at this stage ep's are expected to be in their reset state
|
||||
// TODO: is it worth having a debug_assert for that here?
|
||||
|
||||
// Enable interrupt generation when a buffer is done, when the bus is reset,
|
||||
// and when a setup packet is received
|
||||
// this should be sufficient for device mode, will need more for host.
|
||||
inner.ctrl_reg.inte.modify(|_, w| {
|
||||
w.buff_status()
|
||||
.set_bit()
|
||||
.bus_reset()
|
||||
.set_bit()
|
||||
.dev_resume_from_host()
|
||||
.set_bit()
|
||||
.dev_suspend()
|
||||
.set_bit()
|
||||
.setup_req()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
// enable pull up to let the host know we exist.
|
||||
inner
|
||||
.ctrl_reg
|
||||
.sie_ctrl
|
||||
.modify(|_, w| w.pullup_en().set_bit());
|
||||
})
|
||||
}
|
||||
fn reset(&self) {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.inner.borrow(cs).borrow_mut();
|
||||
|
||||
// clear reset flag
|
||||
inner.ctrl_reg.sie_status.write(|w| w.bus_reset().set_bit());
|
||||
inner
|
||||
.ctrl_reg
|
||||
.buff_status
|
||||
.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
|
||||
// reset all endpoints
|
||||
inner.ep_reset_all();
|
||||
|
||||
// Reset address register
|
||||
inner.ctrl_reg.addr_endp.reset();
|
||||
// TODO: RP2040-E5: work around implementation
|
||||
// TODO: reset all endpoints & buffer statuses
|
||||
})
|
||||
}
|
||||
fn set_device_address(&self, addr: u8) {
|
||||
critical_section::with(|cs| {
|
||||
let inner = self.inner.borrow(cs).borrow_mut();
|
||||
inner
|
||||
.ctrl_reg
|
||||
.addr_endp
|
||||
.modify(|_, w| unsafe { w.address().bits(addr & 0x3F) });
|
||||
// reset ep0
|
||||
inner.ctrl_dpram.ep_buffer_control[0].modify(|_, w| w.pid_0().set_bit());
|
||||
inner.ctrl_dpram.ep_buffer_control[1].modify(|_, w| w.pid_0().set_bit());
|
||||
})
|
||||
}
|
||||
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> UsbResult<usize> {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.inner.borrow(cs).borrow_mut();
|
||||
inner.ep_write(ep_addr, buf)
|
||||
})
|
||||
}
|
||||
fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> UsbResult<usize> {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.inner.borrow(cs).borrow_mut();
|
||||
inner.ep_read(ep_addr, buf)
|
||||
})
|
||||
}
|
||||
fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) {
|
||||
critical_section::with(|cs| {
|
||||
let inner = self.inner.borrow(cs).borrow_mut();
|
||||
|
||||
if ep_addr.index() == 0 {
|
||||
inner.ctrl_reg.ep_stall_arm.modify(|_, w| {
|
||||
if ep_addr.is_in() {
|
||||
w.ep0_in().bit(stalled)
|
||||
} else {
|
||||
w.ep0_out().bit(stalled)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let index = ep_addr_to_ep_buf_ctrl_idx(ep_addr);
|
||||
inner.ctrl_dpram.ep_buffer_control[index].modify(|_, w| w.stall().bit(stalled));
|
||||
})
|
||||
}
|
||||
fn is_stalled(&self, ep_addr: EndpointAddress) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let inner = self.inner.borrow(cs).borrow_mut();
|
||||
let index = ep_addr_to_ep_buf_ctrl_idx(ep_addr);
|
||||
inner.ctrl_dpram.ep_buffer_control[index]
|
||||
.read()
|
||||
.stall()
|
||||
.bit_is_set()
|
||||
})
|
||||
}
|
||||
fn suspend(&self) {}
|
||||
fn resume(&self) {}
|
||||
fn poll(&self) -> PollResult {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.inner.borrow(cs).borrow_mut();
|
||||
|
||||
#[cfg(feature = "rp2040-e5")]
|
||||
if let Some(state) = inner.errata5_state.take() {
|
||||
unsafe {
|
||||
inner.errata5_state = state.update();
|
||||
}
|
||||
return if inner.errata5_state.is_some() {
|
||||
PollResult::None
|
||||
} else {
|
||||
PollResult::Reset
|
||||
};
|
||||
}
|
||||
|
||||
// check for bus reset and/or suspended states.
|
||||
let ints = inner.ctrl_reg.ints.read();
|
||||
let mut buff_status = inner.ctrl_reg.buff_status.read().bits();
|
||||
|
||||
if ints.bus_reset().bit_is_set() {
|
||||
#[cfg(feature = "rp2040-e5")]
|
||||
if inner.ctrl_reg.sie_status.read().connected().bit_is_clear() {
|
||||
inner.errata5_state = Some(errata5::Errata5State::start());
|
||||
return PollResult::None;
|
||||
} else {
|
||||
return PollResult::Reset;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rp2040-e5"))]
|
||||
return PollResult::Reset;
|
||||
} else if buff_status == 0 && ints.setup_req().bit_is_clear() {
|
||||
if ints.dev_suspend().bit_is_set() {
|
||||
inner.ctrl_reg.sie_status.write(|w| w.suspended().set_bit());
|
||||
return PollResult::Suspend;
|
||||
} else if ints.dev_resume_from_host().bit_is_set() {
|
||||
inner.ctrl_reg.sie_status.write(|w| w.resume().set_bit());
|
||||
return PollResult::Resume;
|
||||
}
|
||||
return PollResult::None;
|
||||
}
|
||||
|
||||
let (mut ep_out, mut ep_in_complete, mut ep_setup): (u16, u16, u16) = (0, 0, 0);
|
||||
|
||||
// IN Complete shall only be reported once.
|
||||
inner
|
||||
.ctrl_reg
|
||||
.buff_status
|
||||
.write(|w| unsafe { w.bits(0x5555_5555) });
|
||||
|
||||
for i in 0..32u32 {
|
||||
if buff_status == 0 {
|
||||
break;
|
||||
} else if (buff_status & 1) == 1 {
|
||||
let is_in = (i & 1) == 0;
|
||||
let ep_idx = i / 2;
|
||||
if is_in {
|
||||
ep_in_complete |= 1 << ep_idx;
|
||||
} else {
|
||||
ep_out |= 1 << ep_idx;
|
||||
}
|
||||
}
|
||||
buff_status >>= 1;
|
||||
}
|
||||
|
||||
// check for setup request
|
||||
if ints.setup_req().bit_is_set() {
|
||||
// Small max_packet_size_ep0 Work-Around
|
||||
inner.ctrl_dpram.ep_buffer_control[0].modify(|_, w| w.available_0().clear_bit());
|
||||
|
||||
ep_setup |= 1;
|
||||
inner.read_setup = true;
|
||||
}
|
||||
|
||||
PollResult::Data {
|
||||
ep_out,
|
||||
ep_in_complete,
|
||||
ep_setup,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
//! After coming out of reset, the hardware expects 800us of LS_J (linestate J) time
|
||||
//! before it will move to the connected state. However on a hub that broadcasts packets
|
||||
//! for other devices this isn't the case. The plan here is to wait for the end of the bus
|
||||
//! reset, force an LS_J for 1ms and then switch control back to the USB phy. Unfortunately
|
||||
//! this requires us to use GPIO15 as there is no other way to force the input path.
|
||||
//! We only need to force DP as DM can be left at zero. It will be gated off by GPIO
|
||||
//! logic if it isn't func selected.
|
||||
|
||||
use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set};
|
||||
use rp2040_pac::Peripherals;
|
||||
|
||||
pub struct ForceLineStateJ {
|
||||
prev_pads: u32,
|
||||
prev_io_ctrls: u32,
|
||||
}
|
||||
pub enum Errata5State {
|
||||
WaitEndOfReset,
|
||||
ForceLineStateJ(ForceLineStateJ),
|
||||
}
|
||||
|
||||
impl Errata5State {
|
||||
pub fn start() -> Self {
|
||||
Self::WaitEndOfReset
|
||||
}
|
||||
/// SAFETY: This method steals the peripherals.
|
||||
/// It makes read only use of TIMER and read/write access to USBCTRL_REGS.
|
||||
/// Both peripherals must be initialized & running.
|
||||
pub unsafe fn update(self) -> Option<Self> {
|
||||
let pac = crate::pac::Peripherals::steal();
|
||||
match self {
|
||||
Self::WaitEndOfReset => {
|
||||
if pac.USBCTRL_REGS.sie_status.read().line_state().is_se0() {
|
||||
Some(self)
|
||||
} else {
|
||||
let reset_state = pac.RESETS.reset.read();
|
||||
assert!(
|
||||
reset_state.io_bank0().bit_is_clear()
|
||||
&& reset_state.pads_bank0().bit_is_clear(),
|
||||
"IO Bank 0 must be out of reset for this work around to function properly."
|
||||
);
|
||||
Some(Self::ForceLineStateJ(start_force_j(&pac)))
|
||||
}
|
||||
}
|
||||
Self::ForceLineStateJ(ref state) => {
|
||||
if pac
|
||||
.USBCTRL_REGS
|
||||
.sie_status
|
||||
.read()
|
||||
.connected()
|
||||
.bit_is_clear()
|
||||
{
|
||||
Some(self)
|
||||
} else {
|
||||
finish(&pac, state.prev_pads, state.prev_io_ctrls);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DP_PULLUP_EN_FLAG: u32 = 0x0000_0002;
|
||||
const DP_PULLUP_EN_OVERRIDE_FLAG: u32 = 0x0000_0004;
|
||||
|
||||
fn start_force_j(pac: &Peripherals) -> ForceLineStateJ {
|
||||
let pads = &pac.PADS_BANK0.gpio[15];
|
||||
let io = &pac.IO_BANK0.gpio[15];
|
||||
let usb_ctrl = &pac.USBCTRL_REGS;
|
||||
|
||||
assert!(!usb_ctrl.sie_status.read().line_state().is_se0());
|
||||
assert!(
|
||||
pac.IO_BANK0.gpio[16].gpio_ctrl.read().funcsel().bits() != 8,
|
||||
"Not expecting DM to be function 8"
|
||||
);
|
||||
|
||||
// backup io ctrl & pad ctrl
|
||||
let prev_pads = pads.read().bits();
|
||||
let prev_io_ctrls = io.gpio_ctrl.read().bits();
|
||||
|
||||
// Enable bus keep and force pin to tristate, so USB DP muxing doesn't affect
|
||||
// pin state
|
||||
pads.modify(|_, w| w.pue().set_bit().pde().set_bit());
|
||||
io.gpio_ctrl.modify(|_, w| w.oeover().disable());
|
||||
|
||||
// Select function 8 (USB debug muxing) without disturbing other controls
|
||||
io.gpio_ctrl.modify(|_, w| unsafe { w.funcsel().bits(8) });
|
||||
|
||||
// J state is a differential 1 for a full speed device so
|
||||
// DP = 1 and DM = 0. Don't actually need to set DM low as it
|
||||
// is already gated assuming it isn't funcseld.
|
||||
io.gpio_ctrl.modify(|_, w| w.inover().high());
|
||||
|
||||
// Force PHY pull up to stay before switching away from the phy
|
||||
unsafe {
|
||||
let usbphy_direct = usb_ctrl.usbphy_direct.as_ptr();
|
||||
let usbphy_direct_override = usb_ctrl.usbphy_direct_override.as_ptr();
|
||||
write_bitmask_set(usbphy_direct, DP_PULLUP_EN_FLAG);
|
||||
write_bitmask_set(usbphy_direct_override, DP_PULLUP_EN_OVERRIDE_FLAG);
|
||||
}
|
||||
|
||||
// Switch to GPIO phy with LS_J forced
|
||||
unsafe {
|
||||
usb_ctrl
|
||||
.usb_muxing
|
||||
.write_with_zero(|w| w.to_digital_pad().set_bit().softcon().set_bit());
|
||||
}
|
||||
|
||||
// LS_J is now forced, wait until the signal propagates through the usb logic.
|
||||
loop {
|
||||
let status = usb_ctrl.sie_status.read();
|
||||
if status.line_state().is_j() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ForceLineStateJ {
|
||||
prev_pads,
|
||||
prev_io_ctrls,
|
||||
}
|
||||
}
|
||||
fn finish(pac: &Peripherals, prev_pads: u32, prev_io_ctrls: u32) {
|
||||
let pads = &pac.PADS_BANK0.gpio[15];
|
||||
let io = &pac.IO_BANK0.gpio[15];
|
||||
|
||||
// Switch back to USB phy
|
||||
pac.USBCTRL_REGS
|
||||
.usb_muxing
|
||||
.write(|w| w.to_phy().set_bit().softcon().set_bit());
|
||||
|
||||
// Get rid of DP pullup override
|
||||
unsafe {
|
||||
let usbphy_direct_override = pac.USBCTRL_REGS.usbphy_direct_override.as_ptr();
|
||||
write_bitmask_clear(usbphy_direct_override, DP_PULLUP_EN_OVERRIDE_FLAG);
|
||||
|
||||
// Finally, restore the gpio ctrl value back to GPIO15
|
||||
io.gpio_ctrl.write(|w| w.bits(prev_io_ctrls));
|
||||
// Restore the pad ctrl value
|
||||
pads.write(|w| w.bits(prev_pads));
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue