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
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.
@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.
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.
Ich hab da auch mal was geschrieben: http://www.mikrocontroller.net/forum/read-4-49709.html#new Peter
@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, ...
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.
@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
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...
@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
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).
"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
"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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.