From 2080d8bb6de58eb2da2ca5df2437ac8cc6577661 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Apr 2023 20:56:23 +0200 Subject: [PATCH 1/2] stm32/spi: add support for all word sizes. Co-Authored-By: anton smeenk --- embassy-stm32/src/dma/bdma.rs | 9 +- embassy-stm32/src/dma/dma.rs | 11 +- embassy-stm32/src/dma/gpdma.rs | 9 +- embassy-stm32/src/dma/mod.rs | 49 +-------- embassy-stm32/src/dma/word.rs | 79 +++++++++++++++ embassy-stm32/src/spi/mod.rs | 180 ++++++++++++++++++--------------- 6 files changed, 197 insertions(+), 140 deletions(-) create mode 100644 embassy-stm32/src/dma/word.rs diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index cf1222c4..a23bb8cd 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; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 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::interrupt::{Interrupt, InterruptExt}; use crate::pac; @@ -167,7 +168,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -202,7 +203,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -225,7 +226,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 62c09224..ef1d2757 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}; use embassy_sync::waitqueue::AtomicWaker; 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::interrupt::{Interrupt, InterruptExt}; use crate::pac::dma::vals; @@ -246,7 +247,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -281,7 +282,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -304,7 +305,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } @@ -464,7 +465,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { assert!(len > 0 && len <= 0xFFFF); let dir = Dir::PeripheralToMemory; - let data_size = W::bits(); + let data_size = W::size(); let channel_number = channel.num(); let dma = channel.regs(); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 5c6676a5..5a516ccd 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; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 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::interrupt::{Interrupt, InterruptExt}; use crate::pac; @@ -165,7 +166,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -200,7 +201,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -223,7 +224,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index d29ef4a1..3312ca75 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -21,6 +21,8 @@ pub use gpdma::*; #[cfg(dmamux)] mod dmamux; +pub mod word; + use core::mem; use embassy_cortex_m::interrupt::Priority; @@ -36,53 +38,6 @@ enum Dir { 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; impl_peripheral!(NoDma); diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs new file mode 100644 index 00000000..aef6e970 --- /dev/null +++ b/embassy-stm32/src/dma/word.rs @@ -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); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 7858cb3e..9ce0cebf 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -7,8 +7,7 @@ use embassy_futures::join::join; use embassy_hal_common::{into_ref, PeripheralRef}; 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, Transfer}; +use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Pull}; use crate::pac::spi::{regs, vals, Spi as Regs}; @@ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> { miso: Option>, txdma: PeripheralRef<'d, Tx>, 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> { @@ -234,14 +233,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { if mosi.is_none() { w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); } - w.set_dff(WordSize::EightBit.dff()) + w.set_dff(::CONFIG) }); } #[cfg(spi_v2)] unsafe { T::REGS.cr2().modify(|w| { - w.set_frxth(WordSize::EightBit.frxth()); - w.set_ds(WordSize::EightBit.ds()); + let (ds, frxth) = ::CONFIG; + w.set_frxth(frxth); + w.set_ds(ds); w.set_ssoe(false); }); T::REGS.cr1().modify(|w| { @@ -279,7 +279,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cfg1().modify(|w| { w.set_crcen(false); w.set_mbr(br); - w.set_dsize(WordSize::EightBit.dsize()); + w.set_dsize(::CONFIG); + w.set_fthlv(vals::Fthlv::ONEFRAME); }); T::REGS.cr2().modify(|w| { w.set_tsize(0); @@ -297,7 +298,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso, txdma, rxdma, - current_word_size: WordSize::EightBit, + current_word_size: ::CONFIG, } } @@ -355,7 +356,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 { return; } @@ -364,7 +365,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { unsafe { T::REGS.cr1().modify(|reg| { reg.set_spe(false); - reg.set_dff(word_size.dff()) + reg.set_dff(word_size) }); T::REGS.cr1().modify(|reg| { reg.set_spe(true); @@ -376,8 +377,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(false); }); T::REGS.cr2().modify(|w| { - w.set_frxth(word_size.frxth()); - w.set_ds(word_size.ds()); + w.set_frxth(word_size.1); + w.set_ds(word_size.0); }); T::REGS.cr1().modify(|w| { w.set_spe(true); @@ -393,7 +394,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(false); }); T::REGS.cfg1().modify(|w| { - w.set_dsize(word_size.dsize()); + w.set_dsize(word_size); }); T::REGS.cr1().modify(|w| { w.set_csusp(false); @@ -412,7 +413,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -450,7 +451,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -513,7 +514,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -571,7 +572,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter() { let _ = transfer_word(T::REGS, *word)?; } @@ -581,7 +582,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_read(&mut self, words: &mut [W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(T::REGS, W::default())?; } @@ -591,7 +592,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(T::REGS, *word)?; } @@ -601,7 +602,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); let len = read.len().max(write.len()); for i in 0..len { let wb = write.get(i).copied().unwrap_or_default(); @@ -933,70 +934,89 @@ pub(crate) mod sealed { const REGS: Regs; } - pub trait Word: Copy + 'static { - const WORDSIZE: WordSize; - } - - 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 { + const CONFIG: word_impl::Config; } } -pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {} +pub trait Word: word::Word + sealed::Word {} -impl Word for u8 {} -impl Word for u16 {} +macro_rules! impl_word { + ($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

+ sealed::Instance + RccPeripheral {} pin_trait!(SckPin, Instance); From 3260f6b2aff824a5f7f2a81283b234bdb1bd3e24 Mon Sep 17 00:00:00 2001 From: anton smeenk Date: Tue, 18 Apr 2023 20:56:57 +0200 Subject: [PATCH 2/2] stm32/spi: add new_txonly_nosck constructor, for neopixels, with an example in the stm32g0 directory. --- embassy-stm32/src/spi/mod.rs | 17 ++++ examples/stm32g0/src/bin/spi_neopixel.rs | 101 +++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 examples/stm32g0/src/bin/spi_neopixel.rs diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 9ce0cebf..492d0649 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -177,6 +177,23 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { ) } + pub fn new_txonly_nosck( + peri: impl Peripheral

+ 'd, + mosi: impl Peripheral

> + 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ '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. /// The bus can optionally be exposed externally with `Spi::new()` still. #[allow(dead_code)] diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs new file mode 100644 index 00000000..81fdd15c --- /dev/null +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -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; + } +}