smart-leds-rs / ws2812-spi-rs

Use ws2812 on rust with embedded-hal spi
Apache License 2.0
64 stars 23 forks source link

WS2812 don't seem to receive data from Blue Pill #5

Closed dereulenspiegel closed 5 years ago

dereulenspiegel commented 5 years ago

Hi!

I am trying to control 8 WS2812 from a Blue Pill. The Blue Pill has a STM32F103C8 and afaik it should be fast enough for normal SPI based control of these LEDs. I am using RTFM, since I like the concept and intend to also use it for other projects. But I also think that my issues aren't related to RTFM (at least I hope so). I have created the following example which should let some LEDs light up in different colors, but nothing happens.

#![no_main]
#![no_std]

extern crate panic_semihosting;

use cortex_m_semihosting::hprintln;
use rtfm::app;

use cortex_m::asm::delay;

use stm32f1xx_hal::gpio::*;
use stm32f1xx_hal::prelude::*;
use stm32f1xx_hal::time::MegaHertz;
use stm32f1xx_hal::spi::Spi;
use stm32f1xx_hal::gpio::gpiob::*;
use stm32f1xx_hal::gpio::gpioa::*;

use ws2812_spi::Ws2812;
use smart_leds::{RGB8, SmartLedsWrite};

#[app(device = stm32f1xx_hal::stm32)]
const APP: () = {

  static mut NEOPIXELS: Ws2812<
        Spi<
            stm32f1xx_hal::stm32::SPI1,
            (
                PA5<Alternate<PushPull>>,
                PA6<Input<Floating>>,
                PA7<Alternate<PushPull>>,
            ),
        >,
    > = ();

  #[init]
  fn init() -> init::LateResources {
    hprintln!("Starting init").unwrap();
    let mut core: rtfm::Peripherals = core;
    let mut rcc = device.RCC.constrain();
    let mut afio = device.AFIO.constrain(&mut rcc.apb2);
    let mut flash = device.FLASH.constrain();
    let clocks = rcc
      .cfgr
      .use_hse(8.mhz())
      .sysclk(56.mhz())
      .pclk1(28.mhz())
      .freeze(&mut flash.acr);

    let mut porta = device.GPIOA.split(&mut rcc.apb2);

    let mosi = porta.pa7.into_alternate_push_pull(&mut porta.crl);
    let miso = porta.pa6.into_floating_input(&mut porta.crl);
    let sck = porta.pa5.into_alternate_push_pull(&mut porta.crl);

    let spi_freq: MegaHertz = 3.mhz();
    let mut spi = Spi::spi1(
        device.SPI1,
        (sck, miso, mosi),
        &mut afio.mapr,
        ws2812_spi::MODE,
        spi_freq,
        clocks,
        &mut rcc.apb2,
    );

    let mut leds = Ws2812::new(spi);
    hprintln!("init done, late resources should be there").unwrap();
    init::LateResources {
      NEOPIXELS: leds,
    }
  }

  #[idle(resources = [NEOPIXELS])]
  fn idle() -> ! {
    hprintln!("idle init").unwrap();
    let mut data: [RGB8; 3] = [RGB8::default(); 3];
    let mut empty: [RGB8; 1] = [RGB8::default(); 1];
    data[0] = RGB8 {
        r: 0xFF,
        g: 0x00,
        b: 0x00,
    };
    data[1] = RGB8 {
        r: 0x00,
        g: 0xFF,
        b: 0x00,
    };
    data[2] = RGB8 {
        r: 0x00,
        g: 0x00,
        b: 0xFF,
    };
    resources.NEOPIXELS.write(data.iter().cloned()).unwrap();
    loop {
      resources.NEOPIXELS.write(data.iter().cloned()).unwrap();
    }
  }

  extern "C" {
    fn EXTI0();
    fn UART4();
  }
};

Unfortunately I currently do not have access to a logic analyzer or an oscilloscope. All I currently have is a simple pokit, but this lets me at least confirm that there is data coming out of MOSI and that the IN pin of the LEDs is correctly connected to MOSI. I hope I provided a thorough description of my situation, if you need more I am happy to provide you with all information I have. If anyone has an idea what I am doing wrong or what am I missing, I am glad for all hints.

david-sawatzke commented 5 years ago

It's probably something to do the spi peripheral itself. It can only be set to 3.5 MHz or 1.75 MHz, nothing in between (if i read the datasheet correctly). 3.5 may work, but your hal scales it down, so your frequency is way off.

Using the spi peripheral for this is a bit finicky, there may also be other strange issues. The pokit site says, that they do have a very basic DSO. A screenshot might help, although 1MS is very low.

Just FYI: If you're intending to use interrupts, you'll probably want to disable interrupts while writing the data.

dereulenspiegel commented 5 years ago

Thanks for these hints. I will probably try to first use the ws2812 timer based implementation for now. In the worst case I have to find a specific RTFM compatible variant. Disabling interrupts after init is afaik not possible there.

david-sawatzke commented 5 years ago

FYI: (Almost) all implementation rely on interrupts being disabled. The only way to not have this issue is by using dmas (or interrupts on fast MCUs), since the ws2812 protocol is pretty timing sensitive and fast.