2016-11-29 02:27 PM
Greetings,
I am trying to achieve the following with TIM1 and TIM1_CH1:1. Generate X number of PWM pulses after an event.2. Idle state of PWM output should be low.3. Pulse output should be high immediately after PWM pulse train initiated.4. Pulse output should be low after compare match.5. Duty of PWM is to be around 30%.I've got TIM1_CH1 outputting X number of pulses after an event. However, I can't seem to work out how to configure the timer module to achieve the above requirements. The code below sends out 10 pulses on PA8 each time the User button is pushed on a STM32F4 discovery board. I set my oscilloscope to trigger on the rising edge on PA1 and look at the output of PA8 on another channel. It satisfies all the above requirements, except from the idle state of PWM output (which is high, but I require this to be low).I have tried changing TIM_OCIdleState but does not seem to change the idle state of the PWM output.Ultimately I'm looking at creating a device to trigger other devices. The other devices are to be triggered on rising edge and must be accurately synchronised when the event happens (in the code below, it is the User push button). Suggestions?&sharpinclude ''stm32f4xx.h''&sharpinclude <stdbool.h>void delay(uint32_t delay){ uint32_t i; while(delay--) { for(i = 0; i < 30000; i++) __asm__(''nop''); }}void gpio_cfg(void){ GPIO_InitTypeDef GPIO_InitStructure; /* Setup PA8 as PWM1 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1); /* Setup PA1 as sync */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Setup push button */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure);} void pwm_cfg(void){ TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 30; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC1PolarityConfig(TIM1, TIM_OCPolarity_High); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM1, ENABLE); TIM_CtrlPWMOutputs(TIM1, ENABLE);}void pwm_start(void){ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_RepetitionCounter = 10 - 1; //Send 10 pulses TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single); TIM_TimeBaseStructure.TIM_Period = (100-1); TIM_TimeBaseStructure.TIM_Prescaler = (42000-1); TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_Cmd(TIM1, ENABLE);}int main(void){ /* Initialise system */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //4 bits for pre-emption priority 0 bits for subpriority /* Initialise peripherals */ gpio_cfg(); pwm_cfg(); while(true) { if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) { GPIO_SetBits(GPIOA, GPIO_Pin_1); pwm_start(); delay(500); //blocking delay, approx 500ms with Keil and no code optimisation GPIO_ResetBits(GPIOA, GPIO_Pin_1); } }} #stm32f4 #tim1 #pwm2016-11-30 12:27 AM
Try setting the TIMx_CNT just below the ARR value. This will impose a prescaler-given delay, though (that can be reduced but in your case, given 16-bit timer, not eliminated entirely). It could be eliminated though with a slightly elaborate preparing 0 to the real prescaler by loading it with a forced update, and then setting the required value into the shadow while the timer is stopped.
Another option is to change the TIMx_CHy output from GPIO OUT to AF at the moment of timer start. JW2016-11-30 10:08 AM
Hi robert,
I recommend that you check the example “NpulseWave_TIM_Sync� inside the “� package of the application note .-Hannibal-
2016-12-22 06:28 PM
,
,
Greetings,
Firstly, thanks for the suggestions. I had a look though the above references but in the end I decided implement it using a free-running timer and a timer compare interrupt. It's not as elegant as the PWM method but is adequate for the task at hand (the min time between setting and clearing the pulse in my case is 392us, something the ,STM32F4 at 168MHz can do without issue). There is also a delay of 0.78us between the trigger and the output of pulses, which is fine for my application.
In case anyone is interested, here's the code to get it going (changed from TIM1 ->, TIM5, also generates pulses at a faster rate):
♯ include 'stm32f4xx.h'
,
♯ include <,stdbool.h>,void delay(uint32_t delay)
,
{,
, uint32_t i,,
, while(delay--),
, {,
, , for(i = 0, i <, 30000, i++),
, , , __asm__('nop'),,
, },
}void gpio_cfg(void)
,
{,
, GPIO_InitTypeDef GPIO_InitStructure,,
, GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8,,
, GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT,,
, GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL,,
, GPIO_Init(GPIOA, &,GPIO_InitStructure),,
,
, /* Setup PA1 as sync */,
, GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1,,
, GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT,,
, GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL,,
, GPIO_Init(GPIOA, &,GPIO_InitStructure),, /* Setup push button */
,
, GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0,,
, GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN,,
, GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL,,
, GPIO_Init(GPIOA, &,GPIO_InitStructure),,
},
uint16_t _pulse_us, _period_us,,
volatile uint8_t _pulses,,
volatile bool _pulse_set,void pulse_cfg(void)
,
{,
, TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure,,
, NVIC_InitTypeDef NVIC_InitStructure,,
, /* Enable TIM5 Interrupt */,
, NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn,,
, NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4,,
, NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0,,
, NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE,,
, NVIC_Init(&,NVIC_InitStructure),,
, /* TIM5 clock enable */,
, RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE),,
, /* Time base configuration */,
, TIM_TimeBaseStructure.TIM_Period = 65535,,
, TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1, //Prescale clock to generate 1MHz,
, TIM_TimeBaseStructure.TIM_ClockDivision = 0,,
, TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up,,
, TIM_TimeBaseInit(TIM5, &,TIM_TimeBaseStructure),,
, TIM_ITConfig(TIM5, TIM_IT_CC1, DISABLE),,
, /* TIM5 enable counter */,
, TIM_Cmd(TIM5, ENABLE),,
}void pulse_start(uint8_t pulses, uint16_t pulse_us, uint16_t period_us)
,
{,
, _pulse_us = pulse_us,,
, _period_us = period_us - pulse_us,,
, _pulses = pulses,,
, _pulse_set = false,,
,
, TIM_ITConfig(TIM5, TIM_IT_CC1, ENABLE),,
, TIM_GenerateEvent(TIM5, TIM_EventSource_CC1),,
}void TIM5_IRQHandler(void)
,
{,
, if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET),
, {,
, , if(pulse),
, , {,
, , ,_pulse_set
= false,,
, , , GPIO_ResetBits(GPIOA, GPIO_Pin_8),,
, , , TIM_SetCompare1(TIM5, (uint16_t) (TIM_GetCounter(TIM5) + _period_us)),,
,
, , , if(_pulses),
, , , {,
, , , , _pulses--,,
, , , , if(_pulses == 0),
, , , , {,
, , , , , TIM_ITConfig(TIM5, TIM_IT_CC1, DISABLE),,
, , , , },
, , , },
, , },
, , else,
, , {,
, , , _pulse_set = true,,
, , , GPIO_SetBits(GPIOA, GPIO_Pin_8),,
, , , TIM_SetCompare1(TIM5, (uint16_t) (TIM_GetCounter(TIM5) + _pulse_us)),,
, , },
,
, TIM_ClearITPendingBit(TIM5, TIM_IT_CC1),,
, },
}int main(void)
,
{,
, /* Initialise system */,
, RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE),,
, RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE),,
, NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4), //4 bits for pre-emption priority 0 bits for subpriority,
,
, /* Initialise peripherals */,
, gpio_cfg(),,
, pulse_cfg(),,
,
, while(true),
, {,
, , if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)),
, , {,
, , , GPIO_SetBits(GPIOA, GPIO_Pin_1),,
, , , pulse_start(10, 300, 1000), //Send 10 pulses at 1kHz, 30% duty,
, , , delay(500),,
, , , GPIO_ResetBits(GPIOA, GPIO_Pin_1),,
, , },
, },
}