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 <dirbaio@dirbaio.net> Co-authored-by: anton smeenk <asmeenk@planet.nl>
This commit is contained in:
		
						commit
						08d9e5981e
					
				| @ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; | |||||||
| use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | ||||||
| use embassy_sync::waitqueue::AtomicWaker; | use embassy_sync::waitqueue::AtomicWaker; | ||||||
| 
 | 
 | ||||||
| use super::{Dir, Word, WordSize}; | use super::word::{Word, WordSize}; | ||||||
|  | use super::Dir; | ||||||
| use crate::_generated::BDMA_CHANNEL_COUNT; | use crate::_generated::BDMA_CHANNEL_COUNT; | ||||||
| use crate::interrupt::{Interrupt, InterruptExt}; | use crate::interrupt::{Interrupt, InterruptExt}; | ||||||
| use crate::pac; | use crate::pac; | ||||||
| @ -167,7 +168,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             ptr as *mut u32, |             ptr as *mut u32, | ||||||
|             len, |             len, | ||||||
|             true, |             true, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @ -202,7 +203,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             ptr as *mut u32, |             ptr as *mut u32, | ||||||
|             len, |             len, | ||||||
|             true, |             true, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @ -225,7 +226,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             repeated as *const W as *mut u32, |             repeated as *const W as *mut u32, | ||||||
|             count, |             count, | ||||||
|             false, |             false, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -9,7 +9,8 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | |||||||
| use embassy_sync::waitqueue::AtomicWaker; | use embassy_sync::waitqueue::AtomicWaker; | ||||||
| use pac::dma::regs; | use pac::dma::regs; | ||||||
| 
 | 
 | ||||||
| use super::{Dir, Word, WordSize}; | use super::word::{Word, WordSize}; | ||||||
|  | use super::Dir; | ||||||
| use crate::_generated::DMA_CHANNEL_COUNT; | use crate::_generated::DMA_CHANNEL_COUNT; | ||||||
| use crate::interrupt::{Interrupt, InterruptExt}; | use crate::interrupt::{Interrupt, InterruptExt}; | ||||||
| use crate::pac::dma::vals; | use crate::pac::dma::vals; | ||||||
| @ -246,7 +247,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             ptr as *mut u32, |             ptr as *mut u32, | ||||||
|             len, |             len, | ||||||
|             true, |             true, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @ -281,7 +282,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             ptr as *mut u32, |             ptr as *mut u32, | ||||||
|             len, |             len, | ||||||
|             true, |             true, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @ -304,7 +305,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             repeated as *const W as *mut u32, |             repeated as *const W as *mut u32, | ||||||
|             count, |             count, | ||||||
|             false, |             false, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @ -464,7 +465,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { | |||||||
|         assert!(len > 0 && len <= 0xFFFF); |         assert!(len > 0 && len <= 0xFFFF); | ||||||
| 
 | 
 | ||||||
|         let dir = Dir::PeripheralToMemory; |         let dir = Dir::PeripheralToMemory; | ||||||
|         let data_size = W::bits(); |         let data_size = W::size(); | ||||||
| 
 | 
 | ||||||
|         let channel_number = channel.num(); |         let channel_number = channel.num(); | ||||||
|         let dma = channel.regs(); |         let dma = channel.regs(); | ||||||
|  | |||||||
| @ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; | |||||||
| use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | ||||||
| use embassy_sync::waitqueue::AtomicWaker; | use embassy_sync::waitqueue::AtomicWaker; | ||||||
| 
 | 
 | ||||||
| use super::{Dir, Word, WordSize}; | use super::word::{Word, WordSize}; | ||||||
|  | use super::Dir; | ||||||
| use crate::_generated::GPDMA_CHANNEL_COUNT; | use crate::_generated::GPDMA_CHANNEL_COUNT; | ||||||
| use crate::interrupt::{Interrupt, InterruptExt}; | use crate::interrupt::{Interrupt, InterruptExt}; | ||||||
| use crate::pac; | use crate::pac; | ||||||
| @ -165,7 +166,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             ptr as *mut u32, |             ptr as *mut u32, | ||||||
|             len, |             len, | ||||||
|             true, |             true, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @ -200,7 +201,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             ptr as *mut u32, |             ptr as *mut u32, | ||||||
|             len, |             len, | ||||||
|             true, |             true, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @ -223,7 +224,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||||||
|             repeated as *const W as *mut u32, |             repeated as *const W as *mut u32, | ||||||
|             count, |             count, | ||||||
|             false, |             false, | ||||||
|             W::bits(), |             W::size(), | ||||||
|             options, |             options, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -21,6 +21,8 @@ pub use gpdma::*; | |||||||
| #[cfg(dmamux)] | #[cfg(dmamux)] | ||||||
| mod dmamux; | mod dmamux; | ||||||
| 
 | 
 | ||||||
|  | pub mod word; | ||||||
|  | 
 | ||||||
| use core::mem; | use core::mem; | ||||||
| 
 | 
 | ||||||
| use embassy_cortex_m::interrupt::Priority; | use embassy_cortex_m::interrupt::Priority; | ||||||
| @ -36,53 +38,6 @@ enum Dir { | |||||||
|     PeripheralToMemory, |     PeripheralToMemory, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] |  | ||||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |  | ||||||
| pub enum WordSize { |  | ||||||
|     OneByte, |  | ||||||
|     TwoBytes, |  | ||||||
|     FourBytes, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl WordSize { |  | ||||||
|     pub fn bytes(&self) -> usize { |  | ||||||
|         match self { |  | ||||||
|             Self::OneByte => 1, |  | ||||||
|             Self::TwoBytes => 2, |  | ||||||
|             Self::FourBytes => 4, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| mod word_sealed { |  | ||||||
|     pub trait Word {} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub trait Word: word_sealed::Word { |  | ||||||
|     fn bits() -> WordSize; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl word_sealed::Word for u8 {} |  | ||||||
| impl Word for u8 { |  | ||||||
|     fn bits() -> WordSize { |  | ||||||
|         WordSize::OneByte |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl word_sealed::Word for u16 {} |  | ||||||
| impl Word for u16 { |  | ||||||
|     fn bits() -> WordSize { |  | ||||||
|         WordSize::TwoBytes |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl word_sealed::Word for u32 {} |  | ||||||
| impl Word for u32 { |  | ||||||
|     fn bits() -> WordSize { |  | ||||||
|         WordSize::FourBytes |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct NoDma; | pub struct NoDma; | ||||||
| 
 | 
 | ||||||
| impl_peripheral!(NoDma); | impl_peripheral!(NoDma); | ||||||
|  | |||||||
							
								
								
									
										79
									
								
								embassy-stm32/src/dma/word.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								embassy-stm32/src/dma/word.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||||||
|  | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
|  | pub enum WordSize { | ||||||
|  |     OneByte, | ||||||
|  |     TwoBytes, | ||||||
|  |     FourBytes, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl WordSize { | ||||||
|  |     pub fn bytes(&self) -> usize { | ||||||
|  |         match self { | ||||||
|  |             Self::OneByte => 1, | ||||||
|  |             Self::TwoBytes => 2, | ||||||
|  |             Self::FourBytes => 4, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | mod sealed { | ||||||
|  |     pub trait Word {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait Word: sealed::Word + Default + Copy + 'static { | ||||||
|  |     fn size() -> WordSize; | ||||||
|  |     fn bits() -> usize; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | macro_rules! impl_word { | ||||||
|  |     (_, $T:ident, $bits:literal, $size:ident) => { | ||||||
|  |         impl sealed::Word for $T {} | ||||||
|  |         impl Word for $T { | ||||||
|  |             fn bits() -> usize { | ||||||
|  |                 $bits | ||||||
|  |             } | ||||||
|  |             fn size() -> WordSize { | ||||||
|  |                 WordSize::$size | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     ($T:ident, $uX:ident, $bits:literal, $size:ident) => { | ||||||
|  |         #[repr(transparent)] | ||||||
|  |         #[derive(Copy, Clone, Default)] | ||||||
|  |         pub struct $T(pub $uX); | ||||||
|  |         impl_word!(_, $T, $bits, $size); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl_word!(U1, u8, 1, OneByte); | ||||||
|  | impl_word!(U2, u8, 2, OneByte); | ||||||
|  | impl_word!(U3, u8, 3, OneByte); | ||||||
|  | impl_word!(U4, u8, 4, OneByte); | ||||||
|  | impl_word!(U5, u8, 5, OneByte); | ||||||
|  | impl_word!(U6, u8, 6, OneByte); | ||||||
|  | impl_word!(U7, u8, 7, OneByte); | ||||||
|  | impl_word!(_, u8, 8, OneByte); | ||||||
|  | impl_word!(U9, u16, 9, TwoBytes); | ||||||
|  | impl_word!(U10, u16, 10, TwoBytes); | ||||||
|  | impl_word!(U11, u16, 11, TwoBytes); | ||||||
|  | impl_word!(U12, u16, 12, TwoBytes); | ||||||
|  | impl_word!(U13, u16, 13, TwoBytes); | ||||||
|  | impl_word!(U14, u16, 14, TwoBytes); | ||||||
|  | impl_word!(U15, u16, 15, TwoBytes); | ||||||
|  | impl_word!(_, u16, 16, TwoBytes); | ||||||
|  | impl_word!(U17, u32, 17, FourBytes); | ||||||
|  | impl_word!(U18, u32, 18, FourBytes); | ||||||
|  | impl_word!(U19, u32, 19, FourBytes); | ||||||
|  | impl_word!(U20, u32, 20, FourBytes); | ||||||
|  | impl_word!(U21, u32, 21, FourBytes); | ||||||
|  | impl_word!(U22, u32, 22, FourBytes); | ||||||
|  | impl_word!(U23, u32, 23, FourBytes); | ||||||
|  | impl_word!(U24, u32, 24, FourBytes); | ||||||
|  | impl_word!(U25, u32, 25, FourBytes); | ||||||
|  | impl_word!(U26, u32, 26, FourBytes); | ||||||
|  | impl_word!(U27, u32, 27, FourBytes); | ||||||
|  | impl_word!(U28, u32, 28, FourBytes); | ||||||
|  | impl_word!(U29, u32, 29, FourBytes); | ||||||
|  | impl_word!(U30, u32, 30, FourBytes); | ||||||
|  | impl_word!(U31, u32, 31, FourBytes); | ||||||
|  | impl_word!(_, u32, 32, FourBytes); | ||||||
| @ -7,8 +7,7 @@ use embassy_futures::join::join; | |||||||
| use embassy_hal_common::{into_ref, PeripheralRef}; | use embassy_hal_common::{into_ref, PeripheralRef}; | ||||||
| pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | ||||||
| 
 | 
 | ||||||
| use self::sealed::WordSize; | use crate::dma::{slice_ptr_parts, word, Transfer}; | ||||||
| use crate::dma::{slice_ptr_parts, Transfer}; |  | ||||||
| use crate::gpio::sealed::{AFType, Pin as _}; | use crate::gpio::sealed::{AFType, Pin as _}; | ||||||
| use crate::gpio::{AnyPin, Pull}; | use crate::gpio::{AnyPin, Pull}; | ||||||
| use crate::pac::spi::{regs, vals, Spi as Regs}; | use crate::pac::spi::{regs, vals, Spi as Regs}; | ||||||
| @ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> { | |||||||
|     miso: Option<PeripheralRef<'d, AnyPin>>, |     miso: Option<PeripheralRef<'d, AnyPin>>, | ||||||
|     txdma: PeripheralRef<'d, Tx>, |     txdma: PeripheralRef<'d, Tx>, | ||||||
|     rxdma: PeripheralRef<'d, Rx>, |     rxdma: PeripheralRef<'d, Rx>, | ||||||
|     current_word_size: WordSize, |     current_word_size: word_impl::Config, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | 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> { | |||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn new_txonly_nosck( | ||||||
|  |         peri: impl Peripheral<P = T> + 'd, | ||||||
|  |         mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | ||||||
|  |         txdma: impl Peripheral<P = Tx> + 'd, | ||||||
|  |         rxdma: impl Peripheral<P = Rx> + 'd, // TODO: remove
 | ||||||
|  |         freq: Hertz, | ||||||
|  |         config: Config, | ||||||
|  |     ) -> Self { | ||||||
|  |         into_ref!(mosi); | ||||||
|  |         unsafe { | ||||||
|  |             mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); | ||||||
|  |             mosi.set_speed(crate::gpio::Speed::Medium); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Useful for on chip peripherals like SUBGHZ which are hardwired.
 |     /// Useful for on chip peripherals like SUBGHZ which are hardwired.
 | ||||||
|     /// The bus can optionally be exposed externally with `Spi::new()` still.
 |     /// The bus can optionally be exposed externally with `Spi::new()` still.
 | ||||||
|     #[allow(dead_code)] |     #[allow(dead_code)] | ||||||
| @ -234,14 +250,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|                 if mosi.is_none() { |                 if mosi.is_none() { | ||||||
|                     w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); |                     w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); | ||||||
|                 } |                 } | ||||||
|                 w.set_dff(WordSize::EightBit.dff()) |                 w.set_dff(<u8 as sealed::Word>::CONFIG) | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|         #[cfg(spi_v2)] |         #[cfg(spi_v2)] | ||||||
|         unsafe { |         unsafe { | ||||||
|             T::REGS.cr2().modify(|w| { |             T::REGS.cr2().modify(|w| { | ||||||
|                 w.set_frxth(WordSize::EightBit.frxth()); |                 let (ds, frxth) = <u8 as sealed::Word>::CONFIG; | ||||||
|                 w.set_ds(WordSize::EightBit.ds()); |                 w.set_frxth(frxth); | ||||||
|  |                 w.set_ds(ds); | ||||||
|                 w.set_ssoe(false); |                 w.set_ssoe(false); | ||||||
|             }); |             }); | ||||||
|             T::REGS.cr1().modify(|w| { |             T::REGS.cr1().modify(|w| { | ||||||
| @ -279,7 +296,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|             T::REGS.cfg1().modify(|w| { |             T::REGS.cfg1().modify(|w| { | ||||||
|                 w.set_crcen(false); |                 w.set_crcen(false); | ||||||
|                 w.set_mbr(br); |                 w.set_mbr(br); | ||||||
|                 w.set_dsize(WordSize::EightBit.dsize()); |                 w.set_dsize(<u8 as sealed::Word>::CONFIG); | ||||||
|  |                 w.set_fthlv(vals::Fthlv::ONEFRAME); | ||||||
|             }); |             }); | ||||||
|             T::REGS.cr2().modify(|w| { |             T::REGS.cr2().modify(|w| { | ||||||
|                 w.set_tsize(0); |                 w.set_tsize(0); | ||||||
| @ -297,7 +315,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|             miso, |             miso, | ||||||
|             txdma, |             txdma, | ||||||
|             rxdma, |             rxdma, | ||||||
|             current_word_size: WordSize::EightBit, |             current_word_size: <u8 as sealed::Word>::CONFIG, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -355,7 +373,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn set_word_size(&mut self, word_size: WordSize) { |     fn set_word_size(&mut self, word_size: word_impl::Config) { | ||||||
|         if self.current_word_size == word_size { |         if self.current_word_size == word_size { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @ -364,7 +382,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|         unsafe { |         unsafe { | ||||||
|             T::REGS.cr1().modify(|reg| { |             T::REGS.cr1().modify(|reg| { | ||||||
|                 reg.set_spe(false); |                 reg.set_spe(false); | ||||||
|                 reg.set_dff(word_size.dff()) |                 reg.set_dff(word_size) | ||||||
|             }); |             }); | ||||||
|             T::REGS.cr1().modify(|reg| { |             T::REGS.cr1().modify(|reg| { | ||||||
|                 reg.set_spe(true); |                 reg.set_spe(true); | ||||||
| @ -376,8 +394,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|                 w.set_spe(false); |                 w.set_spe(false); | ||||||
|             }); |             }); | ||||||
|             T::REGS.cr2().modify(|w| { |             T::REGS.cr2().modify(|w| { | ||||||
|                 w.set_frxth(word_size.frxth()); |                 w.set_frxth(word_size.1); | ||||||
|                 w.set_ds(word_size.ds()); |                 w.set_ds(word_size.0); | ||||||
|             }); |             }); | ||||||
|             T::REGS.cr1().modify(|w| { |             T::REGS.cr1().modify(|w| { | ||||||
|                 w.set_spe(true); |                 w.set_spe(true); | ||||||
| @ -393,7 +411,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|                 w.set_spe(false); |                 w.set_spe(false); | ||||||
|             }); |             }); | ||||||
|             T::REGS.cfg1().modify(|w| { |             T::REGS.cfg1().modify(|w| { | ||||||
|                 w.set_dsize(word_size.dsize()); |                 w.set_dsize(word_size); | ||||||
|             }); |             }); | ||||||
|             T::REGS.cr1().modify(|w| { |             T::REGS.cr1().modify(|w| { | ||||||
|                 w.set_csusp(false); |                 w.set_csusp(false); | ||||||
| @ -412,7 +430,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|             return Ok(()); |             return Ok(()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         self.set_word_size(W::WORDSIZE); |         self.set_word_size(W::CONFIG); | ||||||
|         unsafe { |         unsafe { | ||||||
|             T::REGS.cr1().modify(|w| { |             T::REGS.cr1().modify(|w| { | ||||||
|                 w.set_spe(false); |                 w.set_spe(false); | ||||||
| @ -450,7 +468,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|             return Ok(()); |             return Ok(()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         self.set_word_size(W::WORDSIZE); |         self.set_word_size(W::CONFIG); | ||||||
|         unsafe { |         unsafe { | ||||||
|             T::REGS.cr1().modify(|w| { |             T::REGS.cr1().modify(|w| { | ||||||
|                 w.set_spe(false); |                 w.set_spe(false); | ||||||
| @ -513,7 +531,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|             return Ok(()); |             return Ok(()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         self.set_word_size(W::WORDSIZE); |         self.set_word_size(W::CONFIG); | ||||||
|         unsafe { |         unsafe { | ||||||
|             T::REGS.cr1().modify(|w| { |             T::REGS.cr1().modify(|w| { | ||||||
|                 w.set_spe(false); |                 w.set_spe(false); | ||||||
| @ -571,7 +589,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|     pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { |     pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { | ||||||
|         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | ||||||
|         flush_rx_fifo(T::REGS); |         flush_rx_fifo(T::REGS); | ||||||
|         self.set_word_size(W::WORDSIZE); |         self.set_word_size(W::CONFIG); | ||||||
|         for word in words.iter() { |         for word in words.iter() { | ||||||
|             let _ = transfer_word(T::REGS, *word)?; |             let _ = transfer_word(T::REGS, *word)?; | ||||||
|         } |         } | ||||||
| @ -581,7 +599,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|     pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |     pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { | ||||||
|         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | ||||||
|         flush_rx_fifo(T::REGS); |         flush_rx_fifo(T::REGS); | ||||||
|         self.set_word_size(W::WORDSIZE); |         self.set_word_size(W::CONFIG); | ||||||
|         for word in words.iter_mut() { |         for word in words.iter_mut() { | ||||||
|             *word = transfer_word(T::REGS, W::default())?; |             *word = transfer_word(T::REGS, W::default())?; | ||||||
|         } |         } | ||||||
| @ -591,7 +609,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|     pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |     pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { | ||||||
|         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | ||||||
|         flush_rx_fifo(T::REGS); |         flush_rx_fifo(T::REGS); | ||||||
|         self.set_word_size(W::WORDSIZE); |         self.set_word_size(W::CONFIG); | ||||||
|         for word in words.iter_mut() { |         for word in words.iter_mut() { | ||||||
|             *word = transfer_word(T::REGS, *word)?; |             *word = transfer_word(T::REGS, *word)?; | ||||||
|         } |         } | ||||||
| @ -601,7 +619,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||||||
|     pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |     pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | ||||||
|         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } |         unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } | ||||||
|         flush_rx_fifo(T::REGS); |         flush_rx_fifo(T::REGS); | ||||||
|         self.set_word_size(W::WORDSIZE); |         self.set_word_size(W::CONFIG); | ||||||
|         let len = read.len().max(write.len()); |         let len = read.len().max(write.len()); | ||||||
|         for i in 0..len { |         for i in 0..len { | ||||||
|             let wb = write.get(i).copied().unwrap_or_default(); |             let wb = write.get(i).copied().unwrap_or_default(); | ||||||
| @ -933,70 +951,89 @@ pub(crate) mod sealed { | |||||||
|         const REGS: Regs; |         const REGS: Regs; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub trait Word: Copy + 'static { |     pub trait Word { | ||||||
|         const WORDSIZE: WordSize; |         const CONFIG: word_impl::Config; | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     impl Word for u8 { |  | ||||||
|         const WORDSIZE: WordSize = WordSize::EightBit; |  | ||||||
|     } |  | ||||||
|     impl Word for u16 { |  | ||||||
|         const WORDSIZE: WordSize = WordSize::SixteenBit; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[derive(Copy, Clone, PartialOrd, PartialEq)] |  | ||||||
|     pub enum WordSize { |  | ||||||
|         EightBit, |  | ||||||
|         SixteenBit, |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     impl WordSize { |  | ||||||
|         #[cfg(any(spi_v1, spi_f1))] |  | ||||||
|         pub fn dff(&self) -> vals::Dff { |  | ||||||
|             match self { |  | ||||||
|                 WordSize::EightBit => vals::Dff::EIGHTBIT, |  | ||||||
|                 WordSize::SixteenBit => vals::Dff::SIXTEENBIT, |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #[cfg(spi_v2)] |  | ||||||
|         pub fn ds(&self) -> vals::Ds { |  | ||||||
|             match self { |  | ||||||
|                 WordSize::EightBit => vals::Ds::EIGHTBIT, |  | ||||||
|                 WordSize::SixteenBit => vals::Ds::SIXTEENBIT, |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #[cfg(spi_v2)] |  | ||||||
|         pub fn frxth(&self) -> vals::Frxth { |  | ||||||
|             match self { |  | ||||||
|                 WordSize::EightBit => vals::Frxth::QUARTER, |  | ||||||
|                 WordSize::SixteenBit => vals::Frxth::HALF, |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #[cfg(any(spi_v3, spi_v4, spi_v5))] |  | ||||||
|         pub fn dsize(&self) -> u8 { |  | ||||||
|             match self { |  | ||||||
|                 WordSize::EightBit => 0b0111, |  | ||||||
|                 WordSize::SixteenBit => 0b1111, |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #[cfg(any(spi_v3, spi_v4, spi_v5))] |  | ||||||
|         pub fn _frxth(&self) -> vals::Fthlv { |  | ||||||
|             match self { |  | ||||||
|                 WordSize::EightBit => vals::Fthlv::ONEFRAME, |  | ||||||
|                 WordSize::SixteenBit => vals::Fthlv::ONEFRAME, |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {} | pub trait Word: word::Word + sealed::Word {} | ||||||
| 
 | 
 | ||||||
| impl Word for u8 {} | macro_rules! impl_word { | ||||||
| impl Word for u16 {} |     ($T:ty, $config:expr) => { | ||||||
|  |         impl sealed::Word for $T { | ||||||
|  |             const CONFIG: Config = $config; | ||||||
|  |         } | ||||||
|  |         impl Word for $T {} | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(any(spi_v1, spi_f1))] | ||||||
|  | mod word_impl { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     pub type Config = vals::Dff; | ||||||
|  | 
 | ||||||
|  |     impl_word!(u8, vals::Dff::EIGHTBIT); | ||||||
|  |     impl_word!(u16, vals::Dff::SIXTEENBIT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(any(spi_v2))] | ||||||
|  | mod word_impl { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     pub type Config = (vals::Ds, vals::Frxth); | ||||||
|  | 
 | ||||||
|  |     impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER)); | ||||||
|  |     impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER)); | ||||||
|  |     impl_word!(word::U6, (vals::Ds::SIXBIT, vals::Frxth::QUARTER)); | ||||||
|  |     impl_word!(word::U7, (vals::Ds::SEVENBIT, vals::Frxth::QUARTER)); | ||||||
|  |     impl_word!(u8, (vals::Ds::EIGHTBIT, vals::Frxth::QUARTER)); | ||||||
|  |     impl_word!(word::U9, (vals::Ds::NINEBIT, vals::Frxth::HALF)); | ||||||
|  |     impl_word!(word::U10, (vals::Ds::TENBIT, vals::Frxth::HALF)); | ||||||
|  |     impl_word!(word::U11, (vals::Ds::ELEVENBIT, vals::Frxth::HALF)); | ||||||
|  |     impl_word!(word::U12, (vals::Ds::TWELVEBIT, vals::Frxth::HALF)); | ||||||
|  |     impl_word!(word::U13, (vals::Ds::THIRTEENBIT, vals::Frxth::HALF)); | ||||||
|  |     impl_word!(word::U14, (vals::Ds::FOURTEENBIT, vals::Frxth::HALF)); | ||||||
|  |     impl_word!(word::U15, (vals::Ds::FIFTEENBIT, vals::Frxth::HALF)); | ||||||
|  |     impl_word!(u16, (vals::Ds::SIXTEENBIT, vals::Frxth::HALF)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(any(spi_v3, spi_v4, spi_v5))] | ||||||
|  | mod word_impl { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     pub type Config = u8; | ||||||
|  | 
 | ||||||
|  |     impl_word!(word::U4, 4 - 1); | ||||||
|  |     impl_word!(word::U5, 5 - 1); | ||||||
|  |     impl_word!(word::U6, 6 - 1); | ||||||
|  |     impl_word!(word::U7, 7 - 1); | ||||||
|  |     impl_word!(u8, 8 - 1); | ||||||
|  |     impl_word!(word::U9, 9 - 1); | ||||||
|  |     impl_word!(word::U10, 10 - 1); | ||||||
|  |     impl_word!(word::U11, 11 - 1); | ||||||
|  |     impl_word!(word::U12, 12 - 1); | ||||||
|  |     impl_word!(word::U13, 13 - 1); | ||||||
|  |     impl_word!(word::U14, 14 - 1); | ||||||
|  |     impl_word!(word::U15, 15 - 1); | ||||||
|  |     impl_word!(u16, 16 - 1); | ||||||
|  |     impl_word!(word::U17, 17 - 1); | ||||||
|  |     impl_word!(word::U18, 18 - 1); | ||||||
|  |     impl_word!(word::U19, 19 - 1); | ||||||
|  |     impl_word!(word::U20, 20 - 1); | ||||||
|  |     impl_word!(word::U21, 21 - 1); | ||||||
|  |     impl_word!(word::U22, 22 - 1); | ||||||
|  |     impl_word!(word::U23, 23 - 1); | ||||||
|  |     impl_word!(word::U24, 24 - 1); | ||||||
|  |     impl_word!(word::U25, 25 - 1); | ||||||
|  |     impl_word!(word::U26, 26 - 1); | ||||||
|  |     impl_word!(word::U27, 27 - 1); | ||||||
|  |     impl_word!(word::U28, 28 - 1); | ||||||
|  |     impl_word!(word::U29, 29 - 1); | ||||||
|  |     impl_word!(word::U30, 30 - 1); | ||||||
|  |     impl_word!(word::U31, 31 - 1); | ||||||
|  |     impl_word!(u32, 32 - 1); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} | ||||||
| pin_trait!(SckPin, Instance); | pin_trait!(SckPin, Instance); | ||||||
|  | |||||||
							
								
								
									
										101
									
								
								examples/stm32g0/src/bin/spi_neopixel.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								examples/stm32g0/src/bin/spi_neopixel.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | |||||||
|  | #![no_std] | ||||||
|  | #![no_main] | ||||||
|  | #![feature(type_alias_impl_trait)] | ||||||
|  | 
 | ||||||
|  | use defmt::*; | ||||||
|  | use embassy_executor::Spawner; | ||||||
|  | use embassy_stm32::dma::word::U5; | ||||||
|  | use embassy_stm32::dma::NoDma; | ||||||
|  | use embassy_stm32::spi::{Config, Spi}; | ||||||
|  | use embassy_stm32::time::Hertz; | ||||||
|  | use embassy_time::{Duration, Timer}; | ||||||
|  | use {defmt_rtt as _, panic_probe as _}; | ||||||
|  | 
 | ||||||
|  | const NR_PIXELS: usize = 15; | ||||||
|  | const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw
 | ||||||
|  | const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL; | ||||||
|  | 
 | ||||||
|  | struct RGB { | ||||||
|  |     r: u8, | ||||||
|  |     g: u8, | ||||||
|  |     b: u8, | ||||||
|  | } | ||||||
|  | impl Default for RGB { | ||||||
|  |     fn default() -> RGB { | ||||||
|  |         RGB { r: 0, g: 0, b: 0 } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | pub struct Ws2812 { | ||||||
|  |     // Note that the U5 type controls the selection of 5 bits to output
 | ||||||
|  |     bitbuffer: [U5; TOTAL_BITS], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Ws2812 { | ||||||
|  |     pub fn new() -> Ws2812 { | ||||||
|  |         Ws2812 { | ||||||
|  |             bitbuffer: [U5(0); TOTAL_BITS], | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     fn len(&self) -> usize { | ||||||
|  |         return NR_PIXELS; | ||||||
|  |     } | ||||||
|  |     fn set(&mut self, idx: usize, rgb: RGB) { | ||||||
|  |         self.render_color(idx, 0, rgb.g); | ||||||
|  |         self.render_color(idx, 8, rgb.r); | ||||||
|  |         self.render_color(idx, 16, rgb.b); | ||||||
|  |     } | ||||||
|  |     // transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern
 | ||||||
|  |     fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) { | ||||||
|  |         let mut bits = color as usize; | ||||||
|  |         let mut idx = pixel_idx * BITS_PER_PIXEL + offset; | ||||||
|  | 
 | ||||||
|  |         // render one bit in one spi byte. High time first, then the low time
 | ||||||
|  |         // clock should be 4 Mhz, 5 bits, each bit is 0.25 us.
 | ||||||
|  |         // a one bit is send as a pulse of 0.75 high -- 0.50 low
 | ||||||
|  |         // a zero bit is send as a pulse of 0.50 high -- 0.75 low
 | ||||||
|  |         // clock frequency for the neopixel is exact 800 khz
 | ||||||
|  |         // note that the mosi output should have a resistor to ground of 10k,
 | ||||||
|  |         // to assure that between the bursts the line is low
 | ||||||
|  |         for _i in 0..8 { | ||||||
|  |             if idx >= TOTAL_BITS { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             let pattern = match bits & 0x80 { | ||||||
|  |                 0x80 => 0b0000_1110, | ||||||
|  |                 _ => 0b000_1100, | ||||||
|  |             }; | ||||||
|  |             bits = bits << 1; | ||||||
|  |             self.bitbuffer[idx] = U5(pattern); | ||||||
|  |             idx += 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[embassy_executor::main] | ||||||
|  | async fn main(_spawner: Spawner) { | ||||||
|  |     let p = embassy_stm32::init(Default::default()); | ||||||
|  |     info!("Start test using spi as neopixel driver"); | ||||||
|  | 
 | ||||||
|  |     let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); | ||||||
|  | 
 | ||||||
|  |     let mut neopixels = Ws2812::new(); | ||||||
|  | 
 | ||||||
|  |     loop { | ||||||
|  |         let mut cnt: usize = 0; | ||||||
|  |         for _i in 0..10 { | ||||||
|  |             for idx in 0..neopixels.len() { | ||||||
|  |                 let color = match (cnt + idx) % 3 { | ||||||
|  |                     0 => RGB { r: 0x21, g: 0, b: 0 }, | ||||||
|  |                     1 => RGB { r: 0, g: 0x31, b: 0 }, | ||||||
|  |                     _ => RGB { r: 0, g: 0, b: 0x41 }, | ||||||
|  |                 }; | ||||||
|  |                 neopixels.set(idx, color); | ||||||
|  |             } | ||||||
|  |             cnt += 1; | ||||||
|  |             // start sending the neopixel bit patters over spi to the neopixel string
 | ||||||
|  |             spi.write(&neopixels.bitbuffer).await.ok(); | ||||||
|  |             Timer::after(Duration::from_millis(500)).await; | ||||||
|  |         } | ||||||
|  |         Timer::after(Duration::from_millis(1000)).await; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user