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ß
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...?
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?
.... 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)
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...
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...
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...
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 ...
Kleiner Hinweis noch: Meine Routine baut das Array von hinten nach vorne auf. Das sollte einem erfahrenen Programmierer aber auffallen...
Ja ich Newbie.... if((0x01<<i)&&deinByte)array[i]=40; .... geht glaube besser ... lach
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?
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ß
> 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...
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;-)
Kürzer geht immer ;-) , gell Rahul! for(i=1;i;i*=2)array[n++]=d&i?40:30;
>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?
>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.
@Rahul, verstehe Deine Frage nicht. Warum willst Du n zweimal incrementieren?
> 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 :-)
@ 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).
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) ?
@ Witwenbetreuer, die ATMEGAS haben (fast?) alle einen Hardware Multiplizierer. Außerdem würde doch der Compiler einen << 1 daraus machen. MW
man könnte auch i = (i<<1) schreiben...und irgendwann fällt die anfängliche 1 oben aus dem Byte heraus...
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.
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.
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).
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.
> 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 :-)
>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.
@Wolfram: Achtung! Dabei muss i als unsigned deklariert sein, sonst gehts ins Höschen!
Stimmt: for( unsigned char i = 1; i>0; i = i << 1 ) wäre etwas sicherer mit c99
> 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.
Wenn schon, würde ich das so formulieren: for( unsigned char i = 1; i != 0; i = i << 1 ) also != anstatt >
>for( unsigned char i = 1; i != 0; i = i << 1 )
ich gebe zu das ist noch einen Tick sauberer
> 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.
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 ...
Es geht ja um Konstanten! Wen X und Y Variablen sind gehts natürlich nur mit Multiplikation.
>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.
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.
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...
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
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)
> 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ß
*(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...
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;
Wobei ich mir jetzt mit der Operatorenpriorität nicht sicher bin. Im Zweifelsfalle würde ich also schreiben: *(tmp++) = ( d & i ) ? 40 : 30;
@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...
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?
tmp muß auch ein Pointer sein... unsigned char *tmp; (bin mir aber auch nicht ganz sicher.)
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?
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?
>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...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.