2025-05-02 6:53 AM
Hi, guys,
I've bought STM32H745I-DISCO board to make an EtherCAT master, so I successfully employ EtherNet controller, USART3 for debugging but SPI2 has a problem. (I'm mostly novice fir H745)
I decided to use SPI2 for communication with external STM32F4xx controller, which exchanges 26 16-bit words with H745 board within 10kHz time frame, so it's a bit loaded communication. The attached picture renders the snapshot from logical analyser. 4 upper lines are SPI lines, other 3 lines are GPIOs used for debugging purposes - to show interrupt work.
The picture clearly shows that physical lines exchange data - I see expected 26 16-bit words from F4xx and data sent from H745 - in all cycles the picture is the same. But in reality I see only one time words from 4 to 22 in receive buffer (other appears to be zero), after that they are not updated at all. So receive is fault always. SPI2 is used in DMA mode via library function (code is below), also the picture shows that the output buffer is sent correctly. After 26 words reception I have an interrupt from SPI (lower right corner on picture). Each 10kHz I have strobe interrupt from F4xx, which is also properly serviced (lower left part of picture).
For SPI pins D10-D13 are used at "Arduino slot"
So I definitely need some kind advice where I'm wrong. The code was in part generated by Cube IDE. Initially it was a (working) TCP/IP example, redone for (working) EtherCAT stack, so I've added USART3 for debugging (works well) and SPI2. I have checked the pins assignment, do not see problems, GPIO for SPI2 looks also be well configured. Clock sub-system looks to be identical. DMA works to send data in proper way. I will try another board tomorrow for any chance....
The strobe interrupt code:
void slave_cb(void)
{
HAL_StatusTypeDef spi_status;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // arduino pin d5
if (1)
{
pin2_3_cnt[0]++; // statistics
if (1 != G_DR)
{ // error
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_SET); // arduino pin d7
spi_status = HAL_SPI_TransmitReceive_DMA(&hspi2, (uint8_t*)TX_SPI_data, (uint8_t*)RX_SPI_data, TRANSFER_LEN*2);
if (HAL_OK == spi_status)
{ HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET); } // pin D6
else { HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET); }
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_RESET); // arduino pin d7
}
G_DR = 0;
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); // arduino pin d5
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET); // D6
}
The picture shows that D6 line is set after correct exit from HAL_SPI_TransmitReceive_DMA() function
The SPI receive buffer interrupt code:
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET); // arduino pin d6
no_recv++;
if (HAL_SPI_ERROR_NONE != hspi->ErrorCode)
{ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // arduino pin d5
G_DR = -1;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); // arduino pin d5
}
else
{
uint32_t psum = 0;
// check control sum
// uint32_t* b32p = (uint32_t*)RX_SPI_data;
for(int ii=0; ii<TRANSFER_LEN-1; ii++) { psum += RX_SPI_data[ii]; }
int idx = ((psum != RX_SPI_data[TRANSFER_LEN-1]) ? 1 : 0); // 1 on error
if (1 == idx) // only for Breakpoint
{ HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_SET); // D7
G_DR = 0;
for (int ii=0; ii<TRANSFER_LEN-2; ii++) { RX_SPI_data[ii] = 0x0; } }
RX_SPI_data[TRANSFER_LEN-2] = no_recv;
RX_SPI_data[TRANSFER_LEN-1] = 0xAA55AA55;
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_RESET); // D7
} // error
else
{ corr_rcv[idx]++;
G_DR = 1; // received state OK
G_RCV = 1; // received status
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET); // arduino pin d6
spi_status = HAL_SPI_TransmitReceive_DMA(&hspi2, (uint8_t*)TX_SPI_data, (uint8_t*)RX_SPI_data, TRANSFER_LEN*2);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET); // arduino pin d6
}
}
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET); // arduino pin d6
}
This code computes the control sum but it is never OK
Code to init GPIO (external interrupt and "debugging" pins)
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitTypeDef GPIO_InitStruct_D5 = {0};
GPIO_InitTypeDef GPIO_InitStruct_D6 = {0};
GPIO_InitTypeDef GPIO_InitStruct_D7 = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
// ABCxExGHIxK
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOK_CLK_ENABLE();
/*Configure GPIO pin : intr_spi_Pin */
GPIO_InitStruct.Pin = intr_spi_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(intr_spi_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : D5 (PA8) */
GPIO_InitStruct_D5.Pin = GPIO_PIN_8;
GPIO_InitStruct_D5.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct_D5.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_D5);
/*Configure GPIO pin : D6 (PE6) */
GPIO_InitStruct_D6.Pin = GPIO_PIN_6;
GPIO_InitStruct_D6.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct_D6.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct_D6);
/*Configure GPIO pin : D7 (PI8) */
GPIO_InitStruct_D7.Pin = GPIO_PIN_8;
GPIO_InitStruct_D7.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct_D7.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOI, &GPIO_InitStruct_D7);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
Code to init SPI2:
static void MX_SPI2_Init(void)
{
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_SLAVE;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT ; // SPI_NSS_HARD_INPUT;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 0x0;
hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
}
Code to init DMA channels and map SPI2 pins to proper pins on board:
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// HAL_DMA_MuxSyncConfigTypeDef pSyncConfig;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(hspi->Instance==SPI2)
{
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI2;
PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Peripheral clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
/**SPI2 GPIO Configuration
PB4 (NJTRST) ------> SPI2_NSS
PD3 ------> SPI2_SCK
PI2 ------> SPI2_MISO
PB15 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = MODE_AF;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI2 DMA Init */
/* SPI2_RX Init */
hdma_spi2_rx.Instance = DMA1_Stream0;
hdma_spi2_rx.Init.Request = DMA_REQUEST_SPI2_RX;
hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi2_rx.Init.Mode = DMA_NORMAL; // CIRCULAR;
hdma_spi2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hspi,hdmarx,hdma_spi2_rx);
/* SPI2_TX Init */
hdma_spi2_tx.Instance = DMA1_Stream1;
hdma_spi2_tx.Init.Request = DMA_REQUEST_SPI2_TX;
hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi2_tx.Init.Mode = DMA_NORMAL; // CIRCULAR;
hdma_spi2_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hspi,hdmatx,hdma_spi2_tx);
/* SPI2 interrupt Init */
HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI2_IRQn);
}
}
Additional DMA interrupts init:
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
/* DMA1_Stream1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
}
Solved! Go to Solution.
2025-05-06 6:32 AM - edited 2025-05-06 6:33 AM
Here is a good application note for managing cache. It is from TI but is perfectly applicable.
Managing Cache Coherency on Cortex-M7 Based MCUs
This is from ST but isn't as complete IMO:
Level 1 cache on STM32F7 Series and STM32H7 Series
The quick start guide is:
2025-05-02 7:13 AM
> But in reality I see only one time words from 4 to 22 in receive buffer (other appears to be zero), after that they are not updated at all.
Where are you seeing these exactly? With execution paused in debugging mode?
You have no cache management calls, so ensure data cache is disabled.
2025-05-04 11:36 PM
Hi, thank you for the answer,
Only once I see partial correct data (in the middle of packet), but only once. (I stop the debugger on a break-point). After that the data in receive buffer is not changed.
Thanks, looks I need to invalidate cache. But I'm quite surprized - on another machines cache controller snoops the memory bus and read or invalidate data. Ok, will check and return here.
2025-05-05 5:06 AM
maybe you can direct me with some application note, where cache configuration is explained?
2025-05-05 5:33 AM
Maybe just disable it until the code works as expected, in order to eliminate it as a potential issue.
> HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET); // arduino pin d6
> spi_status = HAL_SPI_TransmitReceive_DMA(&hspi2, (uint8_t*)TX_SPI_data, (uint8_t*)RX_SPI_data, TRANSFER_LEN*2);
> HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET); // arduino pin d6
is d6 the CS pin? DMA happens in the background, you need to wait for the transmission to complete before raising CS.
2025-05-05 5:58 AM
D6 is just GPIO output for debugging output for logical analyser.
I'll check D-cache disable soon and write-back, thank you!
2025-05-06 6:22 AM
Yes, ho-ho, you are absolutely right - not switching D-Cache "on" changed the situation.
Still, before closing - I would like to know what is the solution for this case without switching off D-cache? (Performance degrades significantly)
I believe I should invalidate cache lines, which held receive data, and also invalidate cache lines which hold transmit data. (I have seen similar functions in system library sources). Or there another trick with addresses? Could you point me to proper application note?
2025-05-06 6:32 AM - edited 2025-05-06 6:33 AM
Here is a good application note for managing cache. It is from TI but is perfectly applicable.
Managing Cache Coherency on Cortex-M7 Based MCUs
This is from ST but isn't as complete IMO:
Level 1 cache on STM32F7 Series and STM32H7 Series
The quick start guide is:
2025-05-06 6:44 AM
BTW, does it affect Ethernet driver, used for TCP/IP in H745? I know it uses a dedicated DMA. I have not seen issues, but I wonder if D-cache can somehow affect Ethernet work.
2025-05-07 7:36 AM
I see that Ethernet driver includes instructions for d-cache invalidation.
So, the SPI library looks like not ready for modern STM32 controllers. :)
Ok, thank you very much for resolving the issue.