stm32l0xx-hal
stm32l0xx-hal copied to clipboard
SPI Readings Incorrect on MISO LSB
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:
Things I've Tried:
- Adjusting the clock from 100kHz - 2MHz
- Switching Modes (Mode 0 is recommended by slave, but I've tried all 4)
- Using a BME680 as the slave (Same behavior in MISO LSB)
- Using a different STM32L031
- Reading from the slave 10+ times is a row. With the example above, only the first read comes back incorrectly (0x0e).
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:
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;
What are you getting in _result
?
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.
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
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).