Skip to main content
Rogers.Gary
Senior II
February 19, 2018
Question

STM32L4 PWM Phase Delay

  • February 19, 2018
  • 10 replies
  • 4694 views
Posted on February 19, 2018 at 19:02

Hello:

Using the Nucleo STM32L476RG demo board.

On an STM32F4xx, using CMSIS, I am able to create 2 PWM pulses that have the same period. However, the 2nd pulse is delayed by: TIM4->CNT = TIM3->CNT + (timerPeriod /2 ); // =180 degrees

I want to do the same thing using HAL, but it looks like a few things have changed from CMSIS!

I have my code where I have 2 pulses being generated at the correct duty cycle and frequency, but spinning the wheels on how to get the second pulse delayed as shown above.

Can someone please provide some insight on how I could accomplish this? Has HAL made it easier than CMSIS? Or just different?

Thanks!

UPDATE.

Found: DM00405316

Note: this post was migrated and contained many threaded conversations, some content may be missing.
    This topic has been closed for replies.

    10 replies

    Rogers.Gary
    Senior II
    February 19, 2018
    Posted on February 20, 2018 at 00:10

    No answer....ok.

    Can anyone provide some insight as to how I can convert my STM32F4xx code to equivalent HAL code, or something close? I need to perform the same timer functions in HAL as I have in my CMSIS code.

    Anyone?

    henry.dick
    Associate II
    February 20, 2018
    Posted on February 20, 2018 at 03:09

    Didn't see your code so hard to help.

    You can just initialize the timers by 180 degrees.

    It wouldn't be perfectly syncd but close enough.

    Rogers.Gary
    Senior II
    February 20, 2018
    Posted on February 20, 2018 at 03:20

    Hi dHenry,

    Thanks. Just updated my post. Found article DM00405316, with info on converting SPL to LL.

    If HAL was the only choice I would be switching processors...don't want to be a beta tester for the obfuscation that is HAL. I cannot imagine why they would get rid of CMSIS/SPL. I have thousands of lines of code and having LL available makes it possible to 'more easily' move the code to a newer processor, i.e., L series.

    Will see how it works out.

    John Craven
    Senior
    February 20, 2018
    Posted on February 20, 2018 at 05:27

    Setup Master Timer (TIM2) as basic PWM on CH1, only thing extra is to Master Output to Update Event

    /* TIM2 init function */
    static void MX_TIM2_Init(void)
    {
     TIM_ClockConfigTypeDef sClockSourceConfig;
     TIM_MasterConfigTypeDef sMasterConfig;
     TIM_OC_InitTypeDef sConfigOC;
     htim2.Instance = TIM2;
     htim2.Init.Prescaler = 71;
     htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
     htim2.Init.Period = 9999;
     htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
     htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
     if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
     if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
     if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sConfigOC.OCMode = TIM_OCMODE_PWM1;
     sConfigOC.Pulse = 1000;
     sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
     sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
     if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
    
     HAL_TIM_MspPostInit(&htim2);
    }�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

    Set Slave Timer (TIM1) as follows, basic PWM on CH1 , with polarity LOW, Oneshot mode, and Triggered by ITR1

    /* TIM1 init function */
    static void MX_TIM1_Init(void)
    {
     TIM_ClockConfigTypeDef sClockSourceConfig;
     TIM_SlaveConfigTypeDef sSlaveConfig;
     TIM_MasterConfigTypeDef sMasterConfig;
     TIM_OC_InitTypeDef sConfigOC;
     TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
     htim1.Instance = TIM1;
     htim1.Init.Prescaler = 71;
     htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
     htim1.Init.Period = 6000;
     htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
     htim1.Init.RepetitionCounter = 0;
     htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
     if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
     if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     if (HAL_TIM_OnePulse_Init(&htim1, TIM_OPMODE_SINGLE) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
     sSlaveConfig.InputTrigger = TIM_TS_ITR1;
     if (HAL_TIM_SlaveConfigSynchronization(&htim1, &sSlaveConfig) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
     sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
     if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sConfigOC.OCMode = TIM_OCMODE_PWM1;
     sConfigOC.Pulse = 5000;
     sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
     sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
     sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
     sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
     sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
     if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
     sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
     sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
     sBreakDeadTimeConfig.DeadTime = 0;
     sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
     sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
     sBreakDeadTimeConfig.BreakFilter = 0;
     sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
     sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
     sBreakDeadTimeConfig.Break2Filter = 0;
     sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
     if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
     {
     _Error_Handler(__FILE__, __LINE__);
     }
     HAL_TIM_MspPostInit(&htim1);
    }�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

    Code to start timers and manipulate pulse width and phase;

     MX_TIM2_Init();
     MX_TIM1_Init();
     uint32_t period = 10000;
     uint32_t t1 = 1000;
     uint32_t t2 = 1000;
     uint32_t offset = period/2;
     __HAL_TIM_SET_AUTORELOAD(&htim2, period); //set master period
     __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, t2); //set master pulse width
     __HAL_TIM_SET_AUTORELOAD(&htim1, offset + t2); //set slave period
     __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, offset); //set slave pulse width
     HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
     HAL_TIM_OnePulse_Start(&htim1, TIM_CHANNEL_1);
     HAL_Delay(100);
     t1 = 2000; //increase master pulse width
     __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, t1);
     HAL_Delay(100);
     t2 = 2000; //increase slave pulse width
     __HAL_TIM_SET_AUTORELOAD(&htim1, offset + t2);
     __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, offset);
     HAL_Delay(100);
     offset = period/4; //reduce slave offset
     __HAL_TIM_SET_AUTORELOAD(&htim1, offset + t2);
     __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, offset);�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

    Tesla DeLorean
    Guru
    February 20, 2018
    Posted on February 20, 2018 at 15:39

    Surprises me that this won't work under HAL, registers are all exposed and available

    TIM4->CNT = TIM3->CNT + (timerPeriod /2 ); // =180 degrees

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    John Craven
    Senior
    February 20, 2018
    Posted on February 20, 2018 at 15:42

    The alternative, which is probably a little cleaner,(no math and single calls for each parameter change):

    add PWM on CH2 to master timer, using this as master output pulse.

    use PWM on CH1 on master as the phase shift.

    change master TRGO to the Compare Pulse (OC1) event

    Set Slave CH1 pulse width to 1 (delay before the pulse, must be > zero)

    then start things with;

    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); HAL_TIM_OnePulse_Start(&htim1, TIM_CHANNEL_1);

    use these to make changes to timing;

    __HAL_TIM_SET_AUTORELOAD(&htim2, period); //set master period __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, tm); //set master pulse width __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, (offset-1)); //set slave pulse offset __HAL_TIM_SET_AUTORELOAD(&htim1, ts); //set slave pulse width    // change period with 180deg phase 

    __HAL_TIM_SET_AUTORELOAD(&htim2, newperiod); //set master period __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, (

    newperiod/2

    -1)); //set slave pulse offset

     

    John Craven
    Senior
    February 20, 2018
    Posted on February 20, 2018 at 17:59

     For CubeMX users, this is the setup for the clean code alternative i posted.

    0690X00000609kqQAA.png0690X00000609STQAY.png0690X00000609SUQAY.png
    waclawek.jan
    Super User
    February 20, 2018
    Posted on February 20, 2018 at 18:07

    John,

    Could you please express it also in terms of TIM registers content? It's enough to read them out in debugger.

    Thanks,

    JW

    John Craven
    Senior
    February 20, 2018
    Posted on February 20, 2018 at 19:05

    I hope this is what you wanted!

    After Master (TIM2) INIT;

    0690X00000609l0QAA.png

    After Slave (TIM1) INIT;

    0690X00000609gaQAA.png

    After Master (TIM2) PWM Starts;

    0690X00000609YMQAY.png

    Slave (TIM1) after Master PWM Starts only:

    0690X00000609eCQAQ.png

    finally after SLAVE (TIM1) PWM Startup

    0690X00000609eBQAQ.png
    Rogers.Gary
    Senior II
    February 20, 2018
    Posted on February 20, 2018 at 18:53

    Very helpful...thanks, I will test it out a bit later today.

    John Craven
    Senior
    February 21, 2018
    Posted on February 21, 2018 at 05:46

    hereCubeMX ioc file

    NoteI tested this on Nucleo-F303RE.

    I don't have a 476, but i threw a cubemx file together for you.

    ________________

    Attachments :

    N64-303-TIM1-TIM2-CHAINING.ioc.zip : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006Hxfn&d=%2Fa%2F0X0000000b2i%2FHUr3ovNWgDZwzHfzywkKZX2QCT_tVnU2jTOyIQcTZwo&asPdf=false

    N64-476-TIM2-TIM1-Chaining.ioc.zip : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006Hxx2&d=%2Fa%2F0X0000000b2f%2FndAEgVzaixgVdm7hrr2qXGzMWoPgdn7OSNB0HZoqaqs&asPdf=false
    henry.dick
    Associate II
    February 25, 2018
    Posted on February 25, 2018 at 17:27

    if the numbers used here are real, you have 720k ticks in between. a fully software solution is possible here, with its own caveats as just as hardware -based solution does.

    henry.dick
    Associate II
    February 26, 2018
    Posted on February 26, 2018 at 03:13

    if you want to take the software approach, this would be something for your consideration:

    1) set up a free running timer, with the right prescaler.

    2) set up an output match point, and alternate it between the on and off cycles. in the output compare isr, flip the pin.

    3) do the same as in 2) but for a different channel.

    all this requires is a timer with two output compare channels.

    Rogers.Gary
    Senior II
    February 26, 2018
    Posted on February 26, 2018 at 05:46

    I have done this using FreeRTOS with 1ms tick resolution. It's OK...but when the system gets loaded, it gets a bit jittery. Makes more sense to use HW if the timers/pins are available! (and they are available in my case)

    Ivan Elkin
    Visitor II
    March 1, 2018
    Posted on March 01, 2018 at 09:14

    Hi Friends!

    I've tryed to imitate effect of SMS led on Vibe P1 by PWM (slowly getting max lighting and slowly darking)

    I've found when pulse width pass over 0 (i.e. 2, 1, 0, 1, 2, ...) the led get max bright for very short time.

    I've investigate, investigate...

    I've shift the PWM phase i.e. '0' is shifted right (amm) to 21. Now led show lighting smoothly 

    '21' is PWM minimal step.