Hallo zusammen,
ich bin gerade dabei eine Firmware zu schreiben und jetzt habe ich ein
kleines Detailproblem wofür ich nun ein bisschen Hilfe benötige.
In meiner "Problemhardware" gibts es einen Mikrocontroller welcher über
I2C ein Display ansteuert und gleichzeitig müssen über I2C diverse
Eingabeelemente ausgewertet werden. (Taster etc.)
Nun ist es so das ich mir eine Auswerteroutine für die Taster
geschrieben habe, welche über einen Timer-Interrupt aufgerufen wird.
Das eigentliche Problem hierbei ist das es sein kann, das in der
Hauptschleife das Display mit Daten über I2C versorgt wird, der Timer
Interrupts kommt und den aktuellen Datentransfer zum Display beendet und
die Eingabelemente über I2C abfragt. Dabei ist mir aufgefallen das es zu
allerlei Fehlern am Display kommt da das Display dadurch inkonsitente
Daten erhält aber die Displayroutinen "planmäßig" im Programm
weiterlaufen.
Ich habe mir vor einigen Monaten ein C Buch gekauft und habe jede menge
dazugelernt, nur leider findet man in einen solchen Buch keine
Strategien für ein Cleveres Programmkonzept (Um zum Beispiel solche
Dinge zu vermeiden)
Hat irgendjemand von euch eine Idee wie man dieses Problem lösen kann?
Ich hatte schon an eine Art "I2C-Busy-Flag" oder einen anderen Interrupt
gedacht in den das Display in einen Zug beschrieben wird (Soweit ich
weiß können Interrupts nicht durch andere Interrupts unterbrochen
werden, sondern Reihen sich ein). -Nur irgendwie bin ich mir nicht
sicher ob das auch das wahre ist.
Mir sind die CLI/SEI bekannt nur habe ich im Tutorial gelesen das dies
auch nicht die wahre Lösung ist, da man sich dafür andere Probleme
einheimsen kann. Da das Programm größer und größer wird würde ich gerne
eine "Bulletproof" Variante in Betracht ziehen.
Würde mich freuen wenn Ihr mir ein wenig auf die Sprünge helfen könntet.
Während der Ausgabe die Interrupts mittels CLI disablen und danach
mittels SEI wieder enablen.
Dabei strebst du eine möglichst feine Granulierung an. D.h. wenn das bei
dir möglich ist, dann sperrst du die Interrupts nur während der Ausgabe
eines einzelnen Zeichens. Gerade soviel, wie du brauchst, dass das
Display nicht durcheinanderkommt.
> Mir sind die CLI/SEI bekannt nur habe ich im Tutorial gelesen das dies> auch nicht die wahre Lösung ist
Keine Regel ohne Ausnahme.
Natürlich sollst du nicht eine halbe Stunde lang die Interrupts sperren.
Aber wenn es in deinem Programm Abschnitte gibt, die von einem Interrupt
nicht unterbrochen werden dürfen UND die kurz und schnell abgearbeitet
sind, dann spricht da nichts dagegen.
Sinnvollerweise wirst du auf I²C immer erst ein Befehlszyklus komplett
abarbeiten müssen, bevor du auf einen anderen Teilnehmer umschalten
kannst. Man kann eine Übertragung zwar auch abbrechen, aber bei das ist
in deinem Fall nicht sinnvoll. Insofern wird das mit Unterbrechungen
durch ISR's, die dann eine eigene I²C-Kommunikation starten, nicht
funktionieren.
Die allereinfachste Lösung dürfte sein, mehrere getrennte I²C-Busse
einzusetzen. Die Abfrage der Eingabeelemente dürfte ja nicht
zeitkritisch sein, da reicht Software-I²C aus.
Ansonsten tatsächlich die entsprechenden Interrupts sperren (es muß ja
nicht gleich ein komplettes cli() sein), oder der Einsatz eines
Echtzeit-Betriebssystems, bei dem du allerdings dann genauso, wie bei
den Interrupts, während eines I²C-Zyklus Unterbrechungen verbieten
musst.
Oliver
Die Idee mit dem I2C-Busy-Flag ist ein guter Ansatz. Über das Flag
könnte dein Hauptprogramm feststellen,ob deine Eingabeelemente gelesen
werden sollen und dies initieren. Ich würde das zusammen mit einer
State-Machine als Ablaufkontrolle umsetzen.
Gerade bin ich mir nicht sicher was nun wirklich "kurz" oder "lang" ist.
Was ist wenn ich die Interrupts sperre aber Zeitlich gesehen schon 2
Timer Interrupts ausgelöst wurden. - Dann geht doch einer verloren oder?
(Sollte bei Tastern und langsamen Eingabelementen nicht so schlimm
sein)
Den Timerinterrupt hatte ich so organisiert das es eine Art "Event"
Zähler gibt und abhängig vom Zählerstand unterschiedliche
Eingabeelemente abfragt.
Dadurch ist es mir möglich das ich innerhalb einer gewissen Anzahl von
Interrupts unterschiedliche Prioritäten setzen kann. (Das zum Beispiel
Inkrementalgeber öfters abgefragt werden als Taster etc.)
Ich kann leider noch nichts genaueres zu den Hauptteil sagen da dieser
erst Stück für Stück anwächst. Hinterher kommen noch eine Menge
Zustands/-Kontroll abfragen und Displaymenüs hinzu.
Entschuldigung für das Doppelpost aber ich hatte den Post erst jetzt
gesehen.
@Oliver:
Du musst wissen das es sich hierbei um fertige Hardware handelt wo nur
noch die Software geschickt implementiert werden muss. Es ist zwar
vieles beachtet ( z.B. Inkrementalgeber hängen direkt an IO-Pins und
müssen nicht über I2C abgefragt werden), hätte aber nicht gedacht das
ein Temperatursensor ein IO-Expander(+Bedienelemente) sowie ein Display
auf dem I2C-Bus solche "Probleme" machen kann.
@2ter Gast:
Wie habe ich mir das genau vorzustellen?
In Strukturierten Programmieren habe ich noch nicht so viel Erfahrung da
vor etwa 6 Monaten mit C angefangen habe und vorher nur Miniprogramme in
Assembler geschrieben habe. Entwicklung von Hardware liegt mir eher.
(Aber das eine geht nicht ohne den anderen)
Nachtaktiver schrieb:> Gerade bin ich mir nicht sicher was nun wirklich "kurz" oder "lang" ist.> Was ist wenn ich die Interrupts sperre aber Zeitlich gesehen schon 2> Timer Interrupts ausgelöst wurden. - Dann geht doch einer verloren oder?> (Sollte bei Tastern und langsamen Eingabelementen nicht so schlimm> sein)
Also Faustregel:
Alles was mit Benutzerinteraktion zu tun hat, geschieht für den µC in
extremer Zeitlupe. Selbst wenn dein µC mal 100ms nicht auf die Tasten
schaut, ist das für einen Benutzer praktisch noch nicht bemerkbar.
Und 100ms sind für einen µC schon eine lange Zeit.
Du könntest ja zb deine Display Ausgabe auch als ein Event auffassen.
Bei jedem Display-Event werden ein paar Zeichen aufs Display ausgegeben.
Danach geht es reihum alle Eingabeelemente abfragen und beim nächsten
Display-Event werden wieder ein paar Zeichen ausgegeben, solange bis der
auszugebende String komplett am Display ist.
Du solltest die gesamte i2c Geschichte nicht in den Interrupts
ausführen. Glieder sie in dein Hauptprogramm oder in ein Unterprogramm
ein. Deine Interupt-Routine setzt nur ein Flag und das Hauptprogramm
verzweigt in das entsprechende Unterprogramm. Dann kommen sich deine
Abfragen und Ausgaben auch nicht mehr ins Gehege, da sie sich nicht
gegenseitig unterbrechen können. Du hast leider nicht beschrieben, auf
welcher Plattform du arbeitest.
@Falk:
Ich kannte die Artikel (Halbherzig) und habe diese nochmal durchgelesen.
Dabei muss ich mir aber eingestehen das es sich stellenweise um große
Häppchen handelt welche ich schwer auf Mikrocontroller exthrahieren
kann.
Die meiste Zeit ist ja von einen "vollwertigen" Betriebssystem auf PC
Basis die Rede.
Der Multitasking Prozess gibt mir aber die Inspiration das ich die
komplette Hauptschleife in kleinen Teilen Sturkturiere. Und bei jeden
durchlauf einen Aktion durchführe. (z.B)
-Messwert A erfassen
-Messwert A in ASCII umwandeln
-Messwert B erfassen
-Messwert B in ASCII umwandeln
-Messwert C erfassen
-Messwert C kontrolieren
-Kontrolzustände abfragen
-Kontrolzustände auswerten
-Eingabeelemente: auswerten (Werden durch Timer-Interrupt aktualisiert)
-Eingabeelemente: Aktionen ausführen
-Display aktualisieren
- (+Unterpunkte welche mir gerade nicht in den Sinn kamen)
-zurück zum Anfang
@Heiko Bendt:
Als erstes bevor ich es wieder vergesse: Es handelt sich um einen
ATMEGA16 welcher wahrscheinlich auf einen MEGA64 aufgerüstet werden
muss.
Ich selber sehe da kein Problem einige Bytes über I2C in einen Interrupt
abzufragen. Zumal das auch nur gemacht wird wenn z.B ein Taster
abgefragt wird. Wenn ein Inkrementalgeber ausgewertet wird, wird sofort
von den IO-Pins gelesen.Irgendwie müssen diese Eingabeelemente auf
"aktuellen Stand" gehalten werden.
Die Idee die dahinter steckt ist ja, das der Timerinterrupt dafür sorgt
das für die Taster entsprechende Flags wie "BUTTON_A_LONG_CLICK" oder
"BUTTON_A_SHORT_CLICK" gesetzt werden können und das Hauptprogramm
reagierend auf diese Zustände, Aktionen durchführen kann.
Heiko Bendt schrieb:> Du solltest die gesamte i2c Geschichte nicht in den Interrupts> ausführen.
Stimmt!
I2C kostet schon einiges an CPU-Zeit, daher sollte man damit keine
Interrupts blockieren.
Es könnte sogar sinnvoll sein, das I2C selber als Interrupt zu
programmieren. Dazu legt man einen Puffer an, wo das Main die Daten
ablegt und der I2C-Interrupt gibt sie dann aus.
Peter
Nachtaktiver schrieb:> Wie habe ich mir das genau vorzustellen?> In Strukturierten Programmieren habe ich noch nicht so viel Erfahrung da> vor etwa 6 Monaten mit C angefangen habe und vorher nur Miniprogramme in> Assembler geschrieben habe. Entwicklung von Hardware liegt mir eher.> (Aber das eine geht nicht ohne den anderen)http://de.wikipedia.org/wiki/Endlicher_Automat
Im Prinzip hast Du in deiner main-Funktion eine while-Schleife und eine
Variable, mit der du dir den aktuellen Zustand Programms merkst. Den
Zustand wertet Du z.B. mit switch-case ab. In Abhängigkeit von dem
aktuellen Zustand führt dein Programm aktionen aus.
Z.B.
Du hast den Zustand "Display-Ausgabe". Dort machst Du deine
Displayausgabe.
Wenn ein Fehler auftritt könnte es den Zustand "Display-Ausgabe-Fehler"
geben, in dem Du den Fehler behandelt; D.h. du setzt den Zustand auf
"Display-Ausgabe-Fehler" oder aber du bist fertig setzt den Zustand
Display-Ausgabe-fertig. Im Zustand Display-Ausgabe-fertig prüfts dann ob
dein Interrupt das flag "Eingabe-Elemente-Prüfen-Flag" gesetzt hat und
fährst mit dem Zustand "Eingabe-Elemente-Prüfen" fort
@ Nachtaktiver (Gast)
>Ich kannte die Artikel (Halbherzig) und habe diese nochmal durchgelesen.>Dabei muss ich mir aber eingestehen das es sich stellenweise um große>Häppchen handelt
Für Anfänger schon, ja.
>welche ich schwer auf Mikrocontroller exthrahieren kann.
Bitte? Das ist direkt am Beispiel AVR erklärt, mit Beispielprogrammen.
>Die meiste Zeit ist ja von einen "vollwertigen" Betriebssystem auf PC>Basis die Rede.
Nö, keine Sekunde.
>Der Multitasking Prozess gibt mir aber die Inspiration das ich die>komplette Hauptschleife in kleinen Teilen Sturkturiere. Und bei jeden>durchlauf einen Aktion durchführe. (z.B)
Genau so.
>Als erstes bevor ich es wieder vergesse: Es handelt sich um einen>ATMEGA16 welcher wahrscheinlich auf einen MEGA64 aufgerüstet werden>muss.
Und? Der Speicher ist nicht entscheiden, aber das Konzept!
>Ich selber sehe da kein Problem einige Bytes über I2C in einen Interrupt>abzufragen.
Weil du Änfänger bist ;-)
> Zumal das auch nur gemacht wird wenn z.B ein Taster>abgefragt wird. Wenn ein Inkrementalgeber ausgewertet wird, wird sofort>von den IO-Pins gelesen.Irgendwie müssen diese Eingabeelemente auf>"aktuellen Stand" gehalten werden.
Das geht auch anders. Kurze, knackige Interrupts, der Rest in der
Hauptschleife.
>"BUTTON_A_SHORT_CLICK" gesetzt werden können und das Hauptprogramm>reagierend auf diese Zustände, Aktionen durchführen kann.
Eben.
MFG
Falk
Ihr habt mir viele Tipps gegeben wie man das Programm zumindest
sinnvoll/optimal strukturieren könnt. Aber ich muss ganz ehrlich zugeben
das ich immer noch nicht verstehe wie ich die Eingabedaten der Taster
aktuell halte.
Fest steht aufjedenfall, das eine I2C Kommunikation im Interrupts Tabu
ist, da dies viel wertvolle Zeit verschwendet. Für mich ist das nicht
ganz nachvollziehbar (Wie Falk schon Sachte - Ich bin ein Anfänger) und
dementsprechend kann ich das auch akzeptieren.
Soll ich das ganze so interpretieren das ich in meiner "Unendlichen
Automatentafel" (Wie 2ter Gast 2-Post hier darüber) so schön erklärte,
mehrere Unterpunkte "Daten: Eingabeelemente aktualisieren" aufführe?
Dadurch kann der Interrupt schön kurz und knackig ausfallen und ich muss
mir keine Sorgen machen das es zu abgebrochenen Fehlübertragungen kommt.
In diesen Interrupt werden dann nur die Taster ausgewertet und
entsprechende Flags für die weitere Auswertung gesetzt.
Also(?):
1
while(1)
2
{
3
4
switch(Event)
5
{
6
case0:
7
Eingabeelemente_aktualisieren();// Hier werden die Eingabeelemente neu abgefragt/Aktualisiert
Deine Beschreibung bleibt aber trotzdem vage. Wie wärs mit dem
Schaltplan und dem Programm?
Grundsätzlich gilt, daß in den Interrupts möglichst wenig gearbeitet
werden sollte. Ein paar Register auslesen, Flags setzen, Daten in/aus
Sende/Empfangsregister transportieren. Die eigentliche Verarbeitung
geschieht im Haupt/Unterprogramm. Schon garnicht wird in der
Interruptroutine gewartet bis ein bestimmtes Bit z.B durch die Hardware
gesetzt wird.Wenn du nur ein Flag setzt, das etwas erledigt werden muß,
kannst du in Ruhe die momentane Aufgabe zuendebringen bevor du die
nächste anfängst. In deinem Einganspost erscheint es mir, als würdest du
versuchen auf das display zu schreiben und im Interrupt über die gleiche
Schnittstelle zu versuchen etwas einzulesen. Das muß schief gehen.
Übertrage die paar Zeichen zuende zum Display und frage dann die Taster
ab. So schnell wird dein Finger nicht sein, das du den Tastendruck
verpaßt. Könnte dein Inkrementalgeber nicht einen Pinchange Interrupt
auslösen?
Dann hättest du eine regelmäßige Abfrage weniger.
Benutzt du eigentlich Hardware TWI?
Ich finde es wahnsinnig toll das Ihr mir hier so fleißig hilft aber aus
diversen Gründen möchte ich keine Software+Hardware Details hier
veröffentlichen. Der erste Grund ist das es sich um mehrere Platinen
handelt und es sich hier um viele Seiten Schaltplan dreht - Ich kann
schlecht erwarten das Ihr euch darin zurechtfinden wird und gleichzeitig
schrecke ich damit doch nur Leute ab. Die Software möchte ich nicht
veröffentlichen, da allein die wichtigen Grundfunktionen so viel Umfang
einnehmen das Ihr euch darin genauso wenig zurechtfinden werdet. (Ist
nicht "böse" gemeint)
Auch ist es für die Fragestellung eigentlich irrelevant wie genau die
Hardware aussieht, da ja auf den von mir Oben auf die Schnelle
geschrieben C Schnippsel um ein Beispiel handelt. Es sollte eigentlich
um das Prinzip gehen aber scheinbar ist das nach hinten losgegangen.
>In deinem Einganspost erscheint es mir, als würdest du>versuchen auf das display zu schreiben und im Interrupt über die gleiche>Schnittstelle zu versuchen etwas einzulesen. Das muß schief gehen.
Bevor ich es vergessse: Natürlich verwende ich Hardware I2C (200kbit/s)
Genau das ist ja der Sinn meiner Frage!Der Kernproblematik ist
eigentlich:
Es gibt eine Displayroutine welche das Display, welches ein I2C
Interface besitzt, beschreibt (senden), nun kommt der Timerinterrupt,
"killt" diesen I2C Transfer, und fragt eigenständig über I2C (emfpangen)
den Zustand einiger Taster über einen IO-Expander ab. Nach beenden des
Interruptes läuft die Displayroutine weiter und das Display macht mist
da die Daten korrupt sind.
Die eigentliche Fragestellung lautet somit:
Wie kann ich eine solche Unterbrechnung geschickt vermeiden?
Ich weiß halt nicht wie ich sowas geschickt Programmiere. Da "breche"
mir halt die Beine das alles sinnvoll zusammen zubauen. Mein C Buch
konnte mir an dieser Stelle nicht weiterhelfen, sonst hätte ich hier
auch nicht gefragt.
Bis jetzt sind wir so weit gekommen, das dass Anfragen der
Tasterzustände über I2C im Interrupt Fehl am Platz ist und das im
Interrupt selbst, nur die reine Tasterauswertung kommt (Welche kurz
ist).Nur konnte ich bis jetzt nicht nachvollziehen wo eine solche
Aktualisierung sonst hin soll.
Die Inkrementalgeber sind erstmal unwichtig. Wenn der
Taster-Programmteil flüssig läuft sind die Inkrementalgeber ein
leichtest Spiel. (Da mir dann das Prinzip klar sein wird)
Nacktaktiver schrieb:> Die eigentliche Fragestellung lautet somit:> Wie kann ich eine solche Unterbrechnung geschickt vermeiden?> Ich weiß halt nicht wie ich sowas geschickt Programmiere. Da "breche"> mir halt die Beine das alles sinnvoll zusammen zubauen. Mein C Buch> konnte mir an dieser Stelle nicht weiterhelfen, sonst hätte ich hier> auch nicht gefragt.
Bei solchen Fragestellungen ist es oft hilfreich (zumindest für mich)
mich der Fragestellung zu widmen (nicht lachen): Wie mach ich das im
realen Leben?
Wenn ein Klo gerade besetzt ist und jemand anderer kommt und will
dieselbe Resource (Schüssel) benutzen ... wie verhinderst du im realen
Leben, dass da Blödsinn passiert? Welche Möglichkeiten gibt es, welche
Voraussetzungen müssen für jede der Möglichkeiten gegeben sein.
Abstrahiere das Problem in dein tägliches Leben. Du wirst überrascht
sein, wo und wie du derartige Probleme jeden Tage ohne mit der Wimper zu
zucken und ohne groß nachdenken zu müssen lösen kannst!
Das was du weiter oben als "Strategien für ein Cleveres Programmkonzept"
schmerzlich vermisst, sind in Wirklichkeit zu über 80% ganz alltägliche
Strategien aus deinem täglichen Leben, transferiert in ein Programm.
Nacktaktiver schrieb:> Es gibt eine Displayroutine welche das Display, welches ein I2C> Interface besitzt, beschreibt (senden), nun kommt der Timerinterrupt,> "killt" diesen I2C Transfer, und fragt eigenständig über I2C (emfpangen)> den Zustand einiger Taster über einen IO-Expander ab. Nach beenden des> Interruptes läuft die Displayroutine weiter und das Display macht mist> da die Daten korrupt sind.>> Die eigentliche Fragestellung lautet somit:> Wie kann ich eine solche Unterbrechnung geschickt vermeiden?
Wurde ja schon gesagt: Am einfachsten während der LCD-Ausgabe die
Interrupts sperren. wenn das zu nicht akzeptablen Tastenverzögerungen
führt, wird es komplizierter. Dann braucht es eine zentrale Instanz, die
die I²C-Kommunikation koordiniert (auf Betriebssysteme nennt man so
etwas Treiber).
Oliver
Nacktaktiver schrieb:> Es gibt eine Displayroutine welche das Display, welches ein I2C> Interface besitzt, beschreibt (senden), nun kommt der Timerinterrupt,> "killt" diesen I2C Transfer, und fragt eigenständig über I2C (emfpangen)> den Zustand einiger Taster über einen IO-Expander ab. Nach beenden des> Interruptes läuft die Displayroutine weiter und das Display macht mist> da die Daten korrupt sind.
Der Ansatz ist Murks. Einfach ein Flag im Timer setzen, dieses Flag im
Hauptcode auswerten und dann ggf. die Tastenabfrage durchführen.
Mal ein paar Zahlen:
Wenn das ein 4*16-Display ist, das komplett vollgeschrieben wird, sind
das mit Protokolloverhead vielleicht 600 Bit, die ans Dispaly geschickt
werden. Bei 100kHz Takt benötigt das 6ms, bei 400kHz 1,5ms.
Ob sich eine Tastenabfrage mal um 6ms verzögert, ist nun wirklich völlig
egal. Das merkt kein Mensch.
Eine eventgesteuerte Zustandmaschine ist allerdings trotzdem sehr zu
empfehlen.
Oliver
Es sind hier ziemlich gute Tipps gefallen und langsam wird es Zeit das
ein oder andere mal in der Tat umzusetzen. Falls es bedarf gibt werde
ich hier mich einfach wieder melden.
@Karl Heinz Buchegger:
Ich finde das es wirklich ein sehr guter Tipp ist! Es ist schon
irgendwie zum lachen, zeigt aber das wenn man verkrampft an etwas
herangeht sich eigentlich nur unnötige Probleme macht.
Nacktaktiver schrieb:> Soll ich das ganze so interpretieren das ich in meiner "Unendlichen> Automatentafel" (Wie 2ter Gast 2-Post hier darüber) so schön erklärte,> mehrere Unterpunkte "Daten: Eingabeelemente aktualisieren" aufführe?
Genauso so etwas in der Art hatte ich im Sinn. Vergess' bei deinen cases
jeweils das break nicht
Danke! Das Break hatte ich wirklich übersehen. Irgendwie komme ich
gerade richtig gut vorran und aufeinmal kommen mir immer wieder bessere
Ideen.
Gerade hatte ich die ersten Anfänge geschrieben und hatte mir zum Test
einige Werte auf das Display anzeigen lassen. Dabei musste ich
feststellen das der Wert so schnell beschrieben wird, das man es nicht
mitlesen kann.
Also zähle ich einfach nebenbei in der Tasterauswertig in einer Variable
hoch löse irgendwann ein bestimmtes Flag aus und erst dann wird dann 1x
das Display beschrieben. Ist diesen Flag nicht gesetzt wird in der State
Maschine der Display Aktualisieren Teil einfach weggelassen.
Vorhin einen kleinen Zettel beschrieben in der ich die wichtigsten
Sachen chronologisch aufgeführt habe und schreibe ich das ganze einfach
Stück für Stück runter.Auch habe ich schon erste Ideen für eine Art
Error Handler.
Hier mal ein kleiner Teil:
1
unsignedcharMAIN_EVENT_COUNTER=0;
2
unsignedcharCOUNTER=0;
3
4
while(1)
5
{
6
// Main State Maschine
7
switch(MAIN_EVENT_COUNTER)
8
{
9
10
// Aktualisiert den Zustand der Taster
11
case0:
12
// Empfängt die neuen Daten über den IO-Expander ab
13
KEY_DATA=Expander_Read_PINA(PINA_MASK);
14
// Main Event: +1
15
MAIN_EVENT_COUNTER++;
16
// Beendet die Aktuellen Zustand
17
break;
18
19
20
// Aktualisiert den Temperaturwert
21
case1:
22
// Fragt die neue Temperatur ab
23
ACTUAL_TEMPERATURE=LM75_Read_Temperature();
24
// Main Event: +1
25
MAIN_EVENT_COUNTER++;
26
// Beendet den Aktuellen Zustand
27
break;
28
29
// Aktualisiert den Messwert an ADC0 ( aktueller Strommesswert)
30
// Dabei wird ein Mittelwert aus 32 Einzelwerten errechnet
Paar Anregungen:
- Die Nummerierung würde ich durch #defines mit aussagekräftigen
Bezeichner ersetzen.
- Anstatt die State-Machine mit einem Counter abzuarbeiten, den State
ausdrücklich zuweisen, also anstatt
1
++MAIN_EVENT_COUNTER
lieber
1
MAIN_EVENT_COUNTER=UPDATE_TEMPERATURE_STATE
Das hätte mir beim Code-Schnipsel auffallen können.
Und sich mal alles aufschreiben ist eh ein guter Plan.
Diese all-in-one-swich-case-statemachine wird dir irgendwann um die
Ohren fliegen, da ziemlich unwartbar.
Lad dir mal hier:
http://gandalf.arubi.uni-kl.de/avr_projects/
den gcc-port des avr-butterfly herunter, und schau dir die darin
enthaltene statemachine an.
Oliver
@2ter Gast:
Dabei hatte ich es oben sogar selber geschrieben. Werde ich dann mit
übernehmen und einarbeiten.
@Oliver:
Warum unwartbar?
Ich habe mir mal den ganzen Sourcecode vom AVR Butterfly angeschaut,
aber ich muss sagen das ich zum größten Teils nur Bahnhof verstehe.
Sofern ich das aber richtig interpretiere ist die Statemachine dort eine
Funktion welche Daten aus den Flash liest. (Oder?)
Nacktaktiver schrieb:> @Oliver:> Warum unwartbar?
Was schätzt du denn, wie viele Zustände und Zustandswechsel bei einem
Gerät mit mehreren Tasten etc. zusammenkommen?
Oliver
@Oliver:
So einige! (Kurzes drücken, langes drücken +welche mitgezählt werden)
Bis jetzt scheint es aber gut klappen, aber vielleicht ändert sich das
noch.
Zum der Tasterauswertung habe ich eine Testfunktion geschrieben welche
mir die ganze Zeit eine Variable auf dem Display anzeigt. Jeder Taster
kann bei drücken diese Variable verändern. Wenn ich wild auf alle Tasten
drücke und das unterschiedlich (Also ob lang oder kurz) werden wirklich
alle Tasterdrücke erkannt. Ich bezweifel das "wahnsinnig auf alle Tasten
drücken" ein normaler Betriebszustand entspricht.
Das Problem hier ist ja das die Taster nicht direkt an den IO-Pins
hängen, sondern I2C gepollt werden müssen - Das hat ja nichts im
Interrupt zu suchen.
Hat man wirklich ein Gerät wo mehrere Taster braucht dann hängt man
diese direkt an den Mikrocontroller. Dann fällt der Zustand "Taster
Daten Aktualisieren" weg und hat entsprechend keine Probleme mit vielen
unterschiedlichen Zuständen der Eingabeelemente.
Was sollte ich den deiner Meinung nach konkret verändern?
Ich möchte ein kleines Update hier machen. Nach ein bisschen nachdenken
hat mir Oilvers Ansatz mit den Typ aus den AVR Butterfly weitergeholen.
Das ganze Passiert auf eine konstantes Array im Flash welches mithilfe
einer Funktion aktualisiert wird. Dadurch kann man ganz beqeum die
Reihenfolgen, Häufigkeiten etc. umstellen, ohne das man den ganzen Code
Zerstückeln muss.
So sieht die Grundvorbereitung für die State Machine aus:
state_machine.h
Kann man auch so. Der Vorteil dieser Lösung ist ja das Reihenfolgen und
Prioritäten ganz einfach festlegen kann. Wenn du diese Lösung ja jetzt
umstellen musst musst du den Code zurechtstückeln, vorallen dann wenn
nun ein neuer Unterpunkt eingearbeitet werden soll.
Bei mir war ja das Problem das es bei mir ein Programmteil gab welches
in der Hauptschleife öfters ausgeführt werden musste. Jetzt kann die
Aufrufseqeunz mit einer einer Datenarray manipulieren.
(Zumindest gefällt mir diese Lösung so noch besser.)
Kann man auch prima auf LCD Menus anwenden. Vorher hatte ich keinen
blassen Dunst wie man das gut realisieren könnte.
Vielen Dank für eure Hilfreichen Antworten. :)