Library 33- PWM for STM32F4

I had enough of each time configuring PWM for some reason, so I decided to make a library for it. This library allows you to use PWM signal on all possible timers on all possible pins for timer. I made it for controling servos, but it can be used for anything else. PWM works in FAST PWM mode.

You just set your PWM frequency, timer you will use, channel on timer and you are ready to go.

If you need any specific and exotic settings, you should look at my PWM tutorial.



  • Drive PWM signal on all possible PWM timers
  • Only frequency is required for timer initialization
  • Supports all possible output pins for each timer
  • Fast PWM mode
  • Version 1.1 – September 15, 2014
    • PWM library splitted into a 2 libraries. One for PWM and one for timer properties. This allows me to use one library with timer’s settings on more other libraries.
  • Version 2 – January 03, 2015
    • Library has been rewritten, new examples below


    • STM32F4xx
    • STM32F4xx RCC
    • STM32F4xx GPIO
    • STM32F4xx TIM
  • TM
    • TM GPIO
    • defines.h

Table below shows all possible pins for each timer and channel. You can select any of max 3 pins for each output channel.

Timer Channel 1 Channel 2 Channel 3 Channel 4
TIM 1 PA8 PE9 PA9 PE10 PA10 PE13 PA11 PE14
TIM 2 PA0 PA5 PA15 PA1 PB3 PA2 PB10 PA3 PB11
TIM 4 PB6 PD12 PB7 PD13 PB8 PD14 PB9 PD15
TIM 5 PA0 PH10 PA1 PH11 PA2 PH12 PA3 PI0
TIM 10 PB8 PF6
TIM 11 PB9 PF7
TIM 12 PB14 PH6 PB15 PH9
TIM 13 PA6 PF8
TIM 14 PA7 PF9
  • PPx: Pins Pack 1 to 3, for 3 possible channel outputs on timer.

Notes on table above

  • Not all timers are available on all STM32F4xx devices
  • Not all timers works with same tick frequency
  • All timers have 16bit prescaler
  • TIM6 and TIM7 don’t have PWM feature, they are only basic timers
  • TIM2 and TIM5 are 32bit timers
  • TIM9 and TIM12 have two PWM channels
  • TIM10, TIM11, TIM13 and TIM14 have only one PWM channel
  • All channels at one timer have the same PWM frequency!

This library do everything by it’s self. Everything you need to set is a PWM frequency you want to use (let’s say 50Hz for servo motor) for specific timer you will use. Library automatically sets period and prescaler for timer. This value is set, that allows you maximum possible resolution for PWM duty cycle at given frequency.

Everything is explained in example at the bottom. To work with PWM, you will need some typedefs and functions. They are below:


For example, leds on STM32F4-Discovery and Nucleo F401-RE boards are used. STM32F429-Discovery doesn’t have leds connected to PWM pins.

I tested some other pins, if they are actually working with oscilloscope, but there is a lot of pins and timers, so I didn’t check for all possibility. They should work.

Simple Servo controller example

Both projects are available at my Github account, download library below.

TM STM32F4 PWM 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!
  • Kluczyx

    Hi, I use your lib pwm and I have one question. How do I be able to dynamically change the frequency of the pwm? Combines the second day and did not work. Please help these to me.

    • Currently, you can try to use TM_PWM_InitTimer again, but in future I will make a special method for this.

      • Gustavo Sousa

        For gradual velocity increase of stepper motor I can use de TM_PWM_InitTimer too?

        • Not sure if I understand you correct.

          • Gustavo Sousa

            To acelerate a stepper motor, I need to decrease the time between the pulse, and it is the same of up the frequency, right?

            But, I can update the prescaler/period with tm_pwm_initTimer trippingly?

            Maybe I’m try control the stepper motor from a wrong way, like changing the pwm frequency. Do you suggest some other way?

            Thanks for your help.

          • To control stepper motor, use fixed timer speed. Then use interrupt which in which you do your stuff.
            If you want to change frequency, just change max timer value when interrupt happen.

          • Gustavo Sousa

            Thanks Tilen,
            I’ll do that that.

  • Darks Spirits

    Hi bro, i want to control 4 servo, can you help me? Tks

    • Hi.
      Where is the problem?

      • Darks Spirits

        int main(void)
        TIM1->CCR1 = 2300;
        TIM1->CCR2 = 2500;
        while (1)
        Here is the code that I have used to control the servo turn 2 servo. However, it ran in panic before the desired place . I think it was interference

        • Darks Spirits

          Right now I ‘m doing a robot arm so I ‘m trying to control 2 servo . I use the timer 1. I just began learn stm32f4

  • Darks Spirits

    Here is the code that I wrote to control 4 rc servo. I use 4 channels of timer 1 in the leg pe9, PE11, PE13, pe14. but it was not run. I hope you will help me find a flaw in this algorithm. I’m doing a robot arm

    #include “stm32f4xx.h”

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    TIM_OCInitTypeDef TIM_OCInitStructure;

    GPIO_InitTypeDef GPIO_InitStructure;

    void Delay(__IO uint32_t nCount);

    void RC_Servo_Init(void);

    int main(void)



    TIM1->CCR1 = 2400;


    //TIM1->CCR2 = 2700;


    //TIM1->CCR3 = 1500;


    //TIM1 ->CCR2 = 2800;

    //TIM1->CCR3 = 90 * 65535 / 100;

    while (1)



    void RC_Servo_Init(void)


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

    RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOE, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_11| GPIO_Pin_13 | GPIO_Pin_13;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

    GPIO_Init(GPIOE, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_TIM1);

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_TIM1);

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_TIM1);

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_TIM1);

    /* Time base configuration */

    TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/1000000-1;

    TIM_TimeBaseStructure.TIM_Period = 20000;

    TIM_TimeBaseStructure.TIM_ClockDivision = 0;

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);


    /* Servo 1 */

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OCInitStructure.TIM_Pulse = 0;

    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;

    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;

    TIM_OC1Init(TIM1, &TIM_OCInitStructure);

    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

    /* Servo 2 */

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_Pulse = 0;

    TIM_OC2Init(TIM1, &TIM_OCInitStructure);

    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);

    /* Servo 3 */


    TIM_OCInitStructure.TIM_Pulse = 0;



    /* Servo 4 */


    TIM_OCInitStructure.TIM_Pulse = 0;



    TIM_ARRPreloadConfig(TIM1, ENABLE);

    /* TIM1 enable counter */

    TIM_Cmd(TIM1, ENABLE);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);


    void Delay(__IO uint32_t nCount)






    #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 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) */

    while (1)




    • Gary

      It does use an 8MHz crystal, and the HSE is set to that. I can print this out when i start a debug session
      SYSCLK_Frequency = 168000000
      HCLK_Frequency = 168000000
      PCLK1_Frequency = 42000000
      PCLK2_Frequency = 84000000

      These parameters appear to be correct?

      Also, one other question: what is the lowest frequency (or period) that the 16 bit timers can reach – through division and scaling?


      • What about PLL_M variable?
        You can print yes, but huh.
        Ok, tell me which timer and channel. I will take a look.
        Or make and show code.

        Minimal frequency is:
        timer_max_clock / (max_period * max_prescaler)
        prescaler is 16bit for all timers, period can be 16 or 32 bit.

      • Darks Spirits

        Thanks bro
        I completed it. I graduated University of Technology last two weeks.

  • Gary

    The Discovery board has a 168MHz oscillator, so the clock values are a little strange. For example, your default value of 1KHz is 4.55KHz on the Discovery board. What needs to be changed?

    Thanks. nice work by the way!!

    • Make sure you have correct clock speed for your device.
      Check post about that. Link on the top in grey rectangle.

  • David

    Is there anything special about Timer1 and Timer8? I used the lib to create 18 PWM with Timer1, 2, 3, 4, 8, 9. All pwm channels are working (checked with scope) except for Tim1 and Tim8. Syntax is the same for every timer…

  • Pingback: Library 42- Control RC servo with STM32F4 - STM32F4 Discovery()

  • fabi

    Hi Tim,
    I design a GUi in stm32f429 and I want to control two servo by pressing button on my interface. When I wired servos on stm32 my GUI disappear,why?

    • Can you make sure that you didn’t used some pins for PWM which are also used for LCD/SDRAM/TOUCH?

  • stm beginnet

    Hello , Your PWM library is extremely helpful and wanted to thank you for that . However , wanted to discuss a situation. I am beginner and am using this library right now. I might start going through the details of the functions that you have written for this library. But , am facing a small problem. I was testing all channels of the Timers through the scope . I got the output at the tested channels but the channels of Timer1 and Timer8 do not seem to work. Just wanted to ask you is there anything different that I need to take care of for these two Timers for PWM generation ? Or if there is a problem , then have you had a look ? Or in case you want me to find out the problem , do please let me know how and at which function could there be a possibility of the problem so that I can start to look into it .. -Thank you !

    • I had this problem too (found later) and I’ve updated library which is uploaded here. For me, TIM1 and TIM8 starts working.
      The problem was, that these 2 timers need special PWM control output enabled. File tm_stm32f4_pwm.c on line 110 of new library available here on this post at the end.

      • stm beginnet

        Thank you so much for your reply. I will use the new one.

  • Uma Srinath

    Hi Majerle,

    I need to Make a button press and button release servo function:

    servoPressOn(int start, int end, int interval, int delay)


    For(PWM(start); PWM(end), degree++)



    Stop when degree == end -> break

    Delay(interval) //interval makes small adjustments so servo doesn’t immediately




    servoPressOff(int start, int end, int interval, int delay)


    For(PWM(start); PWM(end), degree++)



    Stop when degree == end -> break

    Delay(interval) //interval makes small adjustments so servo doesn’t immediately




    servoRelease(int start, int end, int interval) -> used for both On and Off


    For(PWM(start); PWM(end), degree++)



    Stop when degree == end -> break

    Delay(interval) //interval makes small adjustments so servo doesn’t immediately



    Can you help in configuring GPIO PD12 -PD15 4 pins, TIM4 and PP2 and share the code?


    • Not so kind.
      You have my pwm lib, servo lib, pwm tutorial and explanation on servo lib how to config servo pulse.

      I wont do code for your project.

  • Uma Srinath

    Can you share a PWM servo code cotrolled by push buttons(button press and button release) for STM32F4 Discovery board.

    • Everything you need is explained on web! Use search please.

  • Pepper

    I tested your code with TIM4, Channel 1–>4, pin back 1
    But I had problem with pin PB8, it didn’t work.
    Similarly, I have problem with pin PA15. I wonder if your code about remap pin PA15 has problem?

    • Hi,

      it shouldn’t have.

      • Pepper

        Hi Tilen,
        I tested with my code and use LEDs to check.

        PA1 –> OK, the LED was ON
        PA15 –> the LED was OFF

        My LED is still working, I don’t know what problem is.

  • Filip Zorić

    Is there any chance that there will be HAL library for PWM timers in near future?

    • Can’t guarantee you that.

      • Filip Zorić

        Is there any simple way around that, so I can make pwm timers with HAL drivers, or should I use this one and SPD drivers?

        • Maybe you could check HAL driver examples for your needs.

  • Farhan Musthafa

    May I know the step by step instruction to start with keil?

    • How can be this question related with PWM library?

      • Farhan Musthafa

        I mean that, as from downloaded files I am little confused. could you pls send me the complete file which is already done (any example), that I believe help me understand?

        • It is your problem if everything you do is: Read post (uau, PWM, something for me) and search for download (where is download? I must find download button).

          Read entire post!

      • Farhan Musthafa

        Also, I want to know which code changes the auto reload value and CCRx ?

  • Farhan Musthafa

    After adding all libraries with project I got an error like

    ..FWLIBsrctm_stm32f4_pwm.c(19): error: #5: cannot open source input file “tm_stm32f4_pwm.h”: No such file or directory

    please refer the attached image

    • Ok, you have some misunderstandings in keil structure.

      Only .c files should be added to project and visible there on “project” menu.
      For .h files, you have to specify include paths, where compiler will search for them if you type #include “something.h” if your file.

      So, open options for target, tab “C/C++” and you will found “Include paths” options.
      Add folder path where .h files are stored. Then will work.

      • Farhan Musthafa

        Thanks. that was helpful.

        Now I got an another issue.

        .ObjectsPWM.axf: Error: L6218E: Undefined symbol SystemInit (referred from main.o).

        I have done few research about this errors, still cannot solve it. could you please help me out some way. Thank you again.

        • As I see, you are totally unfamiliar with Keil and STM32 programming.

          Download my project template for Keil where build will be successfully from start.
          Search for Keil uvision project template on this site.

          • Farhan Musthafa

            yes I am a beginner. Having great interest in learning and luckily got to know you and these sites. I hope you dont mind helping me learning necessary stuffs.

            Do you mean that I should find template from stm32f4-discovery .com ?

          • Farhan Musthafa

            Thank you.
            As of now, I am little misunderstanding about define.h. frankly speaking, Dont know about what should I do ion this header files. Could you please let me know?

          • This file is for changing library settings. If you don’t have any changes, then just create file and leave empty.

          • Farhan Musthafa

            Got it

          • Farhan Musthafa

            The errors I got like this ..

            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_ARRPreloadConfig (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_CtrlPWMOutputs (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC1Init (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC1PreloadConfig (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC2Init (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC2PreloadConfig (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC3Init (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC3PreloadConfig (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC4Init (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_OC4PreloadConfig (referred from tm_stm32f4_pwm.o).
            ..OBJCAN.axf: Error: L6218E: Undefined symbol TIM_TimeBaseInit (referred from tm_stm32f4_pwm.o).
            Not enough information to list image symbols.

          • Would you mind to add STM32F4xx TIM module into project?

          • Farhan Musthafa

            Thank you. You’re so helpful.

  • Farhan Musthafa


    First of all, Thank you very much. All my previous discussion helped me a lot and made me to generate PWM output. At this moment, I am able to set PULSE LENGTH (dutycycle) and FREQUENCY of PWM by changing values in “TIM_OCInitStructure.TIM_Pulse = TIM_PULSE” and “TIM_TimeBaseStructure.TIM_Period = TIM_PERIOD;”.

    In my program, values in TIM_PULSE defines dutycycle and values in TIM_PERIOD defines the frequency.

    Next, in-order to develop the user interface control. which means the input values from my GUI should change the values in TIM_PULSE and TIM_PERIOD.

    Could you pls give me some coding idea to update these values whenever the user want to change?

    • Farhan Musthafa

      Kindly Help!
      I would need to know the way to approach the pulse control from user interface.

      Thanks again

  • محــمد مهــدی سلطانی

    Thanks for all off your helps…
    I need to count the pwm pulse to control how many pulse is on the line…
    what should I do?
    could you help me?

    • Use tiner as input counter or pin as interrupt and count.

  • Michael

    Hi, I am trying to use TIM1, CH1, PinsPack2 with your library and STM32F407DISCOVERY. This should result in PE9 pin. Unfortunately there is no output on PE9.
    Attatched see my code – it is your example modified by TIM1.
    TIM4 is initialized in parallel, working perfectly with 1 kHz visible on oscilloscope as desired.
    Any ideas why I have no output on PE9? – this is the complete code.

  • Robert Futch

    Thanks. This works great for me.

    I may have spent too much time wondering why 50% duty didn’t work at 25kHz. 500 was not 50%, it’s pulse on time.

    Here is a quick bit of code that converts duty into pulse on time:

  • Pingback: STM32F4 — analogWrite() — получение PWM/ШИМ сигнала()

  • Sumeet Patil

    New to STM32 and FW codes. I have simple question, what is maximum frequency of PWM signal that can be achieved using the above library? I need to have PWM from 50 KHz to 100KHz, can those be safely achieved with above library. Please revert back as using the above library will save lot of my time.