Forum: Mikrocontroller und Digitale Elektronik 2 mal USART interrupt, wie gestalten?


von Attila C. (attila)


Lesenswert?

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!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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?

von Karl M. (Gast)


Lesenswert?

Attila C. schrieb:
> Ich gehe davon aus dass eine Interruptroutine die andere unterbrechen
> kann.

Nein nicht ohne weiteren Eingriff in den ISR-Code.

von Attila C. (attila)


Lesenswert?

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?

von Karl M. (Gast)


Lesenswert?

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 !

von Karl M. (Gast)


Lesenswert?

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.

von Attila C. (attila)


Lesenswert?

Alles klar! Danke Karl!

von Jim M. (turboj)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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
von Dietrich L. (dietrichl)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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...

von Georg (Gast)


Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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.

von Rainer S. (rsonline)


Lesenswert?

c-hater schrieb:
> Software-Buffering
> kostet weitere Rechenzeit

Minimal.
Hat im Endeffekt aber mehr Vorteile.

von c-hater (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

c-hater schrieb:
> vorzugsweise

"vorzugsweise"?
Dann habe ich ein anderes "vorzugsweise", als du.


Ich würde eher sagen:
> Nur, wenn es nicht anders geht!

von Georg (Gast)


Lesenswert?

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

von Rainer S. (rsonline)


Lesenswert?

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.

von Attila C. (attila)


Lesenswert?

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
von STK500-Besitzer (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Eine äußerst merkwürdige Sourceformatierung hast Du da.

von Thomas E. (picalic)


Lesenswert?

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
von neuer PIC Freund (Gast)


Lesenswert?

Ü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.

von Attila C. (attila)


Lesenswert?

@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?

von Attila C. (attila)


Lesenswert?

@PICfreund: ???

von STK500-Besitzer (Gast)


Lesenswert?

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.

von STK500-Besitzer (Gast)


Lesenswert?

Attila C. schrieb:
> @PICfreund: ???

Sowas interessiert vielleicht bei SQL-Datenbanken, aber nicht bei einem 
AVR-Programm.

von Attila C. (attila)


Lesenswert?

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++;
  }
}

von DraconiX (Gast)


Lesenswert?

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

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
 }

von Attila C. (attila)


Lesenswert?

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" ?

von Attila C. (attila)


Lesenswert?

Ups! Disregard: So funktionert es natürlich nicht!

von Rainer S. (rsonline)


Lesenswert?

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

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Attila C. (attila)


Lesenswert?

@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?"

von Attila C. (attila)


Lesenswert?

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?

von DraconiX (Gast)


Lesenswert?

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.

von neuer PIC Freund (Gast)


Lesenswert?

Ü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.

von Thomas E. (picalic)


Lesenswert?

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().

von Thomas E. (picalic)


Lesenswert?

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
von Attila C. (attila)


Lesenswert?

@Thomas: Danke! :-)

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
}

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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...

von Thomas E. (picalic)


Lesenswert?

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
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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.

von DraconiX (Gast)


Lesenswert?

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?!

von Thomas E. (picalic)


Lesenswert?

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 ;)

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

DraconiX schrieb:
> Auf einem 8Bit AVR mit GCC wird doch int zu int8_t  geschrieben

 Nein.


> oder täusche ich mich da?!

 Ja.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Rainer S. (rsonline)


Lesenswert?

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.

von Thomas E. (picalic)


Lesenswert?

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
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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 !

von Thomas E. (picalic)


Lesenswert?

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
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Thomas E. (picalic)


Lesenswert?

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.

von Attila C. (attila)


Lesenswert?

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
Noch kein Account? Hier anmelden.