diff options
| author | anton smeenk <[email protected]> | 2023-04-18 20:56:57 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-04-18 20:59:25 +0200 |
| commit | 3260f6b2aff824a5f7f2a81283b234bdb1bd3e24 (patch) | |
| tree | d23428ff0278b4a38bf856cde83c11f872876dd5 | |
| parent | 2080d8bb6de58eb2da2ca5df2437ac8cc6577661 (diff) | |
stm32/spi: add new_txonly_nosck constructor, for neopixels, with an example in the stm32g0 directory.
| -rw-r--r-- | embassy-stm32/src/spi/mod.rs | 17 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/spi_neopixel.rs | 101 |
2 files changed, 118 insertions, 0 deletions
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> { | |||
| 177 | ) | 177 | ) |
| 178 | } | 178 | } |
| 179 | 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 | |||
| 180 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. | 197 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. |
| 181 | /// The bus can optionally be exposed externally with `Spi::new()` still. | 198 | /// The bus can optionally be exposed externally with `Spi::new()` still. |
| 182 | #[allow(dead_code)] | 199 | #[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 @@ | |||
| 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 | } | ||
