clock pulses with variable phase on STM32

By | February 5, 2016

The CCRx registers can be set to directly produce regular clock pulses on one or more of the output pins. Square waves are especially simple and up to four channels can have variable phase relationships.

This is part of a series of articles about the general purpose timers found in the STM32 family of ARM cortex processors. In the preceding parts I introduced the TIM3 timer features, showed you how to identify the timer clock and set up the prescaler and reload register, and how to use the output compare interrupts. You can see all the STM32 TIM3 posts here. Code is written using the Standard Peripheral Library for the STM32F4Discovery board. Timer related portions should run directly on other STM32 family members since they all have a TIM3.

A small change for the STM32F4Discovery

This article will switch to using the TIM4 timer simply because it has the output compare pins for TIM4 conveniently connected to four LEDs on the board. That will let me test the code directly on the STM32F4Discovery without wiring up any other LEDs. TIM4 is identical in all respects to TIM3 and everything here will work perfectly well on any of the general purpose timers so long as you take note of the output pins where present on your specific device.

Clock Pulses Using the Output Compare

One of the modes for the output compare channels in the STM32 general purpose timers is toggle output. In this mode, the associated OCx pin for the channel will be toggled each time there is a match between the CCR and the timer’s CNT register. The default behaviour for the CNT register is to count up to the value in the ARR register and then clear, starting again from zero.

That means the OCx pin will output square wave clock pulses at a frequency that is half the frequency at which the CNT register reloads. All this happens in hardware so once configured and set running, the timer will generate the square wave without any further help from your code. No interrupts are required – it all just happens.

There is one small catch, the OCx pin is an Alternate Function output so the actual port pin to which it is connected has to be set up in Alternate Function mode rather than plain output mode. Forgetting to do that is a classic way to waste hours wondering why nothing is happening and no clock pulses appear.

There are four OC channels available on the STM32 family general-purpose timers and each can be enabled independently. All four channels are tied to the same counter and so any events they generate will have the same frequency as the timer’s counter overflow. Because they have their own capture compare register though, each set of clock pulses can be set to trigger at a different instant and so the OCx pins can run with an arbitrary phase difference between them. This is a convenient way to generate quadrature signals or complementary signals.

three-phase clock pulses from the STM32 general purpose timer

The diagram illustrates the CNT register counting up to the value held in the ARR register and then reloading. Three output compare channel registers are set to different values. At each match the corresponding channel triggers producing three sets of clock pulses. The signals all have the same frequency but different phase relationships. Notice that the frequency of the pulses is half the counter reload frequency.

It is pretty easy to adjust the relative phase of the signals on the fly but changing the frequency would be a little involved.

Set up the Timer timebase

As usual, the first task is to determine the frequency of the pulse train. The pulse frequency will be half the timer reload frequency because the output gets toggled on each cycle. Another factor to take into account is the resolution needed to establish the phase differences between the channels. This will determine the number of cycles the CNT register will count for and thus the value that goes in the timer’s ARR register. If the phase difference is not a requirement then just pick a convenient number like 1000 or 256. Remember that the ARR value is one less than the number of counter cycles needed.

For demonstration, I will choose a frequency of 1Hz and a phase resolution of 3600 – that would correspond to a tenth of a degree if that was the kind of thing I wanted.

Calculate the Counter Frequency

Now I have a timer clock frequency, TIMER_Frequency, the desired output pulse frequency, PULSE_Frequency and the number of cycles in the counter, COUNTER_Cycles.

Calculate the Prescaler Constant

All that is left is to calculate the prescaler value, TIMER_Prescale. First, I calculate the input frequency needed by the counter. This will be

COUNTER_Frequency = PULSE_Frequency * COUNTER_Cycles * 2 = 1 * 3600 * 2 = 7200

The doubling is to account for the division by 2 from the output toggle every cycle.

The prescaler is then just

TIMER_Prescale = (TIMER_Frequency / COUNTER_Frequency) – 1 = 72000000/7200 – 1 = 999

Sanity check

At this stage you must check that the prescale value is less than 65536. At low pulse frequencies, or with small values for the COUNTER_Cycles, this can be a problem. If you need to have all this work over a very wide range of pulse frequencies, some extra care will need to be taken to make sure there is not an overflow here. I am using low frequencies just so that I can see the LEDs blink and confirm the correct operation.

I can now write the code to set up the timebase for the timer

 

Configure the output pins and Capture Compare

The timer is now ready to go and should count continuously once started. Before turning it loose, I have to configure the output pins and the capture compare registers. Configuration of pins connected to peripherals like the timer is very similar to setting up a regular GPIO pin. The difference it that I need to tell the pin it will take its output sinal from the Alternate Function and then connect that Alternate Function to the pin. Life would be simpler if any pin could connect to any pin but it can’t. Instead, I have to delve into the reference manual for the appropriate settings. The code for all the board LEDs in this example is:

The last part of the configuration is to set up the actual output compare registers and tell the timer to toggle the outputs on a match between CCRx and CNT:

It looks like I am ready to go. Always before loading code to a target, it is important to have a clear idea in your head about what you will see when it runs. Without that mental picture, the results will always be a surprise.

Putting it all Together

The full code listing below should show all four of the STM32F4Discovery LEDs flashing. The green LED flashes once per second as a timing reference. The Red, Blue and Orange LEDs also flash once per second with a 50% duty cycle but come on in sequence, one third of a second apart.

You can adjust the relative phases by changing the values in the CCRx_Value variables. If any of those variables has a value outside the range 0 to COUNTER_Cycles-1 the corresponding LED will not flash because there can be no match with the CNT register.

While I can probably pick any sensible value for the pulse frequency with suitable choices for the constants mentioned above, the calculation, and the need to keep numbers within practical ranges, makes it difficult to change the pulse frequency on the fly in the program. Not impossible but it would need some care and smooth, glitch free frequency control may take considerable care.

Complete Source Code




9 thoughts on “clock pulses with variable phase on STM32

  1. Fabian

    Hi,

    i have a quesion about your code. I need to start in a predefined state, e.g. all in the same phase. Than i run through different phase steps. after this i need to go back into the predefined state and do it again.

    Because using the toggle function, sometimes all timeroutputs are in phase but sometimes they are shiftet by 180┬░

  2. Pingback: TIM3 on the STM32 - an introduction - Micromouse Online

  3. stm32kt

    Hello,
    I would like to ask regarding the highest speed I can toggle using output compare.
    When I try to toggle the output with the frequency of 1M Hz, I do not get what I expect.

    As attached program, I can get the correct frequency for Channel 1 to 3, but not channel 4. Could you please take a look at it and explain what could be the issue?

    Thank you.

    /* Includes ------------------------------------------------------------------*/
    #include "myTimer4.h"
    
    /* Private variables ---------------------------------------------------------*/
    TIM_TimeBaseInitTypeDef  TIM4_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM4_OCInitStructure;
    
    
    static uint16_t TIM4_PrescalerValue = 0;
    static uint32_t TIM4_Frequency = 42000000;
    static uint16_t uhCapture = 0;
    
    //static uint16_t myTimer = RCC_APB1Periph_TIM4;
    
    
    __IO uint16_t TIM4_CCR1_Val = 42000;
    __IO uint16_t TIM4_CCR2_Val = 4200;
    __IO uint16_t TIM4_CCR3_Val = 420;
    __IO uint16_t TIM4_CCR4_Val = 42;
    
    
    
    /* Private function prototypes -----------------------------------------------*/
    void TIM4_OC_Config(void)
    {
      GPIO_InitTypeDef GPIO_InitStructure;
      NVIC_InitTypeDef NVIC_InitStructure;
    
      /* TIM4 clock enable */
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    
      /* GPIOC clock enable */
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    
      /* GPIOC Configuration: TIM4 CH1 (PD12), TIM4 CH2 (PD13), TIM4 CH2 (PD14) and TIM4 CH4 (PD15) */
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
      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_UP ;
      GPIO_Init(GPIOD, &GPIO_InitStructure);
    
      /* Connect TIM Channels to AF2 */
      GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
      GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);
      GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_TIM4);
      GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_TIM4);
    
      /* Enable the TIM4 global Interrupt */
      NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
    
      /* ---------------------------------------------------------------------------
      	    TIM4 Configuration: Output Compare Toggle Mode:
    
      	    In this example TIM4 input clock (TIM4CLK) is set to 2 * APB1 clock (PCLK1),
      	    since APB1 prescaler is different from 1.
      	      TIM4CLK = 2 * PCLK1
      	      PCLK1 = HCLK / 4
      	      => TIM4CLK = HCLK / 2 = SystemCoreClock /2
    
      	    To get TIM4 counter clock at 42 MHz, the prescaler is computed as follows:
      	       Prescaler = (TIM4CLK / TIM4 counter clock) - 1
      	       Prescaler = ((SystemCoreClock /2) /42 MHz) - 1
    
      	     CC1 update rate = TIM4 counter clock / TM4_CCR1_Val = 1k Hz
      		   ==> So the TIM4 Channel 1 generates a periodic signal with a
      		       frequency equal to 500 Hz.
    
      	     CC2 update rate = TIM4 counter clock / TM4_CCR2_Val = 10k Hz
      		   ==> So the TIM4 Channel 2 generates a periodic signal with a
      		       frequency equal to 5k Hz.
    
      	     CC3 update rate = TIM4 counter clock / TM4_CCR3_Val = 100k Hz
      		   ==> So the TIM4 Channel 3 generates a periodic signal with a
      		       frequency equal to 50k Hz.
    
      	     CC4 update rate = TIM4 counter clock / TM4_CCR4_Val = 1M Hz
      		   ==> So the TIM4 Channel 4 generates a periodic signal with a
      		       frequency equal to 500k Hz.
    
      	    Note:
      	     SystemCoreClock variable holds HCLK frequency and is defined in system_stm32f4xx.c file.
      	     Each time the core clock (HCLK) changes, user had to call SystemCoreClockUpdate()
      	     function to update SystemCoreClock variable value. Otherwise, any configuration
      	     based on this variable will be incorrect.
      	  --------------------------------------------------------------------------- */
    
      	  /* Compute the prescaler value */
      	  TIM4_PrescalerValue = (uint16_t) ((SystemCoreClock / 2) / TIM4_Frequency) - 1;
    
      	  /* Time base configuration */
      	  TIM4_TimeBaseStructure.TIM_Period = 65535;
      	  TIM4_TimeBaseStructure.TIM_Prescaler = TIM4_PrescalerValue;
      	  TIM4_TimeBaseStructure.TIM_ClockDivision = 0;
      	  TIM4_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    
      	  TIM_TimeBaseInit(TIM4, &TIM4_TimeBaseStructure);
    
      	  /* Output Compare Toggle Mode configuration: Channel1 */
      	  TIM4_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
      	  TIM4_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      	  TIM4_OCInitStructure.TIM_Pulse = TIM4_CCR1_Val;
      	  TIM4_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
      	  TIM_OC1Init(TIM4, &TIM4_OCInitStructure);
    
      	  TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Disable);
    
      	  /* Output Compare Toggle Mode configuration: Channel2 */
      	  TIM4_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      	  TIM4_OCInitStructure.TIM_Pulse = TIM4_CCR2_Val;
    
      	  TIM_OC2Init(TIM4, &TIM4_OCInitStructure);
    
      	  TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Disable);
    
      	  /* Output Compare Toggle Mode configuration: Channel3 */
      	  TIM4_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      	  TIM4_OCInitStructure.TIM_Pulse = TIM4_CCR3_Val;
    
      	  TIM_OC3Init(TIM4, &TIM4_OCInitStructure);
    
      	  TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Disable);
    
      	  /* Output Compare Toggle Mode configuration: Channel4 */
      	  TIM4_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      	  TIM4_OCInitStructure.TIM_Pulse = TIM4_CCR4_Val;
    
      	  TIM_OC4Init(TIM4, &TIM4_OCInitStructure);
    
      	  TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Disable);
    
      	  /* TIM enable counter */
      	  TIM_Cmd(TIM4, ENABLE);
    
      	  /* TIM IT enable */
      	  TIM_ITConfig(TIM4, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
    }
    
    void TIM4_InterruptHandler()
    {
    	if (TIM_GetITStatus(TIM4, TIM_IT_CC1) != RESET)
    	{
    		TIM_ClearITPendingBit(TIM4, TIM_IT_CC1 );
    		uhCapture = TIM_GetCapture1(TIM4);
    		TIM_SetCompare1(TIM4, uhCapture + TIM4_CCR1_Val );
    	}
    
    	if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)
    	{
    		TIM_ClearITPendingBit(TIM4, TIM_IT_CC2);
    		uhCapture = TIM_GetCapture2(TIM4);
    		TIM_SetCompare2(TIM4, uhCapture + TIM4_CCR2_Val);
    	}
    
    	if (TIM_GetITStatus(TIM4, TIM_IT_CC3) != RESET)
    	{
    		TIM_ClearITPendingBit(TIM4, TIM_IT_CC3);
    		uhCapture = TIM_GetCapture3(TIM4);
    		TIM_SetCompare3(TIM4, uhCapture + TIM4_CCR3_Val);
    	}
    
    	if (TIM_GetITStatus(TIM4, TIM_IT_CC4) != RESET)
    	{
    		TIM_ClearITPendingBit(TIM4, TIM_IT_CC4);
    		uhCapture = TIM_GetCapture4(TIM4);
    		TIM_SetCompare4(TIM4, uhCapture + TIM4_CCR4_Val);
    	}
    }
    

  4. Peter Harrison Post author

    The problem is that the value you add to the CR4 register is very small. By the time the code has run, the counter has already passed the new compare value and has to go all the way around again before you get a trigger. Thus the output frequency will be about ((42000000/(65536+42))/2 = 320Hz or so.

    You are effectively asking the code to update the register within 1us and there are several statements before you even test the OC4 interrupt.

    The highest frequency I could get with your code and no optimisation was about 140kHz with TIM4_CCR4_Val = 150. even with higher levels of optimisation and a bit of minor tweaking of the code, I could not get a significantly higher frequency with all four channels running. With only one channel running and all the optimisation, I could get a 385kHz output frequency (interrupts at twice that of course). When you add a second or more channels, sooner or later the interrupts will interfere with each other and you will miss the critical timing for the fastest pulse. there will also be some jitter in the others.

    I do not believe it is possible to get the channels of a single timer to output pulse trains at different frequencies without using the interrupt and so your maximum frequency is going to be limited by how fast you can respond to that interrupt.

    So – I would expect that, at a clock frequency of 84MHz, you cannot reasonably expect more than about 140kHz from a single channel and then only if the other channels are at much lower frequencies.

    If you only care about the frequency and can live with narrow pulses, you can set the timer mode to Timing only:

    TIM4_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
    

    And, in the interrupt handler, pulse the pin directly.

    
    if (TIM_GetITStatus(TIM4, TIM_IT_CC4) != RESET) {
    		TIM_ForcedOC4Config(TIM4,TIM_ForcedAction_Active);
    		TIM_ClearITPendingBit(TIM4, TIM_IT_CC4);
    		uhCapture = TIM_GetCapture4(TIM4);
    		TIM_SetCompare4(TIM4, uhCapture + TIM4_CCR4_Val);
    		TIM_ForcedOC4Config(TIM4,TIM_ForcedAction_InActive);
    	}

    With only one channel active, I can then get up to 300kHz but enabling more channels will upset it again because the overhead of setting/clearing the pin takes more time and there will be considerable jitter.

    I had not looked into this before and, I have to say, I am disappointed that the STM32 timers cannot do better than this. Perhaps I have missed something.

  5. stm32kt

    Hi Peter,
    Thank you for taking your time to explain. At least, now I understood the cause.
    So there is no way I can produce higher frequency?
    I am testing for controlling AC servo with high speed pulse input. In order to get the requirement speed, I will need at least 800khz. Do you have any suggestion for that?
    As an another question, how to calculate the execution time in the main loop? When I tried to toggle the pin, it took about 0.5 us. With 84Mhz, it should be a lot faster than 0.5 us right?

    Thank you

  6. Peter Harrison Post author

    There is a lot of overhead in the library calls and a lot more in debug mode.

    With an STM32F401 running at 84MHz:

    In debug mode, this code:

    while(1){
    GPIO_ToggleBits(GPIOB,GPIO_Pin_9);
    }

    gets me 3Mhz on the output pin.

    This code:

    while(1){
    GPIO_SetBits(GPIOB,GPIO_Pin_9);
    GPIO_ResetBits(GPIOB,GPIO_Pin_9);
    }

    gets me 4.4MHz.

    Accessing the registers directly with:

    while(1){
    GPIOB->BSRRH = GPIO_Pin_9;
    GPIOB->BSRRL = GPIO_Pin_9;
    }

    reaches 12MHz but the pulses are very short and need a proper setup to see them.

  7. Peter Harrison Post author

    If you need multiple pulse trains at high speed, you may need to set up several timers in PWM mode. there will be no control of the relative phase of those pulsetrains though.

  8. Harjit Singh

    The STM32 timers have an unbelievably neat DMA feature – look for TIMx_DCR and TIMx_DMAR.

    It is a DMA indirection into the timer registers. What you do is figure out when you want to update a timer register and you setup a DMA on that event. The DMA peripheral address should be the TIMx_DMAR and the TIMx_DCR should point to the timer register(s) of interest.

    When the event happens, the DMA engine will transfer data to TIMx_DMAR. The Timer’s DMA block will read/write the values to the timer registers pointed to by TIMx_DCR.

    I have this working but my coding style is very different. I did find one link that explains this using the ST peripheral library: http://www.cnblogs.com/shangdawei/p/4753284.html

  9. stm32kt

    Hello Harjit,
    Thank you for the information. I will take a look at it and try it out.

Leave a Reply