diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-04-18 19:08:04 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-04-18 19:08:04 +0000 |
| commit | 08d9e5981ece5f8a354dd12b45d146f19b26b820 (patch) | |
| tree | d23428ff0278b4a38bf856cde83c11f872876dd5 | |
| parent | a673b9aa294a55c441f3f61c48481484629b4347 (diff) | |
| parent | 3260f6b2aff824a5f7f2a81283b234bdb1bd3e24 (diff) | |
Merge #1345
1345: Added a neopixel constructor to spi, with an example in the stm32g0 d… r=Dirbaio a=smeenka
For Spi I added the possibility to use the Spi device as a Neopixel driver, with very precise timing, and not dependent on software during the transfer. Even without the --release flag, the timing is perfect.
Note that between the bursts of data the Mosi line should stay on low level. A resistor of 10k can guarantee that, as it seems the the pin is floating between the bursts (on the nucleo-G070RB platform I use).
I created an example for the STM32G0 family.
This example does contain a very simple Neopixel driver, only for one type. But this Neopixel driver can easy be adapted to different types (for example RGBW types).
Co-authored-by: Dario Nieuwenhuis <[email protected]>
Co-authored-by: anton smeenk <[email protected]>
| -rw-r--r-- | embassy-stm32/src/dma/bdma.rs | 9 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/dma.rs | 11 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/gpdma.rs | 9 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/mod.rs | 49 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/word.rs | 79 | ||||
| -rw-r--r-- | embassy-stm32/src/spi/mod.rs | 183 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/spi_neopixel.rs | 101 |
7 files changed, 308 insertions, 133 deletions
diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index cf1222c46..a23bb8cd7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs | |||
| @@ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; | |||
| 9 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | 9 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 11 | ||
| 12 | use super::{Dir, Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::Dir; | ||
| 13 | use crate::_generated::BDMA_CHANNEL_COUNT; | 14 | use crate::_generated::BDMA_CHANNEL_COUNT; |
| 14 | use crate::interrupt::{Interrupt, InterruptExt}; | 15 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 15 | use crate::pac; | 16 | use crate::pac; |
| @@ -167,7 +168,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 167 | ptr as *mut u32, | 168 | ptr as *mut u32, |
| 168 | len, | 169 | len, |
| 169 | true, | 170 | true, |
| 170 | W::bits(), | 171 | W::size(), |
| 171 | options, | 172 | options, |
| 172 | ) | 173 | ) |
| 173 | } | 174 | } |
| @@ -202,7 +203,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 202 | ptr as *mut u32, | 203 | ptr as *mut u32, |
| 203 | len, | 204 | len, |
| 204 | true, | 205 | true, |
| 205 | W::bits(), | 206 | W::size(), |
| 206 | options, | 207 | options, |
| 207 | ) | 208 | ) |
| 208 | } | 209 | } |
| @@ -225,7 +226,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 225 | repeated as *const W as *mut u32, | 226 | repeated as *const W as *mut u32, |
| 226 | count, | 227 | count, |
| 227 | false, | 228 | false, |
| 228 | W::bits(), | 229 | W::size(), |
| 229 | options, | 230 | options, |
| 230 | ) | 231 | ) |
| 231 | } | 232 | } |
diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 62c092241..ef1d27573 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs | |||
| @@ -9,7 +9,8 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | |||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use pac::dma::regs; | 10 | use pac::dma::regs; |
| 11 | 11 | ||
| 12 | use super::{Dir, Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::Dir; | ||
| 13 | use crate::_generated::DMA_CHANNEL_COUNT; | 14 | use crate::_generated::DMA_CHANNEL_COUNT; |
| 14 | use crate::interrupt::{Interrupt, InterruptExt}; | 15 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 15 | use crate::pac::dma::vals; | 16 | use crate::pac::dma::vals; |
| @@ -246,7 +247,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 246 | ptr as *mut u32, | 247 | ptr as *mut u32, |
| 247 | len, | 248 | len, |
| 248 | true, | 249 | true, |
| 249 | W::bits(), | 250 | W::size(), |
| 250 | options, | 251 | options, |
| 251 | ) | 252 | ) |
| 252 | } | 253 | } |
| @@ -281,7 +282,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 281 | ptr as *mut u32, | 282 | ptr as *mut u32, |
| 282 | len, | 283 | len, |
| 283 | true, | 284 | true, |
| 284 | W::bits(), | 285 | W::size(), |
| 285 | options, | 286 | options, |
| 286 | ) | 287 | ) |
| 287 | } | 288 | } |
| @@ -304,7 +305,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 304 | repeated as *const W as *mut u32, | 305 | repeated as *const W as *mut u32, |
| 305 | count, | 306 | count, |
| 306 | false, | 307 | false, |
| 307 | W::bits(), | 308 | W::size(), |
| 308 | options, | 309 | options, |
| 309 | ) | 310 | ) |
| 310 | } | 311 | } |
| @@ -464,7 +465,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { | |||
| 464 | assert!(len > 0 && len <= 0xFFFF); | 465 | assert!(len > 0 && len <= 0xFFFF); |
| 465 | 466 | ||
| 466 | let dir = Dir::PeripheralToMemory; | 467 | let dir = Dir::PeripheralToMemory; |
| 467 | let data_size = W::bits(); | 468 | let data_size = W::size(); |
| 468 | 469 | ||
| 469 | let channel_number = channel.num(); | 470 | let channel_number = channel.num(); |
| 470 | let dma = channel.regs(); | 471 | let dma = channel.regs(); |
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 5c6676a5f..5a516ccda 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs | |||
| @@ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; | |||
| 9 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | 9 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 11 | ||
| 12 | use super::{Dir, Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::Dir; | ||
| 13 | use crate::_generated::GPDMA_CHANNEL_COUNT; | 14 | use crate::_generated::GPDMA_CHANNEL_COUNT; |
| 14 | use crate::interrupt::{Interrupt, InterruptExt}; | 15 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 15 | use crate::pac; | 16 | use crate::pac; |
| @@ -165,7 +166,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 165 | ptr as *mut u32, | 166 | ptr as *mut u32, |
| 166 | len, | 167 | len, |
| 167 | true, | 168 | true, |
| 168 | W::bits(), | 169 | W::size(), |
| 169 | options, | 170 | options, |
| 170 | ) | 171 | ) |
| 171 | } | 172 | } |
| @@ -200,7 +201,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 200 | ptr as *mut u32, | 201 | ptr as *mut u32, |
| 201 | len, | 202 | len, |
| 202 | true, | 203 | true, |
| 203 | W::bits(), | 204 | W::size(), |
| 204 | options, | 205 | options, |
| 205 | ) | 206 | ) |
| 206 | } | 207 | } |
| @@ -223,7 +224,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 223 | repeated as *const W as *mut u32, | 224 | repeated as *const W as *mut u32, |
| 224 | count, | 225 | count, |
| 225 | false, | 226 | false, |
| 226 | W::bits(), | 227 | W::size(), |
| 227 | options, | 228 | options, |
| 228 | ) | 229 | ) |
| 229 | } | 230 | } |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index d29ef4a1f..3312ca752 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -21,6 +21,8 @@ pub use gpdma::*; | |||
| 21 | #[cfg(dmamux)] | 21 | #[cfg(dmamux)] |
| 22 | mod dmamux; | 22 | mod dmamux; |
| 23 | 23 | ||
| 24 | pub mod word; | ||
| 25 | |||
| 24 | use core::mem; | 26 | use core::mem; |
| 25 | 27 | ||
| 26 | use embassy_cortex_m::interrupt::Priority; | 28 | use embassy_cortex_m::interrupt::Priority; |
| @@ -36,53 +38,6 @@ enum Dir { | |||
| 36 | PeripheralToMemory, | 38 | PeripheralToMemory, |
| 37 | } | 39 | } |
| 38 | 40 | ||
| 39 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 40 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 41 | pub enum WordSize { | ||
| 42 | OneByte, | ||
| 43 | TwoBytes, | ||
| 44 | FourBytes, | ||
| 45 | } | ||
| 46 | |||
| 47 | impl WordSize { | ||
| 48 | pub fn bytes(&self) -> usize { | ||
| 49 | match self { | ||
| 50 | Self::OneByte => 1, | ||
| 51 | Self::TwoBytes => 2, | ||
| 52 | Self::FourBytes => 4, | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | mod word_sealed { | ||
| 58 | pub trait Word {} | ||
| 59 | } | ||
| 60 | |||
| 61 | pub trait Word: word_sealed::Word { | ||
| 62 | fn bits() -> WordSize; | ||
| 63 | } | ||
| 64 | |||
| 65 | impl word_sealed::Word for u8 {} | ||
| 66 | impl Word for u8 { | ||
| 67 | fn bits() -> WordSize { | ||
| 68 | WordSize::OneByte | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | impl word_sealed::Word for u16 {} | ||
| 73 | impl Word for u16 { | ||
| 74 | fn bits() -> WordSize { | ||
| 75 | WordSize::TwoBytes | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | impl word_sealed::Word for u32 {} | ||
| 80 | impl Word for u32 { | ||
| 81 | fn bits() -> WordSize { | ||
| 82 | WordSize::FourBytes | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | pub struct NoDma; | 41 | pub struct NoDma; |
| 87 | 42 | ||
| 88 | impl_peripheral!(NoDma); | 43 | impl_peripheral!(NoDma); |
diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs new file mode 100644 index 000000000..aef6e9700 --- /dev/null +++ b/embassy-stm32/src/dma/word.rs | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 2 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 3 | pub enum WordSize { | ||
| 4 | OneByte, | ||
| 5 | TwoBytes, | ||
| 6 | FourBytes, | ||
| 7 | } | ||
| 8 | |||
| 9 | impl WordSize { | ||
| 10 | pub fn bytes(&self) -> usize { | ||
| 11 | match self { | ||
| 12 | Self::OneByte => 1, | ||
| 13 | Self::TwoBytes => 2, | ||
| 14 | Self::FourBytes => 4, | ||
| 15 | } | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | mod sealed { | ||
| 20 | pub trait Word {} | ||
| 21 | } | ||
| 22 | |||
| 23 | pub trait Word: sealed::Word + Default + Copy + 'static { | ||
| 24 | fn size() -> WordSize; | ||
| 25 | fn bits() -> usize; | ||
| 26 | } | ||
| 27 | |||
| 28 | macro_rules! impl_word { | ||
| 29 | (_, $T:ident, $bits:literal, $size:ident) => { | ||
| 30 | impl sealed::Word for $T {} | ||
| 31 | impl Word for $T { | ||
| 32 | fn bits() -> usize { | ||
| 33 | $bits | ||
| 34 | } | ||
| 35 | fn size() -> WordSize { | ||
| 36 | WordSize::$size | ||
| 37 | } | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | ($T:ident, $uX:ident, $bits:literal, $size:ident) => { | ||
| 41 | #[repr(transparent)] | ||
| 42 | #[derive(Copy, Clone, Default)] | ||
| 43 | pub struct $T(pub $uX); | ||
| 44 | impl_word!(_, $T, $bits, $size); | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | |||
| 48 | impl_word!(U1, u8, 1, OneByte); | ||
| 49 | impl_word!(U2, u8, 2, OneByte); | ||
| 50 | impl_word!(U3, u8, 3, OneByte); | ||
| 51 | impl_word!(U4, u8, 4, OneByte); | ||
| 52 | impl_word!(U5, u8, 5, OneByte); | ||
| 53 | impl_word!(U6, u8, 6, OneByte); | ||
| 54 | impl_word!(U7, u8, 7, OneByte); | ||
| 55 | impl_word!(_, u8, 8, OneByte); | ||
| 56 | impl_word!(U9, u16, 9, TwoBytes); | ||
| 57 | impl_word!(U10, u16, 10, TwoBytes); | ||
| 58 | impl_word!(U11, u16, 11, TwoBytes); | ||
| 59 | impl_word!(U12, u16, 12, TwoBytes); | ||
| 60 | impl_word!(U13, u16, 13, TwoBytes); | ||
| 61 | impl_word!(U14, u16, 14, TwoBytes); | ||
| 62 | impl_word!(U15, u16, 15, TwoBytes); | ||
| 63 | impl_word!(_, u16, 16, TwoBytes); | ||
| 64 | impl_word!(U17, u32, 17, FourBytes); | ||
| 65 | impl_word!(U18, u32, 18, FourBytes); | ||
| 66 | impl_word!(U19, u32, 19, FourBytes); | ||
| 67 | impl_word!(U20, u32, 20, FourBytes); | ||
| 68 | impl_word!(U21, u32, 21, FourBytes); | ||
| 69 | impl_word!(U22, u32, 22, FourBytes); | ||
| 70 | impl_word!(U23, u32, 23, FourBytes); | ||
| 71 | impl_word!(U24, u32, 24, FourBytes); | ||
| 72 | impl_word!(U25, u32, 25, FourBytes); | ||
| 73 | impl_word!(U26, u32, 26, FourBytes); | ||
| 74 | impl_word!(U27, u32, 27, FourBytes); | ||
| 75 | impl_word!(U28, u32, 28, FourBytes); | ||
| 76 | impl_word!(U29, u32, 29, FourBytes); | ||
| 77 | impl_word!(U30, u32, 30, FourBytes); | ||
| 78 | impl_word!(U31, u32, 31, FourBytes); | ||
| 79 | impl_word!(_, u32, 32, FourBytes); | ||
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 7858cb3e8..492d0649a 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -7,8 +7,7 @@ use embassy_futures::join::join; | |||
| 7 | use embassy_hal_common::{into_ref, PeripheralRef}; | 7 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 8 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | 8 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; |
| 9 | 9 | ||
| 10 | use self::sealed::WordSize; | 10 | use crate::dma::{slice_ptr_parts, word, Transfer}; |
| 11 | use crate::dma::{slice_ptr_parts, Transfer}; | ||
| 12 | use crate::gpio::sealed::{AFType, Pin as _}; | 11 | use crate::gpio::sealed::{AFType, Pin as _}; |
| 13 | use crate::gpio::{AnyPin, Pull}; | 12 | use crate::gpio::{AnyPin, Pull}; |
| 14 | use crate::pac::spi::{regs, vals, Spi as Regs}; | 13 | use crate::pac::spi::{regs, vals, Spi as Regs}; |
| @@ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> { | |||
| 78 | miso: Option<PeripheralRef<'d, AnyPin>>, | 77 | miso: Option<PeripheralRef<'d, AnyPin>>, |
| 79 | txdma: PeripheralRef<'d, Tx>, | 78 | txdma: PeripheralRef<'d, Tx>, |
| 80 | rxdma: PeripheralRef<'d, Rx>, | 79 | rxdma: PeripheralRef<'d, Rx>, |
| 81 | current_word_size: WordSize, | 80 | current_word_size: word_impl::Config, |
| 82 | } | 81 | } |
| 83 | 82 | ||
| 84 | impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | 83 | impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { |
| @@ -178,6 +177,23 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 178 | ) | 177 | ) |
| 179 | } | 178 | } |
| 180 | 179 | ||
| 180 | pub fn new_txonly_nosck( | ||
| 181 | peri: impl Peripheral<P = T> + 'd, | ||
| 182 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | ||
| 183 | txdma: impl Peripheral<P = Tx> + 'd, | ||
| 184 | rxdma: impl Peripheral<P = Rx> + 'd, // TODO: remove | ||
| 185 | freq: Hertz, | ||
| 186 | config: Config, | ||
| 187 | ) -> Self { | ||
| 188 | into_ref!(mosi); | ||
| 189 | unsafe { | ||
| 190 | mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); | ||
| 191 | mosi.set_speed(crate::gpio::Speed::Medium); | ||
| 192 | } | ||
| 193 | |||
| 194 | Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) | ||
| 195 | } | ||
| 196 | |||
| 181 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. | 197 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. |
| 182 | /// The bus can optionally be exposed externally with `Spi::new()` still. | 198 | /// The bus can optionally be exposed externally with `Spi::new()` still. |
| 183 | #[allow(dead_code)] | 199 | #[allow(dead_code)] |
| @@ -234,14 +250,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 234 | if mosi.is_none() { | 250 | if mosi.is_none() { |
| 235 | w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); | 251 | w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); |
| 236 | } | 252 | } |
| 237 | w.set_dff(WordSize::EightBit.dff()) | 253 | w.set_dff(<u8 as sealed::Word>::CONFIG) |
| 238 | }); | 254 | }); |
| 239 | } | 255 | } |
| 240 | #[cfg(spi_v2)] | 256 | #[cfg(spi_v2)] |
| 241 | unsafe { | 257 | unsafe { |
| 242 | T::REGS.cr2().modify(|w| { | 258 | T::REGS.cr2().modify(|w| { |
| 243 | w.set_frxth(WordSize::EightBit.frxth()); | 259 | let (ds, frxth) = <u8 as sealed::Word>::CONFIG; |
| 244 | w.set_ds(WordSize::EightBit.ds()); | 260 | w.set_frxth(frxth); |
| 261 | w.set_ds(ds); | ||
| 245 | w.set_ssoe(false); | 262 | w.set_ssoe(false); |
| 246 | }); | 263 | }); |
| 247 | T::REGS.cr1().modify(|w| { | 264 | T::REGS.cr1().modify(|w| { |
| @@ -279,7 +296,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 279 | T::REGS.cfg1().modify(|w| { | 296 | T::REGS.cfg1().modify(|w| { |
| 280 | w.set_crcen(false); | 297 | w.set_crcen(false); |
| 281 | w.set_mbr(br); | 298 | w.set_mbr(br); |
| 282 | w.set_dsize(WordSize::EightBit.dsize()); | 299 | w.set_dsize(<u8 as sealed::Word>::CONFIG); |
| 300 | w.set_fthlv(vals::Fthlv::ONEFRAME); | ||
| 283 | }); | 301 | }); |
| 284 | T::REGS.cr2().modify(|w| { | 302 | T::REGS.cr2().modify(|w| { |
| 285 | w.set_tsize(0); | 303 | w.set_tsize(0); |
| @@ -297,7 +315,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 297 | miso, | 315 | miso, |
| 298 | txdma, | 316 | txdma, |
| 299 | rxdma, | 317 | rxdma, |
| 300 | current_word_size: WordSize::EightBit, | 318 | current_word_size: <u8 as sealed::Word>::CONFIG, |
| 301 | } | 319 | } |
| 302 | } | 320 | } |
| 303 | 321 | ||
| @@ -355,7 +373,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 355 | } | 373 | } |
| 356 | } | 374 | } |
| 357 | 375 | ||
| 358 | fn set_word_size(&mut self, word_size: WordSize) { | 376 | fn set_word_size(&mut self, word_size: word_impl::Config) { |
| 359 | if self.current_word_size == word_size { | 377 | if self.current_word_size == word_size { |
| 360 | return; | 378 | return; |
| 361 | } | 379 | } |
| @@ -364,7 +382,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 364 | unsafe { | 382 | unsafe { |
| 365 | T::REGS.cr1().modify(|reg| { | 383 | T::REGS.cr1().modify(|reg| { |
| 366 | reg.set_spe(false); | 384 | reg.set_spe(false); |
| 367 | reg.set_dff(word_size.dff()) | 385 | reg.set_dff(word_size) |
| 368 | }); | 386 | }); |
| 369 | T::REGS.cr1().modify(|reg| { | 387 | T::REGS.cr1().modify(|reg| { |
| 370 | reg.set_spe(true); | 388 | reg.set_spe(true); |
| @@ -376,8 +394,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 376 | w.set_spe(false); | 394 | w.set_spe(false); |
| 377 | }); | 395 | }); |
| 378 | T::REGS.cr2().modify(|w| { | 396 | T::REGS.cr2().modify(|w| { |
| 379 | w.set_frxth(word_size.frxth()); | 397 | w.set_frxth(word_size.1); |
| 380 | w.set_ds(word_size.ds()); | 398 | w.set_ds(word_size.0); |
| 381 | }); | 399 | }); |
| 382 | T::REGS.cr1().modify(|w| { | 400 | T::REGS.cr1().modify(|w| { |
| 383 | w.set_spe(true); | 401 | w.set_spe(true); |
| @@ -393,7 +411,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 393 | w.set_spe(false); | 411 | w.set_spe(false); |
| 394 | }); | 412 | }); |
| 395 | T::REGS.cfg1().modify(|w| { | 413 | T::REGS.cfg1().modify(|w| { |
| 396 | w.set_dsize(word_size.dsize()); | 414 | w.set_dsize(word_size); |
| 397 | }); | 415 | }); |
| 398 | T::REGS.cr1().modify(|w| { | 416 | T::REGS.cr1().modify(|w| { |
| 399 | w.set_csusp(false); | 417 | w.set_csusp(false); |
| @@ -412,7 +430,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 412 | return Ok(()); | 430 | return Ok(()); |
| 413 | } | 431 | } |
| 414 | 432 | ||
| 415 | self.set_word_size(W::WORDSIZE); | 433 | self.set_word_size(W::CONFIG); |
| 416 | unsafe { | 434 | unsafe { |
| 417 | T::REGS.cr1().modify(|w| { | 435 | T::REGS.cr1().modify(|w| { |
| 418 | w.set_spe(false); | 436 | w.set_spe(false); |
| @@ -450,7 +468,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 450 | return Ok(()); | 468 | return Ok(()); |
| 451 | } | 469 | } |
| 452 | 470 | ||
| 453 | self.set_word_size(W::WORDSIZE); | 471 | self.set_word_size(W::CONFIG); |
| 454 | unsafe { | 472 | unsafe { |
| 455 | T::REGS.cr1().modify(|w| { | 473 | T::REGS.cr1().modify(|w| { |
| 456 | w.set_spe(false); | 474 | w.set_spe(false); |
| @@ -513,7 +531,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 513 | return Ok(()); | 531 | return Ok(()); |
| 514 | } | 532 | } |
| 515 | 533 | ||
| 516 | self.set_word_size(W::WORDSIZE); | 534 | self.set_word_size(W::CONFIG); |
| 517 | unsafe { | 535 | unsafe { |
| 518 | T::REGS.cr1().modify(|w| { | 536 | T::REGS.cr1().modify(|w| { |
| 519 | w.set_spe(false); | 537 | w.set_spe(false); |
| @@ -571,7 +589,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 571 | pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { | 589 | pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { |
| 572 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | 590 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |
| 573 | flush_rx_fifo(T::REGS); | 591 | flush_rx_fifo(T::REGS); |
| 574 | self.set_word_size(W::WORDSIZE); | 592 | self.set_word_size(W::CONFIG); |
| 575 | for word in words.iter() { | 593 | for word in words.iter() { |
| 576 | let _ = transfer_word(T::REGS, *word)?; | 594 | let _ = transfer_word(T::REGS, *word)?; |
| 577 | } | 595 | } |
| @@ -581,7 +599,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 581 | pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { | 599 | pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |
| 582 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | 600 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |
| 583 | flush_rx_fifo(T::REGS); | 601 | flush_rx_fifo(T::REGS); |
| 584 | self.set_word_size(W::WORDSIZE); | 602 | self.set_word_size(W::CONFIG); |
| 585 | for word in words.iter_mut() { | 603 | for word in words.iter_mut() { |
| 586 | *word = transfer_word(T::REGS, W::default())?; | 604 | *word = transfer_word(T::REGS, W::default())?; |
| 587 | } | 605 | } |
| @@ -591,7 +609,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 591 | pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { | 609 | pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |
| 592 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | 610 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |
| 593 | flush_rx_fifo(T::REGS); | 611 | flush_rx_fifo(T::REGS); |
| 594 | self.set_word_size(W::WORDSIZE); | 612 | self.set_word_size(W::CONFIG); |
| 595 | for word in words.iter_mut() { | 613 | for word in words.iter_mut() { |
| 596 | *word = transfer_word(T::REGS, *word)?; | 614 | *word = transfer_word(T::REGS, *word)?; |
| 597 | } | 615 | } |
| @@ -601,7 +619,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 601 | pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | 619 | pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |
| 602 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | 620 | unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |
| 603 | flush_rx_fifo(T::REGS); | 621 | flush_rx_fifo(T::REGS); |
| 604 | self.set_word_size(W::WORDSIZE); | 622 | self.set_word_size(W::CONFIG); |
| 605 | let len = read.len().max(write.len()); | 623 | let len = read.len().max(write.len()); |
| 606 | for i in 0..len { | 624 | for i in 0..len { |
| 607 | let wb = write.get(i).copied().unwrap_or_default(); | 625 | let wb = write.get(i).copied().unwrap_or_default(); |
| @@ -933,70 +951,89 @@ pub(crate) mod sealed { | |||
| 933 | const REGS: Regs; | 951 | const REGS: Regs; |
| 934 | } | 952 | } |
| 935 | 953 | ||
| 936 | pub trait Word: Copy + 'static { | 954 | pub trait Word { |
| 937 | const WORDSIZE: WordSize; | 955 | const CONFIG: word_impl::Config; |
| 938 | } | ||
| 939 | |||
| 940 | impl Word for u8 { | ||
| 941 | const WORDSIZE: WordSize = WordSize::EightBit; | ||
| 942 | } | ||
| 943 | impl Word for u16 { | ||
| 944 | const WORDSIZE: WordSize = WordSize::SixteenBit; | ||
| 945 | } | 956 | } |
| 957 | } | ||
| 946 | 958 | ||
| 947 | #[derive(Copy, Clone, PartialOrd, PartialEq)] | 959 | pub trait Word: word::Word + sealed::Word {} |
| 948 | pub enum WordSize { | ||
| 949 | EightBit, | ||
| 950 | SixteenBit, | ||
| 951 | } | ||
| 952 | 960 | ||
| 953 | impl WordSize { | 961 | macro_rules! impl_word { |
| 954 | #[cfg(any(spi_v1, spi_f1))] | 962 | ($T:ty, $config:expr) => { |
| 955 | pub fn dff(&self) -> vals::Dff { | 963 | impl sealed::Word for $T { |
| 956 | match self { | 964 | const CONFIG: Config = $config; |
| 957 | WordSize::EightBit => vals::Dff::EIGHTBIT, | ||
| 958 | WordSize::SixteenBit => vals::Dff::SIXTEENBIT, | ||
| 959 | } | ||
| 960 | } | 965 | } |
| 966 | impl Word for $T {} | ||
| 967 | }; | ||
| 968 | } | ||
| 961 | 969 | ||
| 962 | #[cfg(spi_v2)] | 970 | #[cfg(any(spi_v1, spi_f1))] |
| 963 | pub fn ds(&self) -> vals::Ds { | 971 | mod word_impl { |
| 964 | match self { | 972 | use super::*; |
| 965 | WordSize::EightBit => vals::Ds::EIGHTBIT, | ||
| 966 | WordSize::SixteenBit => vals::Ds::SIXTEENBIT, | ||
| 967 | } | ||
| 968 | } | ||
| 969 | 973 | ||
| 970 | #[cfg(spi_v2)] | 974 | pub type Config = vals::Dff; |
| 971 | pub fn frxth(&self) -> vals::Frxth { | ||
| 972 | match self { | ||
| 973 | WordSize::EightBit => vals::Frxth::QUARTER, | ||
| 974 | WordSize::SixteenBit => vals::Frxth::HALF, | ||
| 975 | } | ||
| 976 | } | ||
| 977 | 975 | ||
| 978 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 976 | impl_word!(u8, vals::Dff::EIGHTBIT); |
| 979 | pub fn dsize(&self) -> u8 { | 977 | impl_word!(u16, vals::Dff::SIXTEENBIT); |
| 980 | match self { | 978 | } |
| 981 | WordSize::EightBit => 0b0111, | ||
| 982 | WordSize::SixteenBit => 0b1111, | ||
| 983 | } | ||
| 984 | } | ||
| 985 | 979 | ||
| 986 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 980 | #[cfg(any(spi_v2))] |
| 987 | pub fn _frxth(&self) -> vals::Fthlv { | 981 | mod word_impl { |
| 988 | match self { | 982 | use super::*; |
| 989 | WordSize::EightBit => vals::Fthlv::ONEFRAME, | 983 | |
| 990 | WordSize::SixteenBit => vals::Fthlv::ONEFRAME, | 984 | pub type Config = (vals::Ds, vals::Frxth); |
| 991 | } | 985 | |
| 992 | } | 986 | impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER)); |
| 993 | } | 987 | impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER)); |
| 988 | impl_word!(word::U6, (vals::Ds::SIXBIT, vals::Frxth::QUARTER)); | ||
| 989 | impl_word!(word::U7, (vals::Ds::SEVENBIT, vals::Frxth::QUARTER)); | ||
| 990 | impl_word!(u8, (vals::Ds::EIGHTBIT, vals::Frxth::QUARTER)); | ||
| 991 | impl_word!(word::U9, (vals::Ds::NINEBIT, vals::Frxth::HALF)); | ||
| 992 | impl_word!(word::U10, (vals::Ds::TENBIT, vals::Frxth::HALF)); | ||
| 993 | impl_word!(word::U11, (vals::Ds::ELEVENBIT, vals::Frxth::HALF)); | ||
| 994 | impl_word!(word::U12, (vals::Ds::TWELVEBIT, vals::Frxth::HALF)); | ||
| 995 | impl_word!(word::U13, (vals::Ds::THIRTEENBIT, vals::Frxth::HALF)); | ||
| 996 | impl_word!(word::U14, (vals::Ds::FOURTEENBIT, vals::Frxth::HALF)); | ||
| 997 | impl_word!(word::U15, (vals::Ds::FIFTEENBIT, vals::Frxth::HALF)); | ||
| 998 | impl_word!(u16, (vals::Ds::SIXTEENBIT, vals::Frxth::HALF)); | ||
| 994 | } | 999 | } |
| 995 | 1000 | ||
| 996 | pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {} | 1001 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 1002 | mod word_impl { | ||
| 1003 | use super::*; | ||
| 997 | 1004 | ||
| 998 | impl Word for u8 {} | 1005 | pub type Config = u8; |
| 999 | impl Word for u16 {} | 1006 | |
| 1007 | impl_word!(word::U4, 4 - 1); | ||
| 1008 | impl_word!(word::U5, 5 - 1); | ||
| 1009 | impl_word!(word::U6, 6 - 1); | ||
| 1010 | impl_word!(word::U7, 7 - 1); | ||
| 1011 | impl_word!(u8, 8 - 1); | ||
| 1012 | impl_word!(word::U9, 9 - 1); | ||
| 1013 | impl_word!(word::U10, 10 - 1); | ||
| 1014 | impl_word!(word::U11, 11 - 1); | ||
| 1015 | impl_word!(word::U12, 12 - 1); | ||
| 1016 | impl_word!(word::U13, 13 - 1); | ||
| 1017 | impl_word!(word::U14, 14 - 1); | ||
| 1018 | impl_word!(word::U15, 15 - 1); | ||
| 1019 | impl_word!(u16, 16 - 1); | ||
| 1020 | impl_word!(word::U17, 17 - 1); | ||
| 1021 | impl_word!(word::U18, 18 - 1); | ||
| 1022 | impl_word!(word::U19, 19 - 1); | ||
| 1023 | impl_word!(word::U20, 20 - 1); | ||
| 1024 | impl_word!(word::U21, 21 - 1); | ||
| 1025 | impl_word!(word::U22, 22 - 1); | ||
| 1026 | impl_word!(word::U23, 23 - 1); | ||
| 1027 | impl_word!(word::U24, 24 - 1); | ||
| 1028 | impl_word!(word::U25, 25 - 1); | ||
| 1029 | impl_word!(word::U26, 26 - 1); | ||
| 1030 | impl_word!(word::U27, 27 - 1); | ||
| 1031 | impl_word!(word::U28, 28 - 1); | ||
| 1032 | impl_word!(word::U29, 29 - 1); | ||
| 1033 | impl_word!(word::U30, 30 - 1); | ||
| 1034 | impl_word!(word::U31, 31 - 1); | ||
| 1035 | impl_word!(u32, 32 - 1); | ||
| 1036 | } | ||
| 1000 | 1037 | ||
| 1001 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} | 1038 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} |
| 1002 | pin_trait!(SckPin, Instance); | 1039 | pin_trait!(SckPin, Instance); |
diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs new file mode 100644 index 000000000..81fdd15cb --- /dev/null +++ b/examples/stm32g0/src/bin/spi_neopixel.rs | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dma::word::U5; | ||
| 8 | use embassy_stm32::dma::NoDma; | ||
| 9 | use embassy_stm32::spi::{Config, Spi}; | ||
| 10 | use embassy_stm32::time::Hertz; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | const NR_PIXELS: usize = 15; | ||
| 15 | const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw | ||
| 16 | const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL; | ||
| 17 | |||
| 18 | struct RGB { | ||
| 19 | r: u8, | ||
| 20 | g: u8, | ||
| 21 | b: u8, | ||
| 22 | } | ||
| 23 | impl Default for RGB { | ||
| 24 | fn default() -> RGB { | ||
| 25 | RGB { r: 0, g: 0, b: 0 } | ||
| 26 | } | ||
| 27 | } | ||
| 28 | pub struct Ws2812 { | ||
| 29 | // Note that the U5 type controls the selection of 5 bits to output | ||
| 30 | bitbuffer: [U5; TOTAL_BITS], | ||
| 31 | } | ||
| 32 | |||
| 33 | impl Ws2812 { | ||
| 34 | pub fn new() -> Ws2812 { | ||
| 35 | Ws2812 { | ||
| 36 | bitbuffer: [U5(0); TOTAL_BITS], | ||
| 37 | } | ||
| 38 | } | ||
| 39 | fn len(&self) -> usize { | ||
| 40 | return NR_PIXELS; | ||
| 41 | } | ||
| 42 | fn set(&mut self, idx: usize, rgb: RGB) { | ||
| 43 | self.render_color(idx, 0, rgb.g); | ||
| 44 | self.render_color(idx, 8, rgb.r); | ||
| 45 | self.render_color(idx, 16, rgb.b); | ||
| 46 | } | ||
| 47 | // transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern | ||
| 48 | fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) { | ||
| 49 | let mut bits = color as usize; | ||
| 50 | let mut idx = pixel_idx * BITS_PER_PIXEL + offset; | ||
| 51 | |||
| 52 | // render one bit in one spi byte. High time first, then the low time | ||
| 53 | // clock should be 4 Mhz, 5 bits, each bit is 0.25 us. | ||
| 54 | // a one bit is send as a pulse of 0.75 high -- 0.50 low | ||
| 55 | // a zero bit is send as a pulse of 0.50 high -- 0.75 low | ||
| 56 | // clock frequency for the neopixel is exact 800 khz | ||
| 57 | // note that the mosi output should have a resistor to ground of 10k, | ||
| 58 | // to assure that between the bursts the line is low | ||
| 59 | for _i in 0..8 { | ||
| 60 | if idx >= TOTAL_BITS { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | let pattern = match bits & 0x80 { | ||
| 64 | 0x80 => 0b0000_1110, | ||
| 65 | _ => 0b000_1100, | ||
| 66 | }; | ||
| 67 | bits = bits << 1; | ||
| 68 | self.bitbuffer[idx] = U5(pattern); | ||
| 69 | idx += 1; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | #[embassy_executor::main] | ||
| 75 | async fn main(_spawner: Spawner) { | ||
| 76 | let p = embassy_stm32::init(Default::default()); | ||
| 77 | info!("Start test using spi as neopixel driver"); | ||
| 78 | |||
| 79 | let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); | ||
| 80 | |||
| 81 | let mut neopixels = Ws2812::new(); | ||
| 82 | |||
| 83 | loop { | ||
| 84 | let mut cnt: usize = 0; | ||
| 85 | for _i in 0..10 { | ||
| 86 | for idx in 0..neopixels.len() { | ||
| 87 | let color = match (cnt + idx) % 3 { | ||
| 88 | 0 => RGB { r: 0x21, g: 0, b: 0 }, | ||
| 89 | 1 => RGB { r: 0, g: 0x31, b: 0 }, | ||
| 90 | _ => RGB { r: 0, g: 0, b: 0x41 }, | ||
| 91 | }; | ||
| 92 | neopixels.set(idx, color); | ||
| 93 | } | ||
| 94 | cnt += 1; | ||
| 95 | // start sending the neopixel bit patters over spi to the neopixel string | ||
| 96 | spi.write(&neopixels.bitbuffer).await.ok(); | ||
| 97 | Timer::after(Duration::from_millis(500)).await; | ||
| 98 | } | ||
| 99 | Timer::after(Duration::from_millis(1000)).await; | ||
| 100 | } | ||
| 101 | } | ||
