2018-02-19 08:30 PM
Here's my code to initialize timers 2 and 3. Keep in mind that right now, this code executes with no problem and there is a 100Hz output on PA5 (TIM2) and PA6 (TIM3)
My objective is to offset TIM3 by 180 degrees from TIM2. I am able to do this in SPL, but have had no success with LL after trying different things, including direct register writes to TIM2 and TIM3. The interrupt handlers are functioning OK for both timers.
What do I need to do to the code below to achieve this?
__STATIC_INLINE void Configure_TIMPWMOutput(void)
{ LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); /* GPIO TIM2_CH1 configuration */ LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_5, LL_GPIO_MODE_ALTERNATE); LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_5, LL_GPIO_PULL_DOWN); LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_5, LL_GPIO_SPEED_FREQ_HIGH); LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_5, LL_GPIO_AF_1); /* GPIO TIM3_CH1 configuration */ LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_6, LL_GPIO_MODE_ALTERNATE); LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_6, LL_GPIO_PULL_DOWN); LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_6, LL_GPIO_SPEED_FREQ_HIGH); LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_6, LL_GPIO_AF_2); /***********************************************/ /* Configure NVIC for TIM2 interrupt */ /***********************************************/ NVIC_SetPriority(TIM2_IRQn, 0); NVIC_SetPriority(TIM3_IRQn, 0); /******************************/ /* Peripheral clocks enabling */ /******************************/ /* Enable timer 2 peripheral clock */ LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); /* Enable timer 3 peripheral clock */ LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3); /***************************/ /* Time base configuration */ /***************************/ /* Set the pre-scaler value to have TIM2 counter clock equal to 10 kHz */ LL_TIM_SetPrescaler(TIM2, __LL_TIM_CALC_PSC(SystemCoreClock, 10000)); LL_TIM_SetPrescaler(TIM3, __LL_TIM_CALC_PSC(SystemCoreClock, 10000)); /* Enable TIM2_ARR and TIM3_ARR register preload. */ LL_TIM_EnableARRPreload(TIM2); LL_TIM_EnableARRPreload(TIM3); /* Set the auto-reload value to have a counter frequency of 100 Hz */ LL_TIM_SetAutoReload(TIM2, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), 100)); LL_TIM_SetAutoReload(TIM3, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM3), 100)); /*********************************/ /* Output waveform configuration */ /*********************************/ /* Set output mode */ LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1); LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1); /* Set output channel polarity */ LL_TIM_OC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCPOLARITY_HIGH); /* Set compare value to half of the counter period (50% duty cycle ) */ LL_TIM_OC_SetCompareCH1(TIM2, (LL_TIM_GetAutoReload(TIM2) / 2)); LL_TIM_OC_SetCompareCH1(TIM3, (LL_TIM_GetAutoReload(TIM3) / 2)); /* Enable TIM2_CCR1 register preload. */ LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH1); LL_TIM_OC_EnablePreload(TIM3, LL_TIM_CHANNEL_CH1); /**************************/ /* TIM2 and TIM3 interrupts set-up */ /**************************/ /* Enable the capture/compare interrupt for TIM2, TIM3 channel 1*/ LL_TIM_EnableIT_CC1(TIM2); LL_TIM_EnableIT_CC1(TIM3); /**********************************/ /* Start output signal generation */ /**********************************/ /* Enable TIM2, TIM3 output channel 1 */ LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); LL_TIM_CC_EnableChannel(TIM3, LL_TIM_CHANNEL_CH1); /* Enable counters */ LL_TIM_EnableCounter(TIM2); LL_TIM_EnableCounter(TIM3); /***********************************************/ /* Enable TIM2 and TIM3 INT Handlers */ /***********************************************/ NVIC_EnableIRQ(TIM2_IRQn); NVIC_EnableIRQ(TIM3_IRQn); /* Force update generation */ LL_TIM_GenerateEvent_UPDATE(TIM2); LL_TIM_GenerateEvent_UPDATE(TIM3); }2018-02-21 06:38 PM
'What is amazing about this, is all the code in register level.'
that's my way of doing anything. I started with spl, and gradually migrated to my own code base - that was made easy as I never used spl directly in any of my code -> I always used spl via a middle layer (of my own).
when hal was introduced I took a look at it and decided to keep investing in my code / middle layer. by now, 99% of my code is free of anything from st, including spl.
'For example - the pretty pictures and drop-down selections in the Cube tool may let you set all the registers you like - but until you understand what setting all of those do, it's pointless.'
spot on. libraries like spl / hal do two things incredibly helpful to st: 1) they lower the hurdle for the users to get started on st's chips - expand their customer base; and 2) they raise the hurdle for the users to switch to other vendors' chips - keep their customers in.
as you are finding out, however, libraries can be quite difficult to work with if you want to anything more than plain vanilla - you will need to invest a lot of timer / efforts to get to know how the library routines work together. I have always found it much easier to simply take a look at the datasheet and work on the registers, especially with the structs now. Once you crack one of them, you have cracked them all -> porting code written for TIM2 to TIM3/4/5, for example, is quite easy, especially if you start your coding with the goal for future portability.
I understand people using oem libraries when they get started. I never understood people using oem libraries directly in their code - this makes their code not portable to other vendors' chips, or to a different library from the same vendor, or continuing to use oem libraries. at that point, you are at the mercy of the vendors - that's not a good place to be, in my view.
2018-02-24 05:01 PM
Still getting a result that is not working. I just do not see how to configure the LL system on how to accomplish this.
Both timers will be the same period. Let's say that period is 10ms and the DC is 25%. Then, I need the second timer's output pulse to occur exactly 1/2 the period time. See the attached (hacked) image. Again, this I can presently do in CMSIS, but not in LL.
If TIMx starts at t = 0, TIMy output will be at t + 5ms. Again - I have the code configured so that right now, a PWM is occurring on TIM2 (PA5), TIM3( PA6) and these 2 signals are in phase. The question is, with a TIM2 period = 10ms, how do I get timer 3 to trigger at TIM2 + 5ms ?
Seems it should be easy....but after looking at the LL mechanisms, not obvious.
Help?
2018-02-25 01:44 AM
In the LL code in opening post, this part from your original code is missing:
//set timer 3 immediately to timerPeriod offset/2 = 180 degrees shift TIM4->CNT = TIM3->CNT + ((uint16_t) (TIM_TimeBaseStructure.TIM_Period / 2 ));That's the key one though - this is where you establish the phase shift.
I don't Cube, so don't know the exact incantation to do this 'properly' in LL, but the direct write to to CNT just as in the original code will do just fine.
John presented you a more complex but more flexible option using the internal master-slave linking
https://community.st.com/0D50X00009XkhLYSAZ
, but yours will do if you are OK too, if you don't intend to change the phase shift on the run.Ah, and you want also to move this
/* Force update generation */
LL_TIM_GenerateEvent_UPDATE(TIM2); LL_TIM_GenerateEvent_UPDATE(TIM3);to *before* you enable the timers.
JW
2018-02-25 08:15 AM
jan,
I had the timer offset in my code, just did not post it above. It looked like this:
TIM3->CNT = TIM2->CNT + ((uint16_t) (LL_TIM_GetAutoReload(TIM2) / 2 ));
The issue all along was the placement of the code 'LL_TIM_GenerateEvent_UPDATE'
Once I moved that as you suggested, it worked.Great!
Thanks again to you and everyone else who provided help with this.
Gary
2018-02-25 08:40 AM
Ok, also seen your post in STM32L4 Phase Delay', gives me a good idea on your coding. No need to answer my message, thanks!