From 3260f6b2aff824a5f7f2a81283b234bdb1bd3e24 Mon Sep 17 00:00:00 2001 From: anton smeenk Date: Tue, 18 Apr 2023 20:56:57 +0200 Subject: [PATCH] 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 9ce0cebfe..492d0649a 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 000000000..81fdd15cb --- /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; + } +}