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-19 11:14 PM
Not this, your original code which worked.
JW
2018-02-20 08:42 AM
Here is the code that works in SPL/CMSIS. I would like to get this to work with the LL implementation.
I tried direct register set and it did not appear to work.
Thanks for looking at this.
#define TIMER_BASE (uint16_t)20000void PWM_TIMER_Configuration( void )
{ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseStructInit( &TIM_TimeBaseStructure ); // 20 KHz timebase, assumes APB1 H/4 TIMCLK4 H/2 uint16_t Prescaler = ((SystemCoreClock / 2) / TIMER_BASE); /* Time base configuration */ // set for 100Hz TIM_TimeBaseStructure.TIM_Period = (uint16_t)( TIMER_BASE / 100 ); TIM_TimeBaseStructure.TIM_Prescaler = Prescaler - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//set timer 3 & 4 to same frequency
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //create pulse width for both channels the same float32_t dutyCycle = (float32_t) (( BPF / 100.0) + 0.25 ); uint16_t onTime = (uint16_t) (TIM_TimeBaseStructure.TIM_Period * dutyCycle );/* Output Compare PWM1 Mode configuration: Channel 1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = onTime; //Timer 4 is on channel 1 TIM_OC1Init(TIM4, &TIM_OCInitStructure); //Set TIM3 channel 2 preload cfg TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); /* Output Compare PWM1 Mode configuration: Channel 2 */ TIM_OC2Init(TIM3, &TIM_OCInitStructure); //set timer 3 immediately to timerPeriod offset/2 = 180 degrees shift TIM4->CNT = TIM3->CNT + ((uint16_t) (TIM_TimeBaseStructure.TIM_Period / 2 ));TIM_Cmd(TIM4, ENABLE);
TIM_Cmd(TIM3, ENABLE); //Set up TIM3 interrrupt NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; // set for RTOS NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_KERNEL_INTERRUPT_PRIORITY; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); }2018-02-20 09:16 AM
1. Keep the two timers off. Then set up the rest.
2. Zero one timers counter and load up the others counter with an offset equal to 1/2 of the period.
3. Enable both timers.
2018-02-20 09:54 AM
ok, thanks....I will give it a try!
2018-02-20 09:05 PM
hi Jan,
You asked if I could post my original code from SPL; please see the code I have posted. Can you suggest how I could set up the LL code I have posted to get a 180 shift/offset on timer 3? I'm essentially doing the same thing in LL as I am in SPL. Set up 2 timers, then perform an OC compare on the respective channels. No matter where I place the code to set TIM2/TIM3, it does not alter the signals - they are still in phase.
Appreciate any help I can get....thanks
2018-02-20 10:24 PM
I am not an SPL guy, but looking at you setup, and the my HAL code from the other thread;
https://community.st.com/0D50X00009XkhLaSAJ
You set up both timers as simple PWM. I don't see the extra bit to do Oneshot mode on the slave timer.
In the HAL init of the Oneshot slave, first there is PWM init, and then Oneshot init;
if (HAL_TIM_OnePulse_Init(&htim1, TIM_OPMODE_SINGLE) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); }
digging into the oneshot init, here is what it adds;
/* Reset the OPM Bit */ htim->Instance->CR1 &= ~TIM_CR1_OPM;/* Configure the OPM Mode */ htim->Instance->CR1 |= OnePulseMode;
I also don't see you set the slave polarity low for CH1. I think you will need to do this, because the oneshotacts inverted to typical PWM.
2018-02-21 03:06 PM
I also think this is wrong, your setting the counter
TIM4->CNT = TIM3->CNT + ((uint16_t) (TIM_TimeBaseStructure.TIM_Period / 2 ));
to set the period, it would be;
TIM4->ARR= TIM3->ARR+ ((uint16_t) (TIM_TimeBaseStructure.TIM_Period / 2 ));
i found a downloaded the F4 SPL;
if you going to try do what i did in HAL with SPL, your going to need to call this on the slave timer;
TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode) with TIM_OPMode_Single
2018-02-21 03:52 PM
You would set the two timers with different period. The pwm generated would be out of whack quickly.
The original code is correct.
2018-02-21 04:31 PM
Hi John,
Thanks again for all your help and code samples. The code I posted above is what I am using in SPL and it works well! So, I'm not sure why CNT vs ARR should (or should not) function coded as I have? I need to have a look at the reference manual and the timer diagram and try to work through it.
That said, I will try the things you have mentioned above. Here's something I am struggling to understand, and maybe someone from ST (if you're reading this) could weigh in on. I found an app note written in 2016 (timer cookbook) that has all kinds of examples for timers and functionality. What is amazing about this, is all the code in register level. Keep in mind that when this was written, HAL was already out, and SPL/CMSIS was at least v1.6. So why doesn't ST write the same kind of cookbook, with the same type of example functionality, using HAL or LL ? It would be useful as some of those examples are more difficult timer examples.Timers are perhaps the most complex section in the device, and if ST could spend a bit more time and effort producing working examples and well written documentation it would go a long way.
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. They don't help when you have a problem to solve - as in how do I get from here to here - only in-depth understanding of the tool set will meet that end. It's bothersome when I see all kinds of diagrams talking about the timers and their sections, but a well written treatise on the subject would include a description of which timer sections are used, registers, setup and so forth. I'm sure ST would find a lot of new customers with better written documentation on this subject.
My diatribe for the year.