cancel
Showing results for 
Search instead for 
Did you mean: 

SPI configuration change and DMA Transmit

livius147
Visitor

I’m currently developing a custom board based on an STM32U585.

On the board, I have three SPI devices connected to the MCU, each managed by its own chip-select line:

* MAX9939 → requires LSB first and can be controlled in transmit-only SPI mode.

* LSM6DSL (IMU) → requires MSB first and full-duplex mode.

* CAT25256 (EEPROM) → also requires MSB first and full-duplex mode.

To handle these differences, I switch the SPI configuration dynamically at runtime. The function I use for reconfiguration is:

int SPI_SetConfig(SPI_HandleTypeDef *hspi, struct _myspi_config_t *config)
{
printf(">>>>>>>>>>>>QUI\r\n");

// Skip re-initialization if configuration already matches
if (hspi->Init.CLKPolarity == config->CPOL &&
hspi->Init.CLKPhase == config->CPHA &&
hspi->Init.FirstBit == config->order &&
hspi->Init.Direction == config->direction &&
hspi->Init.DataSize == config->datasize &&
hspi->Init.BaudRatePrescaler == config->prescaler) {
return 0;
}

// Abort any ongoing transfer
if (HAL_SPI_GetState(hspi) != HAL_SPI_STATE_READY) {
HAL_SPI_Abort(hspi);
}

__HAL_SPI_DISABLE(hspi);

hspi->Init.CLKPolarity = config->CPOL;
hspi->Init.CLKPhase = config->CPHA;
hspi->Init.FirstBit = config->order;
hspi->Init.DataSize = config->datasize;
hspi->Init.Direction = config->direction;
hspi->Init.BaudRatePrescaler = config->prescaler;

if (HAL_SPI_Init(hspi) != HAL_OK) {
printf("SPI_SetConfig()::error while changing polarity\r\n");
return -1;
}

__HAL_SPI_ENABLE(hspi);

return 0;
}

## The issue

When I switch configurations repeatedly between devices, I encounter the following problem:

Calling HAL_SPI_Transmit_DMA() starts normally (no error returned).

However, HAL_SPI_TxCpltCallback() is never called.

If I check the state with HAL_SPI_GetState(), the SPI remains stuck in HAL_SPI_STATE_BUSY_TX.

If I wait in a loop for transmission completion, the loop never exits.

I checked both the SPI and DMA registers, and they appear to be fine. Despite that, the DMA transfer never completes.

If I skip one particular call to SPI_SetConfig(), the issue does not occur. But I don’t understand why this happens, and I’m not confident it won’t show up again under other conditions.

## Question

What could cause HAL_SPI_Transmit_DMA() to get stuck in BUSY_TX after reconfiguring the SPI peripheral multiple times?
How can I ensure safe switching between different SPI configurations without breaking DMA transfers?

Thanks in advance!


Edited to apply source code formatting - please see How to insert source code for future reference.

4 REPLIES 4
Andrew Neil
Super User

Welcome to the forum.

For best results, please see: How to write your question to maximize your chances to find a solution

 

Before adding the complications of DMA, have you got this working without it?

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

I changed to polling mode:

int status = 0;
if ((status=HAL_SPI_Transmit((SPI_HandleTypeDef*)handle, data, length, SPI_WAIT)) != HAL_OK) {
	printf("eeprom_write: HAL_SPI_Transmit error %d\r\n", status);
	return MEMORY_STATUS_NOK;
}

and I get the following message:

eeprom_write: HAL_SPI_Transmit error 1

1 is HAL_ERROR.

It is unable to complete the transfer.

SPI_WAIT is set to 500

I read also the SPI ErrorCode. The Error code is 32.

#define HAL_SPI_ERROR_FLAG                            (0x00000020UL)   /*!< Error on RXP/TXP/DXP/FTLVL/FRLVL Flag  */

I finally found the solution. I think the problem is related to an HW bug, because the SPI device seems unable to transmit both in DMA or Polling mode.

I solved the issue by using this source code to switch among SPI configurations:

int SPI_SetConfig(SPI_HandleTypeDef *hspi, struct _myspi_config_t *config)
{
	if (hspi->Init.CLKPolarity == config->CPOL &&
		hspi->Init.CLKPhase == config->CPHA &&
		hspi->Init.FirstBit == config->order &&
		hspi->Init.Direction == config->direction &&
		hspi->Init.DataSize == config->datasize &&
		hspi->Init.BaudRatePrescaler == config->prescaler) {
		return 0; // No change needed
	}

	if (HAL_SPI_GetState(hspi) != HAL_SPI_STATE_READY) {
		HAL_SPI_Abort(hspi);
	}

	__HAL_SPI_DISABLE(hspi);

	if (hspi->Instance == SPI1) {
		__HAL_RCC_SPI1_FORCE_RESET();
		__HAL_RCC_SPI1_RELEASE_RESET();
	} else if (hspi->Instance == SPI2) {
		__HAL_RCC_SPI2_FORCE_RESET();
		__HAL_RCC_SPI2_RELEASE_RESET();
	} else if (hspi->Instance == SPI3) {
		__HAL_RCC_SPI3_FORCE_RESET();
		__HAL_RCC_SPI3_RELEASE_RESET();
	}

	hspi->Init.CLKPolarity = config->CPOL;
	hspi->Init.CLKPhase = config->CPHA;
	hspi->Init.FirstBit = config->order;
	hspi->Init.DataSize = config->datasize;
	hspi->Init.Direction = config->direction;
	hspi->Init.BaudRatePrescaler = config->prescaler;

	if (HAL_SPI_Init(hspi) != HAL_OK)
	{
		printf("SPI_SetConfig()::error while changing polarity\r\n");
		return -1;
	}

	__HAL_SPI_ENABLE(hspi);

	return 0;
}

I found that __HAL_RCC_SPIx_FORCE_RESET() / __HAL_RCC_SPIx_RELEASE_RESET() macros reset the SPIx device by re-initializing all registers, FIFO and so on. 

I haven't found any equivalent HAL function to get the same result. Now I am able to switch, to transmit/receive in both DMA and polling mode. For these reasons I think this issue is related to an internal HW bug, caused by an internal inconsistent status.


Edited to apply proper source code formatting - please see How to insert source code for future reference.

You need to use the </> button rather than "preformatted"