Hallo miteinander, ich bin mal wieder an einem kleinen Porjekt bei dem ich 2 Schrittmotoren über die serielle Schnittstelle steuern möchte. Also Mega8 + tmc222 und max232. Hardwaretechnisch ist das ganze kein Problem nur stellt sich mir bei jedem meiner Projekte die Frage wie macht man das wenn man Daten vom PC zum µC schickt. Bei dem jetzigen Projekt ist es so das der PC die Positionsdaten quasi x-Achse: 298472 y-Achse: 88292 an den Controller schicken soll er diese Position anfahren solll und evt. dann sagen soll das die Positionierung abgeschlossen ist. Dann sollen evt. noch 1 oder 2 Relais gesteuert werden also müsste nur sowas wie Relais1=1, Relais2=0,... übertragen werden. Wie setzt man das ganze nun softwaremäßig um ? EInen interrupt auf das Recive der seriellen und dann jedesmal Select Case Input Case Relais1=1: Portb.1 = 1; Case y-achse: i2cout ...... ... Irgendwie muss man doch da ein einheiltiches Format treffen? z.b "#wertxachse;wertyachse;statusrelais1;statusrelais2;... nur irendwann ist doch dann ende oder ? da wird der zu übertragende string so ellen lang! habt ihr ein paar ideen dazu wie macht ihr es wenn der computer viele sachen auf dem controller einstellen soll bzw. übertragen soll (als befehl quasi) quellcodes , ideen, anregungen .... gruß avrler
denk dir n protokoll aus, in das du die befehle verpackst. z.b. startbyte (immer gleich) anzahl bytes die folgen --- nutzlast -- checksumme und lass den controller die befehle interpretieren, die in der nutzlast drinstehen. musst natürlich die länge der nutzdaten an die resourcen im controller anpassen. gruß
ja genau um dieses interpretieren der gesendeten befehle geht es mir - wie setzt man das elegant um. avrler
http://www.atmel.com/dyn/products/app_notes.asp?family_id=607 lad dir da mal die AVR068 appnote runter. da is z.b. son serielles protokoll beschrieben. im 1. byte der nutzlast steht der befehl, im rest die daten die der befehl vearbeitet. um das leserlich zu machen kannst du dir da ne headerdatei runterladen wo die befehle und antworten vom stk500 board definiert sind. kannst darauf ja z.b. aufbauen. gruß
Mal ne kleine Querfrage zum Protokoll aus der Appnote, reicht für eine einigermassen sichere Übertrag die Exklusiv-Veroderung der Daten aus, oder lieber einen "kompletten" CRC? Ralf
danke das beispiel aus der apnote ist schonmal sehr gut !! hat jemand noch ein direktes beispiel wie er sowetwas löst ? wie ist die praktische umsetzung im gcc !? ich muss den empfangenen string dann ja irgendwie am besten mit der suche nach einem trennzeichen in die einzelnen befehle kappen und die dann ausführen mit den jeweils übergebenen werten ( in meinem fall z.b schrittanzahl) dankbar für alles was kommt g avrler
zuallererst trennst du mal grundsätzlich das empfangen der Zeichen von der Auswertung. Beides hat nichts miteinander zu tun. In der Empfangs-ISR speicherst du die empfangenen Zeichen einfach in einem Buffer. Wenn du ein vernünftiges Protokoll definierst, dann mach das so, dass du den Anfang deines kompletten Kommandos eindeutig erkenne kannst. Stell dir einfach vor, jemand liest aus einem Buch vor. Er tut dies indem er die Wörter buchstabiert und auch Satzzeichen ausspricht. Deine Aufgabe ist es jetzt aus dem was er vorliest ganze Sätze zu bilden. Dein Problem: Wenn du in den Raum kommst, liest der schon eine Weile. Du brauchst alse eine Methode um mit absoluter Sicherheit den nächsten Satzanfang zu erkennen. Denk dir dazu was aus! Nimm mein (blödes) Beispiel und überleg mal, wie du da das machst. Das ist überhaupt eine gute Strategie! Wenn du vor einem Problem stehst transformiere es in deine Welt und überlege und beobachte dich selbst wie du dieses Problem löst. Wir Menschen machen viele Dinge und sind uns gar nicht bewusst wie wir sie machen. Programmieren heist auch seine Umwelt, einschliesslich sich selbst, zu beobachten und lernen wie dort Probleme gelöst werden. Soweit, so gut: Wenn die ISR also signalisiert, zb über eine globale Variable, liegt in einem Character Buffer ein komplett übertragener Befehl vor. Na ja, und den wertest du aus. zb. in main() volatile unsigned char CommandReveived; char Command[30]; // oder wieviele du halt brauchst ISR( ... ) { // empfängt ein Kommando Zeichen für Zeichen // und stellt das Kommando in 'Command' zur Verfügung // Ist ein Kommando vollständig wird 'CommandReceived' // auf TRUE gestellt } int main() { ... while( 1 ) { if( CommandReceived ) { ... switch(Command[0]) { // 1. Zeichen ist der Befehl case 'M': // Kommando 'MoveTo' // die restlichen empfangenen Zeichen // geben die Position an HandleMove(); break; case 'U': // Kommando 'Up' // Keine weitere Info notwendig // Stift, Fräser abheben HandleUp(); break; case 'D': // Kommando 'Down' // Stift, Fräser absenken HandleDown(); break; case 'W': // Kommando Kühlwasser // das nachste Byte imm Buffer sagt aus // ob 'Wasser ein' oder'Wasser aus' HandleWater(); break; ... // die UART wieder scharf stellen CommandReceiver = FALSE; } } } So ungefähr. Obiges ist nicht optimal, aber es ist mal ein Anfang. Das Problem liegt darin, dass während die Verarbeitung eines Kommandos läuft kein weiteres empfangen werden kann. Ist lösbar. Aber eines nach dem anderen.
ok danke das ist wirklich sehr hilfreich gewesen !!! du verfolgst einen sehr guten ansatz der wie ich denke zu jedem problem eine "mögliche" lösung bietet g! jedoch sehe ich auch daraus das es keine alternative zu einer case oder if auswertung gibt. ? ich habe gehört solche sachen werden mit hilfe von lookuptables gemacht ?! jedoch kann ich mir darunter gerade absolut nichts vorstellen ! danke für eure hilfe !!! schönen abend
> jedoch sehe ich auch daraus das es keine alternative zu einer case > oder if auswertung gibt. ? Oh. Da gibt es noch jede Menge anderer Möglichkeiten. Die von dir angesprochene Lookup-Tabelle ist zb eine. Ich gehe allerdings davon aus, dass du nur wenige Kommandos auf der Schnittstelle hast. Da ist eine switch-case Leiste mehr als ausreichend und erfüllt seinen Job hervorragend. Eine Lookup Tabelle könnte zb so aussehen: Gesucht ist duch die Umsetzung eines Kommandos (in Form eines Characters) in einen Funktionsaufruf. Da gibt es jetzt mehrere Möglichkeiten, je nachdem über wieviele zu erkennenden Kommandos wir reden. Sind es relativ viele, so dass das Alphabet für das Kommandobyte gut ausgenutzt wird, so macht man sich ganz einfach ein Array, wobei dann das Kommandobyte als Index in dieses Array benutzt wird (Handlexxx sind jeweils Funktionen die dafür zuständig sind ein Kommando abzuarbeiten). typedef void (*Fnct)(void); Fnct Functions[] = { HandleA, HandleB, HandleC, HandleD, NULL, HandleE, ... etc HandleZ }; void HandleA() { // Bearbeite das Kommando 'A' } void HandleB() { // Bearbeite das Kommando 'B' } ... Damit ist die Bühne aufgesetzt, jetzt muss nur noch in main(), abhängig vom Kommandobyte die richtige Funktion aufrufen: ... if( Functions[ Command[0] - 'A' ] ) Functions[ Command[0] - 'A' ](); (Command[0], also das erste Byte, wird als index in die Tabelle von oben mit den Funktionspointern genommen und die Funktion ueber den Funktionspointer aufgerufen. Möglichkeit 2: Wenn es nur wenige Kommandos sind, dann ist obiges Array etwas überkandidelt. Die meisten Einträgre sind NULL. Dann ist es ev. besser sich eine Struktur zu basteln struct Entry { char Command; Fnct Function; } Damit kann ich dann gezielt ein Array bauen: struct Entry Functions[] = { { 'M', HandleMove }, { 'U', HandleUp }, { 'D', HandleDown }, { 'W', HandleWater } } Nette kleine Tabelle :-) Jetzt geht man mit dem ersten Zeichen des Kommandos wieder her und durchsucht die Tabelle nach dem entsprechenden Eintrag. Hat man ihn gefunden, hat man dann auch den Funktionspointer und kann ueber diesen die Funktion aufrufen. Bei derartig wenig Kommandos kann man ganz einfach linear suchen: for( i = 0; i < sizeof(Functions)/sizeof(*Functions); ++i ) if( Command[0] == Functions[i].Command ) Functions[i].Function(); Wenn es mehr Kommandos sind, könnte man auch eine binäre suchen machen, oder das Ganze über eine Hashtable realisiern Eine komplett andere Möglichkeit wäre es einen schönen Parser aufzusetzen. Nimmt man dann wenn man nicht nur Kommandos mit einem Buchstaben hat, sondern Schlüsselwörter. Oder ... Es gibt noch viele Möglichkeiten. Aber für wenige Kommandos dürfte eine switch-case Leiste das Einfachste aber nicht notwendigerweise das Schlechteste sein.
tja, was will man mehr. c erklärungen in perfektion. nebenbei: ich empfange das protokoll aus atmels appnote per uart-polling. ist zwar eher unelegant aber viel was anderes hat der controller sonst nicht zu tun. in der auswerteroutine nutze ich dann eine switch-case konstruktion auf das 1. byte in der nutzlast und bearbeite dann den rest der nutzdaten je nach befehl unterschiedlich. zum thema xor über alle bytes oder richtiges crc, wird wohl die umgebung ausschlaggebend sein, in der das ganze zum einsatz kommen soll. für den basteltisch oder ähnliches sollte xor wohl ausreichen. gruß
Für den Basteltisch würde ich sogar ganz darauf verzichten und einfach auf der UART-Ebene die Parity einschalten.
Ok ich steige durch g! ! ! Deine Erklärungen kbuchegg sind wirklich ->perfect<- ! jetzt wo ich das blut geleckt habe - hat jemand noch ein beispiel für einen parser ? damit sollte ja dann so wirklich alles abgedekt sein egal ob es 10 oder 100 befehle sind die ich verarbeiten will. wenn man so einen parser genauso einfach aufbauen könnte wie eine case auswahl ... ein traum g bei einem parser wäre es dann wahrscheinlich so das ich den beginn des command string erstmal erkennen muss so wie in der appnote 0x1b oder was es da war. dann fange ich an den string stück für stück auszuwerten. die reihenfolge ist ja immer die gleiche erst der befehl (die funktion) dann der dazugehörige wert. wenn ich meinen string 20 zeichen lang mache könnte ich dann wenn ich pro funktionsbeschreibung 2 zeichen und pro wert 3 zeichen verwende jeweils max. 4 befehle pro string übertragen. der praser sucht dann die erste funktion(sbeschreibung) schreibt den in eine variable sucht dann weiter nach dem dazugehörigen wert und schreibt diesen wieder in eine variable. dann suche ich die funktion (über den inhalt in der varibalen in der ich die abkürzung gespeichert habe), springe in die funktion, führe sie aus mit dem funktionswert den ich in der anderen variable gespeichert habe - oder ? ich glaube das wäre genau das richtige - denke ich .... eure hilfe - einfach super !!!!
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.