TIMER (JSAPI_TIMER)

Timers are another basic peripheral offered by most MCUs. ST defines four set of timers, as listed below. There are 17 timers, but only a subset is available on each device. The same timer name (e.g. TIM2) may have different functions, depending on the device. The exception seems to be TIM1, which seems to always be the Advanced Control timer.

1.       16-bit Advanced Control, example: TIM1

2.       16-bit General Purpose, examples: TIM3, TIM4, TIM14, TIM15, TIM16, TIM17

3.       32-bit General Purpose, examples: TIM2, TIM5

4.       16-bit Basic, examples: TIM6, TIM7, TIM9, TIM10, TIM11

Again, be sure to check the device datasheet to confirm which timers exist for the device and what features they have. Remember that a 16-bit timer rolls over every 216 cycles, whereas a 32-bit time rolls over every 232 cycles.

Basic timers only have the basic features of up or down counters. General-purpose and advanced control timers support additional features such as PWM, input capture, etc.

Object

extern JSAPI_TIMER timer1, timer2, timer3, timer4, timer5, timer6, timer7, timer8, timer9, timer10, timer11, timer12, timer13, timer14, timer15, timer16, timer17;

Enum

enum EDGE_TRIGGER {

    RISING_EDGE = 1,

    FALLING_EDGE,  

    BOTH_EDGES     

};

Create a basic timer

void MakeTimer(

    unsigned rollover_Hz,     // frequency of the timer

    void (*isr)(void),  // interrupt handler to call

                        // when the timer rolls over

    int priority        // ARM priority level

);

Create a timer with finer control

void MakeTimerWithPrescaler(

    unsigned PSC,       // PSC timer register value

    unsigned ARR        // ARR timer register value

);

Make a PWM timer

int MakePWM(

    int chan_no,

    unsigned rollover_Hz,

    unsigned duty_cycle_in_percent

);

Add a PWM channel

int AddPWM(

    int chan_no,

    unsigned rollover_Hz,

    unsigned duty_cycle_in_percent

);

Set/clear rollover ISR

void SetRolloverISR(

    void (*isr)(void),

    int priority

);

Return timer frequency

unsigned GetClockFrequency(void);

Read timer’s CNT register

unsigned ReadTimer(void);

Reset the timer

void ResetTimer(void);

Enable the timer

void Enable(void);

Disable the timer

void Disable(void);

STM32FF3xx/STM32F4xx/STM32F7xx Series

Perform input capture

int InputCapture(

    int chan_no,

    unsigned ICxF,

    unsigned ICxPSC,

    enum EDGE_TRIGGER edgetype,

    unsigned *captured_value  // [out] captured value

);

Perform Output Compare

int OutputCompare(

    int chan_no,

    unsigned CCR,

    bool idle_high,

    unsigned OCxMode

);

Generate a one-shot pulse

int OneShot(

    int chan_no,

    unsigned CCR,

    bool active_high,

    bool stop_clock

);

 

How to Use

To use the JumpStart Timer API, with a JSAPI_TIMER object (e.g. timer1):

1.       If using a timer for PWM, input capture, or output compare, you should associate GPIO pins with the timer functions by calling the MakeAltFunction function of the JSAPI_GPIO object.

2.       Create the timer function by calling one of the timer’s “Make” functions, e.g. MakeTimer, MakePWM, etc.

Example 1: Simple Rollover Timer

 

void ToggleLED(void)

    {

    led.Toggle();

    putchar('.');

    }

 

    // in main.c

    porta.MakeOutput(LED_PIN, OSPEED_LOW);

    led.MakeIOPin(&porta, LED_PIN);

 

    timer1.MakeTimer(1, ToggleLED, 0);

    while (1)

        ;

 

This code fragment sets up Timer1 with a one Hz rollover rate, or once a second. At each rollover event, the interrupt handler ToggleLED is called to toggle the LED on the ST Nucleo board. A one second blinking rate is easy to see visibly. If the blink rate is too fast, it may not be visible.

Example 2: Generating a PWM

It’s simple to generate a basic PWM (Pulse Width Modulation):

    printf("Use a logic analyzer to check the signal at PA.8\n");

 

    timer1.MakePWM(1, 1, 30);

    porta.MakeAltFunction(8, 1, OSPEED_HIGHEST);

 

This generates a one-Hertz waveform with a duty cycle of 30%, i.e. active high is 0.3 of a second, and the active low is 0.7 of a second. The output wave should look like this:

image

 

Example 3: Input Capture

ST MCUs have very advanced and complex-to-use output compare and input capture features. JumpStart API provides simple API functions sufficient for one’s basic needs.

Here’s the setup code:

    jsapi_cortex_core.SysTick_TimerAddHook(generatePulse);

   

    // timer setup

    timer1.MakeTimerWithPrescaler(8400, 0xFFFFu);

    // PORTA.8 set for Timer Input Capture alternate function

    porta.MakeAltFunction(8, 1, OSPEED_HIGHEST);

 

As with other examples, the board is configured to run at 84MHz. The function generatePulse is added as a hook function to the SysTick timer, and is written to generate a 90-millisecond pulse. To run the program, you must connect PortB.10 to PortA.8.

The timer register PSC divides the timer input clock to drive the timer counter. In this example (and by default in JumpStart API) the F411 timer clock is the HCLK, or the AHB clock, which is the same as the system clock, or 84MHz in this case (note that other ST MCU series have different timer clock sources and different clock speeds for AHB).

To simplify the example, we set PSC (the first argument to the MakeTimerWithPrescaler function) to 8400, so that the timer has an effective rate of 84000000 / 8400 = 10000 cycles per second. The reason 8400 was chosen is that PSC is a 16-bit register, so its maximum value is 65535. This means that each count with the timer is 100 microseconds.

This is the actual code for input capture:

            unsigned start, stop;

            if (timer1.InputCapture(1, 0, 0, RISING_EDGE, &start) < 0) printf("bad val\n");

            if (timer1.InputCapture(1, 0, 0, FALLING_EDGE, &stop) < 0) printf("bad val2\n");;

            printf("pulse width (should be ~900)  %u-%u = %u\n", stop, start, stop-start);

 

Two input capture calls are made to capture the starting edge and the trailing edge. As the pulse is 90 milliseconds wide, the difference should be 900 count, as each count is 100 microseconds.