Teste semf
Kontakt

Timing – TimeBase & Timers

General

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

For using other hardware timers, you can calculate the update frequency by this term:

You can calculate the timer’s update frequency by this term: Update Frequency = TIM_CLOCK / ((Prescaler + 1) * (Period +1))

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

  • Timer: Will be emited 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 by the following steps:
    • Open a timer configuration.
    • Set Internal Clock option to Clock Source.
    • Complete the Counter Settings in Parameter Settings (see above).
    • Enable TIMx update interrupt or Global interrupt in NVIC Settings.
  • OutputCompare: Will be emited by the Output Compare Elapsed Interrupt. OutputCompare can be used for controlling GPIOs, too.
    Using Stm32CubeMx you can enable the timer by the following steps:
    • Open a timer configuration.
    • Set Internal Clock option to Clock Source.
    • Set Channel x to Output Comare No Output.
    • Complete the Counter Settings in Parameter Settings (see above).
    • Enable TIMx break interrupt or Global interrupt in NVIC Settings.

TimeBase multiplicates a hardware timer’s timeout signal to multiple SoftwareTimers. In the following example we use the Systick of a STM32F4 microcontroller to debounce a button, pulsing a led by two SoftwareTimers and have a third one for toggeling a digital output.

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

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

Timer is the mother of all timing classes.
Timer interface diagramm (simplified version)
TimeBase can handle multiple TickReceiver, controlled one Timer
TimeBase class diagramm (simplified version)

Timing Example

Timing is critical on embedded devices. This example shows you how to configure timers correctly and being sure creating no bad side effect.

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 in couse 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 CriticalSection.

For getting no trouble by writing and reading at the same time, you can use a CriticalSection in the setter method.

Timing of multiple tasks with different priorities

Bsp

        #include <HardwareAbstraction/Stm32F7/stm32f7analogout.h>
#include <HardwareAbstraction/Stm32F7/stm32f7criticalsection.h>
#include <HardwareAbstraction/Stm32F7/stm32f7systick.h>
#include <HardwareAbstraction/Stm32F7/stm32f7timer.h>
#include <System/timebase.h>

class Bsp
{
public:
	static Bsp& instance()
	{
		static Bsp bsp;
		return bsp;
	}

	void init()
	{
		m_timer100us.start();
		m_timeBases.timeBase100us.setEnabled();
		m_timeBases.timeBase1ms.setEnabled();
	}

	struct TimeBases
	{
		semf::TimeBase timeBase100us;
		semf::TimeBase timeBase1ms;
	};

	TimeBases& timeBases()
	{
		return m_timeBases;
	}

	struct Analog
	{
		semf::Stm32F7AnalogOut analogOut;
	};
	Analog& analog()
	{
		return m_analogs;
	}

private:
	Bsp()
		:m_timer100us(htim1),
		 m_timeBases
		 {
			semf::TimeBase(m_timer100us, false),  // timeBase100us
			semf::TimeBase(semf::Stm32Systick::instance(), false)  // timeBase1ms
		 },
		 m_analogs
		 {
			semf::Stm32F7AnalogOut(hdac, 1, 
				semf::Stm32F7AnalogOut::Alignment::ALIGN_8BIT_RIGHT)
		 }
	 {

	 }

	semf::Stm32F7Timer m_timer100us;
	semf::Stm32F7CriticalSection m_criticalSection;
	TimeBases m_timeBases;
	AnalogComps m_analogs;
};

ValueX

#include <Interfaces/criticalsection.h>

class ValueX
{
public:
	uint16_t value() const
	{
		return m_value;
	}

	void setValue(uint16_t value)
	{
		// Write access is secured by critical section
		semf::CriticalSection::enter();
		m_value = value;
		semf::CriticalSection::exit();
	}

private:
	uint16_t m_value = 0;
};

App

#include <System/softwaretimer.h>
#include "valuex.h"
#include "bsp.h"

namespace app
{
class App
{
public:
	static App& instance();
	void init();
	void mainLoop();

private:
	App()
	:m_timer300us(Bsp::instance().timeBases().timeBase100us, 3, true),
	 m_timer100ms(Bsp::instance().timeBases().timeBase1ms, 100, true),
	 m_timer2ms(Bsp::instance().timeBases().timeBase1ms, 2, true)
	{
		m_timer300us.timeout.connect(this, &App::mainTask);
		m_timer100ms.timeout.connect(this, &App::calculationOfA);
		m_timer2ms.timeout.connect(this, &App::calculationOfB);
	}

	void mainTask()
	{
		// do something
		Bsp::instance().analog().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;
	ValueX m_a;
	ValueX m_b;
};

Initialization TimeBase

#include "HardwareAbstraction/Stm32/stm32f4systick.h"
#include "Components/System/timebase.h"
#include "Components/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 "HardwareAbstraction/Stm32/stm32f4gpio.h"
#include "Components/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 inputChangedToHighSlot() 
{ 
	// do something 
} 

void inputChangedToLowSlot() 
{ 
	// do something 
} 

digIn.changedToHigh.connect(inputChangedToHighSlot); 
digIn.changedToLow.connect(inputChangedToLowSlot);

Initialization LedBlinking

#include "Components/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);

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 userTimerTimeout()
{
	// do something 
}
userTimer.timeout.connect(&userTimerTimeout);

// 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();

zurück zur Dokumentation