2026-01-14 2:08 AM - last edited on 2026-01-14 2:16 AM by Andrew Neil
Hello everyone,
I am using SPI2 of STM32F103RCT6 to communicate with an AT45DB641E DataFlash device. I am trying to understand why the effective SPI data rate is significantly lower than what I expect based on the configured SPI clock.
Hardware setup
Test description
I am reading the status register of the AT45DB641E.
Once the read command is sent and SS is held low, the flash continuously outputs two status bytes as long as clock pulses are provided.
I measured the execution time of HAL_SPI_TransmitReceive() by placing timestamps before and after the function call, using the DWT cycle counter (DWT->CYCCNT) available on the Cortex-M3.
Here is the code used for testing, the tx and rx buffer are globally defined of size 5000 and datatype uint8_t:
/* Enable trace and debug block */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* Reset the cycle counter */
DWT->CYCCNT = 0;
/* Enable the cycle counter */
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
uint32_t previous_count=0;
uint32_t diff=0;
tx_buffer[0] = status_reg_read_opcode;
// starting to receive status byte continuously
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
previous_count = DWT->CYCCNT;
HAL_SPI_TransmitReceive(&hspi2, tx_buffer, rx_buffer, 5000, 1000);
diff = DWT->CYCCNT - previous_count;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
static void MX_SPI2_Init(void)
{
/* SPI2 parameter configuration*/
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
}
I have also attached my full main.c file.
Measured results
Below is the data I collected manually from the Expressions window in STM32CubeIDE (debug mode).
| No of bytes | cycles taken | actual time (us) | calculated datarate bytes/s | calculated time (us) | time difference | scaling factor (actual / cal) |
1000 | 270711 | 3759.8 | 1152000 | 888.8 | 2870.9 | 4.229 |
| 5000 | 1351484 | 18770.6 | 1152000 | 4444.4 | 14326.2 | 4.223 |
From this, it is clear that the actual transfer time is ~4.2× slower than the theoretical SPI transfer time.
Logic analyzer observations
I then captured the SPI signals using a logic analyzer (PulseView .sr file attached can be opened in pulseview software).
Key observations:
My current understanding / assumptions
In HAL_SPI_TransmitReceive(), the while loop:
while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
{
/* Check TXE flag */
if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U))
{
*(__IO uint8_t *)&hspi->Instance->DR = *((const uint8_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr++;
hspi->TxXferCount--;
/* Next Data is a reception (Rx). Tx not allowed */
txallowed = 0U;
}
/* Wait until RXNE flag is reset */
if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U))
{
(*(uint8_t *)hspi->pRxBuffPtr) = hspi->Instance->DR;
hspi->pRxBuffPtr++;
hspi->RxXferCount--;
/* Next Data is a Transmission (Tx). Tx is allowed */
txallowed = 1U;
}
if ((((HAL_GetTick() - tickstart) >= Timeout) && ((Timeout != HAL_MAX_DELAY))) || (Timeout == 0U))
{
hspi->State = HAL_SPI_STATE_READY;
__HAL_UNLOCK(hspi);
return HAL_TIMEOUT;
}
}
}These software checks introduce delay between successive writes to SPI->DR
This delay causes gaps in SCK, reducing effective throughput.
My questions
Motivation
I understand this might seem like going into excessive detail, but this is purely for learning purposes. I want to understand how SPI really works at the hardware level — not just from an API point of view, but the actual mechanics behind clock generation, data shifting, and timing.
Any insights, corrections, or references would be greatly appreciated.
Thank you for your time.
Solved! Go to Solution.
2026-01-15 6:34 AM
@Ozone wrote:The "S" stands for synchronous, .
No, it stands for 'Serial' (although it is also synchronous).
2026-01-15 6:43 AM
In this you are right ...
Although inter-chip connections via shift registers existed already before '79.
SPI is still synchronous, making it often somewhat difficult to understand for beginners.