Hallo,
ich bin ein ziemlicher Anfänger in der Programmierung mit Atmegas und
wollte mal eine grundsätzliche Frage stellen.
Ich verwende einen Atmega8 und will einen Steuerbefehl über den UART
ausgeben. Danach soll mir das Gerät antworten und einige Messdaten
zurücksenden.
So nun zu meiner Frage:
Soll ich das ganze Interruptgesteuert machen oder einfach den Befehl
senden und danach warten bis etwas zurückkommt, also ohne Interrupt.
Wo liegt der Vor- bzw. Nachteil der beiden Methoden.
Kann es zu irgendwelchen Fehlern kommen, wenn man einfach darauf bis man
die Daten empfängt?
Ich habe hier schon einmal etwas getestet, ist jetzt aber nur ein Auszug
davon und nicht das ganze Programm.
Das Problem ist es funktioniert nicht immer. Einmal funktioniert es gut
und dann bei dem nächsten Befehl nicht mehr, obwohl es genau der gleiche
Code ist und nur der gesendete Befehl und die zu erwartende Antwort
verändert wurde. Kann das ein Problem sein wenn ich einfach auf eine
Antwort warte?
Atmega Anfänger schrieb:> Wo liegt der Vor- bzw. Nachteil der beiden Methoden.
Der Vorteil, es mit Interrupts zu realisieren liegt darin, dass dein
Controller nebenbei noch andere Sachen erledigen kann.
Wenn er die ganze Zeit auf ein Zeichen vom USART wartet, dann ist das
verschenkte Zeit.
Atmega Anfänger schrieb:> Kann es zu irgendwelchen Fehlern kommen, wenn man einfach darauf bis man> die Daten empfängt?
Ja.
Wartschleifen in Programmen sind immer irgendwie doof (weil sinnlos...).
Wieso hast du da ein Delay drin, wenn du danach eine Funktion aufrufst,
die vermutlich auf Polling basiert (wie funktioniert "uart_gets" ?).
Atmega Anfänger schrieb:> Soll ich das ganze Interruptgesteuert machen oder einfach den Befehl> senden und danach warten bis etwas zurückkommt, also ohne Interrupt.
Kommt drauf an, wie komplex deine gesamte Anwendung wird, bzw. werden
könnte.
Wenn die einzige Aufgabe, die oben beschriebene ist, so ist es
sicherlich egal.
In der Polling-Methode wäre dann aber noch ein Timeout angebracht.
Atmega Anfänger schrieb:> Wo liegt der Vor- bzw. Nachteil der beiden Methoden.
Der Vorteil von Interupts ist, wie schon gesagt wurde, dass man nicht
warten muss im Programmcode bis was passiert sondern so lange andere
Dinge machen kann.
Der Nachteil ist, dass mehrere Interrupts gleichzeitig auftreten können
und dann kann das Interrupthandling knifflig werden.
Ich finde, einen "normalen" Programmaufbau (also nicht
Interrupt-basiert) ist für jemand Fremdes besser lesbar. Ich persönlich
baue meine AVR-Codes (benutze den Atmega 88PA) aber auch immer
Interruptgesteuert da man hier, finde ich zumindest, eine bessere
Kontrolle über den Atmega hat wenn auch der Code dabei
"komplexer/undurchsichtiger" wird.
Ich denke, welche Form mal wählt, hängt zum einem von der
Aufgabenstellung ab und zum anderen aber auch von der Vorliebe des
Programmierers.
Weil höchst wahrscheinlich in 'input' nicht das steht, was du denkst,
dass da drinn stehen sollte.
Solche Dinge:
zwischen[0]=input[7];
zwischen[1]=input[8];
zwischen[2]=input[9];
zwischen[3]=input[10];
zwischen[4]=input[11];
zwischen[5]=input[12];
zwischen[6]='\0';
sind immer strategisch gesehen gefährlich. Ein einziges zusätzliches
Leerezeichen oder ein nicht abgeholter Carriage Return oder Line Feed,
der noch im Input Buffer rumlungert, und schon ist das ein Griff ins
Klo.
Lass dir halt input bzw. zwischen mal irgendwo ausgeben. Dann siehst du
genau, aus welchem Grund der
strcmp( zwischen, "ELM327");
keine Übereinstimmung findet.
Wenn du Ratespielchen nicht magst, dann gibt es eine einfache aber
wirkungsvolle Abhilfe dagegen: Lass die Variablen irgendwo ausgeben!
Genau aus diesem Grund finde ich es leichtsinnig, wenn man sich gerade
als Anfänger nicht als allererstes eine Möglichkeit dazu schafft. Sei es
ein LCD oder eine zusätzliche UART, an der ein Terminal-PC hängt und auf
dem man nach Herzenslust Zwischenergebnisse rausgeben kann. Wenn du
deinem Programm mangels geeignetem Debugger nicht während der Laufzeit
zusehen kannst, dann musst du eben das nächst Beste tun: Dein Programm
muss dir helfen festzustellen, was es warum tut. Den Code, den es
abarbeitet, kennst du. Aber die aktuell gerade gültigen Variablenwerte
kennst du nicht. Daher ....
Wenn du dein grundsätzliches Problem gelöst hast, würde ich dir
empfehlen, zumindest den Empfängercode interruptbasiert zu machen. Dann
entgeht dir kein Zeichen, das der ELM senden könnte. Dafür ist es
allerdings nötig, ein paar Flags ins Programm zu nehmen, um dem
Hauptprogramm den Status der Interruptroutine mitzuteilen, und evtl.
Buffer zu behandeln.
Viel Spass mit OBD-II
Aber wie soll ich es denn sonst machen wenn ich eine Antwort von dem OBD
Auslesegerät auswerten will?
Da bleibt mir doch nichts anderes übrig, außer die einzelnen Teile so
rauszusuchen, oder?
Jetzt beim Testen sende ich die Befehle auch mit einem Terminalprogramm.
Also sollten doch die Zeichen auch so ankommen. Aber ich werde es jetzt
nochmal vorher mir zum Terminalprog zurückschicken lassen.
Wie kann ich das ganze dann aber verbessern, damit das im Normalbetrieb
dann später nicht passiert?
mfg
Oliver schrieb:> Wobei eine Debuggerausgabe über UART bei der Entwicklung eines> UART-Treibers schon ein gewisses Henne-Ei-Problem darstellt ;)
Zugegeben. Aber es gibt ja auch noch LCD bzw. andere Möglichkeiten.
Atmega Anfänger schrieb:> Da bleibt mir doch nichts anderes übrig, außer die einzelnen Teile so> rauszusuchen, oder?
Du suchst aber nichts raus.
Du nimmst an, dass das gesuchte genau an diesen Positionen im String
steht!
Das ist ein Unterschied!
Ich habe jetzt das ELM327 Protokoll nicht im Kopf, aber mit Sicherheit
sendet der Chip am Ende einer Zeichenkette so etwas wie in CR oder ein
LF, zumindest ein Marker, das er fertig ist mit der Übertragung.
Während des normalen Zeichenempfangs schreibt die ISR einfach alle
Zeichen in einen Pufferspeicher. Sobald die ISR den 'Ende' Marker
findet, setzt sie ein Flag a la 'Kompletter String empfangen' und das
Hauptprogramm kann den Puffer dann anzeigen, bearbeiten etc.
Paule H. schrieb:> Wenn er die ganze Zeit auf ein Zeichen vom USART wartet, dann ist das> verschenkte Zeit.
für die man aber leider kein Geld kriegt,
wenn man nichts besseres vor hat ist Polling auch ok
Atmega Anfänger schrieb:> Wie kann ich das ganze dann aber verbessern
indem du du die Antworten liest (Tipp: delay)
Atmega Anfänger schrieb:> Kann es zu irgendwelchen Fehlern kommen, wenn man einfach darauf bis man> die Daten empfängt?
Da kannst Du Gift drauf nehmen! Vor allem die Zuweisung
> zwischen[0]=input[7];> zwischen[1]=input[8];> zwischen[2]=input[9];> zwischen[3]=input[10];> zwischen[4]=input[11];> zwischen[5]=input[12];> zwischen[6]='\0';
ist sehr optimistisch. Wenn Dein Input nur um ein Zeichen verrutscht,
geht es schon schief! Während Deines _delay_ms(20) gehen alle Zeichen
bis auf das letzte verloren! Keine Ahnung was uart_gets genau macht,
wahrscheinlich wartet es dann bis in alle Ewigkeit auf diese verlorenen
Zeichen.
Ich würde folgendes empfehlen:
Mach den Empfang mit IRQ-Handler. Der IRQ-Handler schreibt die
empfangenen Bytes in ein char Array (=String). Der String wird jeweils
vom Handler mit ASCII 0 C-konform abgeschlossen.
In dem String sucht die Hauptschleife mittels strstr dann ELM327. Wenn
ELM327 gefunden wurde, wird der String wieder gelöscht.
Servus
Michael
An dem Delay kann es nicht liegen. Wenn ich es händisch eingebe dann bin
ich sicher langsamer als 20 ms.
@michael
Danke für deinen Tipp das werd ich mal versuchen.
>Der Nachteil ist, dass mehrere Interrupts gleichzeitig auftreten können
und dann kann das Interrupthandling knifflig werden.
Nein. Eigentlich nicht. Man darf enfach nicht auf dieselben Variablen
zugreifen.
Atmega Anfänger schrieb:> An dem Delay kann es nicht liegen. Wenn ich es händisch eingebe dann bin> ich sicher langsamer als 20 ms.
Oh, oh.
Eine grosse Fehleinschätzung.
Handeingabe entspricht diesem hier:
1
uart_puts("A");// OBD Reader initialisieren
2
_delay_ms(xxx);
3
uart_puts("T");// OBD Reader initialisieren
4
_delay_ms(xxx);
5
uart_puts(" ");// OBD Reader initialisieren
6
_delay_ms(xxx);
7
uart_puts("Z");// OBD Reader initialisieren
8
//Siehst du den Unterschied?
Nach dem "Z" rauscht dein ODB ohne sonderliche Verzögerung los und der
Empfänger MUSS bereit sein. Und das ist er auch.
Aber nicht hier:
Michael M. schrieb:> In dem String sucht die Hauptschleife mittels strstr dann ELM327. Wenn> ELM327 gefunden wurde, wird der String wieder gelöscht.
Trotzdem würde ich mir zuallerst mal ansehen, wie und warum der String
offenbar manchmal verrutscht ist. Aus diesen Erkentnissen lernt man und
hat dann bei anderen Dingen weniger Probleme, wenn man erst mal diese
Basisproblemchen ausgeräumt hat bzw. davon weiß.
Thomas Eckmann schrieb:> Handeingabe entspricht diesem hier:
Ich hab das anders rum interpretiert. Ich denke er meint: Er simuliert
momentan händisch das OBD Gerät.
Karl Heinz Buchegger schrieb:> Ich hab das anders rum interpretiert. Ich denke er meint: Er simuliert> momentan händisch das OBD Gerät.
und deswegen stört in dem Fall das delay nicht,
wenn die Antwort aber vom OBD kommt gehen evtl. Zeichen verloren.
Das delay ist sowohl sinnlos als auch falsch
Walter S. schrieb:> Das delay ist sowohl sinnlos als auch falsch
Das zweifellos.
Ich denke da hat er etwas falsch interpretiert. Bei "AT Z" steht ja oft
dabei, dass man den Geräten dann erst mal eine kurze Pause gönnen muss
(weil sie ja neu initialisieren). Gemeint ist damit aber, dass man keine
weiteren Kommandos in einem Zeitraum schicken darf. Sich erst mal eine
zeitlang 'tot' stellen, ehe man auf Antwort wartet, ist allerdings
sinnlos. Denn das Gerät antwortet sowieso nicht früher, als wenn es
bereit ist. Nur sollte der Empfänger (also der Mega) tunlichst dann auch
schon bereit sein.
Gut, in seinem Fall mit Handsimulation des Geräts spielt es noch keine
Rolle, drum hab ich auch nichts gesagt.
Karl Heinz Buchegger schrieb:> Michael M. schrieb:>>> In dem String sucht die Hauptschleife mittels strstr dann ELM327. Wenn>> ELM327 gefunden wurde, wird der String wieder gelöscht.>> Trotzdem würde ich mir zuallerst mal ansehen, wie und warum der String> offenbar manchmal verrutscht ist. Aus diesen Erkentnissen lernt man und> hat dann bei anderen Dingen weniger Probleme, wenn man erst mal diese> Basisproblemchen ausgeräumt hat bzw. davon weiß.
Das ist schon richtig. Man sollte sich nicht auf die Fehlertoleranz des
Protokolls verlassen und seine Protokollfehler ignorieren. Aber im
Hinblick auf die (leider nicht genannte) Applikation ist der Ansatz,
erst mal alle Zeichen zuverlässig zu empfangen sicher auch nicht
verkehrt. Nur genau das zu empfangen, was man erwartet und das noch mit
_delay_ms() hingetrimmt wird relativ bald schief gehen.
Zumal der korrekte Rückgabewert wohl eher so aussieht
1
ELM327 v1.4b
2
>
So sieht es zumindest im Datenblatt
http://elmelectronics.com/DSheets/ELM327DS.pdf aus. Wenn da dann die
letzten Zeichen des Versionsstrings oder das neue Prompt > nicht
mitgelesen werden, ist der Fehler schon geklärt. Wie uart_gets()
aussieht, wissen wir ja nicht.
Servus
Michael
Ok das delay ist falsch das hab ich jetzt schon verstanden.
Ja vom OBd Lesegerät kommt nicht nur ELM327 zurück sondern
AT Z ELM327 v1.4 >
Wenn ich aber nur das ELM327 abfrage muss es doch auch reichen oder
nicht?
Also das mit der Strstr Funktion hört sich doch recht gut an oder liege
ich da auch schon wieder falsch?
Michael M. schrieb:> Wenn da dann die> letzten Zeichen des Versionsstrings oder das neue Prompt > nicht> mitgelesen werden, ist der Fehler schon geklärt.
wenn das Prompt > nicht mitgelesen wird, dann passiert gar nichts weil
ich das als Endzeichen genommen habe.
Michael M. schrieb:> Wie uart_gets()> aussieht, wissen wir ja nicht.
1
uint8_tuart_getc(void)
2
{
3
while(!(UCSRA&(1<<RXC)))// warten bis Zeichen verfuegbar
4
;
5
returnUDR;// Zeichen aus UDR an Aufrufer zurueckgeben
6
}
7
8
voiduart_gets(char*Buffer,uint8_tMaxLen)
9
{
10
uint8_tNextChar;
11
uint8_tStringLen=0;
12
13
NextChar=uart_getc();// Warte auf und empfange das nächste Zeichen
Oliver schrieb:> Wobei eine Debuggerausgabe über UART bei der Entwicklung eines> UART-Treibers schon ein gewisses Henne-Ei-Problem darstellt ;)
Nicht mit einer vernünftigen Entwicklungsumgebung. Da gibt's nämlich so
nette Sachen wie Simulatoren und Stimuli-Dateien...
Atmega Anfänger schrieb:> Ja vom OBd Lesegerät kommt nicht nur ELM327 zurück sondern> AT Z ELM327 v1.4 >>> Wenn ich aber nur das ELM327 abfrage muss es doch auch reichen oder> nicht?
Ja klar, näheres unten. Aber eben im Hinterkopf behalten: Es gibt ein
Echo (der ELM327 gibt Eingabezeichen 1:1 zurück) und er schreibt weitere
Daten incl. neuem Prompt.
> Also das mit der Strstr Funktion hört sich doch recht gut an oder liege> ich da auch schon wieder falsch?
Ich finde strstr zum Parsen von Strings recht praktisch. Vor allem
bekommt man einen Zeiger auf die Fundstelle. Weil man ja die Länge des
Suchstrings kennt, kann man schauen, welche Argumente danach kommen:
1
char*p;// Zeiger für die Fundposition
2
floatversion;
3
4
p=strstr(input,"ELM327 v");
5
if(p!=NULL)// ansonsten war kein "ELM327 v" drin
6
{
7
version=(float)strtod(p+8,&p);// Zahl beginnt 8 Zeichen danach
8
// jetzt könnte man mit p weiterparsen, wenn es mehrere Argumente gäbe
9
}
> wenn das Prompt > nicht mitgelesen wird, dann passiert gar nichts weil> ich das als Endzeichen genommen habe.
Ok, das ist eine wichtige Info. Wobei ich sagen würde, > wird in Deiner
uart_getc mitgelesen, aber als Stoppzeichen verworfen. Das macht sehr
wohl einen Unterschied: RXC in UCSRA wird nämlich durch das Lesen von
UDR gelöscht.
>
1
>voiduart_gets(char*Buffer,uint8_tMaxLen)
2
>{...}
Ok, das schaut schonmal gar nicht so verkehrt aus. Trotzdem solltest Du
folgende Fälle bedenken:
Wenn der ELM327 aus irgendwelchen Gründen keine Daten zurückschickt,
bleibt uart_gets() bis in alle Ewigkeit hängen. Denn das erste
uart_getc() wird nie verlassen.
Wenn uart_gets() wegen StringLen < MaxLen - 1 abbricht, bleiben
eventuell Zeichen im UDR stehen. Die werden dann beim nächsten Aufruf
von uart_gets() ausgelesen und stehen vor dem erwarteten Rückgabestring.
Das ist genau der Punkt, wo dann eine Zuordnung wie
zwischen[0]=input[7]; schief geht.
Es bleibt jedem selber überlassen, ob er pollt oder einen IRQ-Handler
benutzt. Ich finde es halt praktisch, wenn der Controller was sinnvolles
wie LCD-Ausgaben erledigen kann, während er auf die Daten wartet. Vor
allem muß man beim Pollen immer aufpassen, daß man nicht in einer
Endlosschleife stecken bleibt, weil der erwartete Input irgendwie fehlt.
Servus
Michael
Ob Code mit Interrupts schlechter lesbar sind, ist eine Frage des
(schlechten) Programmierstils. Dies ist nur ein subjektiver Trugschluß
aus der Situation heraus.
Das ist genau so sinnvoll, als ob an Sagen würde:
"Es regnet nur wenn ich den Fuß vor die Wohnungstür setze."
Wenn man mal aus dem Fenster gucken würde, wäre es offensichtlich, daß
es auch mal zu anderen Zeiten regnet.
Umgekehrt kann man aber sagen:
Wer sich die Mühe macht um über Interrupts und den Code nachzudenken,
entwickelt manchmal die Fähigkeit strukturierten und wirklich lesbaren
Code zu schreiben. Bei Interrupts werden oder sollten Programmteile
ausgegliedert werden. Exakt das ist eines der Kernelemente die
Spaghetti-Code von lesbarem Code unterscheiden.
Bei Kleinstprogrammen fällt das nur nicht so schnell auf, da man nicht
ausgliedert um des Ausgliederns Willen, sondern zum Strukturieren. Hat
man nur ein, zwei Programmfunktionen, so ist nichts zum strukturieren
da. Folglich ist dort kaum ein Unterschied zu erkennen. Mit wachsender
Komplexität wird der Unterschied zwischen "quick&dirty" und
"programmieren mit System" größer.
Ohne System scheitert man an Interrupts. Die Strukturierug zu
unterlassen und Interrupts in Spaghettimanier zu nutzen ist ganz großer
Mist! Kurzfristig mag es gut gehen, aber man stößt schnell an die
Grenzen. Und dann sind natürlich die bösen Interrupts schuld.
Aber nicht die Interrupts sind das Problem, sondern der Progammierstil!
Gruß Carsten