Forum: Mikrocontroller und Digitale Elektronik Byterkennung UART


von Stefan (Gast)


Lesenswert?

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?

von johnny.m (Gast)


Lesenswert?

> 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.

von Thomas O. (Gast)


Lesenswert?

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.

von Profi (Gast)


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

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

von Bjoern M. (salival)


Lesenswert?

Ich mache sowas immer mit einer state-machine:

http://de.wikipedia.org/wiki/Endlicher_Automat

gruss, bjoern.

von Netzpolizei (Gast)


Lesenswert?

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.

von Netzpolizei (Gast)


Lesenswert?

...oder der Empfänger vor dem Sender eingeschaltet wird...

Ich meinte natürlich "nach" statt "vor".

von Bjoern M. (salival)


Lesenswert?

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.

von Bjoern M. (salival)


Lesenswert?

ups, so sollte es heissen:
#define STATE_IDLE    0
#define STATE_RECEIVE 1

default: und 2 breaks; fehlen auch...

von inoffizieller WM-Rahul (Gast)


Lesenswert?

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!)

von Stefan (Gast)


Lesenswert?

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?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>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?

von Stefan (Gast)


Lesenswert?

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.

von Bjoern M. (salival)


Lesenswert?

>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.

von Bjoern M. (salival)


Lesenswert?

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.

von Dirk (Gast)


Lesenswert?

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

von Chief brady (Gast)


Lesenswert?

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

von Michael Wilhelm (Gast)


Lesenswert?

Oder eine 9-bit UART deklarieren, wo dann das 1. Datenbyte ein 1 als 9.
Bit bekommt und die Folgebytes eine 0.

MW

von Rolf Magnus (Gast)


Lesenswert?


von Bjoern M. (salival)


Lesenswert?

@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.

von Stefan (Gast)


Angehängte Dateien:

Lesenswert?

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ß

von Bjoern M. (salival)


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

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?

von Bjoern M. (salival)


Lesenswert?

>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.

von Stefan (Gast)


Lesenswert?

Hallo,

ich hab noch ne Frage.

erkennt der Microcontroller den Unterschied zwischen ASCI und Dezimal?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.