From c7c897bb72bd88eb404871c641de69a542d2b56d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 25 Jun 2021 06:20:45 +0200 Subject: [PATCH 1/3] rp/gpio: add infallible inherent methods --- embassy-rp/src/gpio.rs | 56 ++++++++++++++++++++++++++--------- examples/rp/src/bin/blinky.rs | 5 ++-- examples/rp/src/bin/button.rs | 6 ++-- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ee263fdb..1eddce18 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -7,7 +7,7 @@ use crate::peripherals; use embassy::util::Unborrow; use embassy_extras::{unborrow, unsafe_impl_unborrow}; -use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; +use embedded_hal::digital::v2 as digital; /// Represents a digital input or output level. #[derive(Debug, Eq, PartialEq)] @@ -63,6 +63,15 @@ impl<'d, T: Pin> Input<'d, T> { phantom: PhantomData, } } + + pub fn is_high(&self) -> bool { + !self.is_low() + } + + pub fn is_low(&self) -> bool { + let val = 1 << self.pin.pin(); + unsafe { self.pin.sio_in().read() & val == 0 } + } } impl<'d, T: Pin> Drop for Input<'d, T> { @@ -71,16 +80,15 @@ impl<'d, T: Pin> Drop for Input<'d, T> { } } -impl<'d, T: Pin> InputPin for Input<'d, T> { +impl<'d, T: Pin> digital::InputPin for Input<'d, T> { type Error = !; fn is_high(&self) -> Result { - self.is_low().map(|v| !v) + Ok(self.is_high()) } fn is_low(&self) -> Result { - let val = 1 << self.pin.pin(); - Ok(unsafe { self.pin.sio_in().read() } & val == 0) + Ok(self.is_low()) } } @@ -111,6 +119,29 @@ impl<'d, T: Pin> Output<'d, T> { phantom: PhantomData, } } + + /// Set the output as high. + pub fn set_high(&mut self) { + let val = 1 << self.pin.pin(); + unsafe { self.pin.sio_out().value_set().write_value(val) }; + } + + /// Set the output as low. + pub fn set_low(&mut self) { + let val = 1 << self.pin.pin(); + unsafe { self.pin.sio_out().value_clr().write_value(val) }; + } + + /// Is the output pin set as high? + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } + + /// Is the output pin set as low? + pub fn is_set_low(&self) -> bool { + // todo + true + } } impl<'d, T: Pin> Drop for Output<'d, T> { @@ -119,34 +150,31 @@ impl<'d, T: Pin> Drop for Output<'d, T> { } } -impl<'d, T: Pin> OutputPin for Output<'d, T> { +impl<'d, T: Pin> digital::OutputPin for Output<'d, T> { type Error = !; /// Set the output as high. fn set_high(&mut self) -> Result<(), Self::Error> { - let val = 1 << self.pin.pin(); - unsafe { self.pin.sio_out().value_set().write_value(val) }; + self.set_high(); Ok(()) } /// Set the output as low. fn set_low(&mut self) -> Result<(), Self::Error> { - let val = 1 << self.pin.pin(); - unsafe { self.pin.sio_out().value_clr().write_value(val) }; + self.set_low(); Ok(()) } } -impl<'d, T: Pin> StatefulOutputPin for Output<'d, T> { +impl<'d, T: Pin> digital::StatefulOutputPin for Output<'d, T> { /// Is the output pin set as high? fn is_set_high(&self) -> Result { - self.is_set_low().map(|v| !v) + Ok(self.is_set_high()) } /// Is the output pin set as low? fn is_set_low(&self) -> Result { - // todo - Ok(true) + Ok(self.is_set_low()) } } diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index e4299991..a162ed2f 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs @@ -12,7 +12,6 @@ mod example_common; use defmt::*; use embassy::executor::Spawner; use embassy_rp::{gpio, Peripherals}; -use embedded_hal::digital::v2::OutputPin; use gpio::{Level, Output}; #[embassy::main] @@ -21,11 +20,11 @@ async fn main(_spawner: Spawner, p: Peripherals) { loop { info!("led on!"); - led.set_high().unwrap(); + led.set_high(); cortex_m::asm::delay(1_000_000); info!("led off!"); - led.set_low().unwrap(); + led.set_low(); cortex_m::asm::delay(1_000_000); } } diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index c4d942ff..12bc0e0d 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs @@ -20,10 +20,10 @@ async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PIN_25, Level::Low); loop { - if button.is_high().unwrap() { - led.set_high().unwrap(); + if button.is_high() { + led.set_high(); } else { - led.set_low().unwrap(); + led.set_low(); } } } From 9cf1d5b29cae76b39b709a65afbef00906bfdd8e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 25 Jun 2021 06:23:20 +0200 Subject: [PATCH 2/3] rp/clocks: fix wrong PLL setup --- embassy-rp/src/clocks.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 71e738c4..c3ca4cf6 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -155,15 +155,16 @@ unsafe fn configure_pll( }); p.fbdiv_int().write(|w| w.set_fbdiv_int(0)); - let ref_mhz = XOSC_MHZ / refdiv; - p.cs().write(|w| w.set_refdiv(ref_mhz as _)); + let ref_freq = XOSC_MHZ * 1_000_000 / refdiv; - let fbdiv = vco_freq / (ref_mhz * 1_000_000); - assert!(fbdiv >= 16 && fbdiv <= 520); - assert!((post_div1 >= 1 && post_div1 <= 7) && (post_div2 >= 1 && post_div2 <= 7)); + let fbdiv = vco_freq / ref_freq; + assert!(fbdiv >= 16 && fbdiv <= 320); + assert!(post_div1 >= 1 && post_div1 <= 7); + assert!(post_div2 >= 1 && post_div2 <= 7); assert!(post_div2 <= post_div1); - assert!(ref_mhz <= (vco_freq / 16)); + assert!(ref_freq <= (vco_freq / 16)); + p.cs().write(|w| w.set_refdiv(refdiv as _)); p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); p.pwr().modify(|w| { From 88bc2972f6c1b345d06618137fc6a1e22aeeee51 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 25 Jun 2021 06:23:46 +0200 Subject: [PATCH 3/3] rp/spi: add write-only spi driver --- embassy-rp/Cargo.toml | 3 +- embassy-rp/src/lib.rs | 4 + embassy-rp/src/spi.rs | 179 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/spi.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 323afa1e..3d435a55 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -22,5 +22,6 @@ cortex-m-rt = "0.6.13" cortex-m = "0.7.1" critical-section = "0.2.1" -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="e8635fd05f43b6c21ec462fb8c06140e1fb26961", features = ["rt"] } +rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="fbb1004086225c74ff3c02db9309767cebef5dce", features = ["rt"] } +#rp2040-pac2 = { path = "../../rp/rp2040-pac2" } embedded-hal = { version = "0.2.4", features = [ "unproven" ] } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 80761bc5..1e6417c9 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -14,6 +14,7 @@ pub mod interrupt; pub mod dma; pub mod gpio; +pub mod spi; pub mod uart; mod clocks; @@ -60,6 +61,9 @@ embassy_extras::peripherals! { UART0, UART1, + SPI0, + SPI1, + DMA_CH0, DMA_CH1, DMA_CH2, diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs new file mode 100644 index 00000000..fb8e8423 --- /dev/null +++ b/embassy-rp/src/spi.rs @@ -0,0 +1,179 @@ +use core::marker::PhantomData; + +use embassy::util::Unborrow; +use embassy_extras::unborrow; +use embedded_hal::blocking::spi as eh; +use gpio::Pin; + +use crate::{gpio, pac, peripherals}; + +#[non_exhaustive] +pub struct Config { + pub frequency: u32, +} + +impl Default for Config { + fn default() -> Self { + Self { + frequency: 1_000_000, + } + } +} + +pub struct Spi<'d, T: Instance> { + inner: T, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Spi<'d, T> { + pub fn new( + inner: impl Unborrow, + clk: impl Unborrow>, + mosi: impl Unborrow>, + miso: impl Unborrow>, + config: Config, + ) -> Self { + unborrow!(inner, clk, mosi, miso); + + unsafe { + let p = inner.regs(); + + let clk_peri = crate::clocks::clk_peri_freq(); + assert!(config.frequency <= clk_peri); + + // Find smallest prescale value which puts output frequency in range of + // post-divide. Prescale is an even number from 2 to 254 inclusive. + let presc = (2u32..=254).step_by(2).find(|&presc| { + (clk_peri as u64) < (presc as u64 + 2) * 256 * config.frequency as u64 + }); + + // if this fails, frequency is too low. + let presc = unwrap!(presc); + + // Find largest post-divide which makes output <= baudrate. Post-divide is + // an integer in the range 1 to 256 inclusive. + let postdiv = (1u32..=256) + .rev() + .find(|&postdiv| clk_peri / (presc * (postdiv - 1)) > config.frequency); + let postdiv = unwrap!(postdiv); + + p.cpsr().write(|w| w.set_cpsdvsr(presc as _)); + p.cr0().write(|w| { + w.set_dss(0b0111); // 8bit + w.set_spo(false); + w.set_sph(false); + w.set_scr((postdiv - 1) as u8); + }); + p.cr1().write(|w| { + w.set_sse(true); // enable + }); + + info!("SPI freq: {=u32}", clk_peri / (presc * postdiv)); + + clk.io().ctrl().write(|w| w.set_funcsel(1)); + mosi.io().ctrl().write(|w| w.set_funcsel(1)); + miso.io().ctrl().write(|w| w.set_funcsel(1)); + } + Self { + inner, + phantom: PhantomData, + } + } + + pub fn write(&mut self, data: &[u8]) { + unsafe { + let p = self.inner.regs(); + for &b in data { + while !p.sr().read().tnf() {} + p.dr().write(|w| w.set_data(b as _)); + } + self.flush(); + } + } + + pub fn flush(&mut self) { + unsafe { + let p = self.inner.regs(); + while p.sr().read().bsy() {} + } + } + + fn drain_rx(&mut self) { + unsafe { + let p = self.inner.regs(); + while !p.sr().read().rne() { + p.dr().read(); + } + } + } +} + +impl<'d, T: Instance> eh::Write for Spi<'d, T> { + type Error = core::convert::Infallible; + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.write(words); + Ok(()) + } +} + +mod sealed { + use super::*; + + pub trait Instance { + fn regs(&self) -> pac::spi::Spi; + } + pub trait ClkPin {} + pub trait CsPin {} + pub trait MosiPin {} + pub trait MisoPin {} +} + +pub trait Instance: sealed::Instance {} + +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type { + fn regs(&self) -> pac::spi::Spi { + pac::$type + } + } + impl Instance for peripherals::$type {} + }; +} + +impl_instance!(SPI0, Spi0); +impl_instance!(SPI1, Spi1); + +pub trait ClkPin: sealed::ClkPin + Pin {} +pub trait CsPin: sealed::CsPin + Pin {} +pub trait MosiPin: sealed::MosiPin + Pin {} +pub trait MisoPin: sealed::MisoPin + Pin {} + +macro_rules! impl_pin { + ($pin:ident, $instance:ident, $function:ident) => { + impl sealed::$function for peripherals::$pin {} + impl $function for peripherals::$pin {} + }; +} + +impl_pin!(PIN_0, SPI0, MisoPin); +impl_pin!(PIN_1, SPI0, CsPin); +impl_pin!(PIN_2, SPI0, ClkPin); +impl_pin!(PIN_3, SPI0, MosiPin); +impl_pin!(PIN_4, SPI0, MisoPin); +impl_pin!(PIN_5, SPI0, CsPin); +impl_pin!(PIN_6, SPI0, ClkPin); +impl_pin!(PIN_7, SPI0, MosiPin); +impl_pin!(PIN_8, SPI1, MisoPin); +impl_pin!(PIN_9, SPI1, CsPin); +impl_pin!(PIN_10, SPI1, ClkPin); +impl_pin!(PIN_11, SPI1, MosiPin); +impl_pin!(PIN_12, SPI1, MisoPin); +impl_pin!(PIN_13, SPI1, CsPin); +impl_pin!(PIN_14, SPI1, ClkPin); +impl_pin!(PIN_15, SPI1, MosiPin); +impl_pin!(PIN_16, SPI0, MisoPin); +impl_pin!(PIN_17, SPI0, CsPin); +impl_pin!(PIN_18, SPI0, ClkPin); +impl_pin!(PIN_19, SPI0, MosiPin);