CTL High resolution timer

Comments

7 comments

  • Avatar
    Michael Freeman

    Use the hardware timers to do these kind of things. They can even output your square ware without the need of any code. Read up on the timers for your chip you find they can do all. Always let the hardware do the work.

     

    Michael Freeman

    0
    Comment actions Permalink
  • Avatar
    Jolyon Latham

    Hi Michael,

    Thanks for the fast response, much appreciated.

    My experience thus far has always been using OS provided timers, either CTL (ms) or OS-20 (15xxx ticks persecond). The OS20 tick was always fine enough. My concern is using the hardware clocks and knocking CTL out of kilter. Also since I have not used them directly before I have no sample code, I don't suppose you could point me to a code snippet I could work from?

    I have the "Definitive Guide To The Arm Cortex-M3" so hopefully I can find the answer in there.

    Another thought I had is whether or not it would be possible to increase the frequency of SysTickHandler and use a counter to call a newly written high-resolution ISR and call the CTL ISR every ms?

    Thanks for your help,

    Jo

    0
    Comment actions Permalink
  • Avatar
    Wolfgang Hill

    What flavour of Cortex -M3 are you using? Is it STM32? If so that's what we use and my advice would be to leave the SysTick purely for CTL and use the hardware timers. There should be plenty of timer peripherals whatever uP it is, and the SysTick is designed for just this purpose. Using the hardware clcoks won't knock CTL out of kilter, unless you do something REALLY bad (like write an ISR that takes 10's of msec to execute).

    We have implemented a Delay(usec) function that uses TIM1 of the STM32 and would be happy to provide some sample code snippets (without giving away my companies IP of course) if that would help

    Cheers,

    Wolf

    0
    Comment actions Permalink
  • Avatar
    Jolyon Latham

    Hi Wolf,

    Thanks for the response. The CPU variant is the STM32F103. A code snippet would be great to help me with the validation of this prototype board whilst I gem up on using the hardware timers.

    Much appreciated,

    Jo

    0
    Comment actions Permalink
  • Avatar
    Wolfgang Hill

    No probs.

    This code sets up TIM1 as a free-running 1MHz timer. I have used Rowley's standard register & bit definitions from their STM32F10x.h header (supplied with the CPU support package). Note that this assumes you are running TIM1CLK clock at 36MHz (which you probably are if you're running at 72MHz).

    The Delay() function has a maximum limit of 1/2 the maximum of the counter register (so 32767 for a 16-bit counter). This avoids any nasty wrap-around problems.

    Note that this doesn't actually use up any of the TIM1 CCR's. We also use TIM1's CCR2 to trigger ADC scanning (at a rate of 1KHz) and CCR1 to trigger our TTCAN implementation. It's a bit complicated in that you can't get a 1KHz square wave by just setting a CCR to 1000 - you have to reload it in the ISR with 2000, 3000, 4000, etc, but it means we can use 1 timer for quite a few things at once.

    Enjoy.

    Wolf

     

    //Initialisation:
    #define TIMER_FREQUENCY     1000000     // 1MHz

    void Init()
    {
        // Enable Peripheral Clock
        RCC_APB2ENR |= RCC_APB2ENR_TIM1EN;
        // De-init TIM1 peripheral registers to their default reset values.
        RCC_APB2RSTR = RCC_APB2RSTR_TIM1RST;
        RCC_APB2RSTR = 0;

        // Clock Division = 0
        // Auto-Reload Preload = NOT buffered
        // Edge-Aligned mode
        // Direction = UP
        // One Pulse Mode = Off
        // Update Request can be anything
        // Update Event = Enabled
        // Counter Disabled (for now)
        TIM1_CR1 = 0;

        // Free-Running Period
        TIM1_ARR = 0xFFFFu;
        // Time-Base configuration - Scale to fixed Frequency of 1MHz
        TIM1_PSC = (36000000 / TIMER_FREQUENCY) - 1;
        // No Repetition Counter
        TIM1_RCR = 0;

        // Force an update to load our ARR and Pre-Scalar shadow registers
        TIM1_EGR = TIM1_EGR_UG;
        // Enable the Counter
        TIM1_CR1 |= TIM1_CR1_CEN;
    }
       
    // Blocking usec delay function. Designed to be re-entrant.
    #define MAX_DELAY   INT16_MAX       // 32767 usec Max
    void Delay(uint16 usec)
    {
        uint16 timeout = TIM1_CNT;

        // Conversion = (usec / 1MHz) * Freq
        if (usec <= MaxDelay)
        {
            timeout += (uint16)(((uint32)usec * (TIMER_FREQUENCY / 1000uL)) / 1000uL);
        }
        else
        {
            timeout += (uint16)(((uint32)MAX_DELAY * (TIMER_FREQUENCY / 1000uL)) / 1000uL);
        }
        // This method provides very simple wrap-around handling, BUT limits the
        // range to half of the available bits (due to the cast from unsigned to
        // signed). For this function, we don't care about getting the full 16-bit
        // range so simpler is better.
        while ((int16)(timeout - TIM1_CNT) >= 0)
            ;
    }

    0
    Comment actions Permalink
  • Avatar
    Jolyon Latham

    Thankyou very much Wolf, this is most helpful. I started investigating timers yesterday since I had moved on to the ADC and realised I would need a timer to trigger a conversion. This snippet will help me greatly, thankyou for taking the time to help me out!

    Jo

    0
    Comment actions Permalink
  • Avatar
    Wolfgang Hill

    No worries. Happy to help.

    One thing to watch out for - if you want to use TIM1 to trigger the ADC, you need to have the Main Output enabled (MOE bit of BTDR register). You don't need to bring the CCR all the way out to it's I/O pin, but you do need the MOE on (took me ages to figure that out!).

    I.E. at the end of your initialisation function, add this...

        // TIM1 Main Output Enable (allows ADC to trigger)
        TIM1_BDTR |= TIM1_BDTR_MOE;

    Cheers and Good Luck!

    Wolf

    0
    Comment actions Permalink

Please sign in to leave a comment.