Arduino_core_stm32: NUCLEO-H743ZI2 SPI.transfer() fails with SCLK less than 500 kHz in 1.9.0

Created on 8 Feb 2021  路  5Comments  路  Source: stm32duino/Arduino_Core_STM32

This sketch illustrates a failure of SPI.transfer with a NUCLEO-H743ZI2. The sketch use a SCLK rate of 400kHz.

 #include "SPI.h"
const uint8_t CS_PIN = 10;
void setup() {
  SPI.begin();
  pinMode(CS_PIN, OUTPUT);
}
void loop() {
  SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0));
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(0x55);
  digitalWrite(CS_PIN, HIGH);  
  SPI.endTransaction();
  delay(1);
}

The problem is shown in this trace of SCLK in yellow and MOSI in green. The eighth clock pulse and data are chopped.

nodelay

I looked at the SPI library code and found where the problem occurs, SPI is disabled before the last bit is complete.

The file is: STM32/hardware/stm32/1.9.0/libraries/SPI/src/utility/spi_com.c
If you add a delay here at about line 443:

#if defined(STM32H7xx) || defined(STM32MP1xx)
  /* Close transfer */
  /* Clear flags */
  HAL_Delay(1);  //  <<-------------- added by WHG to delay call LL_SPI_Disable()
  LL_SPI_ClearFlag_EOT(_SPI);
  LL_SPI_ClearFlag_TXTF(_SPI);
  /* Disable SPI peripheral */
  LL_SPI_Disable(_SPI);
#endif

The SPI frame is correct.

delay

I tried to find a SPI status check to replace the delay but was not successful when I looked at the STM32H743/753 Reference Manual.

Bug

All 5 comments

I looked at all the SPI signals with a logic analyzer and got a message about incorrect clock polarity.

I suspect that the SPI peripheral can not be disabled at the end of transfer() since this will cause a polarity problem for some SPI modes and glitches in the signals. Seems like the peripheral must be enabled from beginTransaction() to endTransaction().

See line 306 of STM32/hardware/stm32/1.9.0/libraries/SPI/src/utility/spi_com.c

  /* In order to set correctly the SPI polarity we need to enable the peripheral */
  __HAL_SPI_ENABLE(handle);

Hi @greiman,
Thanks for reporting this issue.
I am able to reproduce this issue.
But I didn't yet find the right way to fix it.
... ongoing ...

I have similar problem on blackpill stm32f401cc, the NSS pin was pulled high before spi transmission ends (SCLK=656K)

Snipaste_2021-02-27_21-15-03

Snipaste_2021-02-27_21-20-17

my fix is simple, wait untill spi transmission ends.

modify spi_com.c

#if defined(STM32H7xx) || defined(STM32MP1xx)
  while(!LL_SPI_IsActiveFlag_EOT(_SPI)); // <------ fix for stm32H7 etc.
#else
  while(LL_SPI_IsActiveFlag_BSY(_SPI)); // <------ fix for stm32f1/f2/f3/f4 etc.
#endif

#if defined(STM32H7xx) || defined(STM32MP1xx)
  /* Close transfer */
  /* Clear flags */
  LL_SPI_ClearFlag_EOT(_SPI);
  LL_SPI_ClearFlag_TXTF(_SPI);
  /* Disable SPI peripheral */
  LL_SPI_Disable(_SPI);
#endif

  return ret;
}

now it works properly

Snipaste_2021-02-27_23-58-25

Hi @mzhboy,
Thank you for sharing your experiment.
I already tried to wait on EOT (for stm32H743) but it doesn't help on the original issue (Clock and Data truncated).
I raised this issue to our SPI experts, and I am waiting their feedback.
By the way, I also tested on STM32MP1, I reproduce this original issue, and waiting on EOT also doesn't help.
Did you try your fix on STM32H7 ?

Concerning STM32F4, I tested on NUCLEO_F429ZI (the only F4 board I have), and I could not reproduce the original issue (Clock and Data truncated). And on your screenshot, I don't see such such behavior.
So I seems you face a different issue with NSS. And your patch seems valid to me at least for other boards than MP1 and H7.
I submitted a PR for that : https://github.com/stm32duino/Arduino_Core_STM32/pull/1312

I don't have H7 board that fix for H7 is just based on reference manual.
I have tested the fix for F4. I have F0/F1/F3/F4/G4 boards but I don't have time to test it(need rewiring).

my issue was happening only when SPI setting was SPI_TRANSMITONLY, SPI_TRANSMITRECEIVE had no problem.

check out spi_com.c


    if (!skipReceive) {
#if defined(STM32H7xx) || defined(STM32MP1xx)
      while (!LL_SPI_IsActiveFlag_RXP(_SPI));
#else
      while (!LL_SPI_IsActiveFlag_RXNE(_SPI));
#endif
      *rx_buffer++ = LL_SPI_ReceiveData8(_SPI);
    }
    if ((Timeout != HAL_MAX_DELAY) && (HAL_GetTick() - tickstart >= Timeout)) {
      ret = SPI_TIMEOUT;
      break;
    }
  }

while (!LL_SPI_IsActiveFlag_RXNE(_SPI)); means wait until last bit shift out or shift in, that's equivalent wait until BSY=0.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mcer12 picture mcer12  路  3Comments

kwastek picture kwastek  路  4Comments

johncblacker picture johncblacker  路  5Comments

romainreignier picture romainreignier  路  7Comments

Jorgen-VikingGod picture Jorgen-VikingGod  路  8Comments