Author Topic: Strange STM32F777 SPI behaviour  (Read 428 times)

0 Members and 1 Guest are viewing this topic.

Offline EnceladusTopic starter

  • Contributor
  • Posts: 18
  • Country: au
Strange STM32F777 SPI behaviour
« on: June 06, 2024, 12:39:52 am »
Hello! I've been having issues with the SPI on STM32F777 microcontroller. Basically I need to read the data from the external ADC fast so I'm using bare registers for receiving while CubeMX is generating the SPI initialisation.

So the SPI configuration looks like this:
spi4_init.png

and the initialisation code is:
Code: [Select]
void MX_SPI4_Init(void)
{
  hspi4.Instance = SPI4;
  hspi4.Init.Mode = SPI_MODE_MASTER;
  hspi4.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
  hspi4.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi4.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi4.Init.NSS = SPI_NSS_SOFT;
  hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi4.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi4.Init.CRCPolynomial = 7;
  hspi4.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi4) != HAL_OK)
  {
    Error_Handler();
  }
}

Please note that the data size is 16 bit and the SPI mode is Receive Only Master.

So here is the code I'm using to receive the 16 bit:

Code: [Select]

        uint16_t tmp = 0;

        // select the AD4100
GPIOE->BSRR |= 0x100000;

// enable SPI
SPI4->CR1 |= SPI_CR1_SPE;

        // set the FIFO threshold to 1/2 of the buffer (16 bit)
CLEAR_BIT(SPI4->CR2, SPI_RXFIFO_THRESHOLD);

// wait for RXNE to set (Rx buffer is not empty)
while (!(SPI4->SR & SPI_SR_RXNE));

// copy data from the register
tmp = *(volatile uint16_t *)&SPI4->DR;

        // disable SPI
SPI4->CR1 &= (~SPI_CR1_SPE);

// deselect the AD4100
GPIOE->BSRR |= 0x0010;

Because the mode of the SPI is receive master only I don't need to send dummy data (though I tried, it didn't help) as it outputs CLK as soon as the SPI is enabled.

The problem is that for some reason the first read clocks more than 16 pulses and then the following reads set the SPI_SR_RXNE too early and the CS goes high before 16 CLK cycles are finished. Please see the images below:

RigolDS1.png
RigolDS0.png

I've tried to clear the FIFO but it didn't help:
Code: [Select]
uint8_t emp = 0;

while ((SPI4->SR & SPI_SR_FRLVL) != SPI_FRLVL_EMPTY)
emp = *(volatile uint8_t *)&SPI4->DR;

I would wait for the BSY flag but according to errata 2.15.1 https://www.st.com/resource/en/errata_sheet/es0334-stm32f76xxx-and-stm32f77xxx-device-errata-stmicroelectronics.pdf there is a bug that wouldn't allow me to do so (I've tried, it freezes in the endless waiting for the BSY flag to be released).

What could possibly cause such behaviour and are there any workarounds? Thank you!
 

Offline EnceladusTopic starter

  • Contributor
  • Posts: 18
  • Country: au
Re: Strange STM32F777 SPI behaviour
« Reply #1 on: June 06, 2024, 01:23:51 am »
OK, seems that I've found what's going on. I've changed the mode from Read Only Master to Full-Duplex Master. In this regime the CLK is only generated when I'm sending something, so I have to send a dummy uint16 to generate 16 CLK pulses:

Code: [Select]
// send dummy data
*(volatile uint16_t*)&SPI4->DR = tmp;
while (!((SPI4->SR) & (1 << 1)));

Now everything works fine and SPI_SR_RXNE flag is being set properly (when 16 bit were received). Looks like the Read Only Master is only to be used with circular DMA...
 

Offline aliarifat794

  • Regular Contributor
  • *
  • !
  • Posts: 138
  • Country: bd
Re: Strange STM32F777 SPI behaviour
« Reply #2 on: June 06, 2024, 06:11:35 am »
You can modify your code like this:
Code: [Select]
void MX_SPI4_Init(void)
{
  hspi4.Instance = SPI4;
  hspi4.Init.Mode = SPI_MODE_MASTER;
  hspi4.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
  hspi4.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi4.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi4.Init.NSS = SPI_NSS_SOFT;
  hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi4.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi4.Init.CRCPolynomial = 7;
  hspi4.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi4) != HAL_OK)
  {
    Error_Handler();
  }
}

uint16_t Read_SPI4_Data(void)
{
    uint16_t tmp = 0;

    // select the AD4100 (assuming active low)
    GPIOE->BSRR |= 0x100000; // Set pin low (assuming bit 20 corresponds to CS)

    // enable SPI
    SPI4->CR1 |= SPI_CR1_SPE;

    // set the FIFO threshold to 1/2 of the buffer (16 bit)
    SET_BIT(SPI4->CR2, SPI_RXFIFO_THRESHOLD);

    // small delay to ensure SPI is ready
    for(volatile int i = 0; i < 10; i++);

    // wait for RXNE to set (Rx buffer is not empty)
    while (!(SPI4->SR & SPI_SR_RXNE));

    // copy data from the register
    tmp = *(volatile uint16_t *)&SPI4->DR;

    // disable SPI
    SPI4->CR1 &= (~SPI_CR1_SPE);

    // deselect the AD4100
    GPIOE->BSRR |= 0x0010; // Set pin high (assuming bit 4 corresponds to CS)

    return tmp;
}
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf