Forum: Compiler & IDEs elegante Timernutzung


von flyingwolf (Gast)


Lesenswert?

Hallo Leute.
ich habe einen interruptgesteuerten Timer, den ich für die Abhandlung
aller zeitabhängigen Größen in meinem Programm verwenden möchte. Bisher
handhabe ich das so dass alle Zeitvariablen global definiert sind und in
der Interruprroutine der Reihe nach in dieser Form abgearbeitet werden.
if (nokey > 0) nokey--;
Ich habe aber eine ganze Reihe dieser Variablen, Timer für Ein- und
Ausschaltvorgänge, Laufzeiten, Warteschleifen, ...
So wird die Interruptroutine immer länger und sie wird alle 10ms
aufgerufen.
Gibt es da eine bessere Lösung?
fw

von R. T. (Gast)


Lesenswert?

Du könntest z.B einen Scheduler verwenden um der Tatsache gerecht zu
werden, dass nicht alle Variablen bei jedem Timerinterrupt abgefragt
werden, sondern in Abständen die sinnvoll sind.

von Jörg Wunsch (Gast)


Lesenswert?

Ich hab da auch mal was geschrieben:

http://www.sax.de/~joerg/avr-timer/

von flyingwolf (Gast)


Lesenswert?

@Jörg
Du hast es nicht zufällig auch in deutsch, oder?

@R T
Scheduler? Einen Terminkalender?! Ich hoffe ich finde was in diesem
Forum dazu. Vieleicht könntest Du aber auch noch etwas genauer werden.

von Jörg Wunsch (Gast)


Lesenswert?

Sorry, nein, in deutsch schreibe ich sowas nicht erst, wofür sollte
das gut sein?  Das Datenblatt musste doch auch lesen können, und so
viel englisch isses ja nun nicht.

von Peter D. (peda)


Lesenswert?

Ich hab da auch mal was geschrieben:

http://www.mikrocontroller.net/forum/read-4-49709.html#new


Peter

von flyingwolf (Gast)


Lesenswert?

@Jörg
>> wofür sollte das gut sein?

Nun, es würde die Sache verienfachen für die deutsch sprechende
Bevölkerung. Klar ist das Datenblatt in englisch, drum ist es schon
recht anstrengend, das zu lesen, und wenn man dann auch noch versucht
sich mit einer zweiten Sprache auseinanderzusetzten, die man noch
weniger versteht, ...

von flyingwolf (Gast)


Lesenswert?

Trotzdem erst mal Danke Euch Allen. Ich schau mir das mal alles an.

von Jörg Wunsch (Gast)


Lesenswert?

Ich verstehe, dass es das für dich vereinfachen würde, aber es würde
für mich die Arbeit verdoppeln.  Meinst du nicht, dass es durchaus
angemessen ist, wenn du schon was ,,geschenkt'' bekommst, dass du
auch
ein wenig Mühe selbst reinstecken darfst?

Ich schreibe seit ca. 20 Jahren praktisch alle meine Programme in
englisch, damit sie mehr als nur eine Minderheit verstehen kann (bis
auf wenige Ausnahmen, bei denen mich jemand explizit dafür bezahlt,
ein in deutsch geschriebenes Programm zu erhalten).  Es gibt also
nicht etwa eine deutsche ,,Urversion'' und dann eine englische
Übersetzung oder sowas, es gibt jeweils nur eine englische Version.

von Peter D. (peda)


Lesenswert?

@Joerg,

Deine Funktion sieht ja etwas einfacher aus, da ein Großteil der Arbeit
an das malloc delegiert wird.

Aber kann es nicht passieren, wenn auch andere Fuktionen das malloc
benutzen, daß durch häufiges Reinstellen und Entfernen von Timern der
Speicher immer mehr fragmentiert wird und irgendwann nicht mehr
ausreicht ?


Will man Funktionen wieder entfernen, dann muß man sich für jede
Funktion den Rückgabewert des Reinstellens merken.
D.h. es ist umständlich wenn eine andere Funktion einen Timer entfernen
will, als die, die den Timer reingestellt hat.

Deshalb habe ich das Entfernen so implementiert, daß es wie das
Reinstellen nur die Funktionsadresse benötigt, was obendrein noch
Speicherplatz spart.
Auch kann man leicht alle Einträge dieser Funktion entfernen, wenn eine
Funktion mehrmals reingestellt wurde (was durchaus Sinn machen kann).

Eine interessante Anwendung des Entfernens ist ja in meinem Beispiel
die Doppeldruckerkennung innerhalb einer bestimmten Zeit.


Peter

von Jörg Wunsch (Gast)


Lesenswert?

Naja, malloc() habe ich genommen, weil ich keine Fahrräder nochmal
erfinden will.  Wenn es eine Speicherverwaltung gibt, sollte man diese
auch nehmen, statt eine private zu implementieren.  Besser würde die
sowieso nicht.

Klar birgt malloc() immer auch das Risiko einer Fragmentierung,
allerdings begrenzt sich das normalerweise statistisch von selbst in
einer Applikation.  Da du ja selbst schon festgestellt hast, dass die
AVRs gut mit RAM ausgestattet sind, kommt man also in der Regel damit
gut zurecht. ;-)  Falls der timer-Code der einzige Nutzer des malloc()
ist, entsteht ohnehin keine Fragmentierung, weil ja dann immer gleich
große Stücke angefordert werden.

Die Variante mit dem Handle zum Entfernen war absichtlich gewählt.
Damit kann man für einunddieselbe Zielfunktion auch mehrere Aufrufe
`pending' haben und nur einen davon wieder löschen.  Die Semantik der
jeweiligen Aufrufe kann sich ja dennoch unterscheiden, da die
Zielfunktion jeweils noch ihren ,,Keks'' durchgereicht bekommt und in
Abhängigkeit davon für die einzelnen Aufrufe unterschiedliche Dinge
implmentieren kann.  Gut, man könnte noch überlegen, die Queue um eine
Funktion zu erweitern, die abhängig von Zielfunktion und Cookie wieder
entfernt.  Der Suchaufwand ist geringfügig größer, dafür muss man sich
das Handle nicht merken.  Habe ich aber bisher nicht gebraucht, das
Merken des Handles war noch nie ein Problem.

Warum soll das Entfernen per Funktionsadresse Speicherplatz sparen?
Das verstehe ich nicht.  Oder meinst du Speicherplatz beim Aufrufer,
weil er sich das Handle nicht merken muss?  Andererseits:
Speicherplatz ist meist genug da...

von peter dannegger (Gast)


Lesenswert?

@Jörg,

"Oder meinst du Speicherplatz beim Aufrufer,
weil er sich das Handle nicht merken muss?"

Ja genau das meinte ich.
Ich habe früher viel auf dem AT89C2051 (nur 128Byte SRAM) gemacht, da
wußte noch keiner was AVRs mal sein werden.
Und seitdem steckt mir das Byteknausern irgendwie im Blut.


Das malloc scheint sehr komplex zu sein.
Insbesondere das Freigeben, wenn noch dahinter ein Block belegt ist,
stelle ich mir sehr kompliziert vor. Würde mich nicht wundern, wenn die
Ausführungszeit sogar länger ist.
Zumindest RAM wird mehr benötigt, da nun ja eine doppelt verlinkte
Liste entsteht, einmal in der Einstellreihenfolge (malloc) und einmal
in der Ausführungszeitfolge (Scheduler).

Gibs dazu vielleicht Grundlagenartikel, wie so ein einfachst-malloc
funktioniert ?



"das Merken des Handles war noch nie ein Problem."

Etwas unschön finde ich daran, daß man dazu eine globale Variable
braucht, wenn verschiedene Funktionen einen Eintrag reinstellen und
entfernen.


Peter

von Jörg Wunsch (Gast)


Lesenswert?

Nein, eine doppelt verlinkte Liste gibt es nicht, die Liste ist
einfach verlinkt und wird von vorn nach hinten abgearbeitet.  Beim
Einstellen eines Events wird es an der enstprechenden Stelle in die
Liste eingefügt, sodass timertick() immer nur am Kopf der Liste die
Events abarbeiten und rauslösen muss, die gerade (über-)fällig sind.
Diese Vorgehensweise basiert auf der Annahme, dass man in der Regel
beim Einstellen des Events durchaus Zeit investieren kann, während
timertick() möglichst schnell arbeiten können soll.

malloc() selbst pflegt keine eigene Liste der allozierten Blöcke,
lediglich bei der Freigabe wird eine Liste der freien Blöcke aufgebaut
(wobei für die Verkettung der einstmals für den Nutzer allozierte
Platz genutzt wird).

Das malloc() habe ich in der avr-libc-Doku einigermaßen ausführlich
beschrieben.  Bertolt Mildner hat auf der avr-libc-dev Mailingliste
noch paar Verbesserungsvorschläge gemacht, die ich mir irgendwann mal
endlich ansehen muss.

> Etwas unschön finde ich daran, daß man dazu eine globale Variable
> braucht, ...

Du kannst dir sicher auch ein halbes Dutzend anderer Varianten
ausdenken ;-), aber die globale Variable dürfte meist die einfachste
sein.  Speicherplatz brauchen sie am Ende alle, ob nun auf dem Stack
oder permanent im RAM ist oft genug egal (bei den paar Bytes, über die
wir hier reden).

von peter dannegger (Gast)


Lesenswert?

"das Merken des Handles war noch nie ein Problem."

Das kann schon ganz lustige Nebeneffekte haben, wenn die Funktion
abgelaufen ist und derselbe Handle inzwischen einer anderen Funktion
zugewiesen wurde.

Auch ist z.B. bei einer Blink-LED, die sich immer wieder selbst
reinstellt, nicht gesichert, daß sie auch immer den gleichen Handle
bekommt.
Die Funktion, die irgendwann die Blink-LED ausschalten will, könnte
dann ganz schön dumm aus der Wäsche gucken.


Bei einer Referenz per Funktionsadresse ist man dagegen ziemlich
sicher, daß auch die gewünschte Funktion beendet wird.


Peter

von peter dannegger (Gast)


Lesenswert?

"Nein, eine doppelt verlinkte Liste gibt es nicht"


Ich hatte jetzt erstmal gedacht, daß malloc an den Anfang jedes
alloziierten Block den Startwert des nächsten freien Blocks ablegt und
so eine Liste anlegt. Wie man nun aber einen Block dazwischen wieder
freigibt, stelle ich mir sehr kompliziert vor. Man müßte wohl dazu im
Blockheader nicht nur die Nachfolgeradresse sondern auch die
Vorgängeradresse und ein Belegt-Flag speichern.
Ich muß mir mal Deine Doku unters Kopfkissen legen.


Peter

von Jörg Wunsch (Gast)


Lesenswert?

Gut, failsafe bezüglich alter Handles ist das Teil natürlich wirklich
nicht.

Wie gesagt, cleanup by function pointer ließe sich sicherlich noch
erweitern, soll nicht das grundsätzliche Problem sein.

So kompliziert ist das mit dem malloc gar nicht.  Für die belegten
Blöcke muss man sich ja nicht zwangsweise was merken, schließlich will
ich hinterher keine garbage collection oder sowas durchziehen können,
sodass ich mich darauf verlassen kann und darf, dass der Aufrufer sich
ja den Block ausreichend gut merkt.

Beim Freigeben bilden die freien Blöcke ein einfach verkettete Liste,
da hängt man den neu freigegebenen Block rein.  Man schaut nochmal
nach, ob man links oder rechts aus zwei Blöcken dann einen machen
kann, das war's eigentlich.

Das Einzige, was man sich im Block selbst (genauer: direkt davor)
merken muss ist die Länge des jeweiligen Blocks, denn diese merkt sich
der Aufrufer ja nicht auch noch für einen. ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.