mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2025-01-11 13:01:30 +11:00
Merge branch 'main' into uart
This commit is contained in:
commit
31b0230da1
|
@ -1,3 +1,11 @@
|
||||||
[build]
|
[build]
|
||||||
# Instruction set of Cortex-M0+
|
# Instruction set of Cortex-M0+
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "inline-threshold=5",
|
||||||
|
"-C", "no-vectorize-loops",
|
||||||
|
]
|
||||||
|
|
20
.github/workflows/check.yml
vendored
Normal file
20
.github/workflows/check.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
on: [push, pull_request]
|
||||||
|
name: CI Checks
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: cargo-check
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: sudo apt-get update
|
||||||
|
- run: sudo apt-get install gcc-arm-none-eabi
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --workspace --examples
|
18
.github/workflows/clippy.yml
vendored
Normal file
18
.github/workflows/clippy.yml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
on: [push, pull_request]
|
||||||
|
name: Clippy check
|
||||||
|
jobs:
|
||||||
|
clippy_check:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
components: clippy
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: -- -Dwarnings
|
19
.github/workflows/rustfmt.yml
vendored
Normal file
19
.github/workflows/rustfmt.yml
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
on: [push, pull_request]
|
||||||
|
name: Code formatting check
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
name: Rustfmt
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
components: rustfmt
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: -- --check
|
19
.github/workflows/tests_host.yml
vendored
Normal file
19
.github/workflows/tests_host.yml
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
on: [push, pull_request]
|
||||||
|
name: On-host tests
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check and Lint
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
## Tests are currently not working on host - disabled until we can find a good solution
|
||||||
|
# - uses: actions-rs/cargo@v1
|
||||||
|
# with:
|
||||||
|
# command: test
|
||||||
|
# args: --target x86_64-unknown-linux-gnu
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1,3 @@
|
||||||
.idea/
|
.idea/
|
||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
|
|
13
memory.x
Normal file
13
memory.x
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
MEMORY {
|
||||||
|
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
||||||
|
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/* ### Boot loader */
|
||||||
|
.boot2 ORIGIN(BOOT2) :
|
||||||
|
{
|
||||||
|
KEEP(*(.boot2));
|
||||||
|
} > BOOT2
|
||||||
|
} INSERT BEFORE .text;
|
|
@ -11,7 +11,13 @@ license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.7.1"
|
cortex-m = "0.7.1"
|
||||||
embedded-hal = "0.2.4"
|
embedded-hal = { version = "0.2.4", features = ["unproven"] }
|
||||||
nb = "1.0.0"
|
|
||||||
rp2040-pac = "0.1.1"
|
|
||||||
embedded-time = "0.10.1"
|
embedded-time = "0.10.1"
|
||||||
|
nb = "1.0.0"
|
||||||
|
rp2040-pac = { git = "https://github.com/rp-rs/rp2040-pac", branch="main" }
|
||||||
|
embedded-time = "0.10.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
cortex-m-rt = "0.6.13"
|
||||||
|
panic-halt = "0.2.0"
|
||||||
|
rp2040-boot2 = { git = "https://github.com/rp-rs/rp2040-boot2-rs", branch="main" }
|
||||||
|
|
30
rp2040-hal/examples/blinky.rs
Normal file
30
rp2040-hal/examples/blinky.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//! Blinks the LED on a Pico board
|
||||||
|
//!
|
||||||
|
//! This will blink an LED attached to GP25, which is the pin the Pico uses for the on-board LED.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::digital::v2::OutputPin;
|
||||||
|
use panic_halt as _;
|
||||||
|
use rp2040_hal::prelude::*;
|
||||||
|
|
||||||
|
#[link_section = ".boot2"]
|
||||||
|
#[used]
|
||||||
|
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS);
|
||||||
|
let mut led_pin = pins.gpio25.into_output();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led_pin.set_high().unwrap();
|
||||||
|
// TODO: Replace with proper 1s delays once we have clocks working
|
||||||
|
cortex_m::asm::delay(500_000);
|
||||||
|
led_pin.set_low().unwrap();
|
||||||
|
cortex_m::asm::delay(500_000);
|
||||||
|
}
|
||||||
|
}
|
33
rp2040-hal/examples/gpio_in_out.rs
Normal file
33
rp2040-hal/examples/gpio_in_out.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
//! Toggle LED based on GPIO input
|
||||||
|
//!
|
||||||
|
//! This will control an LED on GP25 based on a button hooked up to GP15. The button should be tied
|
||||||
|
//! to ground, as the input pin is pulled high internally by this example. When the button is
|
||||||
|
//! pressed, the LED will turn off.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||||
|
use panic_halt as _;
|
||||||
|
use rp2040_hal::prelude::*;
|
||||||
|
|
||||||
|
#[link_section = ".boot2"]
|
||||||
|
#[used]
|
||||||
|
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS);
|
||||||
|
let mut led_pin = pins.gpio25.into_output();
|
||||||
|
let button_pin = pins.gpio15.into_input().pull_up();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if button_pin.is_high().unwrap() {
|
||||||
|
led_pin.set_high().unwrap();
|
||||||
|
} else {
|
||||||
|
led_pin.set_low().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
300
rp2040-hal/src/gpio.rs
Normal file
300
rp2040-hal/src/gpio.rs
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
//! General Purpose Input and Output (GPIO)
|
||||||
|
//!
|
||||||
|
//! To access the GPIO pins you must call the `split` method on the IO bank. This will return a
|
||||||
|
//! `Parts` struct with access to the individual pins:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use rp2040_hal::prelude::*;
|
||||||
|
//! let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||||
|
//! let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Once you have the GPIO pins struct, you can take individual pins and configure them:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! let mut led_pin = pins.gpio25.into_output();
|
||||||
|
//! led_pin.set_high().unwrap();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Input pins support the following options:
|
||||||
|
//! - Pull high, pull low, or floating
|
||||||
|
//! - Schmitt trigger
|
||||||
|
//!
|
||||||
|
//! Output pins support the following options:
|
||||||
|
//! - Slew rate (fast or slow)
|
||||||
|
//! - Drive strength (2, 4, 8 or 12 mA)
|
||||||
|
|
||||||
|
/// Mode marker for an input pin
|
||||||
|
pub struct Input;
|
||||||
|
/// Mode marker for an output pin
|
||||||
|
pub struct Output;
|
||||||
|
/// Mode marker for a pin in an unknown state (generally happens at startup)
|
||||||
|
pub struct Unknown;
|
||||||
|
|
||||||
|
/// This trait adds a method to extract pins from an IO bank and convert them into HAL objects
|
||||||
|
pub trait GpioExt<PADS, SIO> {
|
||||||
|
/// The type of struct that will hold the pins once they're converted to HAL objects
|
||||||
|
type Parts;
|
||||||
|
|
||||||
|
/// Convert the IO bank into a struct of HAL pin objects
|
||||||
|
// TODO: Do we need a marker to check that clocks are up?
|
||||||
|
fn split(self, pads: PADS, sio: SIO, reset: &mut rp2040_pac::RESETS) -> Self::Parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! gpio {
|
||||||
|
($GPIOX:ident, $gpiox:ident, $PADSX:ident, $padsx:ident, $gpioxs:expr, [
|
||||||
|
$($PXi:ident: ($pxi:ident, $i:expr, $is:expr),)+
|
||||||
|
]) => {
|
||||||
|
#[doc = "HAL objects for the "]
|
||||||
|
#[doc = $gpioxs]
|
||||||
|
#[doc = " bank of GPIO pins"]
|
||||||
|
pub mod $gpiox {
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin};
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl GpioExt<pac::$PADSX, pac::SIO> for pac::$GPIOX {
|
||||||
|
type Parts = Parts;
|
||||||
|
|
||||||
|
fn split(self, pads: pac::$PADSX, sio: pac::SIO, resets: &mut pac::RESETS) -> Parts {
|
||||||
|
resets.reset.modify(|_, w| w.$gpiox().clear_bit().$padsx().clear_bit());
|
||||||
|
// TODO: Implement Resets in the HAL
|
||||||
|
while resets.reset_done.read().$gpiox().bit_is_clear() {
|
||||||
|
cortex_m::asm::delay(10);
|
||||||
|
}
|
||||||
|
while resets.reset_done.read().$padsx().bit_is_clear() {
|
||||||
|
cortex_m::asm::delay(10);
|
||||||
|
}
|
||||||
|
Parts {
|
||||||
|
_pads: pads,
|
||||||
|
_sio: sio,
|
||||||
|
$(
|
||||||
|
$pxi: $PXi { _mode: PhantomData },
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Struct containing HAL objects for all the "]
|
||||||
|
#[doc = $gpioxs]
|
||||||
|
#[doc = " pins"]
|
||||||
|
pub struct Parts {
|
||||||
|
_pads: pac::$PADSX,
|
||||||
|
_sio: pac::SIO,
|
||||||
|
$(
|
||||||
|
#[doc = "GPIO pin "]
|
||||||
|
#[doc = $is]
|
||||||
|
pub $pxi: $PXi<Unknown>,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacDriveStrength = pac::$padsx::gpio::DRIVE_A;
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[doc = "HAL object for GPIO pin "]
|
||||||
|
#[doc = $is]
|
||||||
|
pub struct $PXi<MODE> {
|
||||||
|
_mode: PhantomData<MODE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<MODE> $PXi<MODE> {
|
||||||
|
// This is safe because Parts owns the pads, and each pin is responsible
|
||||||
|
// for its own pad
|
||||||
|
fn pad(&self) -> &pac::$padsx::GPIO {
|
||||||
|
unsafe {
|
||||||
|
&(*pac::$PADSX::ptr()).gpio[$i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is safe because Parts owns the SIO. But callers must only touch their
|
||||||
|
// own pin
|
||||||
|
fn sio(&self) -> &pac::sio::RegisterBlock {
|
||||||
|
unsafe {
|
||||||
|
&(*pac::SIO::ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is safe because Parts owns the bank, and each pin is responsible
|
||||||
|
// for its own slice of the bank
|
||||||
|
fn gpio_ctrl(&self) -> &pac::$gpiox::gpio::GPIO_CTRL {
|
||||||
|
unsafe {
|
||||||
|
&(*pac::$GPIOX::ptr()).gpio[$i].gpio_ctrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Configure this pin as an output"]
|
||||||
|
pub fn into_output(self)-> $PXi<Output> {
|
||||||
|
self.pad().reset();
|
||||||
|
self.gpio_ctrl().write_with_zero(|x| { x.funcsel().sio_0() });
|
||||||
|
// TODO: Can we update the PAC to give us a safe register field
|
||||||
|
// instead of `bits`?
|
||||||
|
self.sio().gpio_oe_set.write(|x| unsafe { x.bits(1 << $i) });
|
||||||
|
$PXi { _mode: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Configure this pin as an input"]
|
||||||
|
pub fn into_input(self) -> $PXi<Input> {
|
||||||
|
self.pad().reset();
|
||||||
|
self.gpio_ctrl().write_with_zero(|x| { x.funcsel().sio_0() });
|
||||||
|
self.sio().gpio_oe_clr.write(|x| unsafe { x.bits(1 << $i) });
|
||||||
|
|
||||||
|
$PXi { _mode: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputPin for $PXi<Output> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.sio().gpio_out_clr.write(|x| unsafe { x.bits(1 << $i) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.sio().gpio_out_set.write(|x| unsafe { x.bits(1 << $i) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatefulOutputPin for $PXi<Output> {
|
||||||
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(!self.is_set_high()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.sio().gpio_out.read().bits() & (1 << $i) != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_input_for {
|
||||||
|
($MODE:ident) => {
|
||||||
|
impl InputPin for $PXi<$MODE> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(!self.is_high()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.sio().gpio_in.read().bits() & (1 << $i) != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Not allowed for Unknown since we don't know what state the pad is in
|
||||||
|
impl_input_for!(Input);
|
||||||
|
impl_input_for!(Output);
|
||||||
|
|
||||||
|
impl $PXi<Output> {
|
||||||
|
#[doc = "Configure the drive strength for this output pin"]
|
||||||
|
pub fn drive_strength(self, strength: OutputDriveStrength) -> Self {
|
||||||
|
let converted = match strength {
|
||||||
|
OutputDriveStrength::TwoMilliAmps => PacDriveStrength::_2MA,
|
||||||
|
OutputDriveStrength::FourMilliAmps => PacDriveStrength::_4MA,
|
||||||
|
OutputDriveStrength::EightMilliAmps => PacDriveStrength::_8MA,
|
||||||
|
OutputDriveStrength::TwelveMilliAmps => PacDriveStrength::_12MA,
|
||||||
|
};
|
||||||
|
self.pad().modify(|_, w| w.drive().variant(converted));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Configure the slew rate for this output pin"]
|
||||||
|
pub fn slew_rate(self, slew_rate: OutputSlewRate) -> Self {
|
||||||
|
self.pad().modify(|_, w| w.slewfast().bit(slew_rate == OutputSlewRate::Fast));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $PXi<Input> {
|
||||||
|
#[doc = "Pull this input pin high using internal resistors"]
|
||||||
|
pub fn pull_up(self) -> Self {
|
||||||
|
self.pad().modify(|_, w| w.pue().set_bit().pde().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Pull this input pin low using internal resistors"]
|
||||||
|
pub fn pull_down(self) -> Self {
|
||||||
|
self.pad().modify(|_, w| w.pue().clear_bit().pde().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Allow this input pin to float (i.e. don't pull it high or low)"]
|
||||||
|
pub fn float(self) -> Self {
|
||||||
|
self.pad().modify(|_, w| w.pue().clear_bit().pde().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Enable the schmitt trigger for this input pin"]
|
||||||
|
pub fn enable_schmitt_trigger(self) -> Self {
|
||||||
|
self.pad().modify(|_, w| w.schmitt().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Disable the schmitt trigger for this input pin"]
|
||||||
|
pub fn disable_schmitt_trigger(self) -> Self {
|
||||||
|
self.pad().modify(|_, w| w.schmitt().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio!(
|
||||||
|
IO_BANK0, io_bank0, PADS_BANK0, pads_bank0, "IO_BANK0", [
|
||||||
|
Gpio0: (gpio0, 0, "0"),
|
||||||
|
Gpio1: (gpio1, 1, "1"),
|
||||||
|
Gpio2: (gpio2, 2, "2"),
|
||||||
|
Gpio3: (gpio3, 3, "3"),
|
||||||
|
Gpio4: (gpio4, 4, "4"),
|
||||||
|
Gpio5: (gpio5, 5, "5"),
|
||||||
|
Gpio6: (gpio6, 6, "6"),
|
||||||
|
Gpio7: (gpio7, 7, "7"),
|
||||||
|
Gpio8: (gpio8, 8, "8"),
|
||||||
|
Gpio9: (gpio9, 9, "9"),
|
||||||
|
Gpio10: (gpio10, 10, "10"),
|
||||||
|
Gpio11: (gpio11, 11, "11"),
|
||||||
|
Gpio12: (gpio12, 12, "12"),
|
||||||
|
Gpio13: (gpio13, 13, "13"),
|
||||||
|
Gpio14: (gpio14, 14, "14"),
|
||||||
|
Gpio15: (gpio15, 15, "15"),
|
||||||
|
Gpio16: (gpio16, 16, "16"),
|
||||||
|
Gpio17: (gpio17, 17, "17"),
|
||||||
|
Gpio18: (gpio18, 18, "18"),
|
||||||
|
Gpio19: (gpio19, 19, "19"),
|
||||||
|
Gpio20: (gpio20, 20, "20"),
|
||||||
|
Gpio21: (gpio21, 21, "21"),
|
||||||
|
Gpio22: (gpio22, 22, "22"),
|
||||||
|
Gpio23: (gpio23, 23, "23"),
|
||||||
|
Gpio24: (gpio24, 24, "24"),
|
||||||
|
Gpio25: (gpio25, 25, "25"),
|
||||||
|
Gpio26: (gpio26, 26, "26"),
|
||||||
|
Gpio27: (gpio27, 27, "27"),
|
||||||
|
Gpio28: (gpio28, 28, "28"),
|
||||||
|
Gpio29: (gpio29, 29, "29"),
|
||||||
|
]
|
||||||
|
);
|
|
@ -10,9 +10,11 @@
|
||||||
extern crate cortex_m;
|
extern crate cortex_m;
|
||||||
extern crate embedded_hal as hal;
|
extern crate embedded_hal as hal;
|
||||||
extern crate nb;
|
extern crate nb;
|
||||||
|
|
||||||
pub extern crate rp2040_pac as pac;
|
pub extern crate rp2040_pac as pac;
|
||||||
|
|
||||||
pub mod adc;
|
pub mod adc;
|
||||||
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
|
@ -24,3 +26,4 @@ pub mod timer;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
pub mod usb;
|
pub mod usb;
|
||||||
pub mod watchdog;
|
pub mod watchdog;
|
||||||
|
pub mod xosc;
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
//! Prelude
|
//! Prelude
|
||||||
|
pub use crate::gpio::GpioExt;
|
||||||
|
|
|
@ -21,7 +21,10 @@ fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
|
||||||
unsafe {
|
unsafe {
|
||||||
let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
|
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);
|
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)
|
rom_table_lookup(
|
||||||
|
rom_hword_as_ptr(table) as *const u16,
|
||||||
|
u16::from_le_bytes(tag) as u32,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +266,7 @@ float_funcs! {
|
||||||
/// Convert a float to a signed 64-bit integer, rounding towards -Infinity, and clamping
|
/// Convert a float to a signed 64-bit integer, rounding towards -Infinity, and clamping
|
||||||
/// the result to lie within the range -0x8000000000000000 to 0x7FFFFFFFFFFFFFFF
|
/// the result to lie within the range -0x8000000000000000 to 0x7FFFFFFFFFFFFFFF
|
||||||
0x6c float_to_int64(v: f32) -> i64;
|
0x6c float_to_int64(v: f32) -> i64;
|
||||||
/// Convert a float to a signed fixed point 64-bit integer representation where n
|
/// Convert a float to a signed fixed point 64-bit integer representation where n
|
||||||
/// specifies the position of the binary point in the resulting fixed point representation -
|
/// specifies the position of the binary point in the resulting fixed point representation -
|
||||||
/// e.g. _float2fix(0.5f, 16) == 0x8000. This method rounds towards -Infinity, and
|
/// e.g. _float2fix(0.5f, 16) == 0x8000. This method rounds towards -Infinity, and
|
||||||
/// clamps the resulting integer to lie within the range -0x8000000000000000 to
|
/// clamps the resulting integer to lie within the range -0x8000000000000000 to
|
||||||
|
|
195
rp2040-hal/src/xosc.rs
Normal file
195
rp2040-hal/src/xosc.rs
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
//! Crystal Oscillator (XOSC)
|
||||||
|
// See [Chapter 2 Section 16](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||||
|
|
||||||
|
use core::convert::TryInto;
|
||||||
|
use core::{convert::Infallible, ops::RangeInclusive};
|
||||||
|
|
||||||
|
use embedded_time::{
|
||||||
|
duration::{Duration, Milliseconds},
|
||||||
|
fixed_point::FixedPoint,
|
||||||
|
fraction::Fraction,
|
||||||
|
rate::{Hertz, Megahertz, Rate},
|
||||||
|
};
|
||||||
|
|
||||||
|
use nb::Error::WouldBlock;
|
||||||
|
|
||||||
|
/// State of the Crystal Oscillator (typestate trait)
|
||||||
|
pub trait State {}
|
||||||
|
|
||||||
|
/// XOSC is disabled (typestate)
|
||||||
|
pub struct Disabled;
|
||||||
|
|
||||||
|
/// XOSC is initialized, ie we've given parameters (typestate)
|
||||||
|
pub struct Initialized {
|
||||||
|
freq_hz: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stable state (typestate)
|
||||||
|
pub struct Stable {
|
||||||
|
freq_hz: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// XOSC is in dormant mode (see Chapter 2, Section 16, §5)
|
||||||
|
pub struct Dormant;
|
||||||
|
|
||||||
|
impl State for Disabled {}
|
||||||
|
impl State for Initialized {}
|
||||||
|
impl State for Stable {}
|
||||||
|
impl State for Dormant {}
|
||||||
|
|
||||||
|
/// Possible errors when initializing the CrystalOscillator
|
||||||
|
pub enum Error {
|
||||||
|
/// Frequency is out of the 1-15MHz range (see datasheet)
|
||||||
|
FrequencyOutOfRange,
|
||||||
|
|
||||||
|
/// Argument is bad : overflows, ...
|
||||||
|
BadArgument,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blocking helper method to setup the XOSC without going through all the steps.
|
||||||
|
pub fn setup_xosc_blocking(
|
||||||
|
xosc_dev: rp2040_pac::XOSC,
|
||||||
|
frequency: Hertz,
|
||||||
|
) -> Result<CrystalOscillator<Stable>, Error> {
|
||||||
|
let initialized_xosc = CrystalOscillator::new(xosc_dev).initialize(frequency)?;
|
||||||
|
|
||||||
|
let stable_xosc_token = nb::block!(initialized_xosc.await_stabilization()).unwrap();
|
||||||
|
|
||||||
|
Ok(initialized_xosc.get_stable(stable_xosc_token))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Crystal Oscillator.
|
||||||
|
pub struct CrystalOscillator<S: State> {
|
||||||
|
device: rp2040_pac::XOSC,
|
||||||
|
state: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: State> CrystalOscillator<S> {
|
||||||
|
/// Transitions the oscillator to another state.
|
||||||
|
fn transition<To: State>(self, state: To) -> CrystalOscillator<To> {
|
||||||
|
CrystalOscillator {
|
||||||
|
device: self.device,
|
||||||
|
state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases the underlying device.
|
||||||
|
pub fn free(self) -> rp2040_pac::XOSC {
|
||||||
|
self.device
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrystalOscillator<Disabled> {
|
||||||
|
/// Creates a new CrystalOscillator from the underlying device.
|
||||||
|
pub fn new(dev: rp2040_pac::XOSC) -> Self {
|
||||||
|
CrystalOscillator {
|
||||||
|
device: dev,
|
||||||
|
state: Disabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the XOSC : frequency range is set, startup delay is calculated and set.
|
||||||
|
pub fn initialize(self, frequency: Hertz) -> Result<CrystalOscillator<Initialized>, Error> {
|
||||||
|
const ALLOWED_FREQUENCY_RANGE: RangeInclusive<Megahertz<u32>> =
|
||||||
|
Megahertz(1)..=Megahertz(15);
|
||||||
|
const STABLE_DELAY: Milliseconds = Milliseconds(1_u32);
|
||||||
|
const DIVIDER: Fraction = Fraction::new(256, 1);
|
||||||
|
|
||||||
|
let freq_mhz: Megahertz = frequency.into();
|
||||||
|
|
||||||
|
if !ALLOWED_FREQUENCY_RANGE.contains(&freq_mhz) {
|
||||||
|
return Err(Error::FrequencyOutOfRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.device.ctrl.write(|w| {
|
||||||
|
w.freq_range()._1_15mhz();
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
//1 ms = 10e-3 sec and Freq = 1/T where T is in seconds so 1ms converts to 1000Hz
|
||||||
|
let delay_to_hz: Hertz = STABLE_DELAY.to_rate().map_err(|_| Error::BadArgument)?;
|
||||||
|
|
||||||
|
//startup_delay = ((freq_hz * 10e-3) / 256) = ((freq_hz / 1000) / 256)
|
||||||
|
//See Chapter 2, Section 16, §3)
|
||||||
|
//We do the calculation first.
|
||||||
|
let startup_delay = frequency
|
||||||
|
.checked_div(delay_to_hz.integer())
|
||||||
|
.and_then(|r| r.to_generic::<u32>(DIVIDER).ok())
|
||||||
|
.ok_or(Error::BadArgument)?;
|
||||||
|
|
||||||
|
//Then we check if it fits into an u16.
|
||||||
|
let startup_delay: u16 = (*startup_delay.integer())
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::BadArgument)?;
|
||||||
|
|
||||||
|
self.device.startup.write(|w| unsafe {
|
||||||
|
w.delay().bits(startup_delay);
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
self.device.ctrl.write(|w| {
|
||||||
|
w.enable().enable();
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(self.transition(Initialized { freq_hz: frequency }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A token that's given when the oscillator is stablilzed, and can be exchanged to proceed to the next stage.
|
||||||
|
pub struct StableOscillatorToken {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrystalOscillator<Initialized> {
|
||||||
|
/// One has to wait for the startup delay before using the oscillator, ie awaiting stablilzation of the XOSC
|
||||||
|
pub fn await_stabilization(&self) -> nb::Result<StableOscillatorToken, Infallible> {
|
||||||
|
if self.device.status.read().stable().bit_is_clear() {
|
||||||
|
return Err(WouldBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(StableOscillatorToken { _private: () })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the stablilzed oscillator
|
||||||
|
pub fn get_stable(self, _token: StableOscillatorToken) -> CrystalOscillator<Stable> {
|
||||||
|
let freq_hz = self.state.freq_hz;
|
||||||
|
self.transition(Stable { freq_hz })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrystalOscillator<Stable> {
|
||||||
|
/// Operating frequency of the XOSC in hertz
|
||||||
|
pub fn operating_frequency(&self) -> Hertz {
|
||||||
|
self.state.freq_hz
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables the XOSC
|
||||||
|
pub fn disable(self) -> CrystalOscillator<Disabled> {
|
||||||
|
self.device.ctrl.modify(|_r, w| {
|
||||||
|
w.enable().disable();
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
self.transition(Disabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Put the XOSC in DORMANT state.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This method is marked unsafe because prior to switch the XOSC 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 XOSC to DORMANT state.
|
||||||
|
/// See Chapter 2, Section 16, §5) for details.
|
||||||
|
pub unsafe fn dormant(self) -> CrystalOscillator<Dormant> {
|
||||||
|
//taken from the C SDK
|
||||||
|
const XOSC_DORMANT_VALUE: u32 = 0x636f6d61;
|
||||||
|
|
||||||
|
self.device.dormant.write(|w| {
|
||||||
|
w.bits(XOSC_DORMANT_VALUE);
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
self.transition(Dormant)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue