semf
TimerAndTimeBase

In this tutorial we care about hardware abstracted semf::app::Timer. In most of the applications a lot of semf::SoftwareTimer are needed for polling and debouncing DigitalIns or caring about timeouts or led pulsing. For all these use cases typically a 1ms semf::app::Timer / Systick is used. With semf we have the possibility to handle unlimitied semf::SoftwareTimer with the same elapsing time periode in one semf::app::TimeBase object.

When using other hardware semf::app::Timer you can calculate the update frequency by this term:

‍Update Frequency = TIM_CLOCK / ((Prescaler + 1) * (Period +1)).

There are two ideas behind the classes in the HardwareAbstraction, semf::app::Timer and OutputCompare:

  • semf::app::Timer
    Will be emitted by the Timer Period Elapsed Interrupt. Systick is a special form of a timer available in most microcontroller families and enabled by default (no configuration necessary).
    Using Stm32CubeMx you can enable the timer with the following steps:
    1. Open a timer configuration.
    2. Set Internal Clock option to Clock Source.
    3. Complete the Counter Settings in Parameter Settings (see above).
    4. Enable TIMx update interrupt or Global interrupt in NVIC Settings.
  • OutputCompare
    Will be emitted by the Output Compare Elapsed Interrupt. OutputCompare can be used for controlling GPIOs too.
    Using Stm32CubeMx you can enable the timer with the following steps:
    1. Open a timer configuration.
    2. Set Internal Clock option to Clock Source.
    3. Set Channel x to Output Comare No Output.
    4. Complete the Counter Settings in Parameter Settings (see above).
    5. Enable TIMx break interrupt or Global interrupt in NVIC Settings.

semf::app::TimeBase multiplicates a hardware timer’s timeout signal to multiple semf::SoftwareTimer. In the following example we use the Systick of a STM32F4 microcontroller to debounce a button, pulse a LED with two semf::SoftwareTimer and have a third one for toggling a digital output.

For example in the initialization phase all software timers registered in a semf::app::TimeBase should be disabled. For such purposes TimeBases can be enabled and disabled by calling the function setEnable(true / false).

To add SoftwareTimers to a TimeBase the add() function and for removing a SoftwareTimer from a TimeBase the remove() function can be called.

Timer interface diagramm (simplified)
TimeBase class diagramm (simplified)

Timing Example

Timing is critical on embedded devices. This example shows how to configure timers correctly.

We use two hardware timers and one DAC. The fist hardware timer is configured with a 100us frequency and with NVIC priority 0. For the second one we use the 1ms SYSTICK with the lower priority 1.

The main task has the highest priority because Timer1 has the highest priority 0. The calculation of the value A is done every 100ms and the calculation of value B is done every two milliseconds. In the setter of the values A and B, we use a semf::CriticalSection.

To prevent trouble trying to write and read at the same time you can use a semf::CriticalSection in the setter method.

Timing Example with CriticalSection

Msp

#include "semf/hardwareabstraction/stm32f7/stm32f7analogout.h"
#include "semf/hardwareabstraction/stm32f7/stm32f7criticalsection.h"
#include "semf/hardwareabstraction/stm32f7/stm32f7systick.h"
#include "semf/hardwareabstraction/stm32f7/stm32f7timer.h"
namespace msp
{
class Msp
{
public:
Msp()
:m_timer100us(htim1),
m_analogOut(hdac, 1, semf::Stm32F7AnalogOut::Alignment::Align8BitRight)
{
}
explicit Msp(const Msp& other) = delete;
virtual ~Msp() = default;
void init()
{
m_timer100us.start();
}
semf::Timer& timer100us()
{
return m_timer100us;
}
semf::Timer& systick1ms()
{
return semf::Stm32F4SysTick::instance();
}
semf::AnalogOut& analogOut()
{
return m_analogOut;
}
private:
semf::Stm32F7Timer m_timer100us;
semf::Stm32F7AnalogOut m_analogOut;
semf::Stm32F7CriticalSection m_criticalSection;
};
} // namespace msp
Interface for using DAC (Digital to Analog Converter) hardware module in interrupt mode.
Definition: analogout.h:23

Bsp

#include "msp.h"
namespace bsp
{
class Bsp
{
public:
explicit Bsp(msp::Msp& msp)
:m_timeBase100us(msp.timer100us()),
m_timeBase1ms(msp.systick1ms())
{
}
explicit Bsp(const Bsp& other) = delete;
virtual ~Bsp() = default;
semf::app::TimeBase& timeBase100us()
{
return m_timeBase100us;
}
semf::app::TimeBase& timeBase1ms()
{
return m_timeBase1ms;
}
private:
semf::TimeBase m_timeBase100us;
semf::TimeBase m_timeBase1ms;
};
} // namespace bsp
A TimeBase is the bridge between e.g. a hardware timer (interrupt service routine) and TickReceiver o...
Definition: timebase.h:24
A TimeBase is the bridge between e.g. a hardware timer (interrupt service routine) and TickReceiver o...
Definition: timebase.h:36

ValueX

namespace app
{
class ValueX
{
public:
uint16_t value() const
{
return m_value;
}
void setValue(uint16_t value)
{
// Write access is secured by critical section
m_value = value;
}
private:
uint16_t m_value = 0;
};
} // namespace app

App

#include "semf/system/softwaretimer.h"
#include "valuex.h"
#include "bsp.h"
#include "msp.h"
namespace app
{
class App
{
public:
App(bsp::Bsp& myBsp, msp::Msp& myMsp)
:m_timer300us(myBsp.timeBase100us(), 3, true),
m_timer100ms(myBsp.timeBase1ms(), 100, true),
m_timer2ms(myBsp.timeBase1ms(), 2, true),
m_analogOut(myMsp.analogOut())
{
m_timer300us.timeout.connect(m_timer300usSlot);
m_timer100ms.timeout.connect(m_timer100msSlot);
m_timer2ms.timeout.connect(m_timer2msSlot);
}
void init();
void mainLoop();
private:
void mainTask()
{
// do something
m_analogOut.setValue(m_a.value() + m_b.value());
}
void calculationOfA()
{
// do something
m_a.setValue(1); // in real live, you have a more realistic value
}
void calculationOfB()
{
// do something
m_b.setValue(100); // in real live, you have a more realistic value
}
semf::SoftwareTimer m_timer300us;
semf::SoftwareTimer m_timer100ms;
semf::SoftwareTimer m_timer2ms;
semf::AnalogOut& m_analogOut;
ValueX m_a;
ValueX m_b;
SEMF_SLOT(m_timer300usSlot, App, *this, mainTask);
SEMF_SLOT(m_timer100msSlot, App, *this, calculationOfA);
SEMF_SLOT(m_timer2msSlot, App, *this, calculationOfB);
};
Software SoftwareTimer which can be added to a TimeBase or used singular.
Definition: softwaretimer.h:29
#define SEMF_SLOT(name, className, object, function,...)
Creates a semf slot.
Definition: slot.h:30

Initialization TimeBase

#include "semf/system/softwaretimer.h"
// Microcontroller pin definitions are stored in 'main.h'.
#include "main.h"
// Configure a timebase on basis of 1ms systick timer and enable it
semf::TimeBase timebase1ms(semf::Stm32F4Systick::instance(), true);

Initialization DebouncedDigitalInPolling

#include "semf/input/debounceddigitalinpolling.h"
semf::Stm32F4Gpio gpioButton(GPIOA, GPIO_PIN_1);
// Configure the debounced digital in
// debounced low time: 30ms
// debounced high time: 50ms
semf::DebouncedDigitalInPolling digIn(gpioButton, timebase1ms, 30, 50, false);
void onInputChangedToHigh()
{
// do something
}
void onInputChangedToLow()
{
// do something
}
semf::StaticSlot<> m_onInputChangedToHighSlot(onInputChangedToHigh);
semf::StaticSlot<> m_onInputChangedToLowSlot(onInputChangedToLow);
digIn.changedToHigh.connect(m_onInputChangedToHighSlot);
digIn.changedToLow.connect(m_onInputChangedToLowSlot);
Class for reading and debouncing a digital input (e.g. used for buttons or relays inputs)....
StaticSlot for lightweight signal/slot implementation. This StaticSlot ist for connecting a signal to...
Definition: staticslot.h:27

Initialization LedBlinking

#include "semf/outout/ledblinking.h"
// Setup the digital out, not inverted
semf::Stm32L0Gpio gpioLed(GPIOA, GPIO_PIN_2);
// Configure the led output with 1s off and 3s on time
semf::LedBlinking led(gpioLed, timebase1ms);
led.setBlinking(3000, 1000);
Interface for a blinking led class.
Definition: ledblinking.h:22

Initialization user specific SoftwareTimer

// Setup the software timer with interval 100 (ms) and disable it.
semf::SoftwareTimer userTimer(timebase1ms, 100, false);
// All timers have a timeout signal which can be connected to a
// usecase specific slot.
void onUserTimerTimeout()
{
// do something
}
semf::StaticSlot<> m_onUserTimerTimeoutSlot(onUserTimerTimeout);
userTimer.timeout.connect(m_onUserTimerTimeoutSlot);
// Now we can start the timer
userTimer.start();
// Later we want to change the interval to 250ms
userTimer.stop();
userTimer.reset();
userTimer.setInterval(250);
userTimer.start();