cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 SPI chip select intermittently delayed

AMacI.1
Associate II

I’m working on a project that uses an STM32H745IIK6. Occasionally, 32-bit SPI read data was invalid. This happened in about one out of every two hundred reads. To track down the cause, I added a few lines of code to toggle a GPIO pin as a scope trigger.

STM32H7 SPI cs delay 231002b.png

What I found was unexpected. Trace 1 is the invalid data detect toggle. Trace 2 is the SPI chip select. (firmware controlled GPIO pin) Trace 3 is the SPI clock. Trace 4 is the chip select for a second SPI device that uses the same port. Trace 2 shows that the misread was caused by a delayed chip select. And trace 4 shows that the chip select disable for the second device was also delayed.

An interrupt routine handles the chip select and starts the SPI send/receive.

void SPI2_IRQHandler(void)
{
  /* USER CODE BEGIN SPI2_IRQn 0 */
    LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_11);      // SPI2 CS2_N high, disable
    ReadData = LL_SPI_ReceiveData32(SPI2);            // read
    LL_SPI_DisableIT_RXP(SPI2);                       // disable SPI2 interrupts
    LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_12);    // SPI2 CS1_N low, enable
    LL_SPI_TransmitData32(SPI2, 0xA6000000);          // send data request command
  /* USER CODE END SPI2_IRQn 0 */
}

Looking at the code, it doesn’t seem like there’s any way the SPI clock output could start before the chip select goes low. The scope shows otherwise.

One possible clue is that the delay only happens if the FMC port is active. It’s configured  as FMC_NORSRAM_BANK1, FMC_DATA_ADDRESS_MUX_ENABLE, FMC_MEMORY_TYPE_NOR and FMC_NORSRAM_MEM_BUS_WIDTH_16. The FMC port runs asynchronously with SPI reads. The FMC ALELO pin shares PORTB with the SPI chip select. Maybe there’s some odd interaction.

My workaround solution is to add a read back of the chip select pin just before the SPI send/receive request. A string of nops doesn’t help, but reading back the chip select pin works perfectly.

(void)LL_GPIO_IsInputPinSet(GPIOB, LL_GPIO_PIN_12);

Maybe I’ve got something configured wrong. Or there’s some other subtle bug in the code. But I can’t imagine what. Anyhow, I’d like to hear if anyone has a theory or some insight.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

Looks like a caching/optimization artifact. You need CS low to happen before you transmit data and that's being delayed due to some CM7 magic. Probably the bus is busy due to FMC so it caches that operation while the program continues on. There may be an MPU setting to avoid it if you mark the GPIO area as uncached/unbuffered.

Try __DSB() just prior to LL_SPI_TransmitData32. Probably has the same effect as LL_GPIO_IsInputPinSet but is more explicit about it.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

2 REPLIES 2
TDK
Guru

Looks like a caching/optimization artifact. You need CS low to happen before you transmit data and that's being delayed due to some CM7 magic. Probably the bus is busy due to FMC so it caches that operation while the program continues on. There may be an MPU setting to avoid it if you mark the GPIO area as uncached/unbuffered.

Try __DSB() just prior to LL_SPI_TransmitData32. Probably has the same effect as LL_GPIO_IsInputPinSet but is more explicit about it.

If you feel a post has answered your question, please click "Accept as Solution".

A __DSB() prior to to LL_SPI_TransmitData32() works as well and is cleaner. Thanks!