Forum: Mikrocontroller und Digitale Elektronik Byte-weise versenden


von Frederik W. (ip-freddy)


Lesenswert?

Hallo,

ich bin relativ neu, was Mikrocontroller angeht und ab mal ne Frage.
Ich hab in C# ein Programm geschrieben, mit dem ich auf meinem Arduino 
eine LED an und aus schalten kann. Das mach ich in dem ich einen String 
verschicke der 0 und 1 beinhaltet. Wenn ich jetzt mehrer LED´s mit PWM 
ansteuern will geht das ja so nicht. Ich würd es in 2 Bytes machen, 1. 
Byte die Adresse der LED (z.B. 1) und im 2.Byte würde dann der PWM Wert 
stehen.

Wird das so normaler Weise gemacht und wie realisiere ich das ?

Woher weis das Arduino Board welches das erste und welches das zweite 
Byte ist, wenn die alle nacheinander verschickt werden, wird sowas mit 
einem Start und Stop Bit gemacht ?

Wäre super wenn mir jemand das eben erklärt oder mir eine Seite mit nem 
Tutorial schickt.

: Verschoben durch Admin
von Samuel J. (capstrovor)


Lesenswert?

Hallo.

Ich weiß jetzt nicht genau wie "man" das genau machen sollte...
Aber ich würde es so machen:

Ich würde den PWM-Wert von 0-255 gehen lassen (also genau 1 Byte), und 
dann einfach immer bei jeder Wertänderung immer 8 Bytes senden (wenn du 
8 LEDs hast).

Also wenn du nur die 3. LED mit dem Wert 128 ansteuern willst und die 
anderen bei 0 liegen sollen:

00000000, 00000000, 00000001, 00000000, 00000000, 00000000, 000000000, 
00000000

Nein, ein Start bzw Stopbit brauchst du nicht.
Du kannst ja sagen, sobald 8 Bit im Buffer liegen, soll er den Auslesen. 
Damit hast du die einkommenden Daten immer in der Reihenfolge, wie sie 
verschickt wurde, und zusätzlich noch in Bytes getrennt.

lg

: Bearbeitet durch User
von Frederik W. (ip-freddy)


Lesenswert?

Danke für die schnelle Antwort,

meinst du nicht 10000000 ?

Ich meine Start und Stop Bit, wenn jetzt ein interrupt oder so mal dazu 
kommt oder ein Byte "verschluckt" wird ist ja ALLES hinfällig, mir ist 
grad das DMX Protokoll in den Sinn gekommen, sowas wäre doch genau 
richtig oder nicht ?

Ich hatte ja wie gesagt eigentlich vor nur bestimmte werte zu senden und 
nicht alle.
Also am liebsten würde ich ja immer im 1.Byte die Adresse und im 2. Byte 
den PWM Wert verschicken, also nur das was immer gebraucht wird, kann 
mir dazu jemand ein Beispiel schicken/schreiben ?

von jonas biensack (Gast)


Lesenswert?

Mach doch ne Art Midi, Datenbytes < 127  und Steuerbytes >= 127. Also 
Led1 wäre 127, LED2 128 usw. Verlierst halt ein bisschen Dynamik da du 
nur noch Werte bis 127 übertragen kannst, aber wenn dich das nicht 
stört...

Gruß Jonas

von Wolfgang A. (Gast)


Lesenswert?

Frederik Wayn schrieb:
> Woher weis das Arduino Board welches das erste und welches das zweite
> Byte ist, wenn die alle nacheinander verschickt werden, wird sowas mit
> einem Start und Stop Bit gemacht ?

Wenn du serielle Daten verschickst, sorgen Start- und Stop-Bits dafür, 
dass die einzelnen Bits richtig einem Byte zugeordnet werden. Zum 
Auseinanderhalten deiner Bytes brauchst du einen anderen Mechanismus. 
Üblicherweise verwendet man spezielle Vereinbarungen für die 
übertragenen Bytes, um die Synchronisation zwischen Sender und Empfänger 
sicher zu stellen. Bei Midi sind das die Aufspaltung der Werte für 
Adressen und Daten, bei Textübertragung z.B. die Beschränkung auf 
Buchstaben/Zahlen und Zeilenende (CR, LF), bei Binärübertragung von 
Blöcken ein fester Datenrahmen mit Synchronisationszeichenfolge, 
Blocklänge, Dateninhalt und Prüfsumme. Da sind deiner Phantasie keine 
Grenzen gesetzt.
Du musst nur sicher stellen, dass der Empfänger, auch wenn er mal außer 
Tritt geraten ist, feststellen kann, wo eine neue Übertragung beginnt.

Für deine LED-Steuerung könntest du z.B. auch immer ein Startzeichen 
senden (z.B. "$") und dann direkt hintereinander die Helligkeitswerte 
als Hexadezimalwerte, also bei 3 LEDs "$A01080" bei Helligkeiten 0xA0, 
0x10 und 0x80. Das macht die Struktur sehr einfach, ist aber natürlich 
ineffizent, wenn du viele LEDs hast, die ihre Helligkeit nur selten 
ändern. Das hängt also auch von der Anwendung ab.

von Frederik W. (ip-freddy)


Lesenswert?

Danke für eure Antworten,

Wolfgang, meinst du alles in einem Byte, Start, led Adresse und 
Helligkeit? Mir geht es hauptsächlich darum, mich mit der Seriellen 
Schnittstelle zu beschäftigen und deswegen will ich das nicht als String 
sondern als Byte, entweder als Hex oder Bin.
Wäre super wenn du das noch mal genau erläutern könntest

von Wolfgang (Gast)


Lesenswert?

Frederik Wayn schrieb:
> und deswegen will ich das nicht als String sondern als Byte, entweder als
> Hex oder Bin
Wenn du die Daten in Bytes ohne Verwendung eines Datenrahmen übertragen 
möchtest, muss jedes Byte für sich vollständig vom Empfänger 
interpretierbar sein, z.B. indem von den 8 Bit die oberen 2 Bit die 
Adresse darstellen und die unteren 6 Bit die Helligkeit. Damit könnte 
man also gerade für 4 LEDs die Helligkeit in 64 Stufen übertragen.
Oder bei 3 Adressbits blieben 5 Bits für 32 Helligkeitsstufen.

Wenn man mehr als die 8 Bit braucht, damit der Empfänger weiss, was er 
damit anfangen soll, muss der die nacheinander gesendeten Bytes 
unterscheiden können, indem z.B. besondere Byte für Synchronisation 
reserviert sind, die in den Daten dann nicht auftreten können.

DMX als Beispiel wurde oben schon genannt
http://de.wikipedia.org/wiki/DMX_%28Lichttechnik%29#Zeitliches_Protokoll

von Frederik W. (ip-freddy)


Lesenswert?

Ich wollte mit 2 Byte´s arbeiten und einem Start Bit.

-Startbit
-Adressbyte (für die LED)
-PWM Byte (für die Helligkeit)


Ich hab nur absolut keine Ahnung wie ich das programmiertechnisch machen 
soll, muss ich das Startbit vorgeben oder macht das die Serielle 
Schnittstelle "Automatisch"?

Ist ja eigentlich egal ob ich das Binär oder Hexadezimal mache oder ?

Wie würde das in C# aussehen? Ich hab gestern einen Wert binär 
verschickt und testweise über einen Seriellen Monitor ausgelesen, dabei 
entstehen immer ASCII Zeichen wie "&". Das würde Arduino auch machen 
oder? Wieso zeigt er bei "00100110" dann nicht die Zahl 38 an?

von Wolfgang (Gast)


Lesenswert?

Frederik Wayn schrieb:
> Ich wollte mit 2 Byte´s arbeiten und einem Start Bit.
>
> -Startbit
> -Adressbyte (für die LED)
> -PWM Byte (für die Helligkeit)

Du kannst über die serielle Schnittstelle keine einzelnen Bits 
übertragen, wenn du nicht schmutzige Tricks anwenden willst, die aber 
vom API auf dem PC dann (höchstwahrscheinlich) nicht unterstützt werden.

Der UART sorgt (normalerweise) automatisch dafür, dass die 8 Bit eines 
Byts mit Start- und Stop-Bit umrahmt auf die Leitung gehen.
http://de.wikipedia.org/wiki/UART

von Rolf M. (rmagnus)


Lesenswert?

Samuel J. schrieb:
> 00000000, 00000000, 00000001, 00000000, 00000000, 00000000, 000000000,
> 00000000
>
> Nein, ein Start bzw Stopbit brauchst du nicht.
> Du kannst ja sagen, sobald 8 Bit im Buffer liegen, soll er den Auslesen.
> Damit hast du die einkommenden Daten immer in der Reihenfolge, wie sie
> verschickt wurde, und zusätzlich noch in Bytes getrennt.

Und wenn eins verlorengeht oder der µC erst angeschlossen wird, wenn der 
PC schon sendet, ist bis auf weiteres alles komplett durcheinander. 
Natürlich braucht man eine Erkennung von Anfang und Ende. Und die Daten 
als String zu übertragen ist für's Debugging auch hilfreich. Ich würde 
als Trennzeichen einfach ein Newline nehmen.

Frederik Wayn schrieb:
> Ich wollte mit 2 Byte´s arbeiten und einem Start Bit.
>
> -Startbit
> -Adressbyte (für die LED)
> -PWM Byte (für die Helligkeit)

Ein "Startbit" kannst du nicht übertragen. Sowas gibt's, wie schon 
gesagt wurde, nur innerhalb der Übertragung eines Bytes, und das läuft 
automatisch. Ein einzelnes Bit kannst du nicht senden.

> Ich hab nur absolut keine Ahnung wie ich das programmiertechnisch machen
> soll, muss ich das Startbit vorgeben oder macht das die Serielle
> Schnittstelle "Automatisch"?
>
> Ist ja eigentlich egal ob ich das Binär oder Hexadezimal mache oder ?

Was verstehst du unter "Hexadezimal"? Eine serielle Schnittstelle 
überträgt immer binär.

> Wie würde das in C# aussehen? Ich hab gestern einen Wert binär
> verschickt und testweise über einen Seriellen Monitor ausgelesen, dabei
> entstehen immer ASCII Zeichen wie "&". Das würde Arduino auch machen
> oder?

Wenn du es deinen Arduino als Text anzeigen läßt, ja. Was er sonst 
"macht", hängt ganz und gar davon ab, was du ihm sagst, was er damit 
machen soll. Das "&" ist nichts weiter als die Interpretation des Bytes 
als ASCII-Zeichen.

> Wieso zeigt er bei "00100110" dann nicht die Zahl 38 an?

Weil dein serieller Monitor die ankommenden Daten offenbar als Text 
interpretiert.

von Frederik W. (ip-freddy)


Lesenswert?

Danke schon mal da waren viele nützliche Informationen bei,

Das mit dem Startbit ist mir wichtig, das er weis das zuerst die Adresse 
kommt und dann der PWM Wert, wenn jetzt aus irgendeinem Grund das 
Aressbyte verloren geht und er fängt mit dem PWM Byte und dann kommt das 
AdressByte ist ja alles hinfällig bis ich den Port schließe und wieder 
öffne (ich meine wenn ein Byte verloren geht ist es ja dann so: 
1.PWM-->2.Adresse. Es soll aber so sein: 1.Adresse-->2.PWM)

von Wolfgang (Gast)


Lesenswert?

Frederik Wayn schrieb:
> Das mit dem Startbit ist mir wichtig,

Auch wenn du dich auf den Kopf stellst - ein einzelnes Startbit gibt es 
für die serielle Übertragung mit einem UART nicht. Da mußt du dir etwas 
anderes einfallen lassen.

Du kannst z.B. festlegen, dass eine Sendepause von einer bestimmten 
Mindestlänge den Bytezähler auf 0 setzt, falls du dir das leisten 
kannst.

von Marco S (Gast)


Lesenswert?

Ähnlich HDLC Protokoll mit Asynchronous framing, ohne Adresse und mit 
byte stuffing und für Control die Länge der Nutzdaten. Das sind relativ 
viele Bytes für eine einzelne LED, dafür kannst du alles senden. Und auf 
Empfängerseite kannst du herrlich die ungültigen Daten verwerfen, denn 
der Empfänger synchronisiert sich selbst.

Wenn du so ein enkodiertes Paket geschnürt hast, müssen Sender und 
Empfänger noch die gleiche Sprache sprechen. D.h. gleiche Baudrate, 8 
oder 9 Bit, ob Paritätsbit und wieviel Stopbits. Könnte z.B. 8n1 sein.

Der Arduino hat demnach einen Ringpuffer, wo jedes eingehende Zeichen 
abgelegt wird. Kommt nun das Zeichen der Framekennung herein, kannst du 
im Ringspeicher schauen, ob dort eventuell ein gültiges Paket vorhanden 
ist. Dann könntest du weitere Schritte unternehmen.

Ein Paket könnte dann zwei Nutzbytes enthalten. Das Erste bezeichnet die 
Led und das zweite den PWM-Wert.

von Lenny D. (le-do)


Lesenswert?

Wenn die Anwendung wirklich nur LED PWM ist wäre eine direkte Umsetzung 
von DMX zu über legen, dann kannst du sogar echtes Equipment dranhängen 
bei Gelegenheit.
Wenn es dir ums Lernen der UART Schnittstelle geht wurde ja schon viel 
gesagt:
Einzelnes Bit am Anfang geht nicht, das Startbit heißt so weil es den 
Datenframe begrenzt. lt Wikipedia kann ein UART Frame bis zu 9 bit lang 
sein aber keine Ahnung ob das im uC einfach umzusetzen ist.
Das einzige was du über tragen kannst sind ganze Bytes, die 
Interpretation ist dir freigestellt.
Ich hab letztens was ähnliches programmiert und einfach ein Timeout als 
Synchronisation benutzt, also:
Die meiste Zeit keine Daten.
Sobald 1 Byte kommt als Befehl interpretieren. Manche Befehle sind 1 
Byte lang, andere erwarten bis zu 16 byte Daten. Wenn die korrekte 
Anzahl Bytes innerhalb von der Zeit ankommt, interpretiere den Befehl 
und sende ein Acknowledge zurück, sonst sende einen Fehlercode.
Das geht nur dann hypothetisch schief, wenn du beständig immer was 
sendest, dann ist das naturgemäß natürlich unmöglich die auseinander zu 
halten.

Der einzig andere Weg Bytes auseinander zu halten ist wie bereits 
erwähnt über die erlaubten Werte die so eins annimmt. Bei ASCII 
Übertragung ist das einfach, bei Binärwerten musst du dir selbst was 
über legen, zB Verzicht auf eine PWM Stufe und damit höchste 1 nur bei 
steuerbytes.
Was mir sonst gerade noch einfällt ist die Stückelung der Daten auf 
Nibbles:
1. Byte Steuerdaten 0b1111xxxx für adressnibble (0 bis 15)
2. Byte Steuerdaten 0b0011yyyy falls mehr Adressen gewünscht zweites 
adressnibble
3. Byte Daten 0b0001aaaa höhere 4 bit Daten
4. Byte Daten 0b0000bbbb niedere 4 bit Daten
Zusammensetzen dann mit
Addr= xxxx<<8 | yyyy;
Data= aaaa<<8 | bbbb;
In jedem Fall kannst du mit einem sinnvollen Antwortcode prüfen, was 
passiert ist

Das Prinzip kannst du sogar erweitern um bis zu 12bit Daten und Adresse 
zu senden, da bei 4 Moglichkeiten nur 2 bit die Art des Bytes bestimmen 
müssen.

von Wolfgang (Gast)


Lesenswert?

Lenny D. schrieb:
> ..., bei Binärwerten musst du dir selbst was über legen, zB Verzicht
> auf eine PWM Stufe und damit höchste 1 nur bei steuerbytes.

Der Verzicht auf eine PWM Stufe muss natürlich nicht unbedingt sein. 
Vielfach wird, um das zu vermeiden, das oben erwähne Byte Stuffing 
verwendet. Dazu wird, falls das als Steuerbyte verwendete Byte in den zu 
übertragenden Daten auftritt, der Datenwert durch eine Sondersequenz 
(z.B. doppeltes Steuerbyte) ersetzt. Der Empfänger muss das erkennen und 
wieder rückgängig machen.

Für erste Versuche auf der seriellen Schnittstelle ist das aber etwas 
unübersichtlich.

von c-hater (Gast)


Lesenswert?

Frederik Wayn schrieb:
> Ich wollte mit 2 Byte´s arbeiten und einem Start Bit.
>
> -Startbit
> -Adressbyte (für die LED)
> -PWM Byte (für die Helligkeit)

Das geht nicht. Wenn du wirklich zwei Bytes (also je 8 Bits) für die 
Nutzdaten brauchst, mußt du entweder mit 9 Datenbits pro seriellem 
Datenwort arbeiten, wobei dann das höchstwertige Bit des Datenworts 
darüber entscheidet, ob es sich um eine Adresse oder einen PWM-Wert 
handelt oder du bleibst bei Datenwörtern mit 8 Datenbits, mußt dann aber 
drei davon für eine Nachricht verwenden.

Dann hast du mehr als genug Bits im Datenstrom über, um die 
Framestruktur abzubilden und das Frame insgesamt ziemlich gut gegen 
Empfangsfehler zu sichern.

z.B. (nur ein Vorschlag, es gibt 'zig andere mögliche Varianten)

1. Byte
Bit7: immer gesetzt (Kennzeichen für "StartOfFrame")
Bit0..6: oberste sieben Bit der Adresse

2. Byte
Bit7: niemals gesetzt (kein SOF)
Bit1..6: unterste sechs Bit des PWM-Werts
Bit0: unterstes Bit der Adresse

3. Byte
Bit7: niemals gesetzt (kein SOF)
Bit5..6: oberste zwei Bits des PWM-Werts
Bit0..4: CRC5-Prüfsumme über die 16 Nutzbits des Frames

Damit kannst du beim Empfang eindeutig erkennen, wo ein Frame beginnt 
und nach drei empfangenen Bytes sehr zuverlässig entscheiden, ob das 
Frame vollständig und fehlerfrei empfangen wurde.

von Frederik W. (ip-freddy)


Lesenswert?

Danke für die ausführliche Beschreibung, ich glaub das ist doch ein 
bisschen zu hoch für mich, ich hab keine ahnung wie ich das in c# 
realisieren soll.

Ich kann dann aber im 2.Byte den Wert 255 nicht erreichen oder ?

von Thomas W. (wagneth)


Lesenswert?

Wirf das Korn doch nicht in die Flinte...
Ich denke da wäre eine FSM ganz nützlich.

http://www.mikrocontroller.net/articles/Statemachine

Du musst Dir nur mögliche/sinnvolle Zustände überlegen.
Z.B. :

1. StartOfFrame erkannt.
2. erstes Datenbyte erkannt und abgelegt.
3. zweites Datenbyte erkannt und abgelegt.
4. Kommando erkannt und abgelegt.
5. Prüfsumme erkannt und abgelegt.
6. EndOfFrame erkannt und abgelegt.

Und hier wären die Transitions :
-Bei jeder Zustandsänderung muss ein Byte empfangen und abgelegt worden 
sein.
-Start, EndOfFrame und Prüfsumme wären spezielle "Wörter".
-Ist das 6. Byte kein EOF wird das Frame verworfen.
-Wird kein SOF erkannt gibts auch keinen Frame.
-Stimmt die Prüfsumme nicht ist das Frame ungültig.

...

Baute man eine Variable länge ein,
könnte man mehrere Lämpchenhelligkeiten in einem Frame übertragen.

Setz Dich doch mal 15Min konzentriert hin und stelle sowas auf.
Da kommt bestimmt was gutes raus !

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Frederik Wayn schrieb:
> Danke für die ausführliche Beschreibung, ich glaub das ist doch ein
> bisschen zu hoch für mich, ich hab keine ahnung wie ich das in c#
> realisieren soll.

Dann solltest du vielleicht einfach die Sprache lernen, die du verwenden 
willst? Stichwort: Bitoperationen. Die werden in C# praktisch genauso 
notiert wie C. Aber das kannst du auch nicht, stimmt's?

> Ich kann dann aber im 2.Byte den Wert 255 nicht erreichen oder ?

Im zweiten gesendeten Byte kannst du natürlich niemals 255 erreichen, 
aber das ist völlig unwichtig. Was wichtig ist: du kannst einen PWM-Wert 
von 255 übertragen, weil ein Teil davon im zweiten und der Rest im 
dritten Byte des Frame steckt. Du mußt die entsprechenden Bits bloß aus 
diesen beiden Bytes rausfischen und ordentlich zusammenbauen.

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.