2026-05-20 6:57 AM - last edited on 2026-05-20 8:39 AM by Andrew Neil
I am using the Analog Devices EVAL-ADE9430 Evaluation board for testing one of their devices. The Evaluation board uses the NUCLEO-144 STM32F413ZH processor. I am using the Keil uVision development setup to run and debug code. I am specifically concentrating on the SPI Bus Driver and the timing. I have no previous experience with STM Devices and how the SPI transfers happen in the processor. so I apologize a head of time.
My question is very basic I am trying to understand how the NUCLEO-144 STM32F413ZH board is transmitting the data over the SPI Bus. What setup is involved and what kicks off the actual transmission in the the HAL_SPI_Transmit() code below. The code i am using is transmitting 8 bits at a time.
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
I am trying to understand what's is this statement doing. Does writing this register do something special to kick off the transmission? If there is a good source to read I would appreciate it.
Best regards - mike
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint32_t tickstart;
HAL_StatusTypeDef errorcode = HAL_OK;
uint16_t initial_TxXferCount;
/* Check Direction parameter */
assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));
/* Process Locked */
__HAL_LOCK(hspi);
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
initial_TxXferCount = Size;
if (hspi->State != HAL_SPI_STATE_READY)
{
errorcode = HAL_BUSY;
goto error;
}
if ((pData == NULL) || (Size == 0U))
{
errorcode = HAL_ERROR;
goto error;
}
/* Set the transaction information */
hspi->State = HAL_SPI_STATE_BUSY_TX;
hspi->ErrorCode = HAL_SPI_ERROR_NONE;
hspi->pTxBuffPtr = (uint8_t *)pData;
hspi->TxXferSize = Size;
hspi->TxXferCount = Size;
/*Init field not used in handle to zero */
hspi->pRxBuffPtr = (uint8_t *)NULL;
hspi->RxXferSize = 0U;
hspi->RxXferCount = 0U;
hspi->TxISR = NULL;
hspi->RxISR = NULL;
/* Configure communication direction : 1Line */
if (hspi->Init.Direction == SPI_DIRECTION_1LINE)
{
SPI_1LINE_TX(hspi);
}
#if (USE_SPI_CRC != 0U)
/* Reset CRC Calculation */
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
SPI_RESET_CRC(hspi);
}
#endif /* USE_SPI_CRC */
/* Check if the SPI is already enabled */
if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
{
/* Enable SPI peripheral */
__HAL_SPI_ENABLE(hspi);
}
/* Transmit data in 16 Bit mode */
if (hspi->Init.DataSize == SPI_DATASIZE_16BIT)
{
if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
/* Transmit data in 16 Bit mode */
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}
/* Transmit data in 8 Bit mode */
else
{
if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
{
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint8_t);
hspi->TxXferCount--;
}
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr); 16 bits sent
hspi->pTxBuffPtr += sizeof(uint8_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}
Solved! Go to Solution.
2026-05-20 8:48 AM
Thank you for the explanation, it helps out a great deal! - mike
2026-05-20 8:08 AM
HAL does nothing really mystic here.
For each SPI you have one DR register. Writing to this register trasmits the written value to the transmit buffer, while reading the register copies a value from the receive buffer as described in RM0430 file:///home/klwa4731/Downloads/rm0430-stm32f413423-advanced-armbased-32bit-mcus-stmicroelectronics.pdf (section 29.3.9).
The transmission itself is handled in HW, regardless of HAL or LL or nothing at all is used.
HAL just covers finding the register by accessing it through the handle (and maybe hides differences between STM32 variants).
2026-05-20 8:48 AM
Thank you for the explanation, it helps out a great deal! - mike