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 {
|
pub trait Word: word::Word + sealed::Word {}
|
||||||
const WORDSIZE: WordSize = WordSize::EightBit;
|
|
||||||
|
macro_rules! impl_word {
|
||||||
|
($T:ty, $config:expr) => {
|
||||||
|
impl sealed::Word for $T {
|
||||||
|
const CONFIG: Config = $config;
|
||||||
}
|
}
|
||||||
impl Word for u16 {
|
impl Word for $T {}
|
||||||
const WORDSIZE: WordSize = WordSize::SixteenBit;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
|
||||||
pub enum WordSize {
|
|
||||||
EightBit,
|
|
||||||
SixteenBit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WordSize {
|
|
||||||
#[cfg(any(spi_v1, spi_f1))]
|
#[cfg(any(spi_v1, spi_f1))]
|
||||||
pub fn dff(&self) -> vals::Dff {
|
mod word_impl {
|
||||||
match self {
|
use super::*;
|
||||||
WordSize::EightBit => vals::Dff::EIGHTBIT,
|
|
||||||
WordSize::SixteenBit => vals::Dff::SIXTEENBIT,
|
pub type Config = vals::Dff;
|
||||||
}
|
|
||||||
|
impl_word!(u8, vals::Dff::EIGHTBIT);
|
||||||
|
impl_word!(u16, vals::Dff::SIXTEENBIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(spi_v2)]
|
#[cfg(any(spi_v2))]
|
||||||
pub fn ds(&self) -> vals::Ds {
|
mod word_impl {
|
||||||
match self {
|
use super::*;
|
||||||
WordSize::EightBit => vals::Ds::EIGHTBIT,
|
|
||||||
WordSize::SixteenBit => vals::Ds::SIXTEENBIT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(spi_v2)]
|
pub type Config = (vals::Ds, vals::Frxth);
|
||||||
pub fn frxth(&self) -> vals::Frxth {
|
|
||||||
match self {
|
impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER));
|
||||||
WordSize::EightBit => vals::Frxth::QUARTER,
|
impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER));
|
||||||
WordSize::SixteenBit => vals::Frxth::HALF,
|
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))]
|
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||||
pub fn dsize(&self) -> u8 {
|
mod word_impl {
|
||||||
match self {
|
use super::*;
|
||||||
WordSize::EightBit => 0b0111,
|
|
||||||
WordSize::SixteenBit => 0b1111,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
pub type Config = u8;
|
||||||
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 {}
|
impl_word!(word::U4, 4 - 1);
|
||||||
|
impl_word!(word::U5, 5 - 1);
|
||||||
impl Word for u8 {}
|
impl_word!(word::U6, 6 - 1);
|
||||||
impl Word for u16 {}
|
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…
Reference in New Issue
Block a user