Embedded C/C++: Software-Timer für das Arduino RTOS

Timing ist alles - Teil 4

In Teil 3 ist das selbstgebaute Betriebssystem schon so weit gelangt, dass damit erste Anwendungen realisierbar werden. Verschiedene Tasks können , gesteuert durch das Betriebssystem, in unterschiedlicher zeitlicher Abfolge aufgerufen und abgearbeitet werden. Was jedoch, wenn eine einzige Task selbst verschiedene, zeitabhängige Aufgaben ausführen soll?

Software-Timer


Ein Sw-Timer ist zunächst nichts anderes als eine Variable, hier timer0. Dieser timer0 wird nun mit einem Startwert geladen und anschließend bei jedem Aufruf der Task durch das Betriebssystem um 1 dekrementiert. Sobald timer0 gleich 0 ist, wird die Aufgabe innerhalb der Task ausgeführt und anschließend timer0 erneut mit einem Startwert geladen.

Der Startwert ergibt sich aus der gewünschten Ausführungsperiode exeT0 dividiert durch die Aufrufperiode der Task taskTime_ms. Von diesem Quotienten muss 1 subtrahiert werden, weil der C-Programmierer für z.B. 10 Sekunden von 9 bis 0 rückwärts zählt...

Wenn ein Software-Timer nicht genügt


Werden mehr als ein Sw-Timer von der Anwendung benötigt, dann lassen sich diese günstigerweise in Listen bzw. Arrays organisiert. Bei einigen duzend Sw-Timern innerhalb einer Task wird es dann jedoch für den Anwendungsentwickler recht unübersichtlich.

Ordnung könnte dann z.B. durch einen Aufzählungstypen geschaffen werden:

enum SwTimer { fooTimer, barTimer, anyTimer };

...

uint16_t exeT[3];

exeT[fooTimer] = 1000;

...

static uint16_t timer[3]

timer[fooTimer] = (exeT[fooTimer]/taskTime) - 1;

...

Notwendige Anpassungen des Betriebssystems


Der Task muss vom Betriebsystem die Taskzykluszeit taskTime_ms zur Verfügung gestellt werden. taskTime_ms ist zwar ein Element der task_descriptor-Tabelle, steht aber als solches nicht von "außen" zugreifbar zur Verfügung. Es wäre möglich gewesen das mit Hilfe einer komplexen Betriebssystemfunktion zu realisieren (Suche nach der Task-Adresse in der task_descriptor-Tabelle und Rückgabe von taskTime_ms). Wenngleich 'cool' bzw. elegant, wäre das in Anbetracht der knappen Speicherressourcen des Arduino R3 sehr ineffizient. Stattdessen wurde die Signatur einer Task leicht modifiziert:

uint8_t my_task ( uint16_t taskTime_ms )


Was tun, wenn nun ein Intervall > 65535 ms benötigt wird? Solange das Betriebsystem über keine Echtzeituhr verfügt, ist der Betrieb eines Schedulers zu diesem Zweck nicht möglich. Bis es soweit ist, könnte man bei sehr langen Intervallen z.B. zwei Sw-Timer verschachteln:


if ( api_sw_timer_expire ( &timer[0], 60000 / 1000 ) )

{

if ( api_sw_timer_expire ( &timer[1], 5 ) )

ein_fuenf_minuten-Ei();

}


Zuletzt, die Funktion api_sw_timer_expire ( uint16_t*, uint16_t ) wurde in api.hpp ausgelagert, weil weder ein Element des Betriebssystems noch der Applikation/Task.

Den Code zum Blog gibt es hier:

Zing • 9. Januar 2026