stm32-rs / stm32f1xx-hal

A Rust embedded-hal HAL impl for the STM32F1 family based on japarics stm32f103xx-hal
Apache License 2.0
575 stars 179 forks source link

SCK hiccups when using `blocking::spi::Write::write` rendering `Mosi` data corrupted #441

Open BryceBeagle opened 2 years ago

BryceBeagle commented 2 years ago

If I try to .write more than a certain number of bytes[1] (exactly) in a single call/packet, the SCK signal stops working properly, hiccuping at regular intervals and preventing the data from getting sent. A couple other users on the Embedded Rust matrix server reported having similar issues and it was requested that I open an issue for this.

I am using an stm32f103 bluepill. I have tried with both the v0.9.0 release of the HAL and the head of master (f063d5bef at time of writing).

[1]: With opt-level = 2 this is >13 bytes; with opt-level = 3 this is >28 bytes. These numbers are exact and deterministic


13 bytes of 0b10101010 at opt-level = 2: Screenshot from 2022-09-08 21-51-58

14 bytes of 0b10101010 at opt-level = 2: Screenshot from 2022-09-08 21-53-07


Reproducible snippet:

#![deny(unsafe_code)]
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use embedded_hal::spi::MODE_0;
use nb::block;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};
use stm32f1xx_hal::spi::{NoMiso, NoSck, Spi};

#[entry]
fn main() -> ! {
    rtt_init_print!();

    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = pac::Peripherals::take().unwrap();

    let mut flash = dp.FLASH.constrain();
    let rcc = dp.RCC.constrain();

    let clocks = rcc.cfgr
        .use_hse(8.MHz())
        .sysclk(48.MHz())
        .hclk(12.MHz())
        .pclk1(6.MHz())
        .freeze(&mut flash.acr);

    let mut gpiob = dp.GPIOB.split();
    let mosi = gpiob.pb15.into_alternate_push_pull(&mut gpiob.crh);
    let sck = gpiob.pb13.into_alternate_push_pull(&mut gpiob.crh);
    let mut spi = Spi::spi2(
        dp.SPI2,
        (sck, NoMiso, mosi),
        MODE_0,
        3.MHz(),
        clocks,
    );

    let mut timer = Timer::syst(cp.SYST, &clocks).counter_hz();
    timer.start(60.Hz()).unwrap();

    loop {
        spi.write(&[
            // 13 bytes
            0b10101010, 0b10101010, 0b10101010, 0b10101010,
            0b10101010, 0b10101010, 0b10101010, 0b10101010,
            0b10101010, 0b10101010, 0b10101010, 0b10101010,
            0b10101010,

            // One extra (uncommenting this breaks things with opt-level = 2)
            // 0b10101010,
        ]).unwrap();
        block!(timer.wait()).unwrap();
    }
hawav commented 1 year ago

I've encountered the same issue while driving WS2812B using SPI. No matter what I did, I couldn't get the lights to work properly. After using an oscilloscope, I found that spi.write will cause SCK stops for a period of time after each byte is sent. I was able to work around the issue by using DMA transfers instead.