Forum: Compiler & IDEs Auf Befehl warten ohne Blockierung des AVR


von Andreas S. (Gast)


Lesenswert?

Hallo !

In einem Programm für einen AVR möchte ich per UART Strings als Kommando 
empfangen die mit einem festen Zeichen (!) beginnen und mit einem festen 
Zeichen aufhören (\n). Währenddessen soll aber das Hauptprogramm nicht 
unterbrochen werden. Momentan sieht mein Hauptprogramm so aus, zum 
Empfangen des Kommandos nutze die Methode aus dem AVR-GCC-Tutorial.
1
void uart_gets( char* Buffer, uint8_t MaxLen )
2
{
3
  uint8_t NextChar;
4
  uint8_t StringLen = 0;
5
 
6
    do {
7
        NextChar = uart_getc();
8
    } while (received_char != '!');         // Warte auf und empfange das nächste Zeichen
9
 
10
                                  // Sammle solange Zeichen, bis:
11
                                  // * entweder das String Ende Zeichen kam
12
                                  // * oder das aufnehmende Array voll ist
13
  while( NextChar != '\n' && StringLen < MaxLen - 1 ) {
14
    *Buffer++ = NextChar;
15
    StringLen++;
16
    NextChar = uart_getc();
17
  }
18
 
19
                                  // Noch ein '\0' anhängen um einen Standard
20
                                  // C-String daraus zu machen
21
  *Buffer = '\0';
22
}
23
24
25
int main(){
26
   while(1){
27
      tuDies();
28
      tuDas();
29
30
      empfangeUart();
31
   }
32
}

Für den Empfang einzelner Zeichen nutze ich die UART-Lib von hier
http://homepage.hispeed.ch/peterfleury/avr-software.html

Nun ist es natürlich so, dass erst wieder tuDies() und tuDas() gemacht 
wird, wenn ein Kommando über den UART gekommen ist. Kommt keins, wird 
solange nichts getan, bis ein Kommando kam. Ich hätte es aber gerne so, 
dass alles andere ausgeführt wird und nur wenn ein Kommando angekommen 
ist, wird die entsprechende Aktion ausgeführt. Wie bewerkstellige ich 
dies ?

Viele Grüße,

A.S.

von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:

> Nun ist es natürlich so, dass erst wieder tuDies() und tuDas() gemacht
> wird, wenn ein Kommando über den UART gekommen ist. Kommt keins, wird
> solange nichts getan, bis ein Kommando kam. Ich hätte es aber gerne so,
> dass alles andere ausgeführt wird und nur wenn ein Kommando angekommen
> ist, wird die entsprechende Aktion ausgeführt. Wie bewerkstellige ich
> dies ?

Pseudocode
1
int main()
2
{
3
  ....
4
5
  while( 1 )
6
  {
7
8
    if( Es gibt ein Zeichen an der UART )
9
    {
10
      Zeichen holen
11
12
      if( Zeichen ist Kommando-Endekennung )
13
      {
14
        Führe Kommando aus
15
        Lösche Kommando-String
16
      }
17
18
      else if( Zeichen ist Kommando-Startkennung )
19
        Lösche Kommando-String
20
21
      else
22
        Hänge das Zeichen an den Kommando String an
23
    }
24
25
    Tu dies();
26
    Tu das();
27
  }
28
}


Also eigentlich so wie immer, wenn man ein Programm schreibt, welches 
mehrere Dinge 'quasi' gleichzeitig machen soll:
Man fasst alles als mögliches Ereignis auf, prüft in der Hauptschleife 
reihum alle möglichen Ereignisse darauf hin ab, ob sie vorliegen und 
wenn sie das tun, dann löst man die zugehörige Aktion aus.

In deinem Fall ist dann eben das 'mögliche Ereignis' nicht, dass ein 
Kommando fertig ist, sondern dass ein Zeichen an der UART eingetrudelt 
ist. Und dafür muss man sich dann eben überlegen, was man damit machen 
will.
In deinem Fall hat man mit einem einzelnen Zeichen noch nichts 
angefangen, also muss man viele derartige 'mögliche Ereignisse' sammeln 
(den Kommandostring aufbauen), bis dann eben irgendwann alles vorhanden 
ist, so dass man mit der Abarbeitung beginnen kann.

PS: ein bischen Aufwand wirst du noch in den Teil stecken müssen, der 
einfach das Zeichen anhängt. Denn das Zeichen sollte nur dann angehängt 
werden, wenn vorher eine Startkennung gesichtet wurde. Aber mit einem 
Flag ist das ja kein Problem.

von O. D. (odbs)


Lesenswert?

Das wäre die Variante ohne Interrupt. Die ist übersichtlich und 
funktioniert auch gut.

Allerdings nur, wenn die Hauptschleife so schnell durchlaufen wird, dass 
zwischen den Durchläufen nicht mehr als ein Zeichen an der Schnittstelle 
ankommen kann. Sprich, die Baudrate und die Abarbeitungsgeschwindigkeit 
der Hauptschleife müssen zusammenpassen.

Wir wissen ja nicht, was in der Hauptschleife passiert. Vielleicht steht 
da irgendwo ein längeres _delay_ms(), weil eine LED blinken soll oder 
ähnliches. Jetzt mal unabhängig davon, ob das guter Stil ist, man sieht 
es oft bei Anfängern, die hier Fragen stellen.

In einem solchen Fall bietet sich an, den Receive-Interrupt des USART 
abzufangen und den Kommandostring dort zusammenzusetzen. Wenn ein 
vollständiges Zeichen empfangen ist, kopiert man den String um und setzt 
ein Flag für die Hauptschleife.

Wenn es nicht zu viele verschiedene Kommandos gibt und diese keine 
Parameter enthalten, kann man auch für jedes mögliche Kommando ein 
eigenes Flag vorsehen.

von Karl H. (kbuchegg)


Lesenswert?

Oliver Döring schrieb:
> Das wäre die Variante ohne Interrupt. Die ist übersichtlich und
> funktioniert auch gut.
>
> Allerdings nur, wenn die Hauptschleife so schnell durchlaufen wird, dass
> zwischen den Durchläufen nicht mehr als ein Zeichen an der Schnittstelle
> ankommen kann. Sprich, die Baudrate und die Abarbeitungsgeschwindigkeit
> der Hauptschleife müssen zusammenpassen.


Er benutzt
http://homepage.hispeed.ch/peterfleury/avr-software.html

und von der weiß ich zufällig, dass sie Interruptgesteuert ist und über 
einen kleinen Ringbuffer verfügt, der sich genau um diese 
Problemstellung kümmert.

von Uwe (Gast)


Lesenswert?

Man kann sich nen interupthandler basteln der ne Statemachine drinne hat 
und auf '!' als startbedingung wartet, dann ein Flag setzt den Zähler um 
1 erhöht und den Interuprt beendet bis das nächte Zeichen kommt.
Beim nächsten Zeichen wird geguckt ob das Flag für start schon gesetzt 
ist, wenn ja kann man auf '\n' und maximallänge prüfen, falls positiv 
Zähler um 1 erhöhen und '\0' anhängen, beeden und weiteres Flag für die 
main setzen 'da_ist_was_gueltiges_im_puffer=true".
Falls nicht Zeichen in den Buffer und Zähler eröhen.

von Falk B. (falk)


Lesenswert?

Siehe Multitasking.

von Adib (Gast)


Lesenswert?

die Idde mit der Statemachine im ISR ist sicher nicht schlecht.
Man muss aber beachten, dass das Hauptprogramm (die main-schleife) dann 
auch das Protokoll schnell genug abholt befor ein neues Protokoll 
beginnt.
Einfacher ist sicher Karl Heinz' System mit dem Decoder im 
Hauptprogramm.

WEnn die Verarbeitung eh im Hauptprogramm stattfindet, ist die 
Reaktionszeit gleich.
Beim AVR würde ich aufwendige Verarbeitungen nicht im ISR machen, da 
dann alle anderen ISR solange blockiert sind.

Adib.

von cppler (Gast)


Lesenswert?

Also so wie ich das verstehe sendest Du "!startexyz" als Text und das 
soll interpretiert werden.
Wenn Du Probleme mit der Zeit bekommst weil halt 
"!startenichtmehrxyzsondernabc" länger ist dann kann man zu #defines 
greifen und im sendenden Programm aus "!startyxz" dann "!a" machen.
Statemachine ist schon genannt worden, das solltest Du Dir mal genauer 
ansehen.

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.