As said before, I will explore the Timer and Counter of STM32F0 using CubeMX in this post. In this STM32F0 timer tutorial, I will try to cover as many functions of the STM32F0’s Timer as possible because this peripheral may have the greatest features, functions among the other peripherals. If you have worked with Arduino before, you might realize the limitation in function of those arduino’s timers. However , the STM32’s timers will give you way more functions that will be very useful for many of your applications that you may not think about it before. For STM32F051, it has totally 9 main timers including 7 16-bit timers, 1 24-bit systick timer and 1 32-bit timer. Some of them have 6 channels advanced-control PWM output, deadtime generation… It also has independent and watchdog timer for dealing with program hanging. This tutorial will cover the following function of the timer:

  • Time base interrupt function
  • Counter with external input
  • Input capture
  • PWM generation

1. Time Base Interrupt

STM32F051 has several timer for you to play with including: TIM1, TIM2, TIM3, TIM6, TIM14, TIM15, TIM16, TIM17. Basically, timer and counter are just different on the input clock signal. For timer, the clock source is internal clock which is generated from external crystal internal RC circuit of the STM32F0 Discovery. The following video will show you how to config a timer in CubeMX in order to generate time base interrupt for blinking the green Led (PC9).

Noticed the configurations that I have entered in the timer setting window of TIM3. There are some definitions that need to be mentioned here:

tim1

  • Prescaler: can be understood as a frequency divider for the counter, with range from 0 to 65535. In the video I set to 48000 so it means that the frequency supply for the counter will be equal to 48MHz/48000 = 1000Hz = 1ms.
  • Counter mode: can be count-up or count-down. For count-up: the counter will count from 0 to the value stored in Auto-reload register (Counter period) and then generate Overflow interrupt and start all over again from 0. For count-down: the counter will count from the value stored in Auto-reload register (Counter period) down to 0 and then generate Underflow interrupt and start all over again from stored value.
  • Counter period: the value that the counter will count to or count from (depend on count-up or count-down). In the video I set to 499, it means that the counter will count from 0 to 499 with 1ms cycle, so totally the interrupt time will be 500*1ms =500ms.

 Speaking of the interrupt handler, the HAL library helps us to handle all the checking and clearing status flag bits so we don’t have to worry about them again, just use the following function as an interrupt handler routine. Check the code in the video again:

This Callback function is sharing among all timers interrupt. If you are using more than one Time base interrupt, you need to check the source of the interrupt before executing any command. For example, if you use TIM3 and TIM6 time base interrupt, the Callback function should be like this:

2. External Input Counter

Last tutorial on “External interrupt“, I have shown the example of using external interrupt to count the input signal from the button. In this post, I will introduce another method to count the external input signal using Counter. So what is the different between two methods ? While external interrupt needs to jump into the interrupt routine to do the increment or decrement of a variable, counter can handle the job nicely without jumping anywhere. Therefore, it will be obviously useful when your program has many types of interrupt running. Take a look at the F0’s reference manual, timer section:

clk sourceWe can easily see that the timer has many sources for input clock. The first source is “Internal clock”, which we have discovered in the first part of this tutorial. Now, we’re gonna look at the second source, which is “external input pin (TIx)”. With this source of clock, the timer will apparently become a counter. The other two sources are out of the scope of this tutorial that you can discover it by yourself later. So which pin is compatible for the counter mode ? You can notice these pins with function like “TIMx_CH1” and “TIMx_CH2” which x represents the timer number (1,2,3….).

channel

Picture above is an example of pin PA0, which has the “TIM2_CH1” function. It means that you can use this pin as an input signal for Counter TIM2. Watch the video below and follow yourself. Basically, I will set up the Timer 2 as a counter to count the number of times that I press the blue button on the Discovery kit with CubeMX. I also used STMStudio to monitor the counter value.

Please make sure that you need to Start the timer before doing anything else. The code in the video:

The macro “_HAL_TIM_GetCounter(timer)” is used to retrieve the timer counter.

 3. Input Capture

Another function of the Timer is to identify the width of input signals by using Input capture. It will record a timestamp in memory when an input signal is received. It will also set a flag indicating that an input has been captured so that you can read out the capture value easily through interrupt or event polling.

So what can it be used for ? See the picture below, what if you want to determine the cycle period or the pulse width of the input pulse ? Input capture will help you with that.

soure: electronics-tutorials

Similar to external input counter function, the input capture function also uses these pins with name “TIMx_CH1″,…,”TIMx_CH4”. Each time the input capture is triggered, it will latch the counter value into the Capture/Compare register of the respective channel as seen in the picture below:

ic

The video tutorial shows you how to configure a timer to be used with input capture function. Here, I used TIM2_CH1 as the input capture channel because it is connected to the User button and I want to capture the time between to 2 pushes (1 cycle).

The code in the video: There is a small modification in the code (thanks to Nathan to spot it out) as

is placed into HAL_TIM_IC_CaptureCallback instead of being in main loop.

-Timer Input capture callback:

-Start timer 2 in input capture interrupt mode, channel 1:

-Main loop

4. PWM Generation

As you may hear about this function somewhere before, PWM (Pulse-Width Modulation) is mostly used for controlling the speed of DC motor or changing the brightness of LED or even mixing colors for RGB LED. In this part, I’m going to show you how to initialize the PWM function to control the brightness of the Blue LED on the STM32 Discovery kit.

Digital control is used to create a square wave, a signal switched between on and off. This on-off pattern can simulate voltages in between full on (3.3 Volts) and off (0 Volts) by changing the portion of the time the signal spends on versus the time that the signal spends off. The duration of “on time” is called the pulse width. So basically, when talking about PWM, we need to know immediately of 2 elements: Pulse width and Period. From these 2 elements, we can determine the Duty Cycle (%) of PWM signal. Low duty cycle will result in low brightness (LED) or low speed (DC motor) and vice versa.

Source: protostack

Source: protostack

Similarly to Input Capture function, PWM generation uses the same Capture/Compare register. The only difference is that one capture the pulse width and store into the register while the other compare the register with the counter to trigger the output pin. PWM also uses pins which has TIMx_CH1 to CH4 function. It means that one Timer can generate maximum 4 PWM outputs and the STM32F051 we are using here can generate maximum 16 PWM signals.

pwm

Please watch the following video to know how to setup in CubeMX:

How to calculate the PWM pulse width and period ?

As you may see in the video, I set the Timer prescaler at 24 and Counter period (Autoreload register) at 200. What do these numbers mean ? The STM32F051 chip currently runs at 48MHz then the clock frequency supplies for Timer 3 is: 48MHz/(24+1) = 1.92MHz ~ 0.5us. From that, Timer3 will take (0.5us * 200) = 100us to finish one cycle counting ~ 10kHz. As a result, PWM Period relies on both Prescaler and Counter Period (Autoreload register). Besides, Pulse (Capture/Compare register) will determine the Pulse width.

To make it simple, just use the following steps to calculate prescaler and counter period base on PWM resolution and frequency:

  • Determine the desired PWM resolution (for example: 100 steps, 200 steps, 1000…)
  • Determine the desired PWM frequency (for example: 1kHz, 10kHz, 40kHz, …)
  • Set the Counter Period at the same value as the resolution
  • Set the Prescaler = 48000000/(PWM frequency*PWM resolution) – 1 (thanks to Ruben and Avinash to spot out the mistake)
  • Set the Pulse equal to the desired Pulse width where the value varies from 0 to Counter Period.

The code in the video:

– Start PWM function:

– LED fading code:

Now, you can try yourself making the Green LED fade in and out similarly to the Blue LED. Hope you enjoy the tutorial.

Next tutorial will be about UART.

70 Thoughts on “STM32F0 Tutorial 4: Timer and Counter

  1. Iran Espinoza on 18/06/2015 at 12:27 AM said:

    Thx Le Than Phuc! thank you enough time and dedication to these tutorials. I’m only seeing, right now I start to study your tutorial!

  2. Sukru on 20/06/2015 at 11:39 PM said:

    Where did you study these whole things ? You learnt them from the reference manuel or something else ?

    • Le Tan Phuc on 21/06/2015 at 10:12 AM said:

      I learned to use AVR and PIC many years ago then I moved to STM32F1 when doing my final year project in university. Most of the time I learn it myself with helps from reference manual and google and forums and… :D. That was a challenge because 8bit and 32bit are totally different. However, STM32F1 and F0 are quite similar to each other so it’s not so difficult to switch between them. Actually, I did these tutorials just after I learn to use the function using CubeMX.

  3. I just want to say thanks, your tutorials are amazing. Simple and easy to understand and very useful.

    I had to program sensors on my STM32F4 board for a project and your tutorials on timers and external interrupts were exactly what I needed.

    • Le Tan Phuc on 06/07/2015 at 1:31 PM said:

      Thank you for your comment, with CubeMX and new HAL library, F0 and F4 can be programmed quite similarly for some common functions so these tutorials are not limited to F0 only, you can apply to other types of STM32 as well. I’m glad it helps.

  4. Iran Espinoza on 22/07/2015 at 3:49 AM said:

    Hi Le Tan Phuc, he estado leyendo sobre los timers, spi, dma, y cada dia surgen mas dudas, jeje. pero vallamos al principio, he hecho esto para generar las señales de sincronizacion vga (escogi 640×480@85hz, ya que puedo setear el stm32f0 a 36 mhz, lo cual es el pixel clock de la resolucion que escogi, además de la max. frec. del spi, 36mhz/2=18mhz, lo cual, no es el pixel clock, pero es un multiplo):
    /* USER CODE BEGIN Includes */
    //variables de HSYNC:
    int H_sinc_cont8=0;
    int H_video_Activo=90; //pin hsync en alto 1 C inicia en 10
    int H_porche_frontal=10; //pin hsync en alto 1 B inicia en 6
    int H_sinc_pulso=6; //pin hsync en bajo 0 A inicia en 0
    int H_porche_trasero=104; //pin hsync en alto 1 D inicia en 90
    int H_total_pixeles_por_linea=104; //llegado aqui, resetear

    //Variables de VSYNC:
    int V_contador_de_lineas=0;
    int V_video_Activo=480; //pin vsync en alto 1 A
    int V_porche_frontal=509; //pin vsync en alto 1 D
    int V_sinc_pulso=508; //pin vsync en bajo 0 C
    int V_porche_trasero=505; //pin vsync en alto 1 B

    #define hsinc_alto (GPIOB->BSRR |= GPIO_BSRR_BS_11);
    #define hsinc_bajo (GPIOB->BSRR |= GPIO_BSRR_BR_11);

    #define vsinc_alto (GPIOB->BSRR |= GPIO_BSRR_BS_12);
    #define vsinc_bajo (GPIOB->BSRR |= GPIO_BSRR_BR_12);

    /* USER CODE END Includes */


    if (htim->Instance==TIM3) //check if the interrupt comes from TIM3
    {HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_9);
    H_sinc_cont8++;
    if (H_sinc_cont8<=H_sinc_pulso)
    {
    hsinc_bajo;
    }
    else if (H_sinc_cont8<=H_porche_frontal)
    {
    hsinc_alto;
    }
    else if (H_sinc_cont8<=H_video_Activo) //aqui dibujo por linea
    {
    hsinc_alto;
    }
    else if (H_sinc_cont8<H_porche_trasero)
    {
    hsinc_alto;
    }else{
    H_sinc_cont8=0;
    V_contador_de_lineas++;
    if (V_contador_de_lineas<=V_video_Activo)
    {
    vsinc_alto;
    }
    else if (V_contador_de_lineas<=V_porche_trasero)
    {
    hsinc_alto;
    }
    else if (V_contador_de_lineas<=V_sinc_pulso) //aqui dibujo por linea
    {
    hsinc_bajo;
    }
    else if (V_contador_de_lineas<V_porche_frontal)
    {
    hsinc_alto;
    }
    else{
    V_contador_de_lineas=0;
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
    }
    }

    }

  5. Iran Espinoza on 22/07/2015 at 9:08 AM said:

    Sorry… i put again in english the header:
    I’ve been reading about the timers, SPI, dma, and every day there are more questions, hehe. but Let’s go at first, I did this to generate signals vga sync (chose 640×480 @ 85Hz, because I can setting the stm32f0 to 36 MHz, which is the pixel clock of the resolution chose, besides the max. freq. spi, 36MHz / 2 = 18MHz, which is not the pixel clock, but it is a multiple):

  6. mustapha on 24/07/2015 at 7:58 PM said:

    Thanks Le Tan Phuc, your tutorial is very helpful.

  7. Iran Espinoza on 25/07/2015 at 1:36 AM said:

    lethanpuc Hello, I got a logic analyzer to 24 MSPS, and design a simple code, to see the maximum frequency q can detect and use this code:

    while (1) {
    GPIOC->BSRR |= GPIO_BSRR_BS_8;//Aqui pongo en 1 al pin C8
    GPIOC->BSRR |= GPIO_BSRR_BR_8;//Aqui pongo en 0 al pin C8
    }

    but I only get a maximum of 4 MHz … you know why? Greetings!

    PS: I have to tell you that I have configured from cubemx, change the clock to the fullest, and still does not give faster.

    • Le Tan Phuc on 27/07/2015 at 5:16 PM said:

      hi, first thing you need to check is the maximum output speed, need to set to “High”.
      Then using the set and reset method in main loop will not give you the full speed, only when you trigger the pin using PWM (timer) or SPI or other peripherals as an alternative functions.

      • Iran Espinoza on 28/07/2015 at 12:40 AM said:

        Oh, ok, thx, i´ll try what you say, an 18f2550 (pic, microchip), with the method that i describe, can run at full speed (fosc/4)! 48 mhz/4=12 mhz! more than arm (4 mhz)… so i´ll try the other methods that you recommend me, thx!

  8. Sukru on 29/07/2015 at 3:31 AM said:

    C’mon Le Tan Phuc im waiting for your uart video..:) When will you put it ?

    • Iran Espinoza on 29/07/2015 at 3:39 AM said:

      Or spi!

      • Le Tan Phuc on 03/08/2015 at 4:04 PM said:

        hi guys, I quit my current job to start studying this semester and shift my house this time so I couldn’t reply you as well as making new tut 🙂

        • Iran Espinoza on 04/08/2015 at 8:58 AM said:

          Don´t worry Le Tan Phuc, We are very grateful with your efforts, and we are waiting when you have free time! for the moment, i read and reread and re… the hal archives…

  9. Iran Espinoza on 31/07/2015 at 4:52 AM said:

    Hi Le Tan Phuc, don´t you know, how can i put a timer as master of a timer slave? i´ve tried with diferent configs. without luck, greetings.

  10. sowa on 12/08/2015 at 5:48 AM said:

    Thank you, I was searching for site like this almost 3 months…. Thanks 🙂

    • Iran Espinoza on 14/08/2015 at 12:30 AM said:

      Hi Le Tan Phuc, I want to let you know that thanks to you I managed to learn enough (and what I most need to learn) of the STM32 micro, use the cubemx configurator and hal libraries, helps tremendously, but sometimes redound, and for high speeds, it is not Okay. As a contribution I have to say, to use the internal oscillator produces distortion when using the micro at high speeds, will start using external oscillator, to see if I’m right, although the manual says, must be put into practice. Thanks again!

  11. umit on 03/09/2015 at 3:23 PM said:

    Hi Le Tan Phuc
    We are very grateful with your lessons so When will the next lesson be available?
    thank u again 🙂

    • Le Tan Phuc on 03/09/2015 at 4:03 PM said:

      Hi Umit, I’ll try to do my next tutorial by the end of this month. It’s been postponing quite long.

  12. umit on 03/09/2015 at 6:20 PM said:

    I’m looking forward to it. i wish you success 🙂

  13. Low Ching Fa . on 27/09/2015 at 11:28 AM said:

    Dear Le Tan Phuc,

    Good day.
    Do you try before to generate SPWM by STM32F0?
    If got could you share the tutorial?

    Thank you

    Regards,

    • Le Tan Phuc on 27/09/2015 at 12:04 PM said:

      Hi Ching Fa, I haven’t tried before but I think you can simply do it using this method:
      (1)Let one variable run from 0 to 2pi, for example: 0<=x<=2pi. (2)Get the sin value of that variable: sin(x). (3)Get the Duty cycle value for the timer by calculating: duty=(sin(x)+1)*Period/2. Then the PWM output will correspond to the x angle defined in first step Hope it correct 🙂

  14. dspark on 13/10/2015 at 12:44 PM said:

    Thank for your post. Its very helpful.

  15. vyga on 26/10/2015 at 6:01 AM said:

    Hi, very helpfull info, thanks. Have one question, I am trying to count button presses with “External Input Counter”, count = __HAL_TIM_GetCounter(&htim2); it is working just fine, but for my use I need to reset counter and start from 0 again at some point. Cannot figure it out, tried using another button as interrupt, but was not able to use __HAL_TIM_SetCounter(&htim2, 0)… How can I solve this? Thanks in advance

    • Le Tan Phuc on 28/10/2015 at 9:57 AM said:

      Hi, __HAL_TIM_SetCounter(&htim2, 0) should work. Can you specify more about your button interrupt? can upload your button code here so that I can see what’s wrong.

  16. Ibrahim N Ibrahim on 11/01/2016 at 7:35 PM said:

    good work man, Thanks you very much…
    may you have a tutorial explaining every function in Timer Library.

  17. Nathan on 02/02/2016 at 9:41 PM said:

    Dear Le Tan Phuc,
    This is the first time I’ve felt compelled to post on someone’s page like this, but thank you so much for your marvellous tutorials! I am starting my PhD in robotics in a weeks time and I bought an STM32L discovery board last year, but due to the lack of any decent tutorials online, I had no way to get started with it until I found your website, so once again thank you very much!
    I have one small point to add though; I spent all afternoon trying to diagnose a phantom problem with the input capture part of the tutorial: the input_capture was reading correctly, however the HAL_TIM_IC_CaptureCallback routine was only intermittently activating, and so the line __HAL_TIM_SetCounter(&htim2, 0); wasn’t always getting called, and the timer would keep climbing for 2 or 3 button presses before finally getting reset to zero.

    I tracked the problem to
    void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
    {
    in the stm32Lxx_hal_tim.c file; although this IRQ was being called, the first ‘if’ statement in that handler was being skipped periodically.

    Ultimately a friend I contacted tracked the problem to page 426 of the STM32L reference manual:

    Bit 1 CC1IF: Capture/compare 1 interrupt flag
    If channel CC1 is configured as input:
    This bit is set by hardware on a capture. It is cleared by software or by reading the
    TIMx_CCR1 register.

    So it turned out that every time the line in the int main(loop) while loop:

    input_capture= __HAL_TIM_GetCompare(&htim2, TIM_CHANNEL_1);

    was run, it was reading and resetting the interrupt flag, before the IRQ handler had a chance to check which pin had called it, and so the whole HAL_TIM_IC_CaptureCallback block was skipped… It seems very strange since the interrupt routine should obviously interrupt the main loop, so why the main loop is able to clear interrupt bits is a mystery…

    I don’t know if this is a problem specific to the stm32L series, but I fixed it by removing input_capture= __HAL_TIM_GetCompare(&htim2, TIM_CHANNEL_1); from the main while loop, and instead pasting it inside the CaptureCallback block. This way the flag isn’t cleared by reading until after the interrupt is already running.

    So my changed code is:

    /* USER CODE BEGIN 0 */
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    if (htim->Instance==TIM2)
    {
    input_capture= __HAL_TIM_GetCompare(&htim2, TIM_CHANNEL_1);
    __HAL_TIM_SetCounter(&htim2, 0); //reset counter after input capture
    //interrupt occurs
    }
    }
    /* USER CODE END 0 */

    and my while loop in main is:

    while (1)
    {

    counter = __HAL_TIM_GetCounter(&htim2); //read TIM2 counter value

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    }

    I hope maybe this helps someone in a similar situation with strange errors in their input capture codes.
    Regards,
    Nathan.

    • Le Tan Phuc on 05/02/2016 at 3:37 PM said:

      Hi Nathan, thank you for your detailed comment. You are right, I checked the reference manual again and it says that the CCxIF interrupt flag will be cleared by reading the TIMx_CCRx register. In my code, I just paid attention on getting the capture value without noticing that the capture interrupt may or may not happen. Anyway, I will update the post according to your suggestion. Thanks again 🙂

      • Nathan on 09/02/2016 at 6:34 PM said:

        Thank you once again for your wonderful tutorials. I watched your video again very closely and it did seem like yours was resetting correctly, so maybe the L152 was more sensitive to the interrupt flag being cleared.

        I’ve also read your other articles on accessing the DMA and I2C with a lot of interest: I am currently working on a project to control hypersonic test aircraft (wind tunnel models at this stage) which has meant the need for some very fast programming loops: I managed to get the MPU6050 to cycle as fast as every 2ms on an arduino, and most of that was because the processor was having to handle the read and save (no DMA). So a big part of my learning the STM32 is to continue this research.

        On that note, I noticed your other 2 posts on DMA and the MPU6050 use the older Standard Peripheral Libraries. Is it better for this task than the HAL? Although I know you are very busy, I wonder have you managed to get the DMA to work using the HAL libraries?

        I have been trying to get the DMA to work on the pulse width tutorial above, for two reasons, the first being because it’s a good way to learn, and the second because I will use PWM for servo control and would like to do this with the DMA if possible (the processor will likely be tied up doing filtering and control calculations).

        So far though I haven’t gotten the DMA to work. My attempt so far has been:
        in CubeMX, to set channel 1 of timer 4 to PWM (this corresponds with the blue LED on the L discovery). I’ve set the system clock to 32MHz, and in the TIM4 configuration, set the prescaler to 32 (to give 1MHz) and the counter period to 20,000 (to give 20ms, which is suitable for servos).

        Next I set the DMA to TIM4_CH1, memory to peripheral, circular mode, and left both memory and peripheral incrementing off because I would only be writing one value.

        Then after generating the code, I made a global uint32_t variable called flashy (initialised to 10,000). In the void main code I added:

        HAL_TIM_PWM_Start_DMA(&htim4,TIM_CHANNEL_1,(uint32_t*) flashy,1);

        and in the main while loop:

        flashy++;
        HAL_Delay(10);
        if (flashy==20000)flashy=0;

        __HAL_TIM_ENABLE_DMA(&htim4, TIM_DMA_UPDATE);

        I don’t know if the last term was needed, but in any case nothing happens: no blinking lights. If instead I replace these two terms with:

        HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
        and in the loop:
        __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_1, flashy);

        then it works fine and slowly blinks on and off at 20ms (actually my multimeter reads 48Hz). So I think the PWM timer4 channel1 is correctly configured, but the DMA isn’t…

        Do you have any suggestions?
        Thanks again.
        Nathan.

        • nathan on 17/02/2016 at 1:08 PM said:

          I managed to get it working: seems the problem was in trying to send the single variable flashy to the DMA. The solution was to make it an array 1 cell long.

          It’s not the tidiest bit of code, but this works: Put it in the void main:

          uint32_t flashy[]={20000};
          HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1,flashy,1);

          while(1){
          for (pwm=0;pwm=0;pwm-=100) {
          flashy[0]=pwm;
          HAL_Delay(1); }
          HAL_Delay(10);
          }

          And as above: in cubeMX tim4 channel 1 (connected to the blue LED on the L series) had the DMA assigned. the DMA request was configured as memory to peripheral, circular mode, no incrementing on the addresses. I had it set for Half Word mode too, but since only one bit at a time is being sent here, it didn’t make much difference. I believe it will once I make the flashy array up to 16 or 32 bits long with a corresponding value in the HAL_TIM_PWM_Start_DMA, but I’ll have to go and pinch a scope or logic analyser from another lab before confirming that.

          Thanks again for the wonderful tutorials! I doubt I’d even have one LED turned on without them.

  18. nathan on 17/02/2016 at 1:13 PM said:

    sorry, that should have been:
    while(1){
    for (pwm=0;pwm=0;pwm-=100) {
    flashy[0]=pwm;
    HAL_Delay(1); }
    HAL_Delay(10);
    }

    so that it goes up and down. By the way, htim4.Init.Period gets the timer counter period configured in CubeMX; as I’ll be changing this at a later stage I didn’t want to have to enter in new numbers each time in the for blocks. In my case the prescaler was set to 32, and the counter period 20,000 which is also why it’s incrementing/decrementing by 100 each step through the for loop.

    • nathan on 17/02/2016 at 2:11 PM said:

      The website keeps eating my pasted code: it should have 2 for blocks, one counting up, one down as per your original example.

    • Le Tan Phuc on 23/02/2016 at 7:48 PM said:

      Hi Nathan, it turns out that you solved the problem before I had a chance to see it today cause my site didn’t notify me about new comments. For the DMA with HAL, I only tried with ADC and it worked perfectly. Last time I had free time programming my quadcopter so that I took that chance to write the article about DMA. Since then, none of my project requires high speed data transfer rate so I rarely touch it.

  19. Didier on 25/02/2016 at 6:09 PM said:

    Hello,

    I want to generate a square wave with high status and low statut with different period.
    This signal is composed of 6 pulses. Each pulse has an adjustable time. I want to use a single timer. So I need to know the functions to:
    start the timer.
    Stop the timer.
    Load a value in the timer.
    These functions can be called in an interrupt?

    Sorry for my English because I’am not well in English.

    Thank you in advance.

    • Le Tan Phuc on 02/03/2016 at 9:34 AM said:

      Hi, you can do it inside the interrupt handler routine. Basically, starting, stopping and updating the timer value have already been mentioned in the post, you can try with these function:
      – HAL_TIM_PWM_Start_IT
      – HAL_TIM_PWM_Stop_IT
      – __HAL_TIM_SetCounter
      – __HAL_TÌM_SetAutoreload

      Depends on which channel you use for generating signal, you can use this function to update the ‘on’ time of the signal, just like the PWM part in the post:
      – __HAL_TIM_SetCompare

      Hope it helps.

      • Didier on 05/04/2016 at 6:44 PM said:

        Thanks,
        but this is a constants and not a functions.

        I found a function :
        TIM_Base_SetConfig(TIM_TypeDef *TIMx, TIM_Base_InitTypeDef *Structure)

        Can I use it like this? :

        void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
        {
        if (htim->Instance == TIM2)
        {
        switch (Code4[PPM_idc])
        {
        case 0 :
        {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, SET);

        variable.Prescaler = 48;
        variable.CounterMode = TIM_COUNTERMODE_UP;
        variable.Period = 1000;
        variable.ClockDivision= TIM_CLOCKDIVISION_DIV1;
        TIM_Base_SetConfig(TIM2, &variable);
        }
        case 1 :
        {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, RESET);

        variable.Prescaler = 48;
        variable.CounterMode = TIM_COUNTERMODE_UP;
        variable.Period = 500;
        variable.ClockDivision= TIM_CLOCKDIVISION_DIV1;
        TIM_Base_SetConfig(TIM2, &variable);
        }
        }
        }
        }

        its for put a difrente value in timer 2 every difrente case.
        (there are more two case. I those this only for exemple)

        Thank you in advance.

      • Didier on 06/04/2016 at 4:23 PM said:

        hi,
        now, i undertand what do you mean.
        I change my programme and it work.
        thanks.

        this is my all programme:

        unsigned char PPM_idc = 0;
        unsigned char PPM_Code4[13]={2,0,2,0,2,1,2,0,2,0,2,3};

        void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
        {
        int capture=0;
        if (htim->Instance == TIM2) //Programme pour générer un signal carré de période TIM2
        {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
        }

        if (htim->Instance == TIM3)
        {
        __IO uint16_t T1=1000; // Valeur du temps pour le “1” logic
        __IO uint16_t T0=1500; // Valeur du temps pour le “0” logic
        __IO uint16_t T_attente=10000; // Valeur du temps d’attente
        __IO uint16_t T_ON=220; // Valeur du temps pour l’état haut du “0” et “1”

        if (PPM_idc>12)
        {
        PPM_idc=0;
        }
        switch (PPM_Code4[PPM_idc])
        {
        case 0 : {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, SET);
        capture = __HAL_TIM_GetCounter(&htim3);
        __HAL_TIM_SetCounter(&htim3, capture + T0);
        PPM_idc++;
        }break;

        case 1 : {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, SET);
        capture = __HAL_TIM_GetCounter(&htim3);
        __HAL_TIM_SetCounter(&htim3, capture + T1);
        PPM_idc++;
        }break;

        case 2 : {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, RESET);
        capture = __HAL_TIM_GetCounter(&htim3);
        __HAL_TIM_SetCounter(&htim3, capture + T_ON);
        PPM_idc++;
        }break;

        case 3 : {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, SET);
        capture = __HAL_TIM_GetCounter(&htim3);
        __HAL_TIM_SetCounter(&htim3, capture + T_attente);
        PPM_idc++;
        }break;
        }
        }
        }

        I want to joint a picture of the signal but it’s impossible.
        thank you for all.

  20. the counter overflow on 05/04/2016 at 2:52 AM said:

    Hi!
    Great beginner tut, thanks!
    I have a question.
    Is it possible to set callback for counter overflow when counter (timer handler) is set as Input Capture mode?
    Consider your 3rd example which uses
    HAL_TIM_IC_CaptureCallback for TIM2_CH1.
    I do want to know how many times the counter TIM2_CH1 has overflown (counted full cycle and restarted) in between HAL_TIM_IC_CaptureCallback’s so that i could compute the total (for example – I push the button with looooong period).
    It seems that HAL_TIM_PeriodElapsedCallback is not fired for a TIM_HandleTypeDef if that TIM_HandleTypeDef is configured as Input Capture. Is it really so?

  21. Nice job !
    thank you my friend 🙂

  22. gopal on 17/05/2016 at 6:22 AM said:

    Hi,

    I am gopal, Thanks for your efforts. It is very helpful for my projects. Now I am working on stm32f0 controller. My task is wiegand to serial converter. Could you please help me how to capture the data(DATA0=0’s,DATA1=1’s) using timer.

    Thank you.

  23. Ruben on 31/05/2016 at 3:37 AM said:

    “Set the Prescaler = 48000000/(PWM frequency*PWM resolution)”
    I think this should be “Set the Prescaler = 48000000/(PWM frequency*PWM resolution)-1”, since (correct me if i’m wrong) when the prescaler is set to 0, it divides the clock by 1, and when the prescaler is set to 1 it divides by 2, etc.

    • Le Tan Phuc on 24/06/2016 at 5:29 PM said:

      Thank you for your comment. You are correct, I made a mistake on this part, I’ll edit the post according to this. Thank you for spotting it out.

  24. for ure second tutorial my conter counting 16 777 216 point per pressing key.
    why??
    i followed your instruction. 🙁

  25. i used this trick to correct this problem:

    i=__HAL_TIM_GetCounter(&htim2)<<24;

    but why i have to do this what the hell is going on its register?????
    i used stm32f103RBT

    • i think in processor everything is ok but in stm studio 1 becoming 16777216
      :'(

      • now its working fine and i don’t know why?
        but some time by pushing button it add 2 point instead 1 !!!!!!
        really its counter is terribly inaccurate?

        • Le Tan Phuc on 24/06/2016 at 5:19 PM said:

          Sorry for late reply, did you use the button on the discovery kit or external button? If you use external button, it should have a pull-up resistor and a capacitor connected parallel to the button to eliminate spike or mechanical noises.

  26. Avinash on 12/06/2016 at 8:00 PM said:

    Nice article!

    But one small mistake (don’t get angry 🙁 ) you said is prescaller is 48000 it divides the clock input by 48000 but it actually divides by 48000+1. Becasue if you set prescaller=0 it does not divide by 0 but by 1 as division by 0 is undefined!

    This fact is more strong when the prescaller value is small say 2.

    I was trying to generate 200 Hz but was getting 133 Hz becasue setting prescaller = 2 would divide the clock by 2 but it was actually dividing by 2+1 that is 3!

    Changed it to 1 to get division by 2 and it worked!

    • Le Tan Phuc on 24/06/2016 at 5:36 PM said:

      Thank you for your comment. You are right, it should be like what you said, I made a mistake on that part. I’m also learning this STM32 so making mistakes is unavoidable and I’m so happy that you guys pointed out the mistakes that we could improve together.

  27. for your third section of tut really this line :
    __HAL_TIM_SetCounter(&htim3, 0);
    caused my program to can’t capture timer correctly.
    how it not annoyed you?and why that is in your code/?

    • Le Tan Phuc on 24/06/2016 at 5:45 PM said:

      Hi, in the third part, I used input capture function of Timer 2 (TIM2), not TIM3. What it does is resetting the timer value before another input capture session is triggered so that you can read the capture value between two pushes.

  28. Ouss on 24/06/2016 at 11:25 PM said:

    I have a big problem with the timer of my nucleo L053R8.
    Well i did the same think with time base interrupt function:
    I use the MSI clock 1048khz to generate an interrupte with 10 khz
    With a prescaller =9 and CNT =9 but i am getting a timer frequency of 4khz.
    And when i want to generate a timer with low frequency(<4Khz), it works normally…
    For example to generate a 1khz frequence with my timer i change the prescaller to 99
    It's really weird.
    If some one can help me …
    Thank you very much.

  29. I am trying to use HAL_TIM_PWM_Start_DMA to initiate a circular transfer from a 1024 byte buffer to PWM channel 1. But I am a bit confused how will I set some timing so that the DMA will automatically write in that particular timing interval as I know that without such a trigger, DMA will write to the PWM register as fast as possible which is of no use if our intention is to create a sound with a lower speed say 44KHz. 🙂

  30. SAjjAD on 02/10/2016 at 6:33 PM said:

    Thank you so much for this tutorial…
    I need a timer which doesn’t reset automatically. I want to start and stop it whenever I want. I can something like this:
    TIM2->CNT=0;
    but Isn’t there any standard way to do this?
    start and stop whenever I want and when expired, interrupt fires…
    thank you again

  31. İzzet on 12/10/2016 at 2:37 PM said:

    Hi
    My stm32f0 discovery shows in StmStudio for “count” 536879104 every time and i dont press user button.
    Please Help
    Thank You

    • Le Tan Phuc on 13/10/2016 at 5:22 PM said:

      Hi, actually what you pressed is Reset button, not user button. In Keil, you can go to Flash/Configure Flash Tools… Then go to Debug tab, next to the “Use ST-Link Debugger”, click Settings -> Flash Download -> Tick choose “Reset and Run”, then Ok. Then, everytime you load the program, it will automatically Rest and run your code, which will reset your timer from 0 always.

  32. Георгий Панкратов on 03/11/2016 at 2:18 AM said:

    Hello. Please tell me how to check that the interruption was not only on TIM_CHANNEL_1, but also on other channels of this timer (TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4). Thank you.

    if (htim-> Instance == TIM2)
    {
    input_kapture_1 = __HAL_TIM_GetCompare (& htim2, TIM_CHANNEL_1);
    input_kapture_2 = __HAL_TIM_GetCompare (& htim2, TIM_CHANNEL_2);
    input_kapture_3 = __HAL_TIM_GetCompare (& htim2, TIM_CHANNEL_3);
    input_kapture_4 = __HAL_TIM_GetCompare (& htim2, TIM_CHANNEL_4);
    __HAL_TIM_SetCounter (& Htim2, 0);
    }
    Thank you.

    • Le Tan Phuc on 07/11/2016 at 12:31 AM said:

      Hi, I am not sure what you are doing with 4 channels of the same timer? are you generating PWM or counting pulse or doing input capture? I ask because 1 Timer can have only one overflow interrupt and you can use only 1 channel per timer for counting or input capturing purpose. The only situation that we can use all channels of 1 timer is generating PWM.

      • Andriy on 15/12/2016 at 3:00 PM said:

        Hello! Let me to add to the George que.
        I trying to read 3 pcs of the pulse water flow sensors. They are acting like a Hall sensors – giving a series of the pulses proportionally to the water flow. How to read all of them with the one chip?

  33. Георгий Панкратов on 03/11/2016 at 4:27 PM said:

    Hello Please tell me how to check that the interruption has occurred from what the other channel tim2, not just the first. TIM2_CH2, TIM2_CH3, TIM2_CH4? Thank you.

  34. suk kyu Hong on 08/11/2016 at 1:34 AM said:

    Hi.

    You’re the one of the best skill person at STM32.

    I learn lots of things from your blog.

    Have a nice life.

    God bless you.

    Thanks a lot.

  35. Георгий Панкратов on 08/11/2016 at 9:01 PM said:

    Thank you very much for your reply! Good luck!

  36. Olivia Christy on 11/11/2016 at 8:48 PM said:

    Thank you very much Le. This is a great tutorial. You are great… because you share your knowledge.

  37. Mayur on 14/12/2016 at 5:44 AM said:

    Very Helpful.. Thank you for taking time to put together such a nice tutorial!!

    • master_ on 21/12/2016 at 6:42 PM said:

      first,thank you for your usefull tut,
      the problem is when i fallow your pwm generation tut and want to build it , the below error occure and the target not build.
      error: #20: identifier “GPIO_AF0_TIM3” is undefined

      what this refrenced to??

      thanks

  38. Amit on 06/01/2017 at 8:39 PM said:

    Hi Le, First of all thnx for ur code specially PWM part.
    I am a fresher and I am working on STM32F030R8t6. I am trying to generate the PWM for LED brightness control. I am facing one problem. The problem is prescallar value so, for frequency of 120hz pwm signal what will be the prescallar value.
    And the functions getcounter and capture for timer 2 are they required in pwm code.
    thnq.

Leave a Reply

Post Navigation