diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-06-25 06:43:22 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-06-25 06:43:22 +0200 |
| commit | e6d6e82e54bca88ab1144802d2b716b867934225 (patch) | |
| tree | a143abef212ecf51582e54b13a93da3c95ef0ed0 | |
| parent | a35c8561c7dfafeabe10877a94f8519174dfe4c7 (diff) | |
| parent | 88bc2972f6c1b345d06618137fc6a1e22aeeee51 (diff) | |
Merge pull request #257 from embassy-rs/rp-clocks
rp: fixes and add SPi
| -rw-r--r-- | embassy-rp/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-rp/src/clocks.rs | 13 | ||||
| -rw-r--r-- | embassy-rp/src/gpio.rs | 56 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 4 | ||||
| -rw-r--r-- | embassy-rp/src/spi.rs | 179 | ||||
| -rw-r--r-- | examples/rp/src/bin/blinky.rs | 5 | ||||
| -rw-r--r-- | examples/rp/src/bin/button.rs | 6 |
7 files changed, 239 insertions, 27 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 323afa1ee..3d435a559 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -22,5 +22,6 @@ cortex-m-rt = "0.6.13" | |||
| 22 | cortex-m = "0.7.1" | 22 | cortex-m = "0.7.1" |
| 23 | critical-section = "0.2.1" | 23 | critical-section = "0.2.1" |
| 24 | 24 | ||
| 25 | rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="e8635fd05f43b6c21ec462fb8c06140e1fb26961", features = ["rt"] } | 25 | rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="fbb1004086225c74ff3c02db9309767cebef5dce", features = ["rt"] } |
| 26 | #rp2040-pac2 = { path = "../../rp/rp2040-pac2" } | ||
| 26 | embedded-hal = { version = "0.2.4", features = [ "unproven" ] } | 27 | embedded-hal = { version = "0.2.4", features = [ "unproven" ] } |
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 71e738c4c..c3ca4cf69 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -155,15 +155,16 @@ unsafe fn configure_pll( | |||
| 155 | }); | 155 | }); |
| 156 | p.fbdiv_int().write(|w| w.set_fbdiv_int(0)); | 156 | p.fbdiv_int().write(|w| w.set_fbdiv_int(0)); |
| 157 | 157 | ||
| 158 | let ref_mhz = XOSC_MHZ / refdiv; | 158 | let ref_freq = XOSC_MHZ * 1_000_000 / refdiv; |
| 159 | p.cs().write(|w| w.set_refdiv(ref_mhz as _)); | ||
| 160 | 159 | ||
| 161 | let fbdiv = vco_freq / (ref_mhz * 1_000_000); | 160 | let fbdiv = vco_freq / ref_freq; |
| 162 | assert!(fbdiv >= 16 && fbdiv <= 520); | 161 | assert!(fbdiv >= 16 && fbdiv <= 320); |
| 163 | assert!((post_div1 >= 1 && post_div1 <= 7) && (post_div2 >= 1 && post_div2 <= 7)); | 162 | assert!(post_div1 >= 1 && post_div1 <= 7); |
| 163 | assert!(post_div2 >= 1 && post_div2 <= 7); | ||
| 164 | assert!(post_div2 <= post_div1); | 164 | assert!(post_div2 <= post_div1); |
| 165 | assert!(ref_mhz <= (vco_freq / 16)); | 165 | assert!(ref_freq <= (vco_freq / 16)); |
| 166 | 166 | ||
| 167 | p.cs().write(|w| w.set_refdiv(refdiv as _)); | ||
| 167 | p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); | 168 | p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); |
| 168 | 169 | ||
| 169 | p.pwr().modify(|w| { | 170 | p.pwr().modify(|w| { |
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ee263fdb2..1eddce184 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs | |||
| @@ -7,7 +7,7 @@ use crate::peripherals; | |||
| 7 | 7 | ||
| 8 | use embassy::util::Unborrow; | 8 | use embassy::util::Unborrow; |
| 9 | use embassy_extras::{unborrow, unsafe_impl_unborrow}; | 9 | use embassy_extras::{unborrow, unsafe_impl_unborrow}; |
| 10 | use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; | 10 | use embedded_hal::digital::v2 as digital; |
| 11 | 11 | ||
| 12 | /// Represents a digital input or output level. | 12 | /// Represents a digital input or output level. |
| 13 | #[derive(Debug, Eq, PartialEq)] | 13 | #[derive(Debug, Eq, PartialEq)] |
| @@ -63,6 +63,15 @@ impl<'d, T: Pin> Input<'d, T> { | |||
| 63 | phantom: PhantomData, | 63 | phantom: PhantomData, |
| 64 | } | 64 | } |
| 65 | } | 65 | } |
| 66 | |||
| 67 | pub fn is_high(&self) -> bool { | ||
| 68 | !self.is_low() | ||
| 69 | } | ||
| 70 | |||
| 71 | pub fn is_low(&self) -> bool { | ||
| 72 | let val = 1 << self.pin.pin(); | ||
| 73 | unsafe { self.pin.sio_in().read() & val == 0 } | ||
| 74 | } | ||
| 66 | } | 75 | } |
| 67 | 76 | ||
| 68 | impl<'d, T: Pin> Drop for Input<'d, T> { | 77 | impl<'d, T: Pin> Drop for Input<'d, T> { |
| @@ -71,16 +80,15 @@ impl<'d, T: Pin> Drop for Input<'d, T> { | |||
| 71 | } | 80 | } |
| 72 | } | 81 | } |
| 73 | 82 | ||
| 74 | impl<'d, T: Pin> InputPin for Input<'d, T> { | 83 | impl<'d, T: Pin> digital::InputPin for Input<'d, T> { |
| 75 | type Error = !; | 84 | type Error = !; |
| 76 | 85 | ||
| 77 | fn is_high(&self) -> Result<bool, Self::Error> { | 86 | fn is_high(&self) -> Result<bool, Self::Error> { |
| 78 | self.is_low().map(|v| !v) | 87 | Ok(self.is_high()) |
| 79 | } | 88 | } |
| 80 | 89 | ||
| 81 | fn is_low(&self) -> Result<bool, Self::Error> { | 90 | fn is_low(&self) -> Result<bool, Self::Error> { |
| 82 | let val = 1 << self.pin.pin(); | 91 | Ok(self.is_low()) |
| 83 | Ok(unsafe { self.pin.sio_in().read() } & val == 0) | ||
| 84 | } | 92 | } |
| 85 | } | 93 | } |
| 86 | 94 | ||
| @@ -111,6 +119,29 @@ impl<'d, T: Pin> Output<'d, T> { | |||
| 111 | phantom: PhantomData, | 119 | phantom: PhantomData, |
| 112 | } | 120 | } |
| 113 | } | 121 | } |
| 122 | |||
| 123 | /// Set the output as high. | ||
| 124 | pub fn set_high(&mut self) { | ||
| 125 | let val = 1 << self.pin.pin(); | ||
| 126 | unsafe { self.pin.sio_out().value_set().write_value(val) }; | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Set the output as low. | ||
| 130 | pub fn set_low(&mut self) { | ||
| 131 | let val = 1 << self.pin.pin(); | ||
| 132 | unsafe { self.pin.sio_out().value_clr().write_value(val) }; | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Is the output pin set as high? | ||
| 136 | pub fn is_set_high(&self) -> bool { | ||
| 137 | !self.is_set_low() | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Is the output pin set as low? | ||
| 141 | pub fn is_set_low(&self) -> bool { | ||
| 142 | // todo | ||
| 143 | true | ||
| 144 | } | ||
| 114 | } | 145 | } |
| 115 | 146 | ||
| 116 | impl<'d, T: Pin> Drop for Output<'d, T> { | 147 | impl<'d, T: Pin> Drop for Output<'d, T> { |
| @@ -119,34 +150,31 @@ impl<'d, T: Pin> Drop for Output<'d, T> { | |||
| 119 | } | 150 | } |
| 120 | } | 151 | } |
| 121 | 152 | ||
| 122 | impl<'d, T: Pin> OutputPin for Output<'d, T> { | 153 | impl<'d, T: Pin> digital::OutputPin for Output<'d, T> { |
| 123 | type Error = !; | 154 | type Error = !; |
| 124 | 155 | ||
| 125 | /// Set the output as high. | 156 | /// Set the output as high. |
| 126 | fn set_high(&mut self) -> Result<(), Self::Error> { | 157 | fn set_high(&mut self) -> Result<(), Self::Error> { |
| 127 | let val = 1 << self.pin.pin(); | 158 | self.set_high(); |
| 128 | unsafe { self.pin.sio_out().value_set().write_value(val) }; | ||
| 129 | Ok(()) | 159 | Ok(()) |
| 130 | } | 160 | } |
| 131 | 161 | ||
| 132 | /// Set the output as low. | 162 | /// Set the output as low. |
| 133 | fn set_low(&mut self) -> Result<(), Self::Error> { | 163 | fn set_low(&mut self) -> Result<(), Self::Error> { |
| 134 | let val = 1 << self.pin.pin(); | 164 | self.set_low(); |
| 135 | unsafe { self.pin.sio_out().value_clr().write_value(val) }; | ||
| 136 | Ok(()) | 165 | Ok(()) |
| 137 | } | 166 | } |
| 138 | } | 167 | } |
| 139 | 168 | ||
| 140 | impl<'d, T: Pin> StatefulOutputPin for Output<'d, T> { | 169 | impl<'d, T: Pin> digital::StatefulOutputPin for Output<'d, T> { |
| 141 | /// Is the output pin set as high? | 170 | /// Is the output pin set as high? |
| 142 | fn is_set_high(&self) -> Result<bool, Self::Error> { | 171 | fn is_set_high(&self) -> Result<bool, Self::Error> { |
| 143 | self.is_set_low().map(|v| !v) | 172 | Ok(self.is_set_high()) |
| 144 | } | 173 | } |
| 145 | 174 | ||
| 146 | /// Is the output pin set as low? | 175 | /// Is the output pin set as low? |
| 147 | fn is_set_low(&self) -> Result<bool, Self::Error> { | 176 | fn is_set_low(&self) -> Result<bool, Self::Error> { |
| 148 | // todo | 177 | Ok(self.is_set_low()) |
| 149 | Ok(true) | ||
| 150 | } | 178 | } |
| 151 | } | 179 | } |
| 152 | 180 | ||
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 80761bc50..1e6417c98 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -14,6 +14,7 @@ pub mod interrupt; | |||
| 14 | 14 | ||
| 15 | pub mod dma; | 15 | pub mod dma; |
| 16 | pub mod gpio; | 16 | pub mod gpio; |
| 17 | pub mod spi; | ||
| 17 | pub mod uart; | 18 | pub mod uart; |
| 18 | 19 | ||
| 19 | mod clocks; | 20 | mod clocks; |
| @@ -60,6 +61,9 @@ embassy_extras::peripherals! { | |||
| 60 | UART0, | 61 | UART0, |
| 61 | UART1, | 62 | UART1, |
| 62 | 63 | ||
| 64 | SPI0, | ||
| 65 | SPI1, | ||
| 66 | |||
| 63 | DMA_CH0, | 67 | DMA_CH0, |
| 64 | DMA_CH1, | 68 | DMA_CH1, |
| 65 | DMA_CH2, | 69 | DMA_CH2, |
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs new file mode 100644 index 000000000..fb8e84230 --- /dev/null +++ b/embassy-rp/src/spi.rs | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy::util::Unborrow; | ||
| 4 | use embassy_extras::unborrow; | ||
| 5 | use embedded_hal::blocking::spi as eh; | ||
| 6 | use gpio::Pin; | ||
| 7 | |||
| 8 | use crate::{gpio, pac, peripherals}; | ||
| 9 | |||
| 10 | #[non_exhaustive] | ||
| 11 | pub struct Config { | ||
| 12 | pub frequency: u32, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl Default for Config { | ||
| 16 | fn default() -> Self { | ||
| 17 | Self { | ||
| 18 | frequency: 1_000_000, | ||
| 19 | } | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | pub struct Spi<'d, T: Instance> { | ||
| 24 | inner: T, | ||
| 25 | phantom: PhantomData<&'d mut T>, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<'d, T: Instance> Spi<'d, T> { | ||
| 29 | pub fn new( | ||
| 30 | inner: impl Unborrow<Target = T>, | ||
| 31 | clk: impl Unborrow<Target = impl ClkPin<T>>, | ||
| 32 | mosi: impl Unborrow<Target = impl MosiPin<T>>, | ||
| 33 | miso: impl Unborrow<Target = impl MisoPin<T>>, | ||
| 34 | config: Config, | ||
| 35 | ) -> Self { | ||
| 36 | unborrow!(inner, clk, mosi, miso); | ||
| 37 | |||
| 38 | unsafe { | ||
| 39 | let p = inner.regs(); | ||
| 40 | |||
| 41 | let clk_peri = crate::clocks::clk_peri_freq(); | ||
| 42 | assert!(config.frequency <= clk_peri); | ||
| 43 | |||
| 44 | // Find smallest prescale value which puts output frequency in range of | ||
| 45 | // post-divide. Prescale is an even number from 2 to 254 inclusive. | ||
| 46 | let presc = (2u32..=254).step_by(2).find(|&presc| { | ||
| 47 | (clk_peri as u64) < (presc as u64 + 2) * 256 * config.frequency as u64 | ||
| 48 | }); | ||
| 49 | |||
| 50 | // if this fails, frequency is too low. | ||
| 51 | let presc = unwrap!(presc); | ||
| 52 | |||
| 53 | // Find largest post-divide which makes output <= baudrate. Post-divide is | ||
| 54 | // an integer in the range 1 to 256 inclusive. | ||
| 55 | let postdiv = (1u32..=256) | ||
| 56 | .rev() | ||
| 57 | .find(|&postdiv| clk_peri / (presc * (postdiv - 1)) > config.frequency); | ||
| 58 | let postdiv = unwrap!(postdiv); | ||
| 59 | |||
| 60 | p.cpsr().write(|w| w.set_cpsdvsr(presc as _)); | ||
| 61 | p.cr0().write(|w| { | ||
| 62 | w.set_dss(0b0111); // 8bit | ||
| 63 | w.set_spo(false); | ||
| 64 | w.set_sph(false); | ||
| 65 | w.set_scr((postdiv - 1) as u8); | ||
| 66 | }); | ||
| 67 | p.cr1().write(|w| { | ||
| 68 | w.set_sse(true); // enable | ||
| 69 | }); | ||
| 70 | |||
| 71 | info!("SPI freq: {=u32}", clk_peri / (presc * postdiv)); | ||
| 72 | |||
| 73 | clk.io().ctrl().write(|w| w.set_funcsel(1)); | ||
| 74 | mosi.io().ctrl().write(|w| w.set_funcsel(1)); | ||
| 75 | miso.io().ctrl().write(|w| w.set_funcsel(1)); | ||
| 76 | } | ||
| 77 | Self { | ||
| 78 | inner, | ||
| 79 | phantom: PhantomData, | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn write(&mut self, data: &[u8]) { | ||
| 84 | unsafe { | ||
| 85 | let p = self.inner.regs(); | ||
| 86 | for &b in data { | ||
| 87 | while !p.sr().read().tnf() {} | ||
| 88 | p.dr().write(|w| w.set_data(b as _)); | ||
| 89 | } | ||
| 90 | self.flush(); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn flush(&mut self) { | ||
| 95 | unsafe { | ||
| 96 | let p = self.inner.regs(); | ||
| 97 | while p.sr().read().bsy() {} | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | fn drain_rx(&mut self) { | ||
| 102 | unsafe { | ||
| 103 | let p = self.inner.regs(); | ||
| 104 | while !p.sr().read().rne() { | ||
| 105 | p.dr().read(); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | impl<'d, T: Instance> eh::Write<u8> for Spi<'d, T> { | ||
| 112 | type Error = core::convert::Infallible; | ||
| 113 | |||
| 114 | fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { | ||
| 115 | self.write(words); | ||
| 116 | Ok(()) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | mod sealed { | ||
| 121 | use super::*; | ||
| 122 | |||
| 123 | pub trait Instance { | ||
| 124 | fn regs(&self) -> pac::spi::Spi; | ||
| 125 | } | ||
| 126 | pub trait ClkPin<T: Instance> {} | ||
| 127 | pub trait CsPin<T: Instance> {} | ||
| 128 | pub trait MosiPin<T: Instance> {} | ||
| 129 | pub trait MisoPin<T: Instance> {} | ||
| 130 | } | ||
| 131 | |||
| 132 | pub trait Instance: sealed::Instance {} | ||
| 133 | |||
| 134 | macro_rules! impl_instance { | ||
| 135 | ($type:ident, $irq:ident) => { | ||
| 136 | impl sealed::Instance for peripherals::$type { | ||
| 137 | fn regs(&self) -> pac::spi::Spi { | ||
| 138 | pac::$type | ||
| 139 | } | ||
| 140 | } | ||
| 141 | impl Instance for peripherals::$type {} | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | impl_instance!(SPI0, Spi0); | ||
| 146 | impl_instance!(SPI1, Spi1); | ||
| 147 | |||
| 148 | pub trait ClkPin<T: Instance>: sealed::ClkPin<T> + Pin {} | ||
| 149 | pub trait CsPin<T: Instance>: sealed::CsPin<T> + Pin {} | ||
| 150 | pub trait MosiPin<T: Instance>: sealed::MosiPin<T> + Pin {} | ||
| 151 | pub trait MisoPin<T: Instance>: sealed::MisoPin<T> + Pin {} | ||
| 152 | |||
| 153 | macro_rules! impl_pin { | ||
| 154 | ($pin:ident, $instance:ident, $function:ident) => { | ||
| 155 | impl sealed::$function<peripherals::$instance> for peripherals::$pin {} | ||
| 156 | impl $function<peripherals::$instance> for peripherals::$pin {} | ||
| 157 | }; | ||
| 158 | } | ||
| 159 | |||
| 160 | impl_pin!(PIN_0, SPI0, MisoPin); | ||
| 161 | impl_pin!(PIN_1, SPI0, CsPin); | ||
| 162 | impl_pin!(PIN_2, SPI0, ClkPin); | ||
| 163 | impl_pin!(PIN_3, SPI0, MosiPin); | ||
| 164 | impl_pin!(PIN_4, SPI0, MisoPin); | ||
| 165 | impl_pin!(PIN_5, SPI0, CsPin); | ||
| 166 | impl_pin!(PIN_6, SPI0, ClkPin); | ||
| 167 | impl_pin!(PIN_7, SPI0, MosiPin); | ||
| 168 | impl_pin!(PIN_8, SPI1, MisoPin); | ||
| 169 | impl_pin!(PIN_9, SPI1, CsPin); | ||
| 170 | impl_pin!(PIN_10, SPI1, ClkPin); | ||
| 171 | impl_pin!(PIN_11, SPI1, MosiPin); | ||
| 172 | impl_pin!(PIN_12, SPI1, MisoPin); | ||
| 173 | impl_pin!(PIN_13, SPI1, CsPin); | ||
| 174 | impl_pin!(PIN_14, SPI1, ClkPin); | ||
| 175 | impl_pin!(PIN_15, SPI1, MosiPin); | ||
| 176 | impl_pin!(PIN_16, SPI0, MisoPin); | ||
| 177 | impl_pin!(PIN_17, SPI0, CsPin); | ||
| 178 | impl_pin!(PIN_18, SPI0, ClkPin); | ||
| 179 | impl_pin!(PIN_19, SPI0, MosiPin); | ||
diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index e42999912..a162ed2f7 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs | |||
| @@ -12,7 +12,6 @@ mod example_common; | |||
| 12 | use defmt::*; | 12 | use defmt::*; |
| 13 | use embassy::executor::Spawner; | 13 | use embassy::executor::Spawner; |
| 14 | use embassy_rp::{gpio, Peripherals}; | 14 | use embassy_rp::{gpio, Peripherals}; |
| 15 | use embedded_hal::digital::v2::OutputPin; | ||
| 16 | use gpio::{Level, Output}; | 15 | use gpio::{Level, Output}; |
| 17 | 16 | ||
| 18 | #[embassy::main] | 17 | #[embassy::main] |
| @@ -21,11 +20,11 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 21 | 20 | ||
| 22 | loop { | 21 | loop { |
| 23 | info!("led on!"); | 22 | info!("led on!"); |
| 24 | led.set_high().unwrap(); | 23 | led.set_high(); |
| 25 | cortex_m::asm::delay(1_000_000); | 24 | cortex_m::asm::delay(1_000_000); |
| 26 | 25 | ||
| 27 | info!("led off!"); | 26 | info!("led off!"); |
| 28 | led.set_low().unwrap(); | 27 | led.set_low(); |
| 29 | cortex_m::asm::delay(1_000_000); | 28 | cortex_m::asm::delay(1_000_000); |
| 30 | } | 29 | } |
| 31 | } | 30 | } |
diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index c4d942ff5..12bc0e0d9 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) { | |||
| 20 | let mut led = Output::new(p.PIN_25, Level::Low); | 20 | let mut led = Output::new(p.PIN_25, Level::Low); |
| 21 | 21 | ||
| 22 | loop { | 22 | loop { |
| 23 | if button.is_high().unwrap() { | 23 | if button.is_high() { |
| 24 | led.set_high().unwrap(); | 24 | led.set_high(); |
| 25 | } else { | 25 | } else { |
| 26 | led.set_low().unwrap(); | 26 | led.set_low(); |
| 27 | } | 27 | } |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
