Electronic – STM32F4 SPI Program hangs at TXE non empty

spistm32f4

I have been taking a udemy course on STM32F4. The course uses a different board than the one I have but from same family STM32F4. I couldn't make the SPI driver developed in the course to work in my board, so I tried t0 first test it using HAL.
I am using this video as a reference to communicate to L3GD20 Gyro in the development board. What I'm doing this is writing 0X11 to the CTRL_REG1(0x20) of the Gyroscope and then reading this register to check whether the value is written successfully or not. I could do that in HAL code

// ***** Spi Transmit ***** //
    // 1. Bring slave select low
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
    // 2. Transmit register + data
    spiTxBuf[0] = 0x20;
    spiTxBuf[1] = 0X11;
    HAL_SPI_Transmit(&hspi5, spiTxBuf, 2, 50);
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);

    // ***** Spi Receive ***** //
    // 1. Bring slave select low
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
    // 2. Transmit register + data
    spiTxBuf[0] = 0x20 | 0x80;
    HAL_SPI_Transmit(&hspi5, spiTxBuf, 1, 50);
    // 3. Receive data
    HAL_SPI_Receive(&hspi5, spiRxBuf, 1, 50);
    // 4. Bring slave select high
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

}

Above code works

But to learn the register level code I tried to create a write function

void SPI5_write(unsigned char data) {
    while (!(SPI5->SR & 2)) {}      /* wait until Transfer buffer Empty */
    SPI5->DR = data;                /* write data */
    while (SPI5->SR & 0x80) {}      /* wait for transmission done */
}

and used that to send data (reference)

// ***** Spi Transmit ***** //
    // 1. Bring slave select low
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
    // 2. Transmit register + data
    spiTxBuf[0] = 0x20;
    spiTxBuf[1] = 0X11;
    //HAL_SPI_Transmit(&hspi5, spiTxBuf, 2, 50);

    SPI5_write(spiTxBuf[0]);
    SPI5_write(spiTxBuf[1]);
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);

    // ***** Spi Receive ***** //
    // 1. Bring slave select low
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
    // 2. Transmit register + data
    spiTxBuf[0] = 0x20 | 0x80;
    HAL_SPI_Transmit(&hspi5, spiTxBuf, 1, 50);
    // 3. Receive data
    HAL_SPI_Receive(&hspi5, spiRxBuf, 1, 50);
    // 4. Bring slave select high
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    }

But it's not working.
I checked using debugger. What I understand is that, it sends first byte and when SPI5_write called second time, it hangs at first while loop==> TXE buffer not empty

What causing this?

EDIT: Complete code
IDE : KEIL MDK 5

Best Answer

I think I figured out the issue

SPI was enabled inside the HAL_SPI_Transmit() function. It checks wheter SPI is enabled or not inside the function, if not enabled it will enable it.

So if I want to write my own transmit function, I have to enable the corresponding SPI before transmission

eg:

SPI5->CR1 |=  SPI_CR1_SPE;
SPI5_write(spiTxBuf[0]);
SPI5_write(spiTxBuf[1]);

where

void SPI5_write(unsigned char data) {
    while (!(SPI5->SR & 2)) {}      /* wait until Transfer buffer Empty */
    SPI5->DR = data;                /* write data */
    while (SPI5->SR & 0x80) {}      /* wait for transmission done */
}