cancel
Showing results for 
Search instead for 
Did you mean: 

Stop PWM output after N steps

harry_rostovtsev
Associate II
Posted on August 27, 2012 at 20:01

Is there a way to tell a TIMer on STM32 chips to do some number of steps and automatically stop after finished (and give an interrupt)?  I can do it now with a counter in the ISR but a few pulses slip out before I can shutdown the PWM output or the timer.

11 REPLIES 11
harry_rostovtsev
Associate II
Posted on August 27, 2012 at 21:58

Here's my current code:

uint16_t number_of_steps;
int main(void) {
NVIC_InitTypeDef nvic_init;
NVIC_ClearPendingIRQ(TIM8_UP_TIM13_IRQn);
nvic_init.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
nvic_init.NVIC_IRQChannelSubPriority = 0;
nvic_init.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_init);/* enables the device and sets interrupt priority */
NVIC_ClearPendingIRQ(TIM8_CC_IRQn);
nvic_init.NVIC_IRQChannel = TIM8_CC_IRQn;
nvic_init.NVIC_IRQChannelPreemptionPriority = 1;
nvic_init.NVIC_IRQChannelSubPriority = 1;
nvic_init.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_init);/* enables the device and sets interrupt priority */
/* set priorities of all interrupts in the system... */
NVIC_SetPriority(SysTick_IRQn, 1);
NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 2);
NVIC_SetPriority(TIM8_CC_IRQn, 3);
GPIO_InitTypeDef GPIO_InitStructure;
/* ENABLE GPIOC clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
/* GPIOC Configuration:TIM8 Channel3 in Output */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Connect TIM8 pins to AF */
GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_TIM8);
/* Compute the value to be set in ARR register to generate signal frequency at 57 Khz */
TimerPeriod = (SystemCoreClock / 2000 ) - 1;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* TIM8 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
/* how many steps are we going */
number_of_steps = 20;
/* Time base configuration */
uint16_t Period = 1000000 / 2000; // 2 KHz for 1MHz prescaled
TIM_TimeBaseStructure.TIM_Period = Period - 1; // 1 MHz down to 1 KHz (1 ms)
TIM_TimeBaseStructure.TIM_Prescaler = 120 - 1; // 120 MHz Clock down to 1 MHz (adjust per your clock)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
/* PWM Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_Pulse = Period / 2;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* Output Compare PWM1 Mode configuration: Channelc Pc.08 */
TIM_OC3Init(TIM8, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM8, TIM_OCPreload_Enable);
/* TIM8 enable counter */
TIM_Cmd(TIM8, ENABLE);
TIM_ITConfig(TIM8,TIM_IT_Update, ENABLE);
TIM_CtrlPWMOutputs(TIM8, ENABLE);
while (1)
{
}
}
void TIM8_UP_TIM13_IRQHandler(void) {
if (number_of_steps == current_step) {
TIM8->CCR3 = 0;
TIM8->CR1 &= (uint16_t)~TIM_CR1_CEN; // this is same as TIM_Cmd(TIM8, DISABLE);
TIM_ITConfig(TIM8,TIM_IT_Update, DISABLE);
current_step = 1;
}
current_step++;
// If this is uncommented, I have a few extra pulses that sneak out
//printf(''TIM8 update int
''); 
// If this is commented out, I only get 1 or 2 pulses, as if my ISR goes by to quickly
if (TIM_GetITStatus(TIM8, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM8, TIM_IT_Update);
}
}

If you look in the interrupt handler, you can see my comment specify the problems. Basically, I don't think I'm using the right kind of interrupt (though I got similar behavior when I used the CCR interrupt).
rod_gilchrist
Associate II
Posted on January 29, 2013 at 15:59

Yes. (I am reading your question as that you wish to emit a specified number of PWM pulses.)

The STM32 documentation implies that a timer is either  in Master mode (wherein it can output a signal on an internal trigger line) or Slave mode (wherein it can be triggered from things like an internal trigger line). The truth is that it can be both a Master and a Slave at the same time. 

I use this  understanding to emit a specified number of pulses by connecting two timers 'back to back' and I use the repetition counter on an advanced counter to specify the number of pulses.

Specifically, I put Timer 1 (which has a repetition counter) in PWM mode configured to generate pulses with the timing I want. I then configure its slave controller to be gated by Timer 4 on internal trigger 3 (any of timers 2, 3 or 4 would work, but timer 5 won't due to internal trigger connectivity). I also set Timer 1's master controller to emit an update event on its trigger output.

I then configure Timer 4 in one pulse mode with a wide pulse (longer than 256 Timer 1 pulses) with its master controller outputting a compare output channel on 'trigger out' to gate Timer 1 and its slave controller set to reset Timer 4 based on the signal on internal trigger 0 (i.e. the update event from Timer 1, which only occurs after 'n' PWM pulses, depending on the value set in the Timer 1 repetition counter (which can be 1 to 256)).

I haven't tried it, but I imagine the Timer 1 update would generate an interrupt in the normal way if desired, but with this configuration the pulse count is not dependent on interrupt speed. 

Hope this helps.

rod_gilchrist
Associate II
Posted on January 30, 2013 at 15:52

Stop PWM output after N steps

Is there a way to tell a TIMer on STM32 chips to do some number of steps and automatically stop after finished (and give an interrupt)?  I can do it now with a counter in the ISR but a few pulses slip out before I can shutdown the PWM output or the timer.

My previous post describes how to do this.

One point that I didn't make is that you do have to trigger an update event (i.e. set the update event bit in the Timer 1 EGR register) after you set the pulse count in the repetition register. The update event is required to load the repetition count into the actual down counter.

I do this just before setting the enable bits on the two timers.

Amel NASRI
ST Employee
Posted on January 31, 2013 at 14:18

I guess the break input feature will be helpfull in your case. Did you tried it?

ST.MCU

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

barry
Associate II
Posted on January 27, 2015 at 16:26

Rod,

Have you actually gotten the code to work?

If so, could you post the code.  Is timer 8 completely identical to timer 1.

Thanks

Barry

Posted on January 27, 2015 at 16:57

Is timer 8 completely identical to timer 1.

They are both ''advanced'' timers, so you'd need to initialize the repetition count in the timebase, the N output settings in the channels, and enable the PWM output(input)
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
barry
Associate II
Posted on January 28, 2015 at 19:51

Thanks Clive,

Another question 

I am currently using timer 3(to generate aninterrupt once every  4 seconds)  to control timer 4 (I have hardwareconnected to PE12), but without a master slave connection.  This is not workingvery well to to generate an exact number  (200) of pulses.  It isclose but over a long time frame the stepper motor will drift off the correctstarting point.

Do you think I can use timer 8 and its repetitioncounter as the master to control timer 4 PWM pulses so I get an exact number ofpulses for a stepper motor?

The example in this thread seems to use theadvanced timer to generate the PWM pulses.

Are there any examples in the various STdistributions?

If this can't work, can I use timer 4 in a onepulse mode (and handle 200 interrupts)? The exact timing of the stepper motorpulses is not critical.

Thanks

Barry

Posted on January 28, 2015 at 21:58

// STM32 FIVE 20 KHz PULSES at 50 Hz (20ms Intervals) (TIM1 CH1 PA.08) STM32F4 Discovery - sourcer32@gmail.com
#include ''stm32f4_discovery.h''
/**************************************************************************************/
void RCC_Configuration(void)
{
/* --------------------------- System Clocks Configuration -----------------*/
/* TIM1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* GPIOA clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
}
/**************************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*-------------------------- GPIO Configuration ----------------------------*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect TIM1 pins to AF */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
}
/**************************************************************************************/
void TIM1_Configuration(void)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
uint16_t Period;
Period = 1000000 / 20000; // 20 KHz from 1MHz prescaled
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 1000000) - 1; // Get clock to 1 MHz on STM32F2/F4
TIM_TimeBaseStructure.TIM_Period = Period - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 5 - 1; // Five Pulses per trigger
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* Enable TIM1 Preload register on ARR */
TIM_ARRPreloadConfig(TIM1, ENABLE);
/* TIM PWM1 Mode configuration */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = Period / 2; // 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* Output Compare PWM1 Mode configuration: Channel1 PA.08 */
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* One Pulse Mode selection */
TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single);
/* Input Trigger selection */
TIM_SelectInputTrigger(TIM1, TIM_TS_ITR2); // Slave TIM1, Trigger TIM3 (TS=2)
/* Slave Mode selection: Trigger Mode */
TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Trigger);
/* TIM1 Main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
/* TIM1 enable counter */
TIM_Cmd(TIM1, ENABLE);
}
/**************************************************************************************/
void TIM3_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
uint16_t Period;
Period = 1000000 / 50; // 50 Hz from 1MHz prescaled
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = ((SystemCoreClock / 2) / 1000000) - 1; // Get clock to 1 MHz on STM32F2/F4
TIM_TimeBaseStructure.TIM_Period = Period - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* Trigger of TIM3 Update into TIM1 Slave */
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}
/**************************************************************************************/
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
TIM1_Configuration();
TIM3_Configuration();
while(1); // Don't want to exit
}
/**************************************************************************************/
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf(''Wrong parameters value: file %s on line %d

'', file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
barry
Associate II
Posted on January 28, 2015 at 23:22

Clive,

I'll give it a try.

Thanks

Barry