Forum: Mikrocontroller und Digitale Elektronik Byte über UART auswerten.


von Stefan (Gast)


Lesenswert?

Hallo,

ich hab da mal eine grundlegende Frage:

Wie wird ein Byte über UART ausgewertet?

Nehmen wir mal an der Controller bekommt jetzt eine 00110110 geschickt.
Empfängt er dann sofort das ganze Byte und wertet es aus oder empfängt
er immer nur bitweise nur Bitweise....??

Gruß

von johnny.m (Gast)


Lesenswert?

Die Übertragung ist byteweise

von Jankey (Gast)


Lesenswert?

beim Stopbit lösen die meisten uC einen (coitus)Interrupt(us) aus ...

von Stefan (Gast)


Lesenswert?

Das heißt also im UDR-Register steht dann das ganze empfangene Byte.

Wie kann ich dann aus dem Byte die einzelnen Bits auswerten? Oder hab
ich nur die Möglichkeit das Byte komplett zu verarbeiten...?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

Die meisten Prozessoren können einzelne Bits verarbeiten.
Sonst gibt es noch logische Operationen, mit denen man einzelne Bits
ausmaskieren kann.

Was willst du denn machen?

von Witwenbetreuer (Gast)


Lesenswert?

.... ganz neu oder ? Gott wirds dir vergeben^^
einfach auslesen(am besten in der Koitus Interruptus Unterroutine)und
dann in deinem Programmcode auswerten (Hauptprogramm nach dem
"Wakeup" nach Interrupt)

von Witwenbetreuer (Gast)


Lesenswert?

http://de.wikipedia.org/wiki/Coitus_interruptus (Sorry konnte ich mir
nicht verkneifen :D

von Stefan (Gast)


Lesenswert?

Ich will aus einem Byte jedes Bit überprüfen, ob es 0 oder 1 ist.

Bei einer 1 soll einer Variablen High ein bestimmter Wert zugewisen
werden welcher an die entsprechende Stelle in ein Array geschrieben
wird. Bei 0 das gleiche mit einer Variablen LOW.

Also ungefähr so:

HiGH = 40;
LOW  = 30;

Empfangenes Byte: 00110110

array[30, 40, 40, 30, 40, 40, 30, 30]

Das ganze soll in C und auf nem Atmega funktionieren...

von johnny.m (Gast)


Lesenswert?

Wenn Du das nicht selber hinbekommst, fehlen Dir einfach C- und
µC-Grundlagen. Schau mal ins AVR-GCC-Tutorial auf dieser Seite. Da
steht über Bitüberprüfungen und Bitoperationen usw. was drin. Ansonsten
vielleicht noch ein gutes C-Buch dabei...

> array[30, 40, 40, 30, 40, 40, 30, 30]
...und das bestätigt die Vermutung, dass Dir die absoluten Basics
fehlen (wenn das C-Schreibweise sein sollte?!?). Bitte fang mit
einfacheren Sachen an, bevor Du ne UART bearbeitest. Ist nicht bös
gemeint, nur wenn Dir hier jemand jetzt ein fertiges Codeschnipselchen
schreibt kommt zwei Minuten später die nächste Grundlagenfrage von Dir,
und die Grundlagen sind etwas, ohne die es einfach nicht geht...

von inoffizieller WM-Rahul (Gast)


Lesenswert?

unsigned char i, d,n;
unsigned char array[8];
d = UDR;
n=0;
for(i=1;i<=128;i*=2)
{
  if(d & i)
  {
   array[n++]=40;
  }
  else
  {
   array[n++]=30;
  }
}

man könnte es auch noch etwas verkürzen...

von Witwenbetreuer (Gast)


Lesenswert?

so ähnlich sollte es laufen:

byte i=0;
for(i=0;i<8;i++)
{
  if((deinByte<<i)&&0x01)array[i]=40;
  else array[i]=30;
}
(alle Angaben ohne Gewehr wir sind ja nicht in Israel)hab schon lange
nicht mehr Programmiert ...

von inoffizieller WM-Rahul (Gast)


Lesenswert?

@Johnny: tja...

von inoffizieller WM-Rahul (Gast)


Lesenswert?

Kleiner Hinweis noch: Meine Routine baut das Array von hinten nach vorne
auf. Das sollte einem erfahrenen Programmierer aber auffallen...

von Witwenbetreuer (Gast)


Lesenswert?

Ja ich Newbie....
if((0x01<<i)&&deinByte)array[i]=40; .... geht glaube besser ... lach

von Stefan (Gast)


Lesenswert?

Das war nur ein Beispiel..... Hab mir irgendwie gedacht dass so etwas
kommt...;-)

array[] = {30, 40, 40, 30, 40, 40, 30, 30}; besser...?

Ich will auch gar keinen Codeschnipsel haben, eigentlich will ich nur
wissen wie der UART das Byte verarbeitet und wie ich die einzelnen Bits
auslesen kann....

Bei einem einzelnen Zeichen mach ich es z.B. so:

SIGNAL(SIG_USART_RECV)
{

    unsigned char data;

    while(UCSR0A & (1 << RXC0)) {


  buffer = UDR0;

     switch(data) { .................usw.


Also schau ich ob ein Zeichen im Emfangsdateregister liegt, lese es
aus, führe einen Verglleich durch und je nach dem was es für ein
Zeichen ist, soll irgendetwas passieren.

Nur wie kann ich jetzt das erste Bit eines Byte abfragen? Oder setzt er
mir erst nur ein Bit ins UDR und nach dem verarbeiten kommt das nächste?

von Stefan (Gast)


Lesenswert?

Ups, da war ich etwas langsam!

Danke@ Rahul und Witwenbetreuer!

Das ist mal ne Hilfe mit der man auch was anfangen kann...

Werd es mal ausprobieren und später berichten ob es klappt....


Gruß

von johnny.m (Gast)


Lesenswert?

> Oder setzt er mir erst nur ein Bit ins UDR und nach dem
> verarbeiten kommt das nächste?

Ich mag Wiederholungen eigentlich nicht, aber da es ohne anscheinend
nicht geht: DIE ÜBERTRAGUNG ERFOLGT BYTEWEISE!

Und die Auswertung einzelner Bits ist von anderen oben ziemlich
ausführlich beschrieben worden. Das zum Thema C-Grundlagen
(Bitoperationen) wiederhole ich jetzt nicht...

von johnny.m (Gast)


Lesenswert?

Ah, der €uro fällt Cent-weise...

BTW: Besorge Dir mal eine aktuelle Version vom Compiler. SIGNAL ist
mittlerweile veraltet. ISR heißt das Zauberwort;-)

von Stefan (Gast)


Lesenswert?

Werd ich tun, danke!

von Profi (Gast)


Lesenswert?

Kürzer geht immer ;-) , gell Rahul!

for(i=1;i;i*=2)array[n++]=d&i?40:30;

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>for(i=1;i;i*=2)array[n++]=d&i?40:30;
und was macht du mit n?

for(i=1;i;i*=2,n++)array[n++]=d&i?40:30;
oder?

von Witwenbetreuer (Gast)


Lesenswert?

keine Panik viele Wege führen nach Rom...

von Wolfram (Gast)


Lesenswert?

>while(UCSR0A & (1 << RXC0))
was soll das im UART Empfangsinterrupt? Wenn der aufgerufen ist , dann

liegt ein Empfangenes Zeichen vor da muss man nicht danach pollen.

von Profi (Gast)


Lesenswert?

@Rahul, verstehe Deine Frage nicht.
Warum willst Du n zweimal incrementieren?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

gepennt...

von Karl H. (kbuchegg)


Lesenswert?

> Kürzer geht immer ;-)

Richtig

  tmp = array;
  for( i = 1; i; i *= 2 )
    *(tmp++) = d&i ? 40 : 30;

Obwohl: Es soll schon Compiler gegeben haben, die
diese Transformation selbst gemacht haben :-)

von Martin Kohler (Gast)


Lesenswert?

> for( i = 1; i; i *= 2 )

und wo hört die for Schlaufe auf?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

sobald i=0 ist.

von Karl H. (kbuchegg)


Lesenswert?

@ Martin

rechne einfach mit. i ist ein unsigned char
Wenn i den Wert 0x80 erreicht hat, was ist
dann das Ergebnis von 0x80 * 2.  Beachte aber:
Wir bewegen uns im unsigned char Raum (Das ist
der Schlüssel zum Verständnis).

von Witwenbetreuer (Gast)


Lesenswert?

i *= 2 ist doch Multiplikation mit 2 und Zuweisung zu i ...!?
Dauert eine Multiplikation nicht länger wie eine Addition ? Wenn man
das auf der Assembler Ebene betrachtet unterstützt der ATMEL keine
Multiplikation/Division (da kein Math-Coproz) ?

von Michael Wilhelm (Gast)


Lesenswert?

@ Witwenbetreuer,
die ATMEGAS haben (fast?) alle einen Hardware Multiplizierer. Außerdem
würde doch der Compiler einen << 1 daraus machen.

MW

von inoffizieller WM-Rahul (Gast)


Lesenswert?

man könnte auch i = (i<<1) schreiben...und irgendwann fällt die
anfängliche 1 oben aus dem Byte heraus...

von johnny.m (Gast)


Lesenswert?

Der AVR-GCC-C-Compiler macht z.B. aus einer Multiplikation einer
Variable mit 2 automatisch eine Addition mit sich selbst, und zwar auch
für einen Mega mit Hardware-Multiplier. Ist das schnellste...

@Michael:
Die ATMegas haben tatsächlich alle einen HW-Multiplier.

von Witwenbetreuer (Gast)


Lesenswert?

Danke an alle. Dies Beispiel ist schon einleuchtend. aber wie siehts bei
Operationen aus wie z.B. 3*3 mit einfach eine Stelle verschieben
(Bitweise << 1) ist nicht.

von johnny.m (Gast)


Lesenswert?

Wenn der µC einen Hardware-Multiplier hat (wie eben z.B. die ATMegas),
dann ist auch das kein Problem, da gibts dann spezielle
Assembler-Befehle für (wobei zumindest im AVR-Fall Multiplikationen mit
2^n schneller gehen, wenn man auf den HW-Multiplier verzichtet und sie
durch Schiebeoperationen ersetzt, wie es der AVR-GCC-C-Compiler je nach
eingestellter Optimierung auch macht. Welcher Weg im Einzelnen günstiger
ist, entscheidet der Compiler).

Bei anderen Prozessoren kann man dann z.B. über eine sukzessive
Addition gehen (also für das Beispiel von oben: 3 * 3 = 3 + 3 + 3).

von inoffizieller WM-Rahul (Gast)


Lesenswert?

3*3 ist doch eigentlich nichts anderes als 3*(2+1) = 3*2 + 3*1 Das kann
man durch ein Linksschieben und eine Addition erledigen...
Wenn man mit Konstanten multipliziert, kann man das i.d.R. durch solche
Operationen optimieren, wenn kein HW-Multiplizierer vorhanden ist.

von Karl heinz B. (kbucheg)


Lesenswert?

> Welcher Weg im Einzelnen günstiger
> ist, entscheidet der Compiler).

Das ist überhaupt etwas, was am Anfang (besonders wenn
man von Assembler kommt) schwierig ist: Dem Compiler
zu vertrauen, dass er für solche einfachen Dinge den
besten Weg findet.

Viele Compiler haben Strategien mit, wie sie grade für
Multiplikation mit kleinen Zahlen über Addition bzw.
Shiften schneller zum Ziel kommen. zb.
  i * 10
Wenn keine Hardware dafür zur Verfügung steht, dann generieren
viele Compiler dafür eine Sequenz (am Beispiel einer gedachten
Stack Maschine)

   fetch i
   push
   shift left     ; * 2
   shift left     ; * 4
   pop            ; i wiederholen
   add            ; ( *4 + i ) -> * 5
   shift left     ; * 10

Und so kann man für viele kleine Zahlen Sequenzen finden, die
wesentlich schneller sind als eine allgemeine Softwaremultiplikation.

Als Programmierer muss man nur seinem Compiler vertrauen und den Code
so schreiben, wie er für den Programmierer am besten lesbar bleibt.

Zugegeben: Im aktuellen Beispiel wäre

  for( i = 1; i; i = i << 1 )

klarer gewesen, da es sehr viel besser die Absicht ausdrückt, die
dahinter steckt: ein 1 Bit quer durch einen unsigned char zu shiften.
Und das sollte eigentlich immer das Leitmotiv sein: Schreibe den Code
so, dass er deine Absicht ausdrückt und überlasse die Low-Level
Details wie diese Absicht am besten in Code ausgedrückt werden
kann, dem Compiler. Wenn du eine Multiplikation mit 10 haben willst,
dann schreibe
    i * 10
und nicht die Sequenz von oben
    ( i << 2 ) + 1 ) << 1;
( Es sei denn du schreibst für den 'Obfuscated C Code Contest'
  http://www.de.ioccc.org/main.html :-)

von Wolfram (Gast)


Lesenswert?

>Schreibe den Code so, dass er deine Absicht ausdrückt
for( i = 1; i>0; i = i << 1 )
dürfte den gleichen Code liefern, ist aber sauberer da der char nicht
zusätzlich als Boolscher Wert benutzt wird.

von johnny.m (Gast)


Lesenswert?

@Wolfram:
Achtung! Dabei muss i als unsigned deklariert sein, sonst gehts ins
Höschen!

von Wolfram (Gast)


Lesenswert?

Stimmt:
for( unsigned char i = 1; i>0; i = i << 1 )
wäre etwas sicherer mit c99

von Karl heinz B. (kbucheg)


Lesenswert?

> ist aber sauberer

Stimme ich dir grundsätzlich zu.
Allerdings haben sich viele C-Programmierer (ich auch)
an die 'Unsitte' gewöhnt, einen Vergleich mit 0 nicht
explizit hinzuschreiben, sondern die Kurzform zu wählen.
Das alles in dem Wissen, dass 0 automatisch als FALSE
gewertet wird und alles andere als TRUE.

von Karl heinz B. (kbucheg)


Lesenswert?

Wenn schon, würde ich das so formulieren:

for( unsigned char i = 1; i != 0; i = i << 1 )

also !=  anstatt >

von johnny.m (Gast)


Lesenswert?

...Und dann kann man auch das 'unsigned char' wieder weglassen...

von Wolfram (Gast)


Lesenswert?

>for( unsigned char i = 1; i != 0; i = i << 1 )
ich gebe zu das ist noch einen Tick sauberer

von Karl heinz B. (kbucheg)


Lesenswert?

> Und dann kann man auch das 'unsigned char' wieder weglassen

Streng nach den C-Regeln eigentlich nicht :-)
Die Sache ist die: Was hier ausgenutzt wird, fällt in die
Kategorie 'Overflow'. Was bei einem Overflow aber in C
passieren soll, ist nur bei 'unsigned' exakt definiert. Bei
signed Werten kann die Implementierung machen was
sie will. OK. Meist verhält sie sich wie gewünscht, aber
es wäre auch denkbar, dass eine Implementierung einfach
den Rechner crashen lässt.

Daher: Wenn man auf Byte-Ebene arbeitet ist ein 'unsigned'
praktisch nie verkehrt.

von Witwenbetreuer (Gast)


Lesenswert?

Der Kompiler müsste aber theoretisch einen allgemein gültigen
Algorithmus zur Multiplikation(soweit nicht hardware unterstützt)
besitzen. Multipliziere ich z.B. X*2 so wird er wahrscheinlich
optimieren und einfach Schieben X<<1. Kommen aber 2 unbekannte wie: X*Y
dann sieht die Sache glaube andersch aus. Da wird der umgesetzte
Maschinencode ein wenig aufwändiger sein müssen.

Danke nochmals ...

von Läubi (Gast)


Lesenswert?

Es geht ja um Konstanten! Wen X und Y Variablen sind gehts natürlich nur
mit Multiplikation.

von inoffiziella WM-Rahul (Gast)


Lesenswert?

>Der Kompiler müsste aber theoretisch einen allgemein gültigen
>Algorithmus zur Multiplikation(soweit nicht hardware unterstützt)
>besitzen.

Ganze Zahlen kann man durch Addition und Schleifen multiplizieren.
Das gleiche gilt für die Division.

Wenn man Konstanten miteinander multiplizieren will, dann sollte das
der Compiler von sich aus ausrechnen (preprozessor/Makros...).

Das o.g. Verfahren funktioniert, wenn man eine Variable mit ganzen Zahl
multiplizieren will.

von Witwenbetreuer (Gast)


Lesenswert?

yap mit Schleifen und so hab ich es in Assembler auch schon Programmiert
wobei die Multiplikation der einfachere Weg war, Division war wegen dem
Rest bei ungeraden Zahlen und Division durch NULL Abfrage schon
aufwändiger.

Gelobt sei C ^^..... lach während unsereres Abschlussprojektes der
Technikerschule haben wir eine Steuerung zur Bedienung und Regelung
eines Kaffeerösters projektiert. Zuerst haben wir mit Assembler
angefangen (deswegen Multiplikation Division in Assembler).Nach ein
paar Tagen bin ich dann(zum Glück)auf dieses Forum und den GCC-AVR
Compiler gestossen, so das wir das Projekt noch in diesem Leben beenden
konnten ^^(ca 40 Seiten Quellcode was gerade so in den 8K Speicher
passte). Wenn ich mir das in Assembler Vorstelle ... Hilfe.... Also
nochmals vielen Dank an die Wahnsinnigen :D die beim AVR-GCC-Kompiler
mitgewirkt haben.

von inoffizielle WM-Rahul (Gast)


Lesenswert?

Division würde ich so lösen (kann man bestimmt irgendwo auch
nachgucken...):

unsigned char dividend, divisor, quotient=0;
dividend = 9;
divisor = 4;
for(;dividend>=divisor;dividend-=divisor,quotient++);
quotient sollte am Ende gleich 2 und dividend gleich 1 sein.
ungetestet...

von David B. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Kollegen,

habe versucht mit dem Attiny2313 über dessen TXD leitung ein einzelnes
zeichen zu senden. damit ich das überprüfen kann, verwende ich
empfangsseitig hyperterminal. programmiert ist das alles in C / C++
unter WinAvr. erhalten tue ich allerdings hyroglyphen ( <<<<888<<8888
{} !!!!!ÄÄÄÄÄÄÄÄ} ) sowas in der art eben.

Dieser "zusammengebastelte" sourcecode (siehe Anhang) sollte laut
datenblatt eigentlich ausreichen. einbinden der header, initialisierung
der baudrate und der TXD datenleitung sowie die einstellung von high und
lowbyte. die "Processor frequency" ist im makefile mit F_CPU = 2000000
bereits modifiziert.

ist das ein problem seitens der baud rate oder falsch eingestellter
fusebits ???

für lösungsvorschläge bin ich zutiefst dankbar ;-)

david

von Witwenbetreuer (Gast)


Angehängte Dateien:

Lesenswert?

Habs mal mit ViualStudio.net und dem serialPort Steuerelement
geschrieben(ist billig...zeigt was vom Com1 kommt .... Geschwindigkeit
9600 Baud ohne Handshake 8Bits ohne Parität)Vorraussetzung .net
Framework (Macrosoft)

von Stefan (Gast)


Lesenswert?

> tmp = array;
>  for( i = 1; i; i *= 2 )
>    *(tmp++) = d&i ? 40 : 30;

Kann mir jemand die untersten beiden Zeilen genauer erläutern, verstehe
nicht ganz was es damit auf sich hat...??


Gruß

von inoffizieller WM-Rahul (Gast)


Lesenswert?

*(tmp++) = d&i ? 40 : 30;
Das ist C!

*(tmp++) ist ein Pointer auf eine Variable.
Dadurch kann man ziemlich einfach der Länge nach auf ein Array/String
zugreifen. mit tmp=array wird der Pointer auf den Anfang des Feldes
gesetzt. Da ein Feld in der Regel vom Compiler linear im Speicher
angelegt wird, kann man durch einfaches Pointer-Inkrementieren auf die
einzelnen Einträge zugreifen.

d&i ist eine arithmetische Operation (bitweises "Verunden"), die
entweder 0 oder eine von 0 verschiedene Zahl liefert. 0 ist in C auch
der Wahrheitswert "falsch", alle anderen Zahlen sind "wahr".
"?:" ist eine bedingte Zuweisung: ist d&i !=0, wird *(tmp) 40
zugewiesen, sonst 30.

Das sind übrigens C-Grundlagen...

von johnny.m (Gast)


Lesenswert?

Der ternäre Operator ?: (der heißt deshalb 'ternär', weil er drei
Operanden hat) wertet den Ausdruck links vom Fragezeichen aus. Ist
dieser wahr (ungleich null), dann wird die erste Anweisung (vor dem
Doppelpunkt) ausgeführt, andernfalls die hinter dem Doppelpunkt.

*(tmp++) = d & i ? 40 : 30;

ist also eine Kurzschreibweise für

if(d & i)
    *(tmp++) = 40;
else
    *(tmp++) = 30;

von Karl H. (kbuchegg)


Lesenswert?

Wobei ich mir jetzt mit der Operatorenpriorität nicht sicher
bin.
Im Zweifelsfalle würde ich also schreiben:

*(tmp++) = ( d & i ) ? 40 : 30;

von johnny.m (Gast)


Lesenswert?

@Karl Heinz:
Hab grad noch mal im K&R nachgeschaut: & hat die höhere Priorität,
sollte also auch ohne Klammern gehen. Aber im Zweifelsfall (v.a. dann,
wenn ich grad kein Buch zur Hand habe oder zu faul bin, zu suchen)
spendiere ich auch lieber ein zusätzliches Klammernpärchen. Besser eins
mehr als eins zu wenig...

von Stefan (Gast)


Lesenswert?

Danke für die Antworten!

Ja, C Grundlagen...!! Da fehlen mir noch einige! Aber ich bin fleißig
dabei Sie zu lernen....

Wenn ich es nun so schreibe:

unsigned char i, d;
unsigned char array[8];
unsigned char tmp;

d = UDR0;
n=0;

tmp = array;
  for( i = 1; i; i *= 2 )
    *(tmp++) = (d&i) ? 40 : 30;

}

bekomm ich ne Fehlermeldung

(Fehler: falsches Typ-Argument von »unary *«)

Wieso?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

tmp muß auch ein Pointer sein...

unsigned char *tmp;

(bin mir aber auch nicht ganz sicher.)

von johnny.m (Gast)


Lesenswert?

Es wundert mich, dass Du nur eine Fehlermeldung kriegst. Das was der
inoffizielle schon angeführt hat, ist sicher der Grund für die von Dir
genannte Fehlermeldung. Ich als Comopiler würde aber auch bei einer
Zuweisung an eine Variable mit dem Namen 'n' darauf bestehen, dass
diese zuvor auch deklariert wurde. Oder ist das nur ein Teil Deines
Codes?

von Stefan (Gast)


Lesenswert?

Hey klar, muss als Pointer deklariert werden. Nun klappts! Danke!

Hab da noch ne Frage:

Schreibt der Pointer tmp mir die Werte 30 bzw. 40 beim durchlaufen der
Schleife direkt in das array?

von Stefan (Gast)


Lesenswert?

Hey jonny, das ist nur ein Teil des Codes gewesen.....

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>Schreibt der Pointer tmp mir die Werte 30 bzw. 40 beim durchlaufen >der
Schleife direkt in das array?

Jein, er schreibt es an die Speicherstelle, die durch den Pointer tmp
beschrieben wird. Der teilt sich die Speicherstelle halt mit dem
Array...

von Stefan (Gast)


Lesenswert?

Coole Sache, nun funktioniert alles so wie ich es mir vorgestellt
hatte!

Danke für die Hilfe !

Gruß

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.