Library 56- Extend SPI with DMA for STM32F4xx

As said in one post before, here is SPI DMA library for STM32F4 devices. Instead of onl TX functionality (as in USART DMA library) SPI DMA extension library enables DMA for TX and RX modes at the same time, to receive and transmit data over DMA.

Library supports up to 6 SPIs (max number in STM32F4 devices). It can work in 3 main modes:

  • Send data to slave device, receive data from slave device
  • Send data to slave device, don’t care for received data from slave device
  • Send dummy byte(s) to slave, receive data from slave device

DMA does not make any interrupts after it ends, user will have to check this by itself. There are also functions for that to allow easy check if DMA and SPI are done.



  • Enables DMA feature for TM SPI library
  • Can transmit, send only or receive only data via SPI and DMA
  • Works for all possible SPIs in STM32F4 devices
  • Supports changeable stream and channel settings


    • STM32F4xx
    • STM32F4xx DMA
  • TM
    • TM SPI
    • TM DMA
    • defines.h

Stream and channel settings

STM32F4xx devices have 2 DMA controllers. Each DMA controller has 8 DMA streams where each stream has 8 DMA channels for different peripherals available.

To get all available DMA peripherals, you should take a STM32F4xx Reference manual (1700+ pages) and take a look at DMA section. There are all available streams and channels for different peripheral.

This library uses only USART TX DMA. Default DMA streams and channels are in table below:

SPIx DMA DMA TX Stream DMA TX Channel DMA RX Stream DMA RX Channel
SPI1 DMA2 DMA Stream 3 DMA Channel 3 DMA Stream 2 DMA Channel 3
SPI2 DMA1 DMA Stream 4 DMA Channel 0 DMA Stream 3 DMA Channel 0
SPI3 DMA1 DMA Stream 5 DMA Channel 0 DMA Stream 0 DMA Channel 0
SPI4 DMA2 DMA Stream 1 DMA Channel 4 DMA Stream 0 DMA Channel 4
SPI5 DMA2 DMA Stream 6 DMA Channel 7 DMA Stream 5 DMA Channel 7
SPI6 DMA2 DMA Stream 5 DMA Channel 1 DMA Stream 6 DMA Channel 0

Some SPIs uses also different streams and channels. This can be handy if you have 2 peripherals on the same stream and DMA and you want to enable DMA for both. You can’t do that because only one channel on specific stream can be used at a time. For that purpose, someking of “remapping” was enabled which allows you to select custom Stream and Channel for specific SPI if it is available. Always look for STM32F4xx Reference manual for that settings.



Example was tested using Nucleo-F411 board. Using SPI1, I connected MOSI and MISO pins together to simulate data from slave.

I split example into three parts:

  • In first part, I fill TX_Buffer and sent data over SPI. Because MOSI and MISO pins are connected together, I expected the same result in RX_Buffer after DMA finishes transmission. Image 1 proves successfull result.
    SPI RX Buffer after DMA transmission

    SPI RX Buffer after DMA transmission

    SPI DMA TX Buffer set

    SPI DMA TX Buffer set

  • In second part I sent data over SPI with DMA, but I set RX_Buffer to NULL, which means that I don’t to receive any data from DMA slave, only sent data. RX_Buffer is the same as it was at the end of part one.
  • Last part was to test receive method via SPI. I set TX_Buffer to NULL. In this case, DMA will send all zeros over SPI to slave, just to enable SPI clock for it. Because I had MOSI and MISO pins together, all zeros are received in RX_Buffer.

    SPI zeros sent over SPI DMA

    SPI zeros sent over SPI DMA

Project is available on my Github, download library below.

TM STM32F4 SPI DMA Library


Owner of this site. Also electronic enthusiasts, web developer, 3D printer fan, handball player and more. Big fan of STM32F4 devices. In anticipation of the new Discovery board for STM32F7 lines.

You may also like...

Read before commenting!

Before you make a new comment, make sure you agree with things listed below:

  • - Read post to make sure if it is already posted what you are asking for,
  • - Make sure you have the latest version of libraries used in your project,
  • - Make a clean and grammatically correct written message,
  • - Report as many details as possible, including what have you done so far,
  • - Do NOT post any code here. Use Pastebin,
  • - Do NOT post any error codes here. Use Pastebin,
  • - Specify STM32Fxxx family and used Discovery/EVAL/Nucleo or custom made board,
  • - Make sure your clock is set correct for PLL,
  • - If you are using my HAL drivers, please check this post how to start.
Comment will be deleted on breaking these rules without notification!
  • Pingback: All STM32F4 libraries - STM32F4 Discovery()

  • user

    Hi, your GitHub link at the very bottom of this page is broken. Feel free to delete this comment after fixing the issue 😉

  • kuds

    Dear Majerle,
    does your library 56 also support SPI+DMA with STM32F4 being the slave controlled by another device?
    Thanks for your support!

    • kuds

      Or is there something like a quick and dirty workaround to use your high-level and comfortably usable SPI+DMA libraries in slave-mode? Sure, I’d need to implement some sort of interrupt routine to react to the slave-select-signal but I would be glad if I didn’t have to go through the trouble of manually configuring all the channels and stuff 😉

  • kuds

    Hi, I have another question concerning the efficiency of your SPI+DMA methods. Is it in any case preferable to set up a DMA stream (like it’s done in TM_SPI_DMA_Transmit(…) ) for every kind of transmission? Is there something like a lower limit (few bytes or so) up to which DMA gives more overhead than performance improvements or is DMA always the most efficient way to use SPI? Thank you!

    • Probably everything more than 2-4 bytes is faster using DMA than polling.
      Sending 1 byte is not because you have to setup DMA streams.

      • kuds

        Thank’s for your fast reply! So can I still use pins once configured using TM_SPI_DMA_Init(…) for “normal” SPI or are those pins somehow blocked for DMA-only-use? It would be great if I could choose what method to use, depending on the size of the data to be transferred (the size in my application varies between a single and several thousand bytes).

        • The difference between normal mode and DMA mode is that in DMA mode, DMA sends data to memory, like SPI->DR = data, in polling mode, you have to do it manually.

          If DMA does not work with SPI, you can normally send data using TM_SPI_Send(…) function in pooling mode. If DMA works, you can’t send. You can check if DMA is working for specific SPI using TM_SPI_DMA_Sending or something like that function name 🙂 If it does sending then you can not send data using TM SPI Send function.

          • kuds

            Did I understand you right, that it is principally possible to use the same pins for DMA-SPI and normal SPI/polling but not at the same time? So before accessing the pins with normal SPI methods I’ll have to check if the pins are being used by DMA at the moment. I guess your “TM_SPI_DMA_Sending” method is TM_SPI_DMA_Working() which I can use as kind of a mutex, right? Thanks for your support!

          • It does not work as mutex.

            Like I said, If TM_SPI_DMA_Working() returns false, then you are able to send data using TM_SPI_Send() function, otherwise not.

  • Abirami Sridharan

    Hi, i am new to SPI .. i am working on reading a pressure sensor.. my master is micro(RL78)…. i am trying to send 32bit data buffer and generated code for SPI in Applilet… i have initialized a dummy buffer.. but i always receive an empty buffer… Below is my code… could anyone give me any suggestions

    u32 CO2_SpiDriver::getData(const u32 Buffer) {
    //#[ operation getData(u32)
    u32 transmitBuffer = Buffer; //contains the data to be transmitted
    u8 pBuffer_u8[4];
    memcpy(pBuffer_u8, &Buffer, 4);
    u32 receive = 0U;
    u8 pReceiverBuffer_u8[4];
    memcpy(pReceiverBuffer_u8, &receive, 4); //dummy Receiver buffer

    R_CSI20_Start(); //start the communication

    setCSpin(true); //CS pin active low

    (R_CSI20_Send_Receive(pBuffer_u8, 4, pReceiverBuffer_u8));

    return receive;

    here R_CSI20_Start() and sendRecieve is generated from applilet (i am using These achannels) myy clock is (0,1). while i checked it with logic analyzer the data is sent at the wrong time which means the clocks already started and my MOSI pin does not know when it should send the data

    • Abirami, you are on wrong website if you seek help for RL78. This is STM32 related only.

  • Can Uysal

    Hi there, I am trying to understand where does “settings->tx_stream->ndtr” line references to, but couldn’t find where you defined settings structe. Can you help? I want to learn how to check the state of dma and i2s peripherals.