WBuck
WBuck

Reputation: 5501

Hardware timer instantly completes when first used. STM32F303VC

I'm attempting to setup and use the TIM2 peripheral in one pulse mode (OPM) on the STM32F303 Discovery board.

The issue I'm encountering is that the timer instantly completes after enabling it.

I'm not using interrupt's at this point and I'm just polling the TIM2_SR(status register) UIF bit to determine if the timer has completed.

This only happens the first time I enable the timer, if I use the timer again it works correctly (does not complete instantly).

I've tried resetting the TIM2_CNT register prior to enabling the timer but the result is the same.

use cortex_m_rt::entry;
use stm32f3xx_hal::pac;


#[entry]
fn main( ) -> ! {
    let p = pac::Peripherals::take( ).unwrap( );

    p.RCC.apb1enr.modify( | _, w | w.tim2en( ).set_bit( ) );

    p.TIM2.cr1.write( | w | w
        .urs( ).set_bit( )
        .opm( ).set_bit( )
        .cen( ).clear_bit( ) );

    // I've tried resetting the CNT register at this point
    // in the application but the result was the same. 
    
    // Set the prescaler based on an 8MHz clock.
    p.TIM2.psc.write( | w | w.psc( ).bits( 7999 ) );

    // Here I initialize an LED (GPIOE). I've removed this code to 
    // keep the example as clean as possible.

    let delay = | duration | {         

        p.TIM2.arr.write( | w | w.arr( ).bits( duration ) ); 

        // I've also tried resetting the CNT register here 
        // but the result was the same.       
        p.TIM2.cr1.modify( | _, w | w.cen( ).set_bit( ) );

        while p.TIM2.sr.read( ).uif( ).bit_is_clear( ) { }

        p.TIM2.sr.write( | w | w.uif( ).clear_bit( ) );
    };

    // Enable LED.

    // This call instantly returns.
    delay( 3999 );

    // Disable LED.

    loop { }
}

The example above gets the LED to flicker on and off with little to no delay. If I instead use an endless loop the timer works as intended after the initial call to delay.

loop {

    // Enable LED.

    // The first call in the first loop iteration
    // returns instantly.
    delay( 3999 );

    // Disable LED.
    
    // This call, and every call here after correctly
    // returns after 4 seconds.
    delay( 3999 );
}

I've examined the registers while the application is running and everything appears to be set up correctly.

After reading a similar issue on a different forum it was suggested in that answer to enable the URS bit in the TIM2_CR1 register, which causes an update interrupt/DMA request to only be issued on counter overflow/underflow. This of course did not work.

I get the sense that there is a bit somewhere that I need to reset/set in order to get the timer to function as expected the first time I enable it.

Upvotes: 1

Views: 401

Answers (1)

WBuck
WBuck

Reputation: 5501

OK, so after re-reading the section on timers in the manual I found the following:

The new prescaler ratio is taken into account at the next update event.

OK, so how can I generate an update event via software in order for the prescaler value to get updated prior to starting the timer. That's when I found this (emphsis mine):

In upcounting mode, the counter counts from 0 to the auto-reload value (content of the TIMx_ARR register), then restarts from 0 and generates a counter overflow event. An Update event can be generated at each counter overflow or by setting the UG bit in the TIMx_EGR register (by software or by using the slave mode controller).

So after setting the prescaler value I then set the UG bit in the TIM2_EGR register.

// Set the prescaler based on an 8MHz clock.
p.TIM2.psc.write( | w | w.psc( ).bits( 7999 ) );

// Generate an update event so that the new prescaler
// value gets loaded.
p.TIM2.egr.write( | w | w.ug( ).update( ) );

After forcing an update the timer started to work correctly every time (including the first time).

So, to sum up my issue RT*M...

Upvotes: 0

Related Questions