Schönen Tag, ich hab bei meinem Problem über die Forumssuche nichts passendes gefunden, deswegen ein neuer Thread. Es dreht sich um folgendes: Ich möchte vom PC aus (vorerst mit nem Terminalprogramm) "selbsterdachte" Befehle/Kommandos an ein µC schicken (ATmega16@16MHz). Der µC soll darauf reagieren und eine entsprechende Aktion (in meinem Falle Messung) ausführen und das Ergebnis zurückliefern. Meine Frage bezieht sich auf das kreieren eines funktionierenden Protokolles. Ich stelle mir das so vor: Ich schicke an den µC ein String mit z.B. "FREQ?" der Controller führt die Messung aus und schickt das ERgebnis zurück z.B. "4711"kHz oder auch nur "4711" Dann z.B. eine Spannungsmessung, schicke "ADVAL?" er mißt die Spannung am ADC, gibt zurück "0815mV" oder "0815" usw. Es soll auch möglich sein, dem µC ein Befehl zum setzen bestimmter Registerwerte zu senden, z.B. Vorteiler setzen: "SET_PRESC_128" und der µC gibt zurück "OK" / "DONE" usw. Jetzt meine Frage: Wie kann man das am besten realisieren? Wie erkennt der µC dass ein Befehl zu "Ende" ist, also soll ich eindeutige ASCI Char senden wie das "?" oder beim Setzen von Parametern das "SET..." oder soll ich auf CR und oder LF achten. Ich habe mir mal überlegt wie man das sinvollerweise machen kann, eine schlecht Funktionierende Version habe ich mit einem Empfangspuffer realisiert, der mir den ankommenden Befehl speichert. Das Problem ist, wenn ich ein unbekannten Befehl schicke, füllt sich der Sendepuffer und der nächste Befehl wird nicht ausgeführt. Ich poste mal hier meine (schlechte) Lösung:
inits... volatile unsigned char buffer[8];// Pufferspeicher für Kommandoempfang volatile unsigned char buffer_pos = 0;// aktuelle Schreibposition für den Buffer volatile unsigned char buffer_received = 0;// Kommando empfangen Flag ISR(USART_RXC_vect) { if (!(buffer_received)) { buffer[buffer_pos++] = uart_getc(); if (buffer[buffer_pos - 1] == '?') { buffer_received = 1; buffer_pos = 0; } } } int main(void) { uart_init(); sei(); // Global Interrupts Enable while(1) { if ((buffer_received) && (buffer[buffer_pos] == 'F') && (buffer[buffer_pos + 1] == 'R')) ... && (buffer[buffer_pos + 4] == '?') { buffer_received = 0; //führe Frequenzmessung aus und liefere Ergebnis zurück } if ( // Abfragen nächster Befehl ... } return 0; }
> Wie kann man das am besten realisieren? > Wie erkennt der µC dass ein Befehl zu "Ende" ist, Am allereinfachsten: Pro Befehl eine Zeile d.h. jeder Befehl ist mit einem '\n' abgeschlossen. Das ist auch für den Benutzer am allereinfachsten, dann der ist normalerweise daran gewöhnt (*), dass er seinen Befehl eintippt und dann auf Return drückt. (*) ok. Heutzutage nicht mehr. Heute wird nur noch rumgeklickt.
1. Anhang fehlt. 2. Ich würde ein Befehlsende über CR/LF auswerten. Wenn das Kommando dann unbekannt ist einfach eine Fehlermeldung zurückschicken. Prinzipiell solltest Du über Kommando-Eingaben alles was verstellbar ist auch verstellen können (also alles ausser Fuses) - ist nur eine Frage der Auswertung und Aufarbeitung.
> if ((buffer_received) && (buffer[buffer_pos] == 'F') && > (buffer[buffer_pos + 1] == 'R')) ... && (buffer[buffer_pos + 4] == '?') Das ist doch Unsinn das so zu machen. Dafür gibt es schöne Funktionen, die allesamt in der str...() Familie zu finden sind. http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F
Ich überarbeite mal das "Protokoll", indem ich die Stringfunktionen einbinde. Ist es sinvoll, dass alle Kommandos die man verwendet, alle die selbe Länge haben, also meinetwegen immer 8 Zeichen? Also wenn ich die Kommandos mit einem '\n' abschließe hab ich dann auch davor das '\0' Zeichen drin? Wenn ich das Terminierungszeichen immer am Ende vom String hab, dann kann ich ja auch das auswerten oder?
> Ich überarbeite mal das "Protokoll", indem ich die Stringfunktionen > einbinde. Such dir im Web auch die strn... Funktionen. Besonders strncmp wird hier hilfreich sein. > Ist es sinvoll, dass alle Kommandos die man verwendet, alle die selbe > Länge haben, also meinetwegen immer 8 Zeichen? Für dich als Programmierer schon. Für den Benutzer nicht. Im Zweifel gewinnt der Benutzer. > Also wenn ich die Kommandos mit einem '\n' abschließe hab ich > dann auch davor das '\0' Zeichen drin? Nein. Warum sollte es? Dein Terminal weis nichts von einem '\0'. Aber das sollte ja kein Problem sein in der Empfangsschleife beim Auftreten eines '\n': * den '\n' zu ignorieren * statt dessen den String mit einem '\0' anschliessen * den String damit als vollständig empfangen zu deklarieren und zur weiteren Verarbeitung (mittels buffer_received) freizugeben
Also ich hab folgendes nun realisiert: Zeichen '\r' in der ISR abgefragt (mit \n hat es nicht funktioniert) und statdessen das \0 angefügt. Es funktioniert in der Main- SChleife die Abfrage if ((buffer_received) && !(strncmp("FR?", buffer, 3)) ) { buffer_received = 0; //führe Frequenzmessung aus und liefere Ergebnis zurück } Jedoch gibt der Compiler die Warnung ../main.c:229: warning: passing arg 2 of `strncmp' discards qualifiers from pointer target type Funktioniert aber trotzdem. Das eigentliche Problem ist: Wenn ich eine falsche Eingabe mache, wird buffer_received true gesetzt, aber dadurch dass er nie in die If Schleife springt wird das buffer_received Flag nicht mehr zurückgesetzt. Also springt er in der ISR für den Empfang gar nicht mehr in den Abschnitt wo die Zeichen abgeholt werden. Ich könnte allerdings die Befehlsabfrage in der ISR machen, und nur wenn ein gültiger Befehl kommt, den buffer_received dann auf 1 setzen. Dann wird aber die ISR ziemlich lang und sehr Prozessorlastig. Ich bräuchte also eine Art Indikator, der sich merkt ob ein GÜLTIGER Befehl empfangen wurde. Damit er auf Befehle reagiert und Einstellungen verändert muss ich dann noch realisieren.
Das Ende könnte man auch ziemlich einfach herausfinden, indem man alle Zeichen <=32 herausfiltert und bei Erkennen eines solchen (und einer Mindestpufferbelegung) ein "Stringende-Flag" setzt. Dann kann man die einzelnen Befehle auseinanderklabüstern. Müssen die Befehle unbedingt so lang sein? Ich habe sowas mal mit einzelnen Buchstaben realisiert... Den Empfang sollte man am besten per Ringpuffer und Interrupt organisieren.
(Da habe ich eine Menge Zeug einfach mal überlesen...) >Ich bräuchte also eine Art Indikator, der sich merkt ob ein GÜLTIGER >Befehl empfangen wurde. Deine Befehle besetehen ja aus lesbaren Zeichen. Die befinden sich alle im ASCII oberhalb der Position 31 (32 ist das Leerzeichen). Wenn ein Zeichen <=31 auftritt, sollte entweder ein Fehler aufgetreten sein oder ein Befehl komplett im Puffer liegen. >if ((buffer_received) && (buffer[buffer_pos] == 'F') && >(buffer[buffer_pos + 1] == 'R')) ... && (buffer[buffer_pos + 4] == '?') >{ > buffer_received = 0; > //führe Frequenzmessung aus und liefere Ergebnis zurück >} > >if ( // Abfragen nächster Befehl ... Sowas macht man besser so: while (!(buffer_received)); // wartet auf "String" Dann sollte man sich ein Array mit den Befehlen einrichten. Dieses kann man dann mit zwei Schleifen mit dem empfangenen String vergleichen (eine Schleife für das "Wort" und eins für den "Buchstaben"). Übrigens bietet es sich auch an, noch eine upcase-Funktion zu haben (selberbauen oder aus irgendeiner Library besorgen) - die ermöglicht dann auch den Empfang und die Auswertung "komischer" Strings ("fR?").
> Ich bräuchte also eine Art Indikator, der sich merkt ob ein GÜLTIGER > Befehl empfangen wurde. Wozu. Alles was du brauchst ist: int main() { .... while( 1 ) { // Wenn irgendwas über die serielle gekommen ist if( buffer_received ) { // es ist was da. // welcher Befehl war es denn? if( strncmp( buffer, "ADC", 3 ) == 0 ) { // Aha, es war der ADC Befehl // Befehl abarbeiten } else if( strncmp( buffer, "PORT", 4 ) == 0 ) { // Aha ein PORT Befehl // Befehl abarbeiten } .... else { // kein gültiger Befehl. Fehlermeldung ausgeben etc. } // Wenn in der Zeile ein Befehl war ist er abgearbeitet // Aber auch wenn da keiner war: // An dieser Stelle ist alles soweit fertig, dass der // nächste Befehl wieder anrollen kann buffer_received = 0; } } } Macht euch doch nicht selbst das Leben mit immer noch komplizierteren Konstrukten schwer. buffer_received auf 1 heist doch nicht, dass da ein 'Befehl# angekommen ist. buffer_received auf 1 heist doch lediglich dass eine komplette Eingabezeile vorhanden ist, die jetzt bearbeitet werden kann. Die wird dann bearbeitet, entweder indem da ein Befehl herausgeholt wird oder ein Fehler erzeugt wird oder was auch immer. Aber eines steht auf jeden Fall fest: Nachdem die Zeile bearbeitet wurde ist der Eingangspuffer bereit eine neue Eingabe aufzunehmen. Also wird buffer_received wieder auf 0 gesetzt und gut ists.
Danke, die Hilfe ist echt super! um den Nachteil des aktiven Wartens (Pollings) uz vermeiden, wärs nicht besser in der ISR ein Aufruf zur Befehlauswertung zu geben, also z.B.: ISR(USART_RXC_vect) { if (!(buffer_received)) { buffer[buffer_pos++] = uart_getc(); if (buffer[buffer_pos - 1] <= 31) { buffer[buffer_pos - 1] = '\0'; buffer_received = 1; buffer_pos = 0; aufrufbefehlauswertungsfunktion(); } } } Oder würde das die ISR nur unnötig lang machen? @Karl Heinz: Ich hab mal die Abfrage von Dir realisiert. Das funktioniert super. Manchmal sieht man selbst die einfachsten Möglichkeiten nicht. Bin wohl zu tief in das Programm vertieft gewesen... @Rahul: Du meinst also ähnlich wie mit dem strncomp(blablabla) über 2 Arrays abzufragen, welcher Befehl nun gesendet wurde?
Um nochmal auf den angesprochenen Ringpuffer zurückzukommen, was wäre der Vorteil wenn man den Ringpuffer verwendet gegenüber der Methode in der ISR wie ich es realisiert habe? Immerhin habe ich durch Verwendung der Lese- und Schreibmarke einen erhöhten Aufwand.
>was wäre der Vorteil wenn man den Ringpuffer verwendet gegenüber der >Methode in
der ISR wie ich es realisiert habe?
Man könnte während der Ausführung eines Befehls schon den nächsten
schicken.
Ich habe mir das mit dem Ringpuffer beim Empfang einfach angewöhnt.
Mister mit Kanister wrote: > inits... > > volatile unsigned char buffer[8];// Pufferspeicher für Kommandoempfang > ISR(USART_RXC_vect) > { > if (!(buffer_received)) > { > buffer[buffer_pos++] = uart_getc(); > if (buffer[buffer_pos - 1] == '?') > { > buffer_received = 1; > buffer_pos = 0; > } > } > } Ohh das ist schlecht, wenn die Eingabe länger als 8 Zeichen ist überschreibst Du den Stack und somit deine Rücksprungadresse. Außerdem brauchst Du dafür ganze 8 Byte an kostbaren RAM. Ich würde das über einen kleinen Zustandsautomaten machen. Der ist schnell implementiert, braucht wenig RAM und ist auch schnell im Ablauf.
Schau Dir mal die avrlib von Pascal Stang an. http://hubbard.engr.scu.edu/avr/avrlib/ Da gibt's ein CmdLine-Interface. Da kannst Du auch noch Parameter an deine Befehle dranhängen.
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.