Forum: Mikrocontroller und Digitale Elektronik Elegantes Interrupten


von Stephan R. (Gast)


Lesenswert?

Moin!

Ich bin fleissig dabei, meinen Atmega8 mit NMEA zu füttern, woraus 
diverse Informationen herausgepflückt werden sollen. Dazu verwende ich 
die ISR aus dem Tutorial, klappt alles ganz hervorragend.
Nur zwei Sachen, die mich wurmen:

1.: wie bringe ich der Routine ELEGANT bei, dass Sie den String erst bei 
GPRMC anfangen soll

2.: das Char- Array für die Aufnahme ist (richtig so?) ausserhalb der 
main- Funktion und damit (so meine Auffassung) als globale Variablen 
namens "volatile char uart_string [uart_maxstrlen]" deklariert. Meine 
Versuche, diese in ein handhabbareres (cooles Wort) char-Array zu 
kopieren, haben nicht funktioniert, da gabs Compiler- Mecker. Also hab 
ich das "volatile" rücksichtslos entfernt und plötzlich funktionierts.
Schön ist das sicher nicht, wie also besser lösen?


Die ISR:


ISR(USART_RXC_vect)
{
    unsigned char buffer;


    buffer = UDR;
  if ( uart_str_complete==0 ){
      if (buffer!='\n' && buffer!='\r' && 
uart_str_count<uart_maxstrlen-1){
      uart_string[uart_str_count]=buffer;
      uart_str_count++;
    } else {
      uart_string[uart_str_count]='\0';
      uart_str_count=0;
      uart_str_complete=1;
    }
  }
}

von Purzel H. (hacky)


Lesenswert?

Naja. Durch Vergleichen. Das Vorgehen nennt sich Zustandsmaschine. Im 
Interrupt.

von Stephan R. (Gast)


Lesenswert?

Also

if (array[0] != 'G')
count = 0;
 und von vorne anfangen? (Fänd ich unelegant..!)

von X-Rocka (Gast)


Lesenswert?

man baue sich eine state machine.

ganz grob:

states in ISR_RX:

idle_state: kucke was kommt, wenn startzeichen, kopiere in buffer und 
state=data_state, ansonsten state=idle_state

data_state: solang buffer frei und kein endzeichen, data in buffer. wenn 
endzeichen dann state=idle_state, flag setzen "mir-ham-daten"

...

von X- R. (x-rocka)


Lesenswert?

dat ganze mit switch case

von stefan hennig (Gast)


Lesenswert?

Stephan R. schrieb:
> 1.: wie bringe ich der Routine ELEGANT bei, dass Sie den String erst bei
>
> GPRMC anfangen soll


ich würd's ungefähr so machen (code nicht getestet, einfach so 
hingetippt):
1
static const unsigned char prefix[]="GPRMC";
2
static const unsigned int prefix_len = 5;
3
static       unsigned int prefix_pos = 0;
4
5
             unsigned char new_char = UDR;
6
7
if (prefix_pos < prefix_len)
8
{
9
  if (new_char == prefix[prefix_pos]) {
10
    ++prefix_pos;
11
  } else {
12
    prefix_pos = 0;
13
  }
14
} else {
15
  // Dein Code von oben...
16
  // am Zeilenende:
17
  prefix_pos = 0;
18
  uart_str_complete = 1;
19
}

von Karl H. (kbuchegg)


Lesenswert?

Stephan R. schrieb:
> Moin!
>
> Ich bin fleissig dabei, meinen Atmega8 mit NMEA zu füttern, woraus
> diverse Informationen herausgepflückt werden sollen. Dazu verwende ich
> die ISR aus dem Tutorial, klappt alles ganz hervorragend.
> Nur zwei Sachen, die mich wurmen:
>
> 1.: wie bringe ich der Routine ELEGANT bei, dass Sie den String erst bei
> GPRMC anfangen soll

Heißt das, das du nur an diesem Datensatz interessiert bist?

zb so
1
ISR(USART_RXC_vect)
2
{
3
  unsigned char buffer;
4
5
6
  buffer = UDR;
7
  if ( uart_str_complete == 0 ) {
8
    if (buffer!='\n' && buffer!='\r' &&
9
        uart_str_count<uart_maxstrlen-1){
10
      uart_string[uart_str_count]=buffer;
11
      uart_str_count++;
12
    }
13
    else {
14
      uart_string[uart_str_count] = '\0';
15
      uart_str_count = 0;
16
      if( strncmp( uart_string, "$GPRMC", 6 ) == 0 )
17
        uart_str_complete = 1;
18
    }
19
  }
20
}

Die Zeichen musst du so und so aus dem UDR abholen. Also kannst du sie 
auch zu einer Zeile zusammensetzen, das kostet nicht viel.

Ist die Zeile fertig, überprüfst du, ob du es mit einem GPRMC Satz zu 
tun hast, und nur dann 'belästigst' du die Hauptschleife, in dem du der 
mitteilst, dass ein interessanter Datensatz vorliegt (durch Setzen das 
Complete Flags)

> 2.: das Char- Array für die Aufnahme ist (richtig so?) ausserhalb der
> main- Funktion und damit (so meine Auffassung) als globale Variablen
> namens "volatile char uart_string [uart_maxstrlen]" deklariert. Meine
> Versuche, diese in ein handhabbareres (cooles Wort) char-Array zu
> kopieren, haben nicht funktioniert, da gabs Compiler- Mecker. Also hab
> ich das "volatile" rücksichtslos entfernt und plötzlich funktionierts.
> Schön ist das sicher nicht, wie also besser lösen?

In dem Fall hätte man das volatile auch ruhig wegcasten können.

von Stephan R. (Gast)


Lesenswert?

Danke für die Codevorschläge, arbeite sie ein.

Betreffend volatile: was ist wegcasten?
Ich hatte schlimmere Beschimpfungen befürchtet, dass ich es wage, das 
volatile zu entfernen. Glück gehabt..

von Karl H. (kbuchegg)


Lesenswert?

Stephan R. schrieb:
> Danke für die Codevorschläge, arbeite sie ein.
>
> Betreffend volatile: was ist wegcasten?
> Ich hatte schlimmere Beschimpfungen befürchtet, dass ich es wage, das
> volatile zu entfernen. Glück gehabt..

Dein Glück, dass es sich um ein Array handelt, das noch dazu als 
Stringspeicher verendet wird.
Da tut sich der Optimizer schwer, das Array komplett in 1 oder 2 
Registern zu halten :-) Von daher ist die Gefahr nicht sehr groß, dass 
dir hier der Optimizer einen Streich spielen wird, weil die meisten 
Stringoperationen ja soweiso mittels str... Funktionen erfolgen werden.


wegcasten:
1
volatile char uart_string [uart_maxstrlen];
2
3
....
4
5
   char localCopy[80];
6
7
   strcpy( localCopy, (char*)uart_string );

du weißt ja, das es sicher ist, an dieser stelle uart_string so zu 
behandeln als ob es ein ganz gewöhnliches char Array wäre.

von Norbert (Gast)


Lesenswert?

Hmmm, da kann man ja nur hoffen das in main() recht zügig gearbeitet und 
der Buffer schnellstmöglich wieder freigegeben wird. Ansonsten leidet 
wohl der Anfang des nächsten Datensatzes ein wenig und wird in /dev/null 
kopiert ;-)

Wie wäre es als Alternative mit zwei alternierenden Buffers (der eine 
wird von der ISR beschrieben während der andere von main() abgearbeitet 
wird)?

von Karl H. (kbuchegg)


Lesenswert?

Norbert schrieb:
> Hmmm, da kann man ja nur hoffen das in main() recht zügig gearbeitet und
> der Buffer schnellstmöglich wieder freigegeben wird. Ansonsten leidet
> wohl der Anfang des nächsten Datensatzes ein wenig und wird in /dev/null
> kopiert ;-)

Daran hab ich auch schon mal gedacht.
Wenn du dir aber die Ausgabe eines GPS Devices ansiehst, dann sieht man 
auch mit freiem Auge schon, dass es nach einem Datensatz meistens eine 
kleine Pause einlegt :-)

Bei typischen Updateraten von 5 Datensätzen pro Sekunde, hat man 
meistens genug Zeit, einen Satz zu verarbeiten.

> Wie wäre es als Alternative mit zwei alternierenden Buffers (der eine
> wird von der ISR beschrieben während der andere von main() abgearbeitet
> wird)?

Kann man natürlich auch machen.

von Stephan R. (Gast)


Lesenswert?

Danke Karlheinz, Dein Einzeiler

                    if( strncmp( uart_string, "$GPRMC", 6 ) == 0 )

hat die Sache rund gemacht, läuft. Mit einem Schönheitsfehler:

passing argument discards qualifiers from pointer target type

von Karl H. (kbuchegg)


Lesenswert?

Stephan R. schrieb:
> Danke Karlheinz, Dein Einzeiler
>
>                     if( strncmp( uart_string, "$GPRMC", 6 ) == 0 )
>
> hat die Sache rund gemacht, läuft. Mit einem Schönheitsfehler:
>
> passing argument discards qualifiers from pointer target type


Selbes Problem: volatile.

Aber mitlerweile weißt du ja, wie du sie wegkriegst :-)

von Norbert (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Bei typischen Updateraten von 5 Datensätzen pro Sekunde, hat man
> meistens genug Zeit, einen Satz zu verarbeiten.

Ahhh, wusste ich nicht. Dann reicht ja schon ein mit einer Pendeluhr 
getakteter µC ;-)

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.