cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 TIM1 One Shot Mode configuration

rcstang
Associate
Posted on November 29, 2016 at 23:27

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 #pwm
3 REPLIES 3
Posted on November 30, 2016 at 09:27

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.

JW
Walid FTITI_O
Senior II
Posted on December 23, 2016 at 02:28

 ,

 ,

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),

 ,

 ,  , }

 ,

 , }

 ,

}