Getting Started with STM32H5 ARM Cortex M33: SPI in TX with DMA

In this guide, we shall use DMA to transmit data over SPI bus to relief the CPU and make it free to do something else.

In this guide, we shall cover the following:

  • Why to use DMA with SPI.
  • SPI DMA configuration.
  • Firmware development.
  • Results.

1. Why to Use DMA with SPI:

Using DMA with SPI in STM32 is highly beneficial, especially in applications requiring high-speed communication and reduced CPU load. Here’s why you should use DMA with SPI:

1. Reduced CPU Load

• Without DMA, the CPU must manually read/write data in an interrupt or polling loop.

• With DMA, the CPU offloads data transfer to the DMA controller, freeing it to handle other tasks.

• Useful in real-time applications like displays (e.g., ILI9341), sensors (e.g., IMU), and data logging.

2. Higher Data Transfer Speed

• SPI is fast (up to tens of MHz), and CPU-driven transfers may not keep up.

• DMA allows continuous, high-speed transfers without gaps caused by CPU delays.

3. Efficient Handling of Large Data Buffers

• SPI is commonly used for large data transactions (e.g., SD cards, TFT screens, ADC/DAC).

• DMA can transfer entire buffers (e.g., 512-byte SD card blocks) automatically.

4. No Interrupt Overhead

• Without DMA, the CPU must handle an interrupt for each SPI byte/word.

• With DMA, only one interrupt occurs when the full transfer is complete.

• Reduces latency and power consumption in low-power applications.

5. Bidirectional SPI Transfers

• In full-duplex mode, SPI transmits and receives simultaneously.

• DMA can handle both RX and TX without CPU intervention.

6. Smooth Operation in RTOS-Based Systems

• In FreeRTOS, SPI DMA allows non-blocking communication, letting tasks continue execution.

• Prevents task starvation that would occur with blocking SPI functions.

2. SPI DMA Configuration:

We shall continue from this topic point here.

Open SPI_TX.ioc file as following:

From connectivity, select SPI1, the NVIC and enable SPI1 global interrupt as following:

Then from System Core, select GPDMA1 and enable Ch0 as standard request mode as following:

Configure the CH0 as following:

  • Disable Circular Mode.
  • Set the request to SPI1_TX/I2S1_TX.
  • Priority to low (depending on your application).
  • Direction Memory to Peripheral.

For the source data setting:

  • Source Address to increment.
  • Data width to byte.
  • Keep the rest as default.

For the destination data setting:

  • Disable increment mode.
  • Data width to byte.
  • Keep the rest as default.

Save the project as this will generate the code.

3. Firmware Development:

Once the code has been generated, main.c will be opened or open the main.c from the src folder.

In main.c file, in user begin PV section, declare the following:

#define DataSize 5

uint8_t txData[DataSize]={0x00,0x01,0x02,0x03,0x05};

volatile uint8_t SPI1_TX_Completed=0;

The first one is to define the data size, in this example, it is 5 byte.

Buffer to hold the data to be transmitted which its size defined by the define statement.

A volatile flag to handle the transfer completed by SPI.

Next, in user code begin 3 in while 1 loop:

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, 0);
HAL_SPI_Transmit_DMA(&hspi1, txData, DataSize);
while(SPI1_TX_Completed==0);
SPI1_TX_Completed=0;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, 1);
HAL_Delay(1);

Set PA6 (which is CS line).

Start the transmission using DMA using the following function:

 HAL_SPI_Transmit_DMA(&hspi1, txData, DataSize);

The function takes the following parameters:

  • Handletypedef for the SPI which is hspi1 in this case.
  • TxData, the buffer which stores the data to be transmitted.
  • Data size which is the amount of data to be transmitted.

Wait for the TX to be completed then reset flag to 0 and set PA6 to high again and wait 1ms.

Since we already enabled the interrupt for SPI and DMA is enabled by default, there is a function which will be called once the SPI has finished transmitting all the data which can be found here:

Drivers/STM32H5xx_HAL_Driver/Src/stm32h5xx_hal_spi.c

The function is:

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)

In user code begin 4 of the main.c:

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(hspi->Instance==SPI1)
	{
		SPI1_TX_Completed=1;
	}

}

Since the function can be called by multiple SPIs, we need to check which SPI is being called as following:

if(hspi->Instance==SPI1)

Change according to your SPI.

If it is the needed SPI, set the flag to 1.

Thats all for the Firmware development.

Save the project, build it and run it on your STM32H563Zi board.

4. Results:

By probing using either logic analyzer or oscilloscope, you should get the following:

Happy coding 😉

Add Comment

Your email address will not be published. Required fields are marked *