diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-06-25 06:23:46 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2021-06-25 06:24:14 +0200 |
| commit | 88bc2972f6c1b345d06618137fc6a1e22aeeee51 (patch) | |
| tree | a143abef212ecf51582e54b13a93da3c95ef0ed0 | |
| parent | 9cf1d5b29cae76b39b709a65afbef00906bfdd8e (diff) | |
rp/spi: add write-only spi driver
| -rw-r--r-- | embassy-rp/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 4 | ||||
| -rw-r--r-- | embassy-rp/src/spi.rs | 179 |
3 files changed, 185 insertions, 1 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/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); | ||
