Skip to content

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

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

Closed
greiman opened this issue Feb 8, 2021 · 10 comments · Fixed by #1506
Closed

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

greiman opened this issue Feb 8, 2021 · 10 comments · Fixed by #1506
Assignees
Labels
bug 🐛 Something isn't working
Milestone

Comments

@greiman
Copy link

greiman commented Feb 8, 2021

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.

@greiman
Copy link
Author

greiman commented Feb 9, 2021

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);

@ABOSTM
Copy link
Contributor

ABOSTM commented Feb 11, 2021

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 ...

@fpistm fpistm added the bug 🐛 Something isn't working label Feb 11, 2021
@fpistm fpistm added this to the 2.0.0 milestone Feb 11, 2021
@mzhboy
Copy link

mzhboy commented Feb 27, 2021

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

@ABOSTM
Copy link
Contributor

ABOSTM commented Mar 2, 2021

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 : #1312

@mzhboy
Copy link

mzhboy commented Mar 2, 2021

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.

@fpistm
Copy link
Member

fpistm commented Sep 3, 2021

Hi,

I've made some update on the SPI driver. It should be fine to test them --> #1488

@greiman
Copy link
Author

greiman commented Sep 4, 2021

I downloaded Arduino_Core_STM32-master.zip which has #1488 and replaced my 2.0.0 here:

C:\Users\Bill\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.0.0

I tested with my SdFat library using a NUCLEO-H743ZI2.

#1488 appeared to help since SdFat works with some SD cards but failed with many.

I used the following program to test the new SPI library with the conditions where the SD card fails during initialization.

#include <SPI.h>
void setup() {
  pinMode(SS, OUTPUT);
  digitalWrite(SS, HIGH);
  SPI.begin();
}
void loop() {
  SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0));
  digitalWrite(SS, LOW);
  SPI.transfer(0X55);
  SPI.transfer(0XAA);
  digitalWrite(SS, HIGH);
  SPI.endTransaction();
  delay(1);
}

Here is a capture of the SPI bus using a Saleae Logic Analyzer. The channels are D0 - SS, D1 - MOSI, D2 - MISO, D3 - SCK.
I also captured analog on these channels, A1 - MOSI, A3 - SCK
STM32H743ZI2

Here is the same program on a NUCLEO-F446RE, It does not have the glitches that are present with the NUCLEO-H743ZI2. There is not dead time between bytes.

STM32F446RE

@fpistm
Copy link
Member

fpistm commented Sep 4, 2021

Ok. Thanks for the test. We always waiting feedback from internal experts.

ABOSTM added a commit to ABOSTM/Arduino_Core_STM32 that referenced this issue Sep 23, 2021
…nals

Add a delay before disabling SPI otherwise last-bit/last-clock
may be truncated.
See stm32duino#1294
Computed delay is half SPI clock.

Signed-off-by: Alexandre Bourdiol <[email protected]>
@ABOSTM
Copy link
Contributor

ABOSTM commented Sep 23, 2021

Our experts confirm this issue:

  1. Output signals truncation due to disabling soon after EOT flag clear but before true ending of communication

Description
In SPI mode, when transfer is configured as finite (not endless, CR2 content /= 0), the device signals
an End Of Transfer, rising EOT flag in status register after the last sampling edge of the serial clock.
If the software manages the flag, performs the latest operations to be concluded, clears the flag and
performs a spi disable, it could be possible to truncate the output signals ( SCK, NSS and MOSI in
case of master, otherwise MISO) corrupting the last received/transmitted bit of the last dataframe.

Conditions
The conditions in which the defect occurs are:

  • I2SMODE =0 at I2SCFGR register ( spi mode)
  • TSIZE different from 0 ( finite transfer)
  • Frequency of apb clock >> frequency of serial clock one
  • SPI disable soon after EOT rising event

Implications
Serial Data signal can toggle during latest sampling SCK clock edge of the communication
causing the corruption of its real value.

Workaround
Software can add a delay between the EOT flag clear and the SPI disable providing some time
for sampling correctly the last valid bit of latest dataframe.

@ABOSTM
Copy link
Contributor

ABOSTM commented Sep 23, 2021

Workaround has been implemented for STM32H7 and STM32MP1:
#1506

@fpistm fpistm added this to the 2.1.0 milestone Sep 23, 2021
ABOSTM added a commit to ABOSTM/Arduino_Core_STM32 that referenced this issue Sep 23, 2021
…nals

Add a delay before disabling SPI otherwise last-bit/last-clock
may be truncated.
See stm32duino#1294
Computed delay is half SPI clock.

Signed-off-by: Alexandre Bourdiol <[email protected]>
fpistm pushed a commit that referenced this issue Sep 24, 2021
…nals

Add a delay before disabling SPI otherwise last-bit/last-clock
may be truncated.
See #1294
Computed delay is half SPI clock.

Signed-off-by: Alexandre Bourdiol <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants