Moin! Ich lese mit einem Atmega 1248p (hat 2 USART) eine RC Fernsteuerung und ein GPS Modul aus. Beides per interrupt. Ich gehe davon aus dass eine Interruptroutine die andere unterbrechen kann. Jetzt könnte man per cli() und sei() innerhalb der Interruptroutine genau dies unterbinden. Läuft man dann nicht Gefahr dass die eine oder andere Übertragung "unter den Tisch fällt"? Wie macht man das richtig? Danke!
Attila C. schrieb: > Läuft man dann nicht Gefahr dass die eine oder andere Übertragung "unter > den Tisch fällt"? Das kommt drauf an, was die Interruptroutinen so treiben. Wenn sie kurz sind, keine aufwendigen Berechnungen oder gar Warteschleifen enthalten, wenn also die Laufzeit der Routinen deutlich kürzer ist als die Zeit, die zwischen zwei Zeichen auf der UART vergeht, dann passiert nichts. Bei 9600 Baud hast Du etwa eine Millisekunde Zeit, da kann auch ein µC einiges anstellen. Wenn aber aus der Interruptroutine heraus aufwendiger Kram aufgerufen wird, gar die Ausgabe von formatierten Strings auf eine andere Schnittstelle, dann kracht es. Warum aber willst Du die Interrupts abschalten? Greifen beide Interruptroutinen auf gemeinsame Ressourcen zu?
Attila C. schrieb: > Ich gehe davon aus dass eine Interruptroutine die andere unterbrechen > kann. Nein nicht ohne weiteren Eingriff in den ISR-Code.
Beide Routinen schauen nach einem Zeichen (um den Anfang einer Übertragung zu identifizieren) um dann Zeichen in globale Arrays "zu schaufeln" Das ganze bei 115200 Baud. Also 5 Zeilen Code etwa. Mal angenommen eine Routine liest grade ein byte ein. Nach Übertragung von z.B. 4 bit geht der andere interrupt los und unterbricht. Ist dieses "Szenario" überhaupt denkbar?
Attila, ich verwende jeweils einen TX-, und RX-Fifo pro USART. Die Interruptservice Routinen lesen oder schreiben jeweils nur ein Byte in die FIFO-Puffer. Das wars. Die gesamte Interpretation der eingegangenen Zeichen passiert in der Hauptprogrammschleife, also außerhalb der Interruptverarbeitung !
Nachmals, eine Unterbrechung einer Interruptservice Routinen findet nicht statt, außer man programmiert es speziell und weis dass man einen Stacküberlauf produzieren könnte.
Attila C. schrieb: > Ich gehe davon aus dass eine Interruptroutine die andere unterbrechen > kann. Auf einem Atmega? Nö, der hat nur eine Interrupt Priorität - nur ein Interrupt kann aktiv sein. Mann muss sei/cli anwenden wenn man das so nicht haben will, also Interrupts im Interrupt zulassen will. Dabei kann man sich aber prima in den Fuß schiessen.
Nein. Wie schon geschrieben wurde, sind beim AVR serienmäßig innerhalb von Interupptroutinen Interrupts gesperrt. So lange du also da einfach die Finger von sei und cli lässt, ist alles in Ordnung. Abgesehen davon empfangen die Usarts ihre Daten völlig unabhängig vom Programmablauf. Das Szenario, das nach 4 Bit irgendwer irgendwas unterbricht, gibt es überhaupt nicht. Etwas Datenblattlektüre ist angeraten. Oliver
:
Bearbeitet durch User
Jim M. schrieb: > Auf einem Atmega? Nö, der hat nur eine Interrupt Priorität das stimmt nicht ... > - nur ein Interrupt kann aktiv sein. das stimmt (im Normalfall) allerdings schon. Die Prioritäten sind nur dann von Bedeutung, wenn - 2 Interrupts exakt gleichzeitig auftreten - innerhalb einer ISR mehrere andere Interrupts auftreten. Dann wird die mit höherer Priorität zuerst ausgeführt - im 2. Fall nach Verlassen der laufenden ISR.
Attila C. schrieb: > Mal angenommen eine Routine liest grade ein byte ein. Nach Übertragung > von z.B. 4 bit geht der andere interrupt los und unterbricht. Ist dieses > "Szenario" überhaupt denkbar? Du solltest Dir ansehen, wie eine UART funktioniert und wann sie ihren RX-Interrupt auslöst. Und wenn ein µC einen 8-Bit-Zugriff macht, erfolgt der nicht sequentiell mit einzelnen Bits, sondern das ist ein Zugriff.
Attila C. schrieb: > Mal angenommen eine Routine liest grade ein byte ein. Nach Übertragung > von z.B. 4 bit geht der andere interrupt los und unterbricht. Ist dieses > "Szenario" überhaupt denkbar? Nein. -> Rx1_Byt1 -> Rx1_Byt2 -> Rx1_Byt3 -> Rx2_Byt1 -> Rx1_Byt4 usw. Was soll da unterbrochen werden ? Rufus Τ. F. schrieb: > Du solltest Dir ansehen, wie eine UART funktioniert und wann sie ihren Genau. Wenn ein Byt empfangen worden ist, wird ein Interrupt ausgelöst, nicht beim 5-ten bit oder so. Da gibt es ganz einfach nichts was unterbrochen werden kann...
Jim M. schrieb: > Auf einem Atmega? Nö, der hat nur eine Interrupt Priorität - nur ein > Interrupt kann aktiv sein. Und selbst wenn Interrupt 2 den Interrupt1 unterbricht, was soll passieren? Ist ISR2 fertig, wird ISR1 fortgesetzt und alles ist gut. Unter 2 Voraussetzungen: 1. Die ISR sind korrekt programmiert, sichern Register usw., aber sonst funktioniert sowieso nichts. 2. Der Ablauf einer ISR ist viel kürzer als die Zeit zwischen 2 Zeichen. Beides ist nicht schwierig sondern eher selbstverständlich. Georg
Attila C. schrieb: > Ich lese mit einem Atmega 1248p (hat 2 USART) eine RC Fernsteuerung und > ein GPS Modul aus. Beides per interrupt. > > Ich gehe davon aus dass eine Interruptroutine die andere unterbrechen > kann. Kann sie normalerweise nicht. > Jetzt könnte man per cli() und sei() innerhalb der > Interruptroutine genau dies unterbinden. Genau damit (mit sei()) machst du es erst möglich... > Läuft man dann nicht Gefahr > dass die eine oder andere Übertragung "unter den Tisch fällt"? Diese Gefahr besteht immer. Jedenfalls immer dann, wenn in der ISR mehr passiert, als zwischen zwei Zeichen Rechenzeit verfügbar ist. Der Trick ist also, die Programmierung so effizent zu gestalten, das zwischen zwei Zeichen immer genug Rechenzeit verfügbar ist, um sie zu verarbeiten. Bei den AVR-Hardware-UARTs ist es allerdings nicht ganz so kritisch, weil die in Hardware bereits ein Double-Buffering bietet. Hier darf also die Bearbeitung eines Zeichens auch "gelegentlich mal" etwas länger dauern, nämlich fast zwei Zeichenlängen. Der dadurch aufgenommene "Kredit" an Rechenzeit muss bei einer kontinuierlichen Übertragung allerdings bei den nächsten Zeichen dadurch wieder ausgeglichen werden, dass deren Behandlung schneller als in einer Zeichenlänge erfolgt, denn ein weiterer "Kredit" ist erst dann möglich, wenn der vorige zurückgezahlt wurde. Man kann das Prinzip des Double-Buffering aber mittels Software-FIFO noch ausweiten und damit weitere Entspannung bekommen. Die Zeichenverarbeitung erfolgt dann nicht mehr in der ISR, sondern in main(), in der ISR werden die Zeichen bloss in den FIFO verfrachtet, aus dem sie dann in main() gelesen werden. Aber auch mit allen Buffertricks muss am Ende gelten: Die Verarbeitung der Zeichen muss im Schnitt mindestens mit der Geschwindigkeit des Zeicheneingangs erfolgen. D.h.: Buffering löst nur das Problem kurzfristiger Engpässe an Rechenzeit. Und dazu kommt: Software-Buffering kostet weitere Rechenzeit, über die zur Zeichenverarbeitung eigentlich nur nötige hinaus. In der Gesamtbilanz der Rechenzeit ist das also eigentlich kontraproduktiv.
c-hater schrieb: > Software-Buffering > kostet weitere Rechenzeit Minimal. Hat im Endeffekt aber mehr Vorteile.
Rainer S. schrieb: > c-hater schrieb: >> Software-Buffering >> kostet weitere Rechenzeit > > Minimal. > Hat im Endeffekt aber mehr Vorteile. Das kommt schlicht drauf an. Nämlich auf die Gesamtstruktur der Anwendung und die Datenraten, die zu verarbeiten sind. Es gibt durchaus Anwendungen, in denen Software-FIFOs kontraproduktiv sind. Je höher die Datenrate, desto wahrscheinlicher ist das so. Denn, wie man leicht erkennen kann, wirkt sich der Overhead eines Software-FIFO mit steigender Datenrate immer stärker negativ aus. D.h.: wenn es eng wird, sollte man vorzugsweise versuchen, den Datenstrom mit der höchsten Rate direkt in der ISR zu verarbeiten.
c-hater schrieb: > vorzugsweise "vorzugsweise"? Dann habe ich ein anderes "vorzugsweise", als du. Ich würde eher sagen: > Nur, wenn es nicht anders geht!
c-hater schrieb: > sollte man vorzugsweise versuchen, den > Datenstrom mit der höchsten Rate direkt in der ISR zu verarbeiten. Was immer man unter Verarbeiten versteht - empfangene Werte auf einer Speicherkarte zu speichern gehört ganz sicher nicht in die ISR, solche Behauptungen führen einen Anfänger nur in die Irre (gewollt?). Georg
c-hater schrieb: > D.h.: wenn es eng wird, sollte man vorzugsweise versuchen, den > Datenstrom mit der höchsten Rate direkt in der ISR zu verarbeiten. Attila C. schrieb: > Ich lese mit einem Atmega 1248p (hat 2 USART) eine RC Fernsteuerung und > ein GPS Modul aus. Beides per interrupt. Das sollte locker gehen. Ich selbst benutze den Atmega 1248p, bzw. den etwas kleineren mit 2 seriellen Schnittstellen und mache es so, dass ich einen 16 Byte Ringbuffer habe für beide Eingänge. Funktioniert einwandfrei. Eine Schnittstelle mit maximal 19200 Baud (höher geht aber auch) und die andere mit 100000 Baud. Wobei hier noch die Frage ist, ob das GPS Modul unbedingt schnell ausgelesen werden braucht.
Ich habe es jetzt so gelöst:
1 | ISR(USART0_RX_vect) |
2 | {
|
3 | static int z=0; |
4 | |
5 | if (UDR0==0xEE) |
6 | z=0; |
7 | else
|
8 | {
|
9 | data[z]=UDR0; |
10 | z++; |
11 | }
|
12 | }
|
13 | |
14 | ISR(USART1_RX_vect) |
15 | {
|
16 | static int z=0,state=0; |
17 | if (UDR1=='$') |
18 | {
|
19 | z=0; |
20 | state=0; |
21 | }
|
22 | if(UDR1=='G'&&z==0) |
23 | z++; |
24 | if(UDR1=='P'&&z==1) |
25 | z++; |
26 | if(UDR1=='R'&&z==2) |
27 | z++; |
28 | if(UDR1=='M'&&z==3) |
29 | z++; |
30 | if(UDR1=='C'&&z==4) |
31 | z++; |
32 | if(UDR1==','&&z==5) |
33 | {
|
34 | z=0; |
35 | state=1; |
36 | }
|
37 | if(state==1) |
38 | {
|
39 | gpsdata[z]=UDR1; |
40 | z++; |
41 | }
|
42 | }
|
-- Mit den C-Tags wirds etwas lesbarer. -rufus
:
Bearbeitet durch User
Bei einem AVR-Controlelr musst du das UDR in einer Variablen zwischenspeichern, wenn du es mehrfach verwenden willst. Beim ersten Lesezugriff wird es gelöscht.
Außer der merkwürdigen Formatierung finde ich es auch wenig elegant und mit vermutlich ineffizientem Ergebniscode gelöst. Ich würde ein const char Array vorschlagen mit dem Inhalt "$GPRMC,". Dann etwa so:
1 | if(UDR1 == keyword[z]) |
2 | z+=1; |
3 | else
|
4 | z=0; |
:
Bearbeitet durch User
Über deine USART0 Routine freuen sich die Hackers. Schön Code einschleusen wegen fehlender Überwachung. O.K. du killst wahrscheinlich nur dein RAM, aber trotzdem böse.
@Rufus: Was ist an der Formatierung falsch oder wie müsste es besser aussehen? Ich mache grundsätzlich bei jedem "blauen code" (Atmel Studio 7) ein tab und rücke somit nach rechts und zwar solange bis es um etwas anderes geht. Beispiel: Erbsen Tomaten Gurken Speck Leber Hack @Thomas: Es gelingt mir nicht deinen Vorschlag umzusetzen. Ich "erkenne" zwar so wie Du vorschlägst das "$GPRMC" weiss aber nicht wie es dannach weitergehen soll. Im besonderen ist mir nicht klar unter welcher Bedingung denn z wieder 0 werden soll?
Attila C. schrieb: > @Rufus: Was ist an der Formatierung falsch oder wie müsste es > besser > aussehen? Ich mache grundsätzlich bei jedem "blauen code" (Atmel Studio > 7) ein tab und rücke somit nach rechts und zwar solange bis es um etwas > anderes geht. Beispiel: > > Erbsen > Tomaten > Gurken > > Speck > Leber > Hack > Deine Abfragen haben doch alle die gleiche "Priorität": Sie werden alle abgearbeitet. Ein weiteres Einrücken liest sich, als würde der Programmteil vom weniger stark eingerückten Programmteil abhängen. Verwirrt einfach nur. > @Thomas: Es gelingt mir nicht deinen Vorschlag umzusetzen. Ich "erkenne" > zwar so wie Du vorschlägst das "$GPRMC" weiss aber nicht wie es dannach > weitergehen soll. Im besonderen ist mir nicht klar unter welcher > Bedingung denn z wieder 0 werden soll? z kannst =0 setzen, sobald ein "," auftaucht. Dann musst du die vorher empfangenen Zeichen in eine Zahl umwandeln. In der ISR solltest du aber nur einen Puffer füllen und vielleicht noch das Start- und das Endezeichen erkennen.
Attila C. schrieb: > @PICfreund: ??? Sowas interessiert vielleicht bei SQL-Datenbanken, aber nicht bei einem AVR-Programm.
Ist das so eleganter: ISR(USART1_RX_vect) { static int y=0,z=0,state=0; char desig[7]="$GPRMC,"; if (UDR1=='$') { y=1; state=0; z=0; } if (UDR1==desig[y]) { y++; state=1; } else { y=0; } if(state==1) { gpsdata[z]=UDR1; z++; } }
Du solltest das mindestens so machen, denn dein UDR0 ist nach der IF Abfrage schon wieder leer:
1 | ISR(USART0_RX_vect) |
2 | {
|
3 | static int z=0; |
4 | uint8_t inBuffer = UDR0; |
5 | |
6 | if (inBuffer==0xFF) |
7 | z=0; |
8 | else
|
9 | {
|
10 | data[z]=inBuffer; |
11 | z++; |
12 | }
|
13 | }
|
P.s. auf dem Handy Code zu schreiben ist seeehr Mühselig :-D
Attila C. schrieb: > Ist das so eleganter: Eleganter ist nicht immer besser und schneller. So ?
1 | uint8_t gpschar = UDR1; |
2 | state = 0; |
3 | |
4 | switch(z) |
5 | {
|
6 | case 0x00: |
7 | if(gpschar == '$') z++; |
8 | else z = 0; |
9 | break; |
10 | case 0x01: |
11 | if(gpschar == 'G') z++; |
12 | else z = 0; |
13 | break; |
14 | case 0x02: |
15 | if(gpschar == 'P') z++; |
16 | else z = 0; |
17 | break; |
18 | case 0x03: |
19 | if(gpschar == 'R') z++; |
20 | else z = 0; |
21 | break; |
22 | case 0x04: |
23 | if(gpschar == 'S') z++; |
24 | else z = 0; |
25 | break; |
26 | default : |
27 | if (z > BufferSize) { z=0; break; } |
28 | if(gpschar != 0x0D) z++; |
29 | else state = 1; |
30 | }
|
31 | |
32 | gpsdata[z]=gpschar; |
33 | if(state==1) { |
34 | setflag; // oder etwas anderes... |
35 | }
|
Ich habe es jetzt so: ISR(USART1_RX_vect) { static int y=0,z=0; char desig[7]="$GPRMC,"; if (UDR1=='$') { y=1; z=0; } if (UDR1==desig[y]) y++; else y=0; if(y>6) { gpsdata[z]=UDR1; z++; } } @Marc: Danke! Ist ein "switch" schneller als 6 mal "if" ?
Die ISR ist schon zu überladen. Bei der ISR das Zeichen kurz in den Ringbuffer schreiben (ist wirklich nicht schwer) und in der main() alle andere verarbeiten. So hast Du auch keine Probleme mit dem Mehrfachzugriff auf UDR0/1. So wie oben beschrieben. https://www.mikrocontroller.net/articles/FIFO
Attila C. schrieb: > @Marc: Danke! Ist ein "switch" schneller als 6 mal "if" ? Nein, langsamer. Aber switch wird normalerweise auch zu if übersetzt. Es sind nur ein paar Takte mehr und es wird immer nur ein case ausgeführt. Attila C. schrieb: > Ups! Disregard: So funktionert es natürlich nicht! Was funktioniert nicht ?
:
Bearbeitet durch User
@Marc: Meine letzte Lösung finktionert nicht weil im "else" Teil y auf 0 gesetzt wird und somit die Bedingung y>6 genau ein mal gegeben ist. @Rainer: 1) Danke! 2) Tatsächlich? Diese ISR wäre so "überladen?"
So, jetzt aber: ISR(USART1_RX_vect) { static int y=0,z=0; char desig[7]="$GPRMC,"; if (UDR1=='$') { z=0; } if (UDR1==desig[z]) z++; if(z>6) { gpsdata[z]=UDR1; z++; } } Ist das immer noch zuviel "gerödel" für eine ISR?
Nein, aber deine UDR1 ist nach dem ersten Lesen bei der ersten IF Schleife schon "leer". UDR wird nach dem Lesen gelöscht. Bei der zweiten IF Abfrage liefert UDR dann immer 0x00.
Überlebt dein Programm folgendes als Eingabe?
1 | ser.write(0xEE); |
2 | for (uint16_t i=0; i < 4096; i++) |
3 | {
|
4 | ser.write('k'); |
5 | ser.write('i'); |
6 | ser.write('l'); |
7 | ser.write('l'); |
8 | }
|
Meistens ist es bei Schnittstellen recht schön, wenn Eingabefehler abgefangen werden. Vermeidet Folgefehler.
Marc V. schrieb: > Eleganter ist nicht immer besser und schneller. Hm - und welches Codemonster macht der Compiler aus Deinem switch-case? Was soll an dem Array nicht gut sein? Einen Zeiger+Index auf ein (const char) Array im ROM kriegt der Compiler sicher mit ein paar Befehlen gebacken, und dann ist es maschinentechnisch nur noch eine einzige Vergleichsoperation und ein bedingter Sprung, nicht womöglich 6 davon. Ich gebe Dir Recht, daß "eleganter" nicht IMMER besseren Code erzeugt, hier kann ich mir aber nicht vorstellen, daß eine "if" oder "switch"-Orgie besser sein soll. Außerdem ist das mit dem Array doch viel besser les- und wartbar, und ließe sich ggf. auch leichter auf mehrere Schlüsselstrings erweitern, was ich mir mit so einem Swirch-Case nicht so gut vorstellen kann. Im Prinzip ist ein Ringpuffer schon besser, die Auswertung muss man aber so oder so machen, und das Prinzip (ob mit Array, if oder switch) gilt natürlich ggf. auch für eine Auswertung in der main().
Attila, ich denke, es wäre doch besser, das $-Zeichen aus dem String zu nehmen, da es ja den Status und Index auf jeden Fall zurücksetzen soll:
1 | ISR(USART1_RX_vect) |
2 | {
|
3 | static int z=0; |
4 | const char desig[7]="GPRMC,"; |
5 | |
6 | char indata = UDR1; |
7 | |
8 | if (indata=='$') |
9 | {
|
10 | z=0; |
11 | }
|
12 | else
|
13 | {
|
14 | if (UDR1==desig[z]) |
15 | z++; |
16 | else
|
17 | z=0; |
18 | }
|
19 | if(z>5) |
20 | {
|
21 | gpsdata[z-6]=indata; |
22 | z++; |
23 | }
|
24 | }
|
:
Bearbeitet durch User
Attila C. schrieb: > Ist das immer noch zuviel "gerödel" für eine ISR? Natürlich nicht, aber die Abfrage mit Index ist langsamer als Abfrage auf einzelne Zeichen. Deswegen meine Bemerkung wegen Elegant. Hier ist etwas schnelleres (wieder aus dem Kopf, ohne Gewähr auf funktionieren)
1 | volatile uint8_t gpsflag = 0; // Flag dient zur Abarbeitung der GPS |
2 | |
3 | ISR(USART1_RX_vect) |
4 | {
|
5 | uint8_t gpschar = UDR1; |
6 | if (gpsflag == 0) { // Falls nicht abgearbeitet, wird gar nicht erst reingesprungen... |
7 | static uint8_t z = 0; |
8 | uint8_t state = 0; |
9 | switch(z) |
10 | {
|
11 | case 0x00: |
12 | if(gpschar != '$') z = 0; |
13 | break; |
14 | case 0x01: |
15 | if(gpschar != 'G') z = 0; |
16 | break; |
17 | case 0x02: |
18 | if(gpschar != 'P') z = 0; |
19 | break; |
20 | case 0x03: |
21 | if(gpschar != 'R') z = 0; |
22 | break; |
23 | case 0x04: |
24 | if(gpschar != 'S') z = 0; |
25 | break; |
26 | default : |
27 | if (z > BufferSize) { z=0; break; } |
28 | if(gpschar == 0x0D) state = 255; |
29 | else state = 1; |
30 | }
|
31 | if(state > 0) { |
32 | gpsdata[z++]=gpschar; |
33 | if (state == 255) gpsflag = 1; // main() setzt es wieder auf 0 nach Abarbeitung... |
34 | }
|
35 | }
|
36 | }
|
Thomas E. schrieb: > Hm - und welches Codemonster macht der Compiler aus Deinem switch-case? LOL. Das stimmt schon mal nicht. Erstens ist mein "Codemonster" wenn nicht kürzer, dann bestimmt nicht länger als deine bzw. Attila's Lösung. Und schneller ist es auf jeden Fall. Und du bzw. Attila hast nicht mal eine Prüfung auf Zeilenende, Bufferüberschreitung, ob die vorige GPS-Meldung schon abgearbeitet worden ist... Und mein "Codemonster" hat das alles und ist trotzdem schneller und kürzer als das, was du da bei Attila rumgeändert hast. Aber das ist ein Irrtum der schon immer rumgeistert - kürzeren C-code gleichzusetzen mit kürzeren und schnelleren ASM-Code. Schneller als mit den von mir geposteten Codeschnipsel geht es nur mit if-Abfragen, aber auch dann nur unwesentlich. > Was soll an dem Array nicht gut sein? Einen Zeiger+Index auf ein (const > char) Array im ROM kriegt der Compiler sicher mit ein paar Befehlen > gebacken, und dann ist es maschinentechnisch nur noch eine einzige > Vergleichsoperation und ein bedingter Sprung, nicht womöglich 6 davon. Aha. Nur packt man die Arraydeklaration nicht in die ISR rein, sondern macht das ausserhalb. Aber lerne fleissig weiter, eines Tages wirst du es auch verstehen...
Marc V. schrieb: > Erstens ist mein "Codemonster" wenn nicht kürzer, dann bestimmt nicht > länger als deine bzw. Attila's Lösung. > Und schneller ist es auf jeden Fall. Das glaube ich erst, wenn ich den erzeugten ASM-Code sehe. Leider benutze ich keine Attinys und habe dehalb auch nicht den Compiler installiert, um es selbst zu überprüfen. Marc V. schrieb: > Und du bzw. Attila hast nicht mal eine Prüfung auf Zeilenende, > Bufferüberschreitung, ob die vorige GPS-Meldung schon abgearbeitet > worden ist... Meine Absicht war nicht, mustergültigen und wasserdichten Code abzuliefern, sondern das Prinzip zu zeigen, wie die empfangene Zeichenkette erkannt werden soll. Marc V. schrieb: > Nur packt man die Arraydeklaration nicht in die ISR rein, sondern macht > das ausserhalb. Warum? Das Array sollte im ROM abgelegt werden (deshalb "const") und muss nur im Kontext der ISR gültig sein. Wenn der Compiler dafür Code in der ISR selbst erzeugt, taugt er nichts... Edit: ok, habe mal ein bisschen Google befragt, "const" alleine reicht wohl nicht, um das Array ins ROM zu verlagern. Evtl. muss da noch so was wie "PROGMEN" hin.
:
Bearbeitet durch User
Thomas E. schrieb: > Das glaube ich erst, wenn ich den erzeugten ASM-Code sehe. Dann compiliere es doch mal. > Leider benutze ich keine Attinys und habe dehalb auch nicht den Compiler > installiert, um es selbst zu überprüfen. Der TO benutzt auch keine ATTinys. > Warum? s.o. > Meine Absicht war nicht, mustergültigen und wasserdichten Code > abzuliefern, sondern das Prinzip zu zeigen, wie die empfangene > Zeichenkette erkannt werden soll. Hast du aber nicht, zumindest nicht richtig. Und meine Absicht war es nicht, deinen Beitrag schlechtzumachen, sondern nur dem TO aufzuzeigen dass kurzer C-Code nicht gleichzusetzen ist mit kurzem ASM-Code. Wie oben schon gesagt, mit if geht es am schnellsten und das sollte in einer ISR schon wichtig sein. switch ist nächstschnellere Variante, ist aber meistens übersichtlicher und leichter anzupassen bzw. zu verändern als if. Rechnen mit Arrays und zugehörigen Indexen gehört bestimmt nicht zu den schnellsten Operationen. Man kann sich nur darauf verlassen, dass der Compiler schlau genug ist, z.B. nach deinem Vergleich:
1 | if(z>5) |
die Register für gleich darauf folgenden Schreibvorgang nicht mit neuen Werten zu laden, sondern die alten Werte zu behalten. Ausserdem ist z als int deklariert, was die Sache noch zusätzlich verlangsamt.
Marc V. schrieb: > Ausserdem ist z als int deklariert, was die Sache noch zusätzlich > verlangsamt. Auf einem 8Bit AVR mit GCC wird doch int zu int8_t geschrieben, oder täusche ich mich da?!
Marc V. schrieb: >> Leider benutze ich keine Attinys und habe dehalb auch nicht den Compiler >> installiert, um es selbst zu überprüfen. > Der TO benutzt auch keine ATTinys. Ja, ich meinte "AVR" allgemein - ist aber wohl in Bezug auf das Thema egal, um Tiny oder Mega. Marc V. schrieb: > Rechnen mit Arrays und zugehörigen Indexen gehört bestimmt nicht zu > den schnellsten Operationen. Sicher ist der Vergleich mit einer Konstanten direkt (cmpi-Befehl?) schneller, als den Vergleichswert erst aus dem Flash holen zu müssen. Aber in Deiner Variante hat man entweder 5 Vergleiche oder 5 cases (was ungefähr auf das gleiche hinauslaufen müsste), die alle immer in der ISR abgearbeitet werden müssen, bis der passende Case oder die passende if-Bedingung gefunden wurde. Also muss man wohl mit 5x compare, branch if (not) equal oder so, rechnen, macht min. 11 Befehle. um in den passenden (default) Case-Zweig zu gelangen. Beim Array muss nur einmal der Index auf die Startadresse addiert werden (beim AVR wohl alles im Z-Register), dann kann er sich die Daten mit LPM direkt aus dem Array holen, dann folgt schon direkt der Vergleich mit den Daten, sollte also auf kaum mehr, als 5 oder 6 Befehle kommen. Ich werde mir aber jetzt nicht extra AVR-Studio installieren, um das zu überprüfen ;)
DraconiX schrieb: > Auf einem 8Bit AVR mit GCC wird doch int zu int8_t geschrieben Nein. > oder täusche ich mich da?! Ja.
Thomas E. schrieb: > if-Bedingung gefunden wurde. Also muss man wohl mit 5x compare, branch > if (not) equal oder so, rechnen, macht min. 11 Befehle. um in den > passenden (default) Case-Zweig zu gelangen. Ja, sind bei default case 5*3 Takte, ergibt 15 Takte oder 937.5ns. Alle anderen Fälle sind nur schneller. Und bei dir (Attila) wird aber 2 Mal geprüft, einmal auf ein Zeichen aus "GPRMC," (mind. 8 Takte) und einmal auf z>5 (mind. 7 Takte). Glaubst du, deine 15 Takte sind schneller als meine 15 Takte ? Ganz abgesehen davon, dass die Abfrage:
1 | if (UDR1==desig[z]) |
2 | z++; |
3 | else
|
4 | z=0; |
immer in die Hose geht, nach z>5 sowieso...
:
Bearbeitet durch User
Attila C. schrieb: > @Rainer: 1) Danke! 2) Tatsächlich? Diese ISR wäre so "überladen?" Ja, finde ich schon. Also da ist ja nur ein 'kleiner' Mikroprozessor am Werken. Bei der ISR gilt meiner Meinung nach: So kurz wie möglich. Ich hab' ein paar Jahre Programmiererfahrung. Wenn Du einmal den Ringbuffer programmiert hast hast kannst Du den auch für andere Projekte verwenden.
Marc V. schrieb: > und einmal auf z>5 (mind. 7 Takte). Wieso sind das mindestens 7 Takte? Selbst wenn der Compiler blöd ist und vergisst, daß er z schon in einem Register hat, kommt mir das zuviel vor. Und wenn Du schon den zusätzlichen Vergleich (z>5) bei mir in die Zeitberechnung einrechnest, solltest Du aber auch Deine "if (gpsflag == 0)" und "if(state > 0)" nicht unter den Tisch fallen lassen! Marc V. schrieb: > Ganz abgesehen davon, dass die Abfrage: if (UDR1==desig[z]) > z++; > else > z=0; > > immer in die Hose geht, nach z>5 sowieso... Da ist was dran - wie gesagt, ging es mir nur um das Prinzip, die Rx-Bytes mit dem Array zu vergleichen. Das lässt sich aber leicht korrigieren, etwa so:
1 | const char desig[7]="GPRMC,"; // von mir aus auch außerhalb der ISR... |
2 | |
3 | ISR(USART1_RX_vect) |
4 | {
|
5 | static uint8_t z=0; |
6 | |
7 | char indata = UDR1; |
8 | |
9 | if (indata=='$') |
10 | {
|
11 | z=0; |
12 | }
|
13 | else
|
14 | {
|
15 | if(z<6) |
16 | {
|
17 | if (indata==desig[z]) |
18 | z++; |
19 | else
|
20 | z=0; |
21 | }
|
22 | else
|
23 | {
|
24 | gpsdata[z-6]=indata; |
25 | if(z<BUFFSIZE+6) |
26 | z++; |
27 | }
|
28 | }
|
29 | }
|
:
Bearbeitet durch User
Thomas E. schrieb: > Wieso sind das mindestens 7 Takte? Selbst wenn der Compiler blöd ist und > vergisst, daß er z schon in einem Register hat, kommt mir das zuviel > vor. LOL. Weil z als integer deklariert ist... > Und wenn Du schon den zusätzlichen Vergleich (z>5) bei mir in die > Zeitberechnung einrechnest, solltest Du aber auch Deine "if (gpsflag == > 0)" und "if(state > 0)" nicht unter den Tisch fallen lassen! LOL. Wenn wir schon bei Korinthen sind: Was ist mit deinen " if (indata=='$') " ? Unter den Tisch fallen lassen ? Und damit das mal klar ist, mein Papa ist stärker als deiner !
Marc V. schrieb: > Erstens ist mein "Codemonster" wenn nicht kürzer, dann bestimmt nicht > länger als deine bzw. Attila's Lösung. Naja - siehe unten! Ungefähr die doppelte Codegröße! > Und schneller ist es auf jeden Fall. LOL! Um wieviel? 2 oder 3 Takte? Wenn man noch den Fehler (z wird nicht incrementiert) korrigiert, ist der Geschwindigkeitsvorteil dahin... Original aus der .lss Datei kopiert:
1 | void testfunc2(void) |
2 | { |
3 | uint8_t gpschar = UDR; |
4 | 396a: 80 91 c6 00 lds r24, 0x00C6 |
5 | if (gpsflag == 0) { // Falls nicht abgearbeitet, wird gar nicht erst reingesprungen... |
6 | 396e: 90 91 0a 03 lds r25, 0x030A |
7 | 3972: 91 11 cpse r25, r1 |
8 | 3974: 35 c0 rjmp .+106 ; 0x39e0 <testfunc2+0x76> |
9 | static uint8_t z = 0; |
10 | uint8_t state = 0; |
11 | switch(z) |
12 | 3976: e0 91 08 03 lds r30, 0x0308 |
13 | 397a: e2 30 cpi r30, 0x02 ; 2 |
14 | 397c: 99 f0 breq .+38 ; 0x39a4 <testfunc2+0x3a> |
15 | 397e: 28 f4 brcc .+10 ; 0x398a <testfunc2+0x20> |
16 | 3980: ee 23 and r30, r30 |
17 | 3982: 41 f0 breq .+16 ; 0x3994 <testfunc2+0x2a> |
18 | 3984: e1 30 cpi r30, 0x01 ; 1 |
19 | 3986: 59 f0 breq .+22 ; 0x399e <testfunc2+0x34> |
20 | 3988: 16 c0 rjmp .+44 ; 0x39b6 <testfunc2+0x4c> |
21 | 398a: e3 30 cpi r30, 0x03 ; 3 |
22 | 398c: 71 f0 breq .+28 ; 0x39aa <testfunc2+0x40> |
23 | 398e: e4 30 cpi r30, 0x04 ; 4 |
24 | 3990: 79 f0 breq .+30 ; 0x39b0 <testfunc2+0x46> |
25 | 3992: 11 c0 rjmp .+34 ; 0x39b6 <testfunc2+0x4c> |
26 | { |
27 | case 0x00: |
28 | if(gpschar != '$') z = 0; |
29 | 3994: 84 32 cpi r24, 0x24 ; 36 |
30 | 3996: 21 f1 breq .+72 ; 0x39e0 <testfunc2+0x76> |
31 | 3998: 10 92 08 03 sts 0x0308, r1 |
32 | 399c: 08 95 ret |
33 | break; |
34 | case 0x01: |
35 | if(gpschar != 'G') z = 0; |
36 | 399e: 87 34 cpi r24, 0x47 ; 71 |
37 | 39a0: d9 f7 brne .-10 ; 0x3998 <testfunc2+0x2e> |
38 | 39a2: 08 95 ret |
39 | break; |
40 | case 0x02: |
41 | if(gpschar != 'P') z = 0; |
42 | 39a4: 80 35 cpi r24, 0x50 ; 80 |
43 | 39a6: c1 f7 brne .-16 ; 0x3998 <testfunc2+0x2e> |
44 | 39a8: 08 95 ret |
45 | break; |
46 | case 0x03: |
47 | if(gpschar != 'R') z = 0; |
48 | 39aa: 82 35 cpi r24, 0x52 ; 82 |
49 | 39ac: a9 f7 brne .-22 ; 0x3998 <testfunc2+0x2e> |
50 | 39ae: 08 95 ret |
51 | break; |
52 | case 0x04: |
53 | if(gpschar != 'S') z = 0; |
54 | 39b0: 83 35 cpi r24, 0x53 ; 83 |
55 | 39b2: 91 f7 brne .-28 ; 0x3998 <testfunc2+0x2e> |
56 | 39b4: 08 95 ret |
57 | break; |
58 | default : |
59 | if (z > BUFFSIZE) { z=0; break; } |
60 | 39b6: e1 32 cpi r30, 0x21 ; 33 |
61 | 39b8: 78 f7 brcc .-34 ; 0x3998 <testfunc2+0x2e> |
62 | if(gpschar == 0x0D) state = 255; |
63 | 39ba: 8d 30 cpi r24, 0x0D ; 13 |
64 | 39bc: 11 f0 breq .+4 ; 0x39c2 <testfunc2+0x58> |
65 | else state = 1; |
66 | 39be: 91 e0 ldi r25, 0x01 ; 1 |
67 | 39c0: 01 c0 rjmp .+2 ; 0x39c4 <testfunc2+0x5a> |
68 | case 0x04: |
69 | if(gpschar != 'S') z = 0; |
70 | break; |
71 | default : |
72 | if (z > BUFFSIZE) { z=0; break; } |
73 | if(gpschar == 0x0D) state = 255; |
74 | 39c2: 9f ef ldi r25, 0xFF ; 255 |
75 | else state = 1; |
76 | } |
77 | if(state > 0) { |
78 | gpsdata[z++]=gpschar; |
79 | 39c4: 21 e0 ldi r18, 0x01 ; 1 |
80 | 39c6: 2e 0f add r18, r30 |
81 | 39c8: 20 93 08 03 sts 0x0308, r18 |
82 | 39cc: f0 e0 ldi r31, 0x00 ; 0 |
83 | 39ce: e0 5f subi r30, 0xF0 ; 240 |
84 | 39d0: fc 4f sbci r31, 0xFC ; 252 |
85 | 39d2: 80 83 st Z, r24 |
86 | if (state == 255) gpsflag = 1; // main() setzt es wieder auf 0 nach Abarbeitung... |
87 | 39d4: 9f 3f cpi r25, 0xFF ; 255 |
88 | 39d6: 21 f4 brne .+8 ; 0x39e0 <testfunc2+0x76> |
89 | 39d8: 81 e0 ldi r24, 0x01 ; 1 |
90 | 39da: 80 93 0a 03 sts 0x030A, r24 |
91 | 39de: 08 95 ret |
92 | 39e0: 08 95 ret |
Und hier der aus meinem vorhergehenden Posting:
1 | void testfunc(void) |
2 | { |
3 | static uint8_t z=0; |
4 | |
5 | char indata = UDR; |
6 | 3930: 90 91 c6 00 lds r25, 0x00C6 |
7 | |
8 | if (indata=='$') |
9 | 3934: 94 32 cpi r25, 0x24 ; 36 |
10 | 3936: 19 f4 brne .+6 ; 0x393e <testfunc+0xe> |
11 | { |
12 | z=0; |
13 | 3938: 10 92 09 03 sts 0x0309, r1 |
14 | 393c: 08 95 ret |
15 | } |
16 | else |
17 | { |
18 | if(z<6) |
19 | 393e: 80 91 09 03 lds r24, 0x0309 |
20 | 3942: 28 2f mov r18, r24 |
21 | 3944: 30 e0 ldi r19, 0x00 ; 0 |
22 | { |
23 | if (indata==desig[z]) |
24 | 3946: f9 01 movw r30, r18 |
25 | { |
26 | z=0; |
27 | } |
28 | else |
29 | { |
30 | if(z<6) |
31 | 3948: 86 30 cpi r24, 0x06 ; 6 |
32 | 394a: 30 f4 brcc .+12 ; 0x3958 <testfunc+0x28> |
33 | { |
34 | if (indata==desig[z]) |
35 | 394c: e0 50 subi r30, 0x00 ; 0 |
36 | 394e: fd 4f sbci r31, 0xFD ; 253 |
37 | 3950: 20 81 ld r18, Z |
38 | 3952: 92 13 cpse r25, r18 |
39 | 3954: f1 cf rjmp .-30 ; 0x3938 <testfunc+0x8> |
40 | 3956: 05 c0 rjmp .+10 ; 0x3962 <testfunc+0x32> |
41 | else |
42 | z=0; |
43 | } |
44 | else |
45 | { |
46 | gpsdata[z-6]=indata; |
47 | 3958: e6 5f subi r30, 0xF6 ; 246 |
48 | 395a: fc 4f sbci r31, 0xFC ; 252 |
49 | 395c: 90 83 st Z, r25 |
50 | if(z<BUFFSIZE+6) |
51 | 395e: 86 32 cpi r24, 0x26 ; 38 |
52 | 3960: 18 f4 brcc .+6 ; 0x3968 <testfunc+0x38> |
53 | z++; |
54 | 3962: 8f 5f subi r24, 0xFF ; 255 |
55 | 3964: 80 93 09 03 sts 0x0309, r24 |
56 | 3968: 08 95 ret |
:
Bearbeitet durch User
Thomas E. schrieb: > Wenn man noch den Fehler (z wird nicht incrementiert) korrigiert, ist > der Geschwindigkeitsvorteil dahin... z wird aber inkrementiert, genauer hinschauen. Bei dir wird in der ISR nicht: a) Auf Endzeichen geprüft b) Das Ende der GPS-Daten signalisiert c) Buffer Inhalt vom überschreiben geschützt somit kann: a) ISR nie wissen, ob ein GPS-Datensatz zu Ende ist - der Empfang geht endlos weiter... b) main() nie wissen, ob ein GPS-Datensatz empfangen worden ist - es wird endlos gewartet... c) Der alte Datensatz kann von neuen GPS-Daten überschrieben werden oder, noch schlimmer, in der main() werden alte und neue Daten gleichzeitig bearbeitet... Lass es aber gut sein, das ist keine Raketenwissenschaft, es ist absolut unnötig, hier irgendwelche Kenntnisse zu beweisen... P.S. Deine Routine ist besser, schneller und kürzer, ich bin raus, jetzt OK ?
:
Bearbeitet durch User
Marc V. schrieb: > z wird aber inkrementiert, genauer hinschauen. Aber nicht an der richtigen Stelle - schau selber genau hin! Ich will hier auch keine Kenntnisse beweisen, aber (sorry, mein Eindruck) Deine etwas besserwisserische Art und Frechheiten wie diese: > Aber lerne fleissig weiter, eines Tages wirst du es auch verstehen... sind halt schon eine Vorlage... Im Übrigen finde ich auch, daß das Thema hier genügend durch ist.
Schade eigentlich dass bei soviel Kompetenz das Ganze in einen Schwanzvergleich ausarten muss! Wie auch immer: Ich habe mal wieder sehr viel gelernt und bedanke mich ganz herzlich bei allen Beteiligten!
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.