The STM32 Cube IDE supports all features of the STM32 microcontrollers, but it is quite difficult to use. The STM32duino is the official Arduino implementation from STM, which is easy to use – but provides only the basic functionality!

I was missing I2S in Arduino, so I was wondering, what it takes to convert the code generated from Cube to an Arduino Library for my STM32F411 Black Pill.

Unfortunately I did not find any guidance for this on the internet. Therefore I share here my lessens learned:

Setting up DMA I2S in CubeIDE

Here are the CubeIDE configuration steps that I have executed. I was getting these values mostly from tutorials.

  • Create a new project for your processor in STM32CubeIDE
  • In Multimedia: activate I2S3
    • Select: Full Duplex Master
    • Select: Master Clock Output (optional)
    • In DMA Setting:
    • Define I2S_EXT_RX and SPI3_TX with priority High
    • Mode Circular
    • Use Fifo, Threashold, Data Width: Half Word
    • Burst Size: 4 Inc
    • In DMA Settings change Maximum Output Speed to Very High
  • In System Core RCC
    • Set High Speed Clock: Crystal/Ceramic Resonator
    • Activate Audio Clock Input (optional)
  • Clock Configuration
    • I changed PLL Source Mux to HSE (to use the external clock)
  • You can generate the Documentation, which might be quite handy
  • Save the settings: this updates the ioc file and is generating the source code.

From CubeIDE to Arduino Library

The source files in an Arduino library should be located in the src directory. In addition you need to add your file!

Copy files to the src directory

The key question is which of the generated files, need to be copied into the Arduino Library.
Here is what I have found for my scenario:

  • main.h
  • stm32f4xx_it.h
  • main.c: basic application code
  • stm32f4xx_hal_msp.c: config the hardware resources for i2s
  • stm32f4xx_it.c: DMA interrupt handler: DMA1_Stream0_IRQHandler, DMA1_Stream5_IRQHandler

I suggest to add the ioc file which contains all configuration settings to the docs directory for reference and I also recommend to add an empty hal_conf_extra.h file as a starting point to resolve conflicts.

Adding Missing Functionality

You can add your own API functionality in the main.h and main.c or you can create your own additional implementation files.

Resolving Compiler and Linker Errors

This is really the starting point. When you compile your current solution, you will get some compiler or linker errors because some methods have been defined multiple times. My approach to this was to resolve the conflicts with some defines:


#ifdef ARDUINO
// WARDNING: The SysTick_Handler leads to conflicts, so we rename it to make sure that it is not used!
#define SysTick_Handler NA_SysTick_Handler


In some cases you might also need to deactivate some standard Arduino functionality. This can be done in the hal_conf_extra.h file mentioned above.

Example Library

My STM32F411 Arduino i2s library can be found on Github!

Final Comments

The whole experience was quite unpleasant and the STM32 HAL is utterly complex: Finally there were no error messages if something was not working, so I had to pretty much rely on trial and error and it does not help that Arduino currently has no debugging support for the STM32!


Volodymyr · 17. February 2024 at 12:08

how to fit all this into the STM32F411??????
Thank you.

    pschatzmann · 17. February 2024 at 17:17

    I have added quite some functionality in the mean time. The output example gives the following on a Black Pill:

    Sketch uses 214412 bytes (40%) of program storage space. Maximum is 524288 bytes.
    Global variables use 12584 bytes (9%) of dynamic memory, leaving 118488 bytes for local variables. Maximum is 131072 bytes.

Leave a Reply

Avatar placeholder

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