Hab da mal ne allgemene Frage: Wie erkennt die UART, welches Byte für was verwendet werden soll? Also will ich zum Beispiel zwei AD-Wandler benutzen. Den einem zum steuern für ein Servo, den anderen zum steuern eines PWM Signals. Die AD-Wandler beziehen ihre Spannungen über ein Poti. Nun funktioniert es ja so, das bei einer bestimmten am AD-Wandler anliegenden Spannung ein Wert zwischen 0 und 255 erzeugt wird. Diesen Wert will ich dann über UART zum steuern des PWM und Servos an einen anderen Controller schicken. Doch wie erkennt der andere Controller, ob er diesen Wert jetzt für das PWM oder für den Servo verwenden soll. Muss ich da noch irgendwie ein Byte mitschicken, welches angibt für was das nachfolgende Byte verwendet werden soll. Wie realisiert man so etwas am besten?
> Muss ich da noch irgendwie ein Byte mitschicken, welches angibt für > was das nachfolgende Byte verwendet werden soll. Das wirst Du wohl müssen. Das Übertragungsprotokoll ist eine Sache, die Du als Anwender in der Hand hast. Wenn es nur zwei Möglichkeiten der Verwendung gibt, dann kannst Du z.B. eine 9-Bit-Übertragung machen (wenn Du nicht unbedingt ein zweites Stopbis brauchen solltest) und das 9. Datenbit als Indikator für den Verwendungszweck nutzen.
oder einfach anhang der Reichenfolge. Du überträgst also immer 2 Bytes nacheinander der 2te Controller weiß das das erste Byte für xxx ist und das 2te für xxx.
Thomas meint, Du schickst die Bytes immer paarweise mit einer Pause dazwischen, welche Du auswertest. Wie immer gibt es noch weitere Möglichkeiten: Du kannst Parity für die Übertragung eines weiteren Bits zweckentfremden. Es gibt ja mehrere Möglichkeiten für Parity: no, even, odd, mark, space. Bei den beiden letzten hast Du es selbst in der Hand, ob L oder H übertragen werden soll. Das würde ich verwenden. Oder even/odd, dann bast Du z.B. als Kennzeichen des 2.Bytes einen Parity-Error.
Hi, erst mal Danke für die Infos! Welche Methode ist den zu empfehlen? Hab 6 Bytes die in einen Programmdurchlauf gesendet werden sollen. Wie funktioniert das denn genau mit der Zeichenfolge? Ungfähr so in der Interrupt Routine des sendenden µC: 1. Start Interrupt 2. Weise dem UDR0 den Wert des ersten zu übertragenden Byte zu. 3. Pause 4. Weise dem UDR0 den Wert des zweiten zu übertragenden Byte zu. 5. Pause.....usw. 6. nach dem 6 Byte ende Interrupt Und wie liest der empfangene Controller die dann aus? Im Moment hab ich eine Auswertung, welche mir ein emfangenes Byte bitweise ausließt und dann je nach 0 oder 1 entsprechenden Werte in ein Array schreibt. Kann ich diese Auswertung einfach zweimal hintereinander laufen lassen und beim zweiten mal einfach die Werte in ein anderes Array schreiben? Gruß Stefan
Ich mache sowas immer mit einer state-machine: http://de.wikipedia.org/wiki/Endlicher_Automat gruss, bjoern.
Das mit der Interrupt-Routine würde ich nicht machen. Sende die Daten lieber in der Hauptschleife. Das Warten, bis das nächste Byte gesendet werden kann, dauert (für µC-Verhältnisse) ziemlich lange, und währenddessen ist alles andere blockiert, wenn du das in der Interrupt-Routine machst. > Im Moment hab ich eine Auswertung, welche mir ein emfangenes Byte > bitweise ausließt und dann je nach 0 oder 1 entsprechenden Werte > in ein Array schreibt. Wieso bitweise? Hat der lesende µC keinen UART, USART oder USI, oder ist der schon von was anderem belegt? > Kann ich diese Auswertung einfach zweimal hintereinander laufen > lassen und beim zweiten mal einfach die Werte in ein anderes > Array schreiben? Warum solltest du das nicht können? Wichtig bei der Übertragung mehrerer Bytes ist, daß du dir was zur Synchronisation überlegst. Falls bei der Übertragung was verlorengeht oder der Empfänger vor dem Sender eingeschaltet wird, muß ja irgendwie sichergestellt werden, daß der Empfänger die Daten nicht versetzt kriegt und damit die Bytes für ihn auf einmal eine ganz andere Bedeutung haben. Das Ende eines Pakets kann man z.B. mit einer Pause mit definierter Mindestlänge kennzeichnen oder mit einer Bytesequenz, die in den Nutzdaten nicht vorkommt.
...oder der Empfänger vor dem Sender eingeschaltet wird... Ich meinte natürlich "nach" statt "vor".
koennte ungefaehr so aussehen: #define STATE_IDLE #define STATE_RECEIVE uart_fsm(uint8_t c) { static uint8_t state=STATE_IDLE; static uint8_t counter=0; switch(state) { case STATE_IDLE: buffer[0]=c; counter = 1; state = STATE_RECEIVE; break; case STATE_RECEIVE: buffer[counter++]=c; if (counter>6) { receive_flag=1; state = STATE_IDLE; } } } keine gewaehr. gruss, bjoern.
ups, so sollte es heissen: #define STATE_IDLE 0 #define STATE_RECEIVE 1 default: und 2 breaks; fehlen auch...
Wie wäre es, ein Feld(Array) zu benutzen? unsigned char daten[6]; In das kann man dann ganz wunderbar die zu sendenden Daten schreiben. Dann kann man mit einem Zaehler von 0 bis 5 die Dinger in einer Schleife verschicken. Quasi so: USART initialisiern Feld füllen tue folgendes, solange Zaehler < 6: { warte, ob Sendepuffer frei ist schreibe Feldeintrag(Zaehler) ins UDR erhöhe Zaehler } Der Empfang sieht ähnlich aus... (Es handelt sich definitiv um keinen C-Code!)
Danke für die Antworten! Das mit dem state kapier ich nicht ganz. Aber der Tip mit dem Array von Rahul hört sich ganz gut an! Also beim senden ungefähr so: unsigned char feld[6]; unsigned char zaehler = 0; feld[0] = Byte1; feld[1] = Byte2; feld[2] = Byte3; feld[3] = Byte4; feld[4] = Byte5; feld[5] = Byte6; while(zaehler <=5) { if(1 << UDRE) { UDR0 = feld[zaehler]; zaehler++; } } Und wie beim empfangen?
>if(1 << UDRE) da fehlt zwar noch das Register, aber sonst sieht das gut aus. >Und wie beim empfangen? Guck dir mal die Beispiele im Datenblatt an. Das beste wäre sich ein Protokoll auszudenken, bei dem ein Start-Byte benutzt wird, dessen Wert sonst nicht vorkommt. Z.B. 0x80, dann sollten der Wert der folgenden Bytes kleiner als 128 sein. >Im Moment hab ich eine Auswertung, welche mir ein emfangenes Byte >bitweise ausließt und dann je nach 0 oder 1 entsprechenden Werte in >ein Array schreibt. >Kann ich diese Auswertung einfach zweimal hintereinander laufen >lassen und beim zweiten mal einfach die Werte in ein anderes Array >schreiben? Was meinst du damit? Welche Art Daten überträgst du jetzt?
Das mit dem Startbyte ist mein Problem, da weiß ich nicht wie ich das realisieren soll. Denn meine Werte in den nachfolgenden Bytes sind zwischen 0 und 255. >Im Moment hab ich eine Auswertung, welche mir ein emfangenes Byte >bitweise ausließt und dann je nach 0 oder 1 entsprechenden Werte in >ein Array schreibt. Mit diesem empfangenen Byte erzeuge ich ein Signal mit 8 verschiedenen Impulsen. Das Signal wird durch ein 18ner Array erzeugt, wobei die Felder Aray 3,5,7,9,11,13,15 und 17 mit entsprechenden Werten für die Impulslängen beschrieben werden. Nun werden bei einem empfangenen Byte die einzelnen Bits auf 0 oder 1 verglichen. Ist das erste Bit 1 schreibe eine 30 in das Feld 3 das Array, ist das Bit 0 schreibe eine 20 rein....usw. >Kann ich diese Auswertung einfach zweimal hintereinander laufen >lassen und beim zweiten mal einfach die Werte in ein anderes Array >schreiben? Da wollte ich das ganze nochmal wiederholen, um das zweite Byte in ein anderes Array zu schreiben. Nur gibt es halt das Problem, dass der emfangene Controller wiisen muss, welches Byte das erste und was das zweite ist. >Welche Art Daten überträgst du jetzt? Im Moment lese ich eine Spannung über einen AD-Wandler ein, welcher mir einen Wert zwischen 0 und 255 erzeugt. Diesen sende ich dann an den anderen Mikrocontroller. Nun will ich noch einen zweiten AD-Wandler zusätzlich benutzen.
>Das mit dem state kapier ich nicht ganz.
Das hatte ich befuerchtet. Ansich ist das keine Hexerei, aber man muss
es verstanden haben, damit man es benutzen kann. Interessant wirds wenn
man das empfangene Zeichen auch noch auswertet und ein paar mehr
Zustaende dazukommen:
sagen wir mal dein protokoll sieht so aus:
als erstes zeichen kommt die unterscheidung zwischen pwm und servo;
danach kommen, sagen wir mal entweder 2 byte, falls es pwm werte sind
oder 3 byte, falls es servowerte sind.
jedes empfangene zeichen wird in die state-machine "geschmissen" und
je nach dem, in welchem zustand sich die fsm befindet, fuehrt sie
bestimmte befehle aus und wechselt ggfalls den zustand.
switch (state)
{
case STATE_IDLE:
if(c='a')
{
counter=0;
state=STATE_PWM;
}
if(c='b')
{
counter=0;
state=STATE_SERVO;
}
break;
case STATE_PWM:
buffer[counter++]=c;
if (counter==2)
{
setze_pwm(buffer);
state = STATE_IDLE;
}
break;
case STATE_SERVO:
buffer[counter++]=c;
if (counter==3)
{
setze_servo(buffer);
state = STATE_IDLE;
}
break;
default:
break;
}
ich hoffe, es wird ein bisschen verstaendlicher. viel mit worten kann
ich da nicht mehr zu erklaeren; ueber "automatentheorie" wurden ganze
buecher geschrieben und es ist ein elementares werkzeug der informatik.
gruss, bjoern.
if(c='a') sollte besser if(c=='a') heissen :) koennte genausogut if(c==1) oder so heissen. ich waehl immer ascii-zeichen fuer befehle, damit ich das ganze auch schnell mit einem terminalprog testen kann... gruss, bjoern.
Hi, erstmal solltest du Dir ein FIFO erstellen. Beispiele findest du in der Fleury Uart Lib oder im GCC Port von Martin Thomas zum Butterfly. Als naechstes wuerdem ich mir ein Protokoll ausdenken z.B. Uppernibble vom Byte ist die Startadresse z.B. "1111" das Lowernibble kannst du dann als Teilnehmer (AVR0 o. AVR1 usw.) benutzen, dann ein Byte als Command or Data Erkennung. Das naechste Byte als Commandlength oder Datalength und zum Schluss CRC und ein Stopbyte. So kannst du wirklich sicher sein das ein komplettes Frame empfangen wurde. Vom Programmiertechnischen hat dir schon Bjoern Mueller weitergeholfen. Gruß, Dirk
Und wenn du die Daten einfach nibbleweise sendest? Zu sendendes Byte: 0x83 Übertragen wird: 0xF8 0xF3 für Servo oder 0xE8 0xE3 für PWM D.h. das erste Nibble eines Bytes gibt an Servo (E) oder PWM (F) und im zweiten Nibble stehen die Nutz-Bits
Oder eine 9-bit UART deklarieren, wo dann das 1. Datenbyte ein 1 als 9. Bit bekommt und die Folgebytes eine 0. MW
@Rolf: Den (letztlich) gleichen Link hab ich schon um 14:05 gepostet. Allerdings finde ich nicht, dass man damit viel anfangen kann. Zumindest nicht, wenn man noch nie was von FSMs gehoert hat... gruss, bjoern.
Hallo, erst ein mal vielen Dank für die ganzen Antworten! Hab es nun mal nach der Methode von Björn Mueller versucht. Wollte einfach nur mal zum testen zwei LED's ein und aus schalten. Leider haut das mit dem STATE nicht hin. Wenn ich es auf so mache, klappts: //Interrupt UART ISR(USART_RX_vect ) { unsigned char buffer; buffer = UDR0; switch(buffer) { case '1': // LED1 ein/ausschalten if (PORTB & (1 << PB2)) PORTB &= ~(1 << PB2); else PORTB |= (1 << PB2); break; case '2'://LED2 ein/ausschalten if (PORTB & (1 << PB0)) PORTB &= ~(1 << PB0); else PORTB |= (1 << PB0); break; } } Nur auf diese Weise klappt das dann mit der Byteerkennung nicht. Hab mal den kompletten Code mit den STATE angehängt, vielleicht kann ja mal jemand schauen wo der Fehler liegt. Gruß
Falls beide Varianten das gleiche machen sollen, hast du das Konzept noch nicht verstanden. Eine fsm fuehrt pro Durchlauf nur die Befehle aus, die zum jeweils aktuellen state gehoeren. Du wechselt zwar bei einem empfangen Zeichen in einen anderen state, aber die Aktionen aus diesem werden erst beim naechsten Durchlauf(=naechstes Zeichen wird empfangen) ausgefuehrt. Du muesstet also, damit dein fsm-code das gleiche macht wie der "normale" code ein weiteres beliebiges Zeichen senden, das die fsm einen "Takt weiterschaltet". Desweiteren setzt du mit "unsigned char state = STATE_IDLE;" die fsm bei jedem Durchlauf auf den Grundzustand. Du willst aber eigentlich, dass die fsm sich bei jedem Durchlauf an ihren beim vorigen Durchlauf gesetzten state "erinnert". Das geht entweder mit einer globalen Variablen "state" oder mit dem Bezeichner "static" innerhalb der Funktion. Konkret: aender mal "unsigned char state = STATE_IDLE;" in "static unsigned char state = STATE_IDLE;" und sende 2 Zeichen also zb "ax" oder "bz", dann sollte sich deine fsm so verhalten, also ob du an den "normalen" code "1" oder "2" schickst. gruss, bjoern.
Cool, so gehts! Also heißt das konkret, das erste Byte (also das 'a') sagt mir das er beim nächsten Durchlauf und einem empfangenen Byte den case STATE_LICHT1 ausführt. Dabei ist es egal, welchen Inhalt das zweite Byte besitzt weil ich darauf keine Abfrage mache. Es wird, egal was in dem Byte steht dieser case ausgeführt. Wenn ich jetzt aber den Inhalt des Bytes verwenden will, muss wieder eine Zuweisung von c auf z.B. den Servo erfolgen. Richtig?
>Also heißt das konkret, das erste Byte (also das 'a') sagt mir das er >beim nächsten Durchlauf und einem empfangenen Byte den case >STATE_LICHT1 ausführt. Jetzt hats "klick" gemacht ;) >Wenn ich jetzt aber den Inhalt des Bytes verwenden will, muss wieder >eine Zuweisung von c auf z.B. den Servo erfolgen. Genau. Was in dem jeweiligen state passiert, bestimmst du. Du kannst zb einen buffer fuellen und einen counter hochzaehlen, falls du in dem state eine bestimmte Anzahl bytes erwartest; oder einfach nur mit dem empfangenen byte zb eine andere Funktion aufrufen und gleich wieder in den "warte-auf-weitere-befehle"-state zurueckwechseln. Du muss auch nicht zwingend aus dem idle-state rauswechseln, wenn ein befehl ankommt, der keine weiteren daten erwartet. in deinem beispiel mit den 2 leds koenntest du auch einfach nur die led an/ausschalten und einfach weiter auf andere Befehle warten. gruss, bjoern.
Hallo, ich hab noch ne Frage. erkennt der Microcontroller den Unterschied zwischen ASCI und Dezimal?
Nö. Für ihn sind das alles nur Bits. Eigentlich handelt es sich bei den beiden Sachen nur um verschiedene Darstellungsformen. ASCII ist nichts anderes als eine Tabelle mit Symbolen, die für Menschen verständlich ist. Und das Dezimalsystem ist wird auch nur benutzt, weil der Mensch im Regelfall 10 Finger hat. Mit den 10 Fingern könnte man in binären Zahlenraum allerdings wesentlich mehr zählen... Wenn man abfragt x == 'U', dann setzt der Compiler für 'U' die Hexadezimalzahl 0x55, was eine andere Darstellungsform für 85 dezimal oder 01010101 binär ist, sofern er mit der ASCII-Tabelle arbeitet. Man könnte sich auch eine eigene Tabelle aufbauen, was man für einfache Verschlüsselungstechniken benutzen könnte.
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.