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
voiduart_gets(char*Buffer,uint8_tMaxLen)
2
{
3
uint8_tNextChar;
4
uint8_tStringLen=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
intmain(){
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.
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.
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.
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.
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.
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.
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.