Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STM32 SPI: extra clock cycle after configuration change (SpiDeviceWithConfig) #3524

Open
fbeutel opened this issue Nov 11, 2024 · 0 comments

Comments

@fbeutel
Copy link

fbeutel commented Nov 11, 2024

When using multiple SPI slaves with an STM32f091RB, I encounter the following problem:

When I change the SPI configuration to MODE_1 and then back to MODE_0, this results in an extra clock cycle, which leads to corrupt data when the CS pin is software controlled. (see code and screenshots below).

    // Prepare two SPI configurations, MODE_0 and MODE_1
    let mut config_mode0 = embassy_stm32::spi::Config::default();
    config_mode0.frequency = DOWNSTREAM_SPI_FREQ;
    config_mode0.mode = embassy_stm32::spi::MODE_0;

    let mut config_mode1 = embassy_stm32::spi::Config::default();
    config_mode1.frequency = DOWNSTREAM_SPI_FREQ;
    config_mode1.mode = embassy_stm32::spi::MODE_1;

    let cs_pin = Output::new(p.PC9, Level::High, Speed::Low);
    let mut spidev_with_cfg = SpiDeviceWithConfig::new(spi_bus, cs_pin, config_mode0);

    // First set the SPI config to MODE_1
    spi_bus.lock(|rc_bus| {
        let mut bus = rc_bus.borrow_mut();
        // When the following line is commented out, we get the expected behavior.
        // Only when we temporarily switch to MODE_1, an extra clock cycle is visible in the data
        bus.set_config(&config_mode1);
    });

    // Then send data using the device (with MODE_0)
    spidev_with_cfg.transfer_in_place(&mut [64u8, 0, 0]);

When the line bus.set_config(&config_mode1); is active, e.g. we temporarily switch to a different SPI mode (without sending any actual data)
Screenshot_20241111_155028

When the line is commented out and we never change the SPI config, or when we disable and re-enable the SPI bus between setting the configs:
Screenshot_20241111_153146

Workaround

A potential workaround is to change the code in Spi::set_config such that it disables and re-enables the SPI peripheral after applying the configuration change. For example, the following alteration of Spi::set_config seems to fix the problem:

#[cfg(any(spi_v1, spi_f1, spi_v2))]
self.info.regs.cr1().modify(|w| {
    w.set_spe(false); // Temporarily disable SPI
    w.set_cpha(cpha);
    w.set_cpol(cpol);
    w.set_br(br);
    w.set_lsbfirst(lsbfirst);
});

// After applying a cpha or cpol change, we disable and re-enable the SPI peripheral
// because otherwise the SPI peripheral behaves weirdly for software-managed CS pins.
#[cfg(any(spi_v1, spi_f1, spi_v2))]
self.info.regs.cr1().modify(|w| {
    w.set_spe(true); // Re-enable SPI
});

Is this behavior known or documented somewhere?
Or should I open a pull request with the described workaround?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant