stm32-rs / stm32l0xx-hal

A hardware abstraction layer (HAL) for the STM32L0 series microcontrollers written in Rust
BSD Zero Clause License
96 stars 60 forks source link

SPI Readings Incorrect on MISO LSB #81

Open mcopela opened 4 years ago

mcopela commented 4 years ago

I'm seeing a strange problem with the Full Duplex SPI library I can't figure out. Any help is appreciated!

The LSB on my reads usually comes back flipped even though my oscilloscope shows the slave output to be as expected. Only the MISO LSB is affected. This happens no matter what I'm reading or which slave device I connect.

I'm running code on a STM32L031 Evaluation Board. The example below is with a TI CC1200 as the slave.

Example

The master writes 0x3D two times. The slave returns 0x0F two times (As confirmed by the oscilloscope). The transfer function incorrectly returns 0x0E 0x0F

My Code:

#![no_std]
#![no_main]

//Generical Must Use Stuff
extern crate panic_semihosting;
use cortex_m_semihosting::hprintln;
use cortex_m_rt::entry;
use stm32l0xx_hal::{pac, prelude::*, rcc::Config};

// SPI Stuff
use stm32l0xx_hal::spi::{Polarity,Mode,Phase};

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

    // Configure the clock.
    let dp = pac::Peripherals::take().unwrap();
    let mut rcc = dp.RCC.freeze(Config::hsi16());

    // Setup SPI.
    let gpioa = dp.GPIOA.split(&mut rcc);   
    let sck = gpioa.pa5;              
    let miso = gpioa.pa6;
    let mosi = gpioa.pa7;
    let mode = Mode{polarity:Polarity::IdleLow,phase:Phase::CaptureOnFirstTransition};
    let mut ss = gpioa.pa3.into_push_pull_output();
    ss.set_high().unwrap();
    let mut spi = dp
        .SPI1
        .spi((sck, miso, mosi), mode, 100.khz(), &mut rcc);

    // Buffer to be used
    let mut buffer: [u8; 2] = [0x3D; 2];

    //Transfer the 2 byte buffer
    ss.set_low().unwrap();
    let _result = spi.transfer(&mut buffer).unwrap();
    ss.set_high().unwrap();

    //Print the Buffer
    hprintln!("Buffer After Transfer: {},{}", buffer[0],buffer[1]).unwrap();

    loop{}
}

Code Output:

Buffer After Transfer: 14,15

Oscilloscope Output:

ZoomedIn

ZoomedOut

Things I've Tried:

dzarda commented 4 years ago

I think we may be operating the peripheral incorrectly. The manual states that specific bits in SPI_CR1 be adjusted only while not enabled (SPE = 0). For example:

obrazek

This is confirmed when inspecting SPI driver code from ChibiOS HAL:

  /* SPI setup and enable.*/
  spip->spi->CR1 &= ~SPI_CR1_SPE;
  spip->spi->CR1  = spip->config->cr1 | SPI_CR1_MSTR | SPI_CR1_SSM |
                    SPI_CR1_SSI;
  spip->spi->CR2  = spip->config->cr2 | SPI_CR2_SSOE | SPI_CR2_RXDMAEN |
                    SPI_CR2_TXDMAEN;
  spip->spi->CR1 |= SPI_CR1_SPE;
dzarda commented 4 years ago

What are you getting in _result?

mcopela commented 4 years ago

What are you getting in _result?

The _result is the same as buffer. If I swap _result with buffer in the print statement I get the same output. Thanks for the help.

mcopela commented 4 years ago

I think we may be operating the peripheral incorrectly. The manual states that specific bits in SPI_CR1 be adjusted only while not enabled (SPE = 0). For example: ...

I downloaded the crate locally and implemented the change; I set the settings first, then set SPE=1. It didn't seem to fix my problem. I'll keep messing with the settings.

Changes to STM32L0XX-HAL::spi


                    hprintln!("Changing CR1");
                    #[allow(unused)]
                    spi.cr1.write(|w| unsafe {
                        w.spe()
                            .clear_bit()
                    });
                    hprintln!("CR1: {}",spi.cr1.read().bits());

                    spi.cr1.write(|w| unsafe {
                        w.cpha()
                            .bit(mode.phase == Phase::CaptureOnSecondTransition)
                            .cpol()
                            .bit(mode.polarity == Polarity::IdleHigh)
                            .mstr()
                            .set_bit()
                            .br()
                            .bits(br)
                            .lsbfirst()
                            .clear_bit()
                            .ssm()
                            .set_bit()
                            .ssi()
                            .set_bit()
                            .rxonly()
                            .clear_bit()
                            .dff()
                            .clear_bit()
                            .bidimode()
                            .clear_bit()
                            .spe()
                            .clear_bit()
                    });
                    hprintln!("CR1: {}",spi.cr1.read().bits());

                    spi.cr1.write(|w| unsafe {
                        w.cpha()
                            .bit(mode.phase == Phase::CaptureOnSecondTransition)
                            .cpol()
                            .bit(mode.polarity == Polarity::IdleHigh)
                            .mstr()
                            .set_bit()
                            .br()
                            .bits(br)
                            .lsbfirst()
                            .clear_bit()
                            .ssm()
                            .set_bit()
                            .ssi()
                            .set_bit()
                            .rxonly()
                            .clear_bit()
                            .dff()
                            .clear_bit()
                            .bidimode()
                            .clear_bit()
                            .spe()
                            .set_bit()
                    });
                    hprintln!("CR1: {}",spi.cr1.read().bits());

Output

Changing CR1
CR1: 0
CR1: 820
CR1: 884
Buffer After Transfer: 14,15
almusil commented 4 years ago

Sounds to me like you are hitting HW bug. Take look at the errata section 2.6.2.

The problem is basically speed of the SCK pin. Unless your SCK pin connection has capacitance higher than 30 pF, setting the SCK pin speed as High or Very High should mitigate the issue. (It worked for me).