Library 05- SPI for STM32F4

Let’s say something about SPI. SPI (or Serial Peripheral Interface) is a protocol named by Motorola. With him you can control sensors, SD card and much more.

SPI protocol works in a ways where there is one master and multiple slaves, In other words, master is our STM32F429 Discovery board and let’s say, SD card is slave.

SPI uses 3 main wires. Because more slaves can be connected to one master on same SPI peripheral, there is 4th pin called SS. SPI pin names are:

  • SCLK: Serial clock, clock for synchronization slave with master
  • MOSI: Master Out Slave In. data sent from master to slave
  • MISO: Master In Slave Out, data send from slave to master
  • SS: Slave Select, with this pin low, you enable slave. This is useful, when you have more devices on same SPI. If you have more devices as slaves, you have to manually set pins for every slave separately. When you want to send data to some slave, you put SS pin for that slave low, send data and put it back high. If SS pin is HIGH, slave does not detect your incoming data.
Connect master with slave


Our STM32F429 Discovery supports 6 SPIs. Except SPI6, all SPIs have at least 2 configurable pins, which you want to use. I searched for pins used for peripheral and show to you in bottom table.

Pins pack 1 Pins pack 2 Pins pack 3
SPI1 PA7 PA6 PA5 PB5 PB4 PB3 2
SPI2 PC3 PC2 PB10 PB15 PB14 PB13 PI3 PI2 PI0 1
SPI3 PB5 PB4 PB3 PC12 PC11 PC10 1
SPI4 PE6 PE5 PE2 PE14 PE13 PE12 2
SPI5 PF9 PF8 PF7 PF11 PH7 PH6 2
SPI6 PG14 PG12 PG13 2

On the 8th column is APB. APB means Advanced Peripheral Bus. Inside our mcu are APB1 and APB2. This two are used for peripheral clock configuration. There is also AHB (Advanced High-performance Bus) for GPIO pins.

The difference between APB1 and APB2 is:

  1. APB1 has clock 4x slower than processor speed, in our case we have 180MHz core clock, APB1 has 45MHz
    1. APB1 communication bus has 45MHz, so SPI works also on 45MHz
    2. And because minimal prescaler for SPI is 2, max frequency is 45/2 = 22.5 MHz
    3. SPI Clock = APBx / prescaler = 45MHz  / 2 = 22.5MHz
    4. By default, prescaler 32 is used:
      1. SPI Clock = APBx / prescaler = 45MHz / 32 = 1.40625MHz
  2. APB2 has clock 2x slower than processor speed, in our case we have 180MHz core clock, APB2 has 90MHz
    1. APB2 communication bus has 90MHz
    2. And because minimal prescaler for SPI is 2, max frequency is 45MHz
    3. SPI Clock = APBx / prescaler = 90MHz / 2 = 45MHz
    4. By default, prescaler 32 is used:
      1. SPI Clock = APBx / prescaler = 90MHz / 32 = 2.8125MHz

This means that SPI1, SPI4, SPI5 and SPI6 have maximum frequency of 45MHz, SPI2 and SPI3 have 22.5MHz. In both cases frequency can be slower, set with internal prescalers.



  • All possible SPI’s available on STM32F4
  • 8- or 16- bit SPI mode
  • Default SPI configuration
    • Master mode
    • 8 data bits
    • prescaler set to divide APBx clock with 32
    • Clock is low on idle and data sampled at rising edge (SPI Mode 0)
    • first bit is MSB bit
    • 2 wires full-duplex
    • SS pin is software configurable


    • STM32F4xx
    • STM32F4xx RCC
    • STM32F4xx GPIO
    • STM32F4xx SPI
  • TM
    • TM GPIO
    • defines.h
    • attributes.h

You can configure your settings in defines.h file:

If you want to initialize SPI1 and set pins PA7, PA6 and PA5 for MOSI, MISO and SCK then you would call something like that:

or with pins pack 2

This principle works for all 6 SPIs.

Then you want to send some data over SPI. You can do this with

This function sends 8bit data over SPIx. When SPI finished transmission, 8bit received data is returned.

Functions and enumerations

Main example

Project is available on Github, download library below.

TM STM32F4 SPI Library

SPI library for all SPI peripherals


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!
  • David Fira Mladinescu

    Hey, I tryed to use your SPI library with an STM32F429 board but when I compile the program I get the following error: “SPI4 undeclared (first use in this function), TM_SPI_Init()”.

    • It looks like you are having some problems with your CMSIS library.
      SPI4 exists in STM32F4xx libraries.

      • David Fira Mladinescu

        Ok, I finally found out how to use uVision. I tryed like 3 IDEs for this board and when I finally got to use uVision I was getting errors over errors because I didn’t know how to use the enviroment. No I’m getting on the right path and now everything compiles fine. Great job with the website, it helped me a ton.

  • phivu

    Dear Sir. I have just programed my code to read acceleraton data from KX84-2050 (datasheet is in attached file) to stm324f01 RE through SPI1. Unfortunately, it seems it is stuck in sending and reading function. Please help me to fix that error. Many thanks.

    • That’s the reason, why I made libraries for STM32F4.
      Use it!

      • phivu

        Dear Sir,
        It still not run when I follow your structure in library spi.c Therefore, Can you help me to check more carefully and tell me what i’m wrong please. Best Regards,
        Phi Vu.

        • SPIHandle.SPI_CPOL = SPI_CPOL_High;
          SPIHandle.SPI_CPHA = SPI_CPHA_2Edge;

          In main.c you have this. according to the datasheet, it should be CPOL_Low and CPHA_1Edge. Try and report.

          • phivu

            I fixed as you proposed already. But it stiil not run. I think it stuck in send function or frequency is not true because for printf ” starting program”, it can run well through hyperterminal

          • phivu

            Hi tilen , I increased the prescaller but it still not run. May be can you help me to fix my code for send and receive data from KXP84 -2050

          • I dont have the module for this to test at home.

          • Nguyen Master

            Did you reset Pin CS (Chip Select) on KXP84 module? I see in timing diagram has a reset signal to receive data but in your SPI_send_data subroutine didn’t have that one. Im learning with Arm too, very nice to meet u, hope we cant help together to study more about this. My mail on fb

          • phivu

            Actually, you can see in my program PB6 used as Pin CS to active or deactive the signal from KXP84 🙂

          • Nguyen Master

            Did you check your clock frequency , i see the minimum cycle per 1 clock is 260ns, it means the maximum of clock frequency is 3.8MHz, with higher frequency i afraid it cant work.

          • phivu

            Hi Nguyen Master and Tilen,
            I have just updated my program. Currentlly, it can escape from the stuck sending function as I mentioned before. However, Ianother problem occured, the value of this sensor shown me in hyperterminal is 0 for analog input signal. Therefore, can you check and tell me what I should fix more. below is my new code.
            By the way, Nguyen Master, I think the frequency of SPI will be 84Mhz (my micro) /4 =21 HMhz and divide more by prescaller of 128 ; therefore we have the lower frequency of 1 Mhz, right?

          • phivu

            Although , I sucessfully program this type of sensor with arduino due before already, i donot know why in new micro, it cannot operate as I want.

          • phivu

            Hi Tilen, I fixed as you proprosed but it is still not working. I think that it is stuck in send function at the initial commands for KXP84-2050. By the way, I think it also relates to the frequency of micro stm32f401 re for SPI communication. For KXP84-2050, it is 1 Mhz

        • You need to increase prescaler for spi clock.
          Open defines.h add lines below and edit X with your spi


          //Options can be overwriten in defines.h file
          #define TM_SPIx_PRESCALER SPI_BaudRatePrescaler_128

  • bqpd

    Dear Sir. I’m not sure but i think i detected a bug. Shouldn’t the returning data type of “uint8_t TM_SPI_Send(…)” be uint16_t instead of uint8_t? Otherwise I wouldn’t be able to retrieve and send 16 bit words. The same for the parameter “uint8_t data”. Forgive me if I’m wrong.
    Yours sincerely, bqpd.
    P.S. Thanks for yor great work. It makes my work a lot easier.

    • Hi. It’s not a bug. If you need to send 16bit over spi, call function twice.

      • bqpd

        Don’t get me wrong – but isn’t this unnessescary – if i’dd change the data type and define SPI_InitStruct.SPI_DataSize = SPI_DataSize_16b – then the 16 bit would be written in the SPI->DR Register and everything would be sent in 1 call of TM_SPI_Send(). Or am I wrong?

        • Right.
          But its the same if is twice 8bit or one 16bit.

          • bqpd

            Sure – but i wasn’t sure if that was your intention. Just wanted to point that out.

            Because of the fact that you can change “#define TM_SPIx_DATASIZE SPI_DataSize_8b” in “define.h”, some people might think that “TM_SPI_Send()” is able to send 16 bit words at once. Just wanted to point it out and make sure i’m not wrong.
            Thanks for your work and support.

          • Ok, I will add new methods for 16-bit data length if SPI is configured in 16bit mode.

  • Nikolaj

    Dear Tilen,
    I attached an ILI9341 TFT to my STM32F4-DISCOVERY. Using your libraries for the display – it was smooth.
    Now I want to get the resistive touch interface running. Many Chinese displays are using the SPI interface for the touch interface using the ADS7843 controller. With this controller, we need to write an 8 bit command and then read two 8 bit bytes back from the controller after 5 us.

    When I try to use your library with TM_SPI_SendMulti(SPI3, DataOut, DataIn,1);, I don’t get any data back. After the 1 byte command is sent, the function doesn’t wait for data to come back. I guess it’s the timing that’s wrong.
    I guess the easiest way would be to first issue the send command and then later issue two read commands each reading the result data. Then we should have perfect control.
    But I can’t find the functions in your library to do this. Can you suggest how this could be done?
    I am attaching an image (from the ADS7843 datasheet) showing the SPI data of what I want to capture.

    I thank you in advance.


    • Very simple! I assume you know how SPI works!!

      uint8_t data[2];
      TM_SPI_Send(SPIx, yourbyte); //you will get back data which you can ignore
      //Wait for busy
      TM_SPI_ReadMulti(SPIx, data, 0x00, 2); //receive 2 bytes, send 0x00 dummy bytes, 2 bytes


  • Nikolaj

    🙂 Yeah… Very simple. And it Works too. Now both display and touch interface is working well. Thanks!

  • Syrer


    – Would you please point me out to the source of the differences between APB1 and APB2? I’m missing that somehow. Beside the Clock tree in the Reference manual, if any.
    – I have a question about the return value of the TM_SPI_Send(SPI_TypeDef*, uint8_t). What does it exactly return? Does it wait for an answer from the slave and return it back to the main? I assume that because it checks for the RXNE flag. What happens if no response comes from the slave, or there should be always a respond from the SPI slave?

    Thanx in advance 🙂

    • Stm32f4xx reference manual is for you. 1713 pages 🙂
      Look how spi works. You send something and simultaneous slave sends you something back. This information may be useful or not. My lib returns what slave sends.

      • Syrer

        uhm, ok .. I expected such answer 🙂 .. Never mind, I am getting better everyday. Your website helps me alot.
        Well i wanted to interface the SHT75 temperature and humidity sensor and though it has a SPI Interface, but it does not, so I will come to this library later 🙂

  • Christian Julius

    What did you change in new version? Does not compile with other libraries…

    In file included from .TMLIBinc/tm_stm32f4_ili9341_ltdc.h:71:0,
    from TMLIBsrctm_stm32f4_ili9341_ltdc.c:19:
    .TMLIBinc/tm_stm32f4_spi.h:470:8: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’
    Process terminated with status 1 (0 minutes, 2 seconds)
    1 errors, 0 warnings (0 minutes, 2 seconds)

    • As you can see, inside dependencies section you will find “attributes.h” file.
      There “fix” for GCC based compilers with __weak parameter function.
      Download and include!

  • Christian Julius

    Compilation of whole actual lib package with C99 syntax, -Wall, Os

    ————– Build: FlashRelease in stm32F429_project —————
    Compiling: SPLsrcstm32f4xx_fmc.c
    Compiling: SPLsrcstm32f4xx_gpio.c
    Compiling: SPLsrcstm32f4xx_ltdc.c
    Compiling: SPLsrcstm32f4xx_rcc.c
    Compiling: SPLsrcstm32f4xx_rng.c
    Compiling: SPLsrcstm32f4xx_spi.c
    Compiling: TMLIBemwinGUIConf.c
    Compiling: TMLIBemwinGUIDRV_stm32f429i_discovery.c
    In file included from .TMLIB/tm_stm32f4_ili9341_ltdc.h:71:0,
    from TMLIBemwinglobal_includes.h:34,
    from TMLIBemwinGUIDRV_stm32f429i_discovery.c:68:
    .TMLIB/tm_stm32f4_spi.h:470:8: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’
    TMLIBemwinGUIDRV_stm32f429i_discovery.c: In function ‘_GetPixelformat’:
    TMLIBemwinGUIDRV_stm32f429i_discovery.c:327:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    TMLIBemwinGUIDRV_stm32f429i_discovery.c: In function ‘_DMA_Index2ColorBulk’:
    TMLIBemwinGUIDRV_stm32f429i_discovery.c:680:86: warning: unused parameter ‘SizeOfIndex’ [-Wunused-parameter]
    TMLIBemwinGUIDRV_stm32f429i_discovery.c: In function ‘_DMA_Color2IndexBulk’:
    TMLIBemwinGUIDRV_stm32f429i_discovery.c:701:86: warning: unused parameter ‘SizeOfIndex’ [-Wunused-parameter]
    TMLIBemwinGUIDRV_stm32f429i_discovery.c: In function ‘_LCD_MixColorsBulk’:
    TMLIBemwinGUIDRV_stm32f429i_discovery.c:721:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    TMLIBemwinGUIDRV_stm32f429i_discovery.c: In function ‘_LCD_InitController’:
    TMLIBemwinGUIDRV_stm32f429i_discovery.c:899:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    TMLIBemwinGUIDRV_stm32f429i_discovery.c:915:5: warning: implicit declaration of function ‘DMA2D_ITConfig’ [-Wimplicit-function-declaration]
    TMLIBemwinGUIDRV_stm32f429i_discovery.c: In function ‘_LCD_DrawBitmap16bpp’:
    TMLIBemwinGUIDRV_stm32f429i_discovery.c:1095:105: warning: unused parameter ‘BytesPerLine’ [-Wunused-parameter]
    Process terminated with status 1 (0 minutes, 5 seconds)
    1 errors, 7 warnings (0 minutes, 5 seconds)

    • Use pastebin for things like that.
      And download attributes.h file

      • Christian Julius

        No change. The project is propely set up.

        • Redownlaod SPI.

          • Christian Julius

            Err….well, I downloaded the whole tree of libs and replaced it with my version of 2 weeks ago. And from now on my application cant compile anymore. And I am not familar enough with the dependencies of you libs and hoe they wor together.
            There is a problem mit GCC but where…. no idea.

          • Ok,
            you can still download my entire list of libraries in “All libraries” section is link to “download all”.
            I’ve made now recompilation for my GCC based project with this lib and it works OK.
            So problem must be at your side.

          • Christian Julius

            I think so….. I use emblocks and some settings are deep inside the IDE…. I removed all custom SPI settings and it compiles now… now I will re insert them one by one and see whats goind on….

          • Ok.
            If you need instant chat or support, you can add me to skype.
            To get info, you can open “About” section for more.

          • Christian Julius

            Im an on linux here and have no skype. I reinserted the expressions and the strange error appears again. But I dont use custom spi pinpacks, the Disco Board has only 2 I mean. so that is ok for me.

            I only have a chat here:


          • Christian Julius

            Got it! I am idiot !!!
            Just insert the line below in your spi.h and it works…

            #include “stm32f4xx.h”
            #include “stm32f4xx_rcc.h”
            #include “stm32f4xx_gpio.h”
            #include “stm32f4xx_spi.h”
            #include “defines.h”
            #include “attributes.h” <<<< =====
            #include "tm_stm32f4_gpio.h"

          • I did that for you before I told you to redownload lib.

          • Christian Julius

            🙂 Maybe you want to include my “text sroller”, nice for having logs on the display but the bottle neck is the set pixel routine, the calculations for landscape, potrait etc cost time, every time the routine is called. So I removed all checks for illegal params.
            Is it spossible to attach -C files here?

          • Um, well this is not related with SPI 🙂
            But you can send them to my email address.

          • Christian Julius

            Send it to you facebook site this second….

  • Saputra Ahmad Edi

    hi, i just got error to send data over spi stm32f4-disco to arduino as slave console, it doesnt send correctly, for example i send 3 byte data 0xFF 0xFE 0xF4, on arduino i receive 0xFF 0x04 0x56 or any wrong number. but if i debug using keil, it mean i send data one by one, it send correctly and i receive 0xFF 0xFE 0xF4, do you have any idea about it?

    and one more, is there any relation between AHB GPIO SPEED with APB SPI speed?

    • SPI works great. I have it in multiple proejcts without problems.
      By meaning Arduino I assume you use UNO version.
      I don’t know how you receive data on arduino side but I think that you are too slow with receiving from Arduino.
      With debug it works because you send byte by byte with let’s say 100ms minimum because you are pressing the button for next step.

      GPIO pin can toggle at Sysclk/2 maximum.
      Yes, it is relation. Please give more info what you want to know here exactly.

      • Saputra Ahmad Edi

        thanks for advanced, i guess so, but when i send to ethernet module w5100 and i read it back i got an error to (i have succed with arduino). i make prescaler slow than before but it just as same result. i give some delay about 100ms and it works but sometime still not.

        sysclk/2, do you mean 168Mhz/2(stm32f407) or system clock for AHB? can i set AHB speed slower than SPI clock SCK?

        i want to work with ethernet, i am new with neteworking and this stm32 board, so try to make library for ethetnet modul because i dont know how to work with PHY. there is so many library we have to add

        • Ok, this is definetelly not problem with my SPI library.
          For ethernet and F407, use my ethernet library with external PHY.
          It will be much faster, and it allows you to do changes you want.

          I tried with W5100 and it worked for first time but then I came across that external PHY is better choice and LWIP.

          • Saputra Ahmad Edi

            yes is not your SPI library, i try to change convert your library to c++, because i have ever made some library before like usart gpio and so on, i want reduce my memory usage because i will work with many device and i think my other device is more memory consuming, like i want work with printer

            i’ve tried your phy library, but i got error when compiling it, the error is memory not enough, error code = L060xx i dont remember, memory compilation is 4MB, i dont know why

          • Saputra Ahmad Edi

            oh error code is l6050U, i just try to compile again

  • Tomasz Urbaniak

    Hello Tilen,
    I’m using stm32f4-discovery.
    I’ve just discovered that for SPI1 only the PinPack_2 works correctly.
    TM_SPI_PinsPack_1 fails to work.

    Could that be a pin conflict? I remember using the pins defined in PinPack_1 but with another library, and then it worked.
    Overall, I appreciate your huge work with your libraries!

    • Hi,

      I’m using SPI1 with PP1 without any problems.
      Please redownlaod all libraries and it’s dependencies and try again.

      Make sure that your pinout in real is like it says in library.

  • Fabien R

    Thanks for this library.
    It saves time to test things.
    Nevertheless, I had to add the SSI bit to make my adns3080 work on my stm32f429 discovery.
    Not sure if it is specific.


  • abdurrahman korkmaz

    hi friend,
    i used spi1 for grafic lcd. lcd is ok but usarts not work propably. do you have any idea?

  • Linh Nguyen Duy

    Hi Tilen. Please help me for this situation.
    I am using both STM32F4 DISCOVERY and CORE STM32F429 board from
    I use your SPI library with standard driver to read ADS1292 chip from TI but I have a tricky problem.
    SPI2 was used to read data from ADS1292, the code works fine with STM32F4 DISCOVERY but when I change to STM32F429 it doesn’t work.
    I already checked all registers of SPI2, they were same on both boards.
    Could you help me please!
    thank you so much

    • When you use program from F4-Discovery on your F429 board, does it even run? SPI2 works on all STM32F4 series. I’ve successfully used it on F401, F411, F429, F439 and F407. On others, I didn’t tested. Make sure hardware is ok and everything else.

      Also, try to make blinky project for F4-Disco and use the same hex on F429. Check if board starts.

      • Linh Nguyen Duy

        Thank you Tilen for quick reply
        I use your sample project to use both on F4 Discovery and SMT32F429.
        When change to STM32F429, i just change the target of project from F4 Discovery USB to STM32F429I-Discovery USB.
        As I mentioned above, in this program I also use SPI1 to read data from another chip and it works fine. and I am sure that F429 works well.
        One thing I did not understand is SPI2 works on F4 Discovery but not on F429.

        • Is pinout the same? Can you make sure that?
          Connect logic/oscilloscope, check if SCK pin is active.

          Use pastebin to show me sample code for your project. Or you can send it to my email if this is confidential.

          • Linh Nguyen Duy

            I send you email Please check and help me.
            I can be sure that pinout is the same.

          • Rum Lyen

            Have you succeeded at the end with the interfacing of ADS1292 ? I have the same chip to interface. I request for the update of the project.
            Thank you.

    • Linh Nguyen Duy

      One more thing is that in the code, I also used SPI1 to read data from another chip and it work fine on STM32F4 Discovery and stm32f429

  • Jorge Del Aguila

    Hello TM!

    I’m trying to get data from the discovery’s onboard accelerometer. The code I have for this is the following:

    I have debbuged the program and I always get 255 for both OUT_X_L and OUT_X_H. The SPI is configured as default with 8 bit datasize.

    Here is the accelerometer datasheet:

    • Problem is in your read and write data functions.
      CS pin MUST be low for entire time, in your case, it is low only for one byte.

      Check datasheet again. When you wanna read byte, you would do something like that:
      1. CS low,
      2. send register address with read command
      3. send 1 dummy byte and read received data
      4. CS high.

      for writing, it is the same, just send register address with write command.

      • Jorge Del Aguila

        Thanks man! I forgot to send the dummy byte. Accelerometer giving data nicely! Great work 😀

  • Gary

    Hi TM,

    I hope you can take a moment to read my post and provide some insight into what might be happening. This device is a bit weird, so using even your well written libraries might be a challenge.
    I’m using an STM32F405 device, and with an SPI serial flash chip from Micron, the M25P128.
    I started out with the Standard Peripheral Library example called “spi_flash.c”, which is supposedly made for this device. It has a very strange read and write behavior! I have the CS line pulled high by a 6K8 resistor, and the layout is OK, so no issues there. If I select an address on the device to write to (using mode 3) it seems to write ok, and if I read it back right after, it does OK when compared and displays the same values. However, if I try to re-write the same address, this is where there’s trouble – it trashes the read values. There’s a command to “sector erase”, but running this in mode 3 seems to hang the device while it’s waiting for the “WIP” (write in progress) bit to go low. Very strange.

    So just a couple of questions: do you know anyone that’s used your SPI library with this device?
    Also, are you aware of an issue with the SPI modes on this device? I found a thread on the STM32 forum where they talked about having to change SPI modes on the fly, from 3 for writing, to 0 for reading. It’s apparently some kind of issue with clocking in the dummy byte ending on a low to high transition, and getting data out on a high to low transition. This is apparently some anomaly on the STM32F4 devices.

    Thanks for any help you could provide!

    • Hello Gary,

      As you mentioned, you write it and read it back again ok. So I think, problem is with your flash.

      Im aware of many spi bugs, for example if peripheral clock apb2 is >=4x slower than system clock, you will fail reading spi busy flag because spi is too slow for you to set this flag. So you have to fill data register, wait for spi to become busy and then wait for spi to release busy.

      I’ve also found reading problems on spi mode 1 (at least here, maybe more nodes also) when reading. Some bytes were wrong read. Solution was to slow down apb bus.

      With modes I never had problems.
      If you have a chance, use logic analyzer and check what you get and what is in stm memory.

      • Gary

        That’s interesting about the apb2 speed. I got a bit lost on your logic:
        is this valid ?
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
        And this is not?
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;

        Directly from the ST example for the flash device is the sendByte function:

        uint8_t sFLASH_SendByte(uint8_t byte)
        /*!< Loop while DR register in not empty */
        while (SPI_I2S_GetFlagStatus(sFLASH_SPI, SPI_I2S_FLAG_TXE) == RESET);

        /*!< Send byte through the SPI1 peripheral */
        SPI_I2S_SendData(sFLASH_SPI, byte);

        /*!< Wait to receive a byte */
        while (SPI_I2S_GetFlagStatus(sFLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET);

        /*!< Return the byte read from the SPI bus */
        return SPI_I2S_ReceiveData(sFLASH_SPI);

        Where could I insert the check for validating the registers? Just before the call to:
        while (SPI_I2S_GetFlagStatus(sFLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET);

        Which register and what bit would I be checking for?

        Thanks again for the polite and prompt help!

        • You are messing some things here.
          STM32 has bus interfaces for communication between peripherals.
          For example, GPIO registers are connected to AHB (Advanced HighSpeed Bus) while peripherals are in general connected to APB1,2 (Advanced Peripheral Bus 1 or 2). Bus 1 is in general 4x times slower than AHB and APB2 is 2x times slower than AHB.
          AHB works the same as CPU speed in most cases.

          The case you said is SPI prescaler. This is a prescaler divider for SPI clock which is divided from APB clock.

          For example, main CPU speed is 180MHz, so AHB bus has 180MHz and APB2 bus has 90MHz.
          SPI1 is connected on APB2 bus, so if you set SPI baudrate prescaler to 4, SPI clock will be 90MHz/4 = something MHz.
          Read my article again and check ST site with detailed description about that.

          • Gary

            TM –
            Sorry, and and thanks for the clarification. You said this:

            “if peripheral clock apb2 is >=4x slower than system clock, you will fail reading spi busy flag because spi is too slow for you to set this flag”

            I am using APB2Periph_SPI1. If my clock is 168Mhz, and APB2 is > 168/2 (84Mhz), are you saying the SPI will have problems because the APB2 should be not greater than 42Mhz?

            If so, you say to do this: “So you have to fill data register, wait for spi to become busy and then wait for spi to release busy.”

            Can you explain that in context of the sample function I pasted: which register and what bit?

            Thanks again,

          • First question is NO. Faster that is APB clock, better is, but APB2 can’t be faster than 84MHz by specifications. What I wanna tell you is, that if APB2 for example is >= 4x slower than CPU clock, in this case if APB2 is 42 or less MHz, you will reading SPI busy flag. For example:

            SPI1->DR = 0x05; //Send 0x05;

            while (SPI->SR & SPI_SR_BUSY); //Wait till SPI is busy. You will go directly after this statement, because SPI interface clock (APB) is too slow to set it faster and you CPU is too fast.

            You need to do something like that:

            SPI1->DR = 0x05;while (!(SPI1->SR & SPI_SR_BUSY)); //Wait SPI to become busy
            while ((SPI1->SR & SPI_SR_BUSY)); //Wait SPI to release busy

            But this is not something you need to worry I think. Use Reference manual. There is everything explained about registers.

          • Gary

            Ok, thanks again. I am pretty sure something on the registers is causing this very strange problem in the code. Why it would write and read one time, then erase sector doesn’t work…it’s all weird. Also I will try to scope the clock and data to see what they are looking like.

            Just one other comment on pull-up for CS: I think the internal pull-ups are adequate for CS line, but it’s a bit ambiguous. Micron says 10K is OK, Spansion says 100K is good (for very similar devices) Since internal pull up is about 40K, I think it’s alright for Micron. I thought at first maybe this is part of the problem, but I tried an external 10K pull up, and it made no difference.

            I will also try a Spansion SPI device, same footprint, and see if that works. As you said, maybe it’s problem with the device.

          • You need PULL-UP on I2C for example, where outputs are Open-Drain.
            In SPI, all 4 pins (including CS) are Push-Pull, so there is no need for pullup resistor.

  • LB IN HB

    Hello Tilen,
    I am learning a lot about stm32f4 using your libraries. Thanks for all your work.

    I am using a Olimex stm32-e407 board and need to use a custom pinout for SPI.
    I see you have a TM_SPI_PinsPack_Custom option, but I am not sure how to use this.

    Do you have any example code for settings the custom pins?


    • Check Github.

      • LB IN HB

        Sorry. Yes it was right there. I looked everywhere but there.

        Thanks, and sorry for troubling you.

  • Pradeep Chl

    Hi TM,

    Sorry for my mistake to post in a different section. I have standard peripheral drivers version 1.0.0 while in your main.c file of SPI you have mentioned that it need a 1.4.0 or greater version of standard peripheral drivers. Do you know where I will find the required library or if possible a project template including the library for stm32f4 discovery board?


  • Gary


    I am looking for some suggestions on reading an odd number of bits from a device (LT2433) which is an SPI device.There are 4 modes it can be run in, but I am looking at just a regular SPI transfer. The maximum clock rate is < 2MHz.
    To read, and after asserting CS low, the first bit of the 19 bit stream is a 0 to indicate EOC. I'm planning on reading it just once per second, so no problem with data rates.

    My question is, in your experience, what is the best way to handle this? Should it be set up just like regular SPI? What would be the best way to get 19 bits using 8 bit read setup, and then, how to validate the EOC bit at position of bit 18?

    Thanks for your help…

    • Hi,

      Read 3bytes,shoft them properly into one variable and read bit 19 🙂

  • Gary

    Thanks for that, sounds simple enough. I have the setup for 8 bit reads. However, since the device never “accepts” commands, should SPI be set up for:

    SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Rx

    When I need to perform a “read”, right now I do this:

    GPIOE->BSRRH |= LT2433_CS; // set (CS) low
    for (uint32_t i = 0; i DR;
    while( SPI2->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore
    GPIOE->BSRRL |= LT2433_CS; // set (CS) high
    return dataReg;

    So…how would I get this to read 3 bytes? Because once CS is low, the device will shift all 19 bits, so not sure how this would happen when the read size on SPI is just 8 bits?

    • When CS goes low, device won’t just shift out by itself. You are master so you must clock out pulses for shifting.

      Another option is to use my ReadMulti function.

  • Gary

    Thanks for posting that, so it is 3 consecutive reads. Then shift into a 32 bit word.
    About the clock, I’m using the device with “internal oscillator” to take advantage of filtering (fo is at GND). See attached image from data sheet. So it generates clock internally @17.5KHz and puts it out on SCK pin. So, can I use SPI with an external clock as opposed to it generating one at some divisor of APB2 bus?

  • Majid Barz

    Hi Tilen.
    First, thanks a lot for your libraries. they are great and they help me so much.
    I have a trouble using “TM_SPI_WriteMulti” function. my purpose is to send three 8 bit data continuously(24 bit). when I set the prescaler to 32(around 2.5MHz) everything is almost fine. but when I set the prescaler to 8(around 12MHz) there is a little gap between every 8 clock cycles. can I do something to fix that so all 24 cycles be continuous without gap? Thanks so much again
    here is what my logic analyzer shows:

    • Gap is always the same, but it looks other way on different clock speed.

      You can use DMA to avoid that.

  • Tamas

    Hi Tilen!

    I want to communicate with two ADC-s on SPI. Is it possible to use different MOSI and MISO (MOSI_0, MISO_0, MOSI_1, MISO_1) but with the same SCLK and CS pin?

    • No. All pins together, only csis different for each chip.

      • Tamas

        Thanks. And if the SPI of the chip needs 32 bit data word, can I send two 16 bits data word with the STM32 (or 4 8 bits data word). but then do i have to control the cs pin manually?

  • ko

    Dear Tilen,
    thanks for your phantastic libraries. I’m trying to get SPI working on a STM32F407 with custom pins, just like in your example on Github. Your example code is doing well, but as soon as I change the custom pins from A7, A6, B3 (MOSI, MISO, CLK) to D0, D6, D3 nothing works anymore. I just changed the Port to GPIOD and the Pins in TM_SPI_InitCustomPinsCallback. What’s the problem? I’m looking forward to your answer and appreciate your work!

    • What are D0, D6, D3 pins?

      • Ko

        Pins number GPIO_Pin_0, GPIO_Pin_6 and GPIO_Pin_3 on GPIOD.

        • Which SPI can use those pins as SPI? Which SPI number?

          • ko

            Here’s my modified function from your Github example:
            I thought with a custom pin pack I could just use _any_ combination of pins for SPI. Seems like this was wrong! BTW: Why is there a limitation? Is there a workaround if I already etched a PCB with tracks connected to the pins mentioned above? Thanks for your help!

          • For hardware spi is no workaround.

  • Engineer

    Is it possible to mix 2 pin packs? because there is no comment in the pin definitions that it is not possible

    • It is. PinsPack are my “virtual” thing to prepare some groups of pins for fast initialziation. Check how TM_SPI_PinsPack_Custom should be used for initialization. Callback will be called. Check documentation.

      • Engineer

        Thanks for your quick response

  • Entropy

    Hi Tilen, sorry to ask irrelevant question to the topic here but i have been being irritated with the facts that ARM’s CMSIS had to set this HAL this way, “(GPIOx)->BSRRH = (GPIO_Pin)” to set it Low and “(GPIOx)->BSRRL = (GPIO_Pin)” to set that corresponding pin High? is it me or they swapped it the other way around, reading the manual it is seen that the BSSR register is used to bitwise write access to GPIOx_ODR which is applicable to the output GPIO pins, although i couldn’t locate the exact register on the manual it is obvious splits into 2 halves of 16 bits, so why are the pins to be set “Low” held in the BSSRH and pins to be set high into the BSSRL? in the occasion that all pins are set high or low how will this scenario turn up? sorry to ask too many questions but it confuses me! Cheers

    • BSRR register is 32-bit register in STM32.
      As STM32 has 16 GPIO lines on each PORT, you can use the same register to set pin value.

      If you set value to lower 16 bits, you will set corresponding bit and if you set 1 to upper 16 bits, you will set corresponding pin LOW.

      If you check BSRRH and BSRRL values in structure, they are both declared as 16 bits, meaning they “point” to the same value as BSRR register but not as 32bit value but 16 bit value. It is ok that way.

  • Victor

    Which function i have to use to read SPI from master?

  • Yash

    Hi! I am trying to configure the MCU using STMCubeMX. I am unable to figure out how to add multiple slaves to SPI1.

    • Not relevant with this lib 😉
      What you need in STM32Cube to connect multi slaves to single SPI? Connect them with traces together MOSI MISO SCK pins and one pin for each slave for CS.

      • Yash

        Thanks for such a quick response! I am sorry that my doubts will go further out of context for this article.
        So, I am configuring STM32L476 using CubeMX. Now, for SPI1, I have selected mode as Full-Duplex Master, and Hardware NSS Signal as Output. I have got one SPI1_NSS line with that. I don’t know how to have SPI1_NSS2 on this board.

        Another doubt is regarding ADC. For ADC1, there are IN1 .. IN15, and for each one single-ended or differential. What do these types mean?
        Please suggest any alternative way of communication as this post seems to spam the discussion of this article. Thanks!

        • Use SPI1 and set software NSS pin. Then for each slave declare their own NSS pin. You don’t have too much control with hardware NSS pin.

          Regarding ADC, I’m not answering here since it is irrelevant topic.

  • Tomi


    I have a 5 channel ADC. Accordind to its datasheet it sends “data frames” so i have to confiugure a frame register to set which channels i want to read. I can communicate with the adc. If i set into the register the channels I need, the adc sends me the channels i set. But here is the problem. I receive only the adresses of the channels. So from the adresses that i receive ii know that the frame settings are good but after the adress, where the converted data is there are only zeros. I enabled the conversion, i did everything what the datasheet said. And the adc cant be bad because i have more and neither of them wants to send me the data. only the adresses. What is the problem?

    • I’m not sure what this has to do with SPI but anyway. Check datasheet for your device (I assume you use SPI ADC).

  • Victor

    Thank you for your libraries. I learned a lot with them and I’m using them in my projects.
    Unfortunately, I’m having a problem reading data from my IMU sensor. Can you help me with that?

    • Victor

      I solved it. The problem was in a function I was using. Sorry to bother you.
      Thanks againg for your blog. You can delete my post if you want.

  • sameena shaikh

    Hi Tilen,
    I am trying to read the external accelerometer values through spi interface to my stm32f429i discovery board. But i am not able to do so. Can You help me with this?

    I am displaying values through uart. But the value is always 0 as initialised

    Thanks & Regards
    Sameena shaikh

    This is the code i have written. Used stm32cube mx to generate the initializations

    #include “main.h”
    #include “stm32f4xx_hal.h”

    SPI_HandleTypeDef hspi1;

    UART_HandleTypeDef huart1;

    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_SPI1_Init(void);
    static void MX_USART1_UART_Init(void);

    #ifdef _GNUC_
    /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
    set to ‘Yes’) calls __io_putchar() */
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* _GNUC_ */

    * @brief Retargets the C library printf function to the USART.
    * @param None
    * @retval None
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART */
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);

    return ch;

    uint8_t address=0, data=0;
    uint8_t x_acc=0, y_acc=0, z_acc=0;

    int main(void)





    HAL_SPI_Transmit(&hspi1, &address, 1, 50);

    HAL_SPI_Transmit(&hspi1, &data, 1, 50);



    HAL_SPI_Transmit(&hspi1, &address, 1, 50);

    HAL_SPI_Transmit(&hspi1, &data, 1, 50);



    HAL_SPI_Transmit(&hspi1, &address, 1, 50);

    HAL_SPI_Transmit(&hspi1, &data, 1, 50);



    HAL_SPI_Transmit(&hspi1, &address, 1, 50);

    HAL_SPI_Transmit(&hspi1, &data, 1, 50);


    while (1)

    address = 0x32;
    HAL_SPI_Transmit(&hspi1, &address, 1, 50);

    HAL_SPI_Receive(&hspi1, &x_acc, 1, 50);


    printf(“x_axis: %drn”, x_acc);



    • sameena shaikh

      The acclerometer is adxl345

      • It is hard to tell without datasheet and everything. I suggest you use logic analyzer and inspect the code. I’m sure SPI works ok, just problem might be in logic how you read data. I don’t believe that 1 byte is enough to read to get all the info from sensor. Read datasheet carefully.

        • sameena shaikh

          Yeah i have read the datasheet and loaded the values according to that. I have even changed the no of bytes, but still just 0 values. Would be of great help if you could look into it and let me know about the solution.

          • You have to send 0x32 command via SPI to device and then send 6 bytes to get the data. Meaning you have to call HAL_SPI_Transmit function at first and then you have to send 6 bytes using HAL_SPI_Receive function and save data to array. Declare array using uint8_t data[6] and then call receive function with 6 bytes.

          • Sameena Shaikh

            Thank You so much. I’ll try this out.

          • Sameena Shaikh

            Hi Tilen,
            I tried this too. It didnt work. Here’s the updated code. I am stuck with this since long now. Need to get this done. Hoping for help on this from you.

            #include “main.h”
            #include “stm32f4xx_hal.h”

            SPI_HandleTypeDef hspi1;

            void SystemClock_Config(void);
            static void MX_GPIO_Init(void);
            static void MX_SPI1_Init(void);

            uint32_t dev_id;
            uint8_t address, data, acc;

            uint8_t data[6];

            int main(void)



            uint8_t dev_id;
            uint8_t address, data, acc;


            /*dev_id = HAL_GetDEVID();


            HAL_SPI_Transmit(&hspi1, &address, 1, 50);

            HAL_SPI_Transmit(&hspi1, &data, 1, 50);

            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);


            HAL_SPI_Transmit(&hspi1, &address, 1, 50);

            HAL_SPI_Transmit(&hspi1, &data, 1, 50);

            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);

            while (1)


            HAL_SPI_Transmit(&hspi1, &address, 1, 50);

            HAL_SPI_Receive(&hspi1, &data, 6, 50);

            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);




            /** System Clock Configuration
            void SystemClock_Config(void)

            RCC_OscInitTypeDef RCC_OscInitStruct;
            RCC_ClkInitTypeDef RCC_ClkInitStruct;

            /**Configure the main internal regulator output voltage


            /**Initializes the CPU, AHB and APB busses clocks
            RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
            RCC_OscInitStruct.HSIState = RCC_HSI_ON;
            RCC_OscInitStruct.HSICalibrationValue = 16;
            RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
            RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
            RCC_OscInitStruct.PLL.PLLM = 8;
            RCC_OscInitStruct.PLL.PLLN = 50;
            RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
            RCC_OscInitStruct.PLL.PLLQ = 7;
            if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
            _Error_Handler(__FILE__, __LINE__);

            /**Initializes the CPU, AHB and APB busses clocks
            RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
            RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
            RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV8;
            RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV4;

            if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
            _Error_Handler(__FILE__, __LINE__);

            /**Configure the Systick interrupt time

            /**Configure the Systick

            /* SysTick_IRQn interrupt configuration */
            HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);

            /* SPI1 init function */
            static void MX_SPI1_Init(void)

            /* SPI1 parameter configuration*/
            hspi1.Instance = SPI1;
            hspi1.Init.Mode = SPI_MODE_MASTER;
            hspi1.Init.Direction = SPI_DIRECTION_2LINES;
            hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
            hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
            hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
            hspi1.Init.NSS = SPI_NSS_SOFT;
            hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
            hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
            hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
            hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
            hspi1.Init.CRCPolynomial = 10;
            if (HAL_SPI_Init(&hspi1) != HAL_OK)
            _Error_Handler(__FILE__, __LINE__);


            /** Configure pins as
            * Analog
            * Input
            * Output
            * EVENT_OUT
            * EXTI
            static void MX_GPIO_Init(void)

            GPIO_InitTypeDef GPIO_InitStruct;

            /* GPIO Ports Clock Enable */

            /*Configure GPIO pin Output Level */

            /*Configure GPIO pin : PA4 */
            GPIO_InitStruct.Pin = GPIO_PIN_4;
            GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
            HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


            /* USER CODE BEGIN 4 */

            /* USER CODE END 4 */

            * @brief This function is executed in case of error occurrence.
            * @param None
            * @retval None
            void _Error_Handler(char * file, int line)
            /* USER CODE BEGIN Error_Handler_Debug */
            /* User can add his own implementation to report the HAL error return state */
            /* USER CODE END Error_Handler_Debug */

            #ifdef USE_FULL_ASSERT

            * @brief Reports the name of the source file and the source line number
            * where the assert_param error has occurred.
            * @param file: pointer to the source file name
            * @param line: assert_param error line source number
            * @retval None
            void assert_failed(uint8_t* file, uint32_t line)
            /* USER CODE BEGIN 6 */
            /* User can add his own implementation to report the file name and line number,
            ex: printf(“Wrong parameters value: file %s on line %drn”, file, line) */
            /* USER CODE END 6 */



            * @}

            * @}

            /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

          • Sameena Shaikh

            Complete 6 bytes would be for x,y,x all acceleration. As of now i was just trying to read x axis acceleration through 0x32