mikrocontroller.net

Forum: Compiler & IDEs Problem mit Interrupts


Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: 2ter Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Siehe Interrupt

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Heiko Bendt (heiko_b)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: 2ter Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  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

Autor: Nacktaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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(?):
while(1)
{

switch (Event) 
{
case 0:
Eingabeelemente_aktualisieren(); // Hier werden die Eingabeelemente neu abgefragt/Aktualisiert
Event = Event+1

case 1:
Instruktion_1(); // z.B Messwerte erfassen
Event = Event+1

case 2:
Instruktion_2(); // z.B Zustände abfragen
Event = Event+1

case 3:
Instruktion_3(); // z.B Zustände auswerten& Error Flags setzen
Event = Event+1

case 4:
Eingabeelemente_aktualisieren(); // Hier werden die Eingabeelemente neu abgefragt/Aktualisiert
Event = Event+1

case 5:
Instruktion_5(); // z.B Zustände der Eingabeelemente auswerten, Aktionen durchführen - Werden per Interrupt aktuell gehalten
Event = Event+1

case n:
Instruktion_4(); // z.B Wenn vorhanden: Fehlermeldungen ausgeben
Event = Event+1

case n+1:
Instruktion_5(); // z.B Display aktualisieren
Event = 0


default: // Wenn ungültig - Event Zähler zurücksetzen
Event = 0
}

}
return 0;
}


ISR(TIMER0_OVF_vect) // Super-Duper-Kleiner-Interrupt

{
Taster_Auswerten(); // Daten werden per Mainloop auf aktuellen Stand gehalten
}


Anstatt den Zahlen kann man auch gute Namen nehmen, aber zur 
verdeutlichung denke ich sollte dies reichen.

Autor: Heiko Bendt (heiko_b)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: Nacktaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Nacktaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: 2ter Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Nacktaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
  unsigned char MAIN_EVENT_COUNTER=0;  
  unsigned char COUNTER=0;

  while(1)
{ 
// Main State Maschine
  switch(MAIN_EVENT_COUNTER)  
  {

// Aktualisiert den Zustand der Taster 
    case 0:
    // Empfängt die neuen Daten über den IO-Expander ab      
      KEY_DATA = Expander_Read_PINA( PINA_MASK);
    // Main Event: +1      
      MAIN_EVENT_COUNTER++;
    // Beendet die Aktuellen Zustand  
    break;
  

// Aktualisiert den Temperaturwert
    case 1:
    // Fragt die neue Temperatur ab
      ACTUAL_TEMPERATURE = LM75_Read_Temperature();
    // Main Event: +1
      MAIN_EVENT_COUNTER++;
    // Beendet den Aktuellen Zustand
    break;

// Aktualisiert den Messwert an ADC0 ( aktueller Strommesswert)
// Dabei wird ein Mittelwert aus 32 Einzelwerten errechnet
    case 2:
    // Zähler vorladen
      COUNTER = 31;
        
    // Alten Wert löschen
      ACTUAL_VOLTAGE = 0;

    // Bearbeitungsschleife
        while(COUNTER) {
        
        // Neuen Messwert addieren
        ACTUAL_CURRENT += ADC_Read( ADC1);

        // Bearbeitungszähler: -1
        COUNTER--;
        }
    
    // Mittelwertbildung
      ACTUAL_CURRENT = ACTUAL_CURRENT/32;
    
    // Main Event: +1
      MAIN_EVENT_COUNTER++;
    // Beendet den Aktuellen Zustand
    break;  
  
...
...
...

  }


Autor: 2ter Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
 ++MAIN_EVENT_COUNTER 
 lieber
 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Nacktaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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?)

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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?

Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
/* ------------------------------------------------------------------------------- */

// Zustandstabelle für die Main Statemachine
const unsigned char    STATE_MACHINE_ARRAY [] PROGMEM = {
      
            // Abarbeitungsreihenfolge
            0, 1, 2, 0, 1, 0, 1, 2, 255
};

// Hier listet man alle benötigten Zustände
#define          TU_ETWAS_A      0
#define          TU_ETWAS_B      1
#define          TU_ETWAS_N      2

// Reset Zustand
#define          STATE_MACHINE_ZURUECKSETZEN  255

/* ------------------------------------------------------------------------------- */




Unsere eigentliche Statemachine könnte so aussehen:
state_machine.c
/* ------------------------------------------------------------------------------- */
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include <state_machine.h>


  unsigned int   STATE_MACHINE_HANDLER=0;  
  unsigned char  i=0;

while(1) {

  // Liest den aktuellen Zustand aus den Flash
  STATE_MACHINE_HANDLER = pgm_read_byte(&STATE_MACHINE_ARRAY[i]);
  
  // Inkrementiert die Array Nummer aus den Flash
  i++;


switch(STATE_MACHINE_HANDLER) {


/* ------------------------------------------------------------------------------- */
  
  case TU_ETWAS_A:
      // Tu_Etwas_A Programmteil
  break;


/* ------------------------------------------------------------------------------- */

  case TU_ETWAS_B:
      // Tu_Etwas_A Programmteil
  break;

/* ------------------------------------------------------------------------------- */

  case TU_ETWAS_N:
      // Tu_Etwas_A Programmteil
  break;


/* ------------------------------------------------------------------------------- */

  case STATE_MACHINE_ZURUECKSETZEN:
      i=0
  break;
}

}
return 0;
}

Autor: 2ter Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich kenne den AVR Butterfly Code nicht, aber so wie es jetzt aussieht, 
könnte man auch

while(1)
{
 TuEtwas_A();
 TuEtwas_B();
 TuEtwas_N();
TuEtwas_A();
 TuEtwas_B();
TuEtwas_A();
TuEtwas_N();

} 

schreiben.

Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.)

Autor: 2ter Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn das für Dich die einfachste Lösung deines Problems ist... happy 
hacking.

Autor: Nachtaktiver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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. :)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.