Hallo,
ich baue gerade eine Art Fernbedienung, dafür müssen natürlich
Bitsequenzen an eine IR-LED ausgegeben werden. Hier erstmal eine
(erläuterte) Beispielsbitsequenz:
Herstellercode Parity Genre Data ID Parity
00101010 01001100 0000 1110 0110 0100000000 00 01101000
Es wird von links angefangen zu übertragen. Der Herstellercode, die
erste Parity, das Genre und die ID sind bei jeder Übertragung gleich.
Die 10 Datenbits sollen an die Sendefunktion übergeben werden. Die
Parity soll von der Funktion selbstständig berechnet werden, um Speicher
zu sparen. Sie wird aus dem 3., 4. und 5. Byte per XOR errechnet.
Meine Überlegung war jetzt die folgende: In einer Sendefunktion (ihr
Name sei JAPAN) wird eine 64 Bit-Variable mit den Fixwerten
initialisiert, dann wird der übergebene Datenwert darangeschoben und die
Parity ausgerechnet. Ich weiß jedoch nicht, wie ich das realisieren
kann. Würde jemand so freundlich sein, mir mit einem Codeschnipsel
weiterzuhelfen?
Ich brauche nur die fertige 64 Bit-Variable, die Data und Parity
beinhaltet. Wie ich die durchlaufen kann, ist nicht das Problem.
Vielen Dank im Vorraus, Sven
Alles was du brauchst sind die bitweisen Operatoren:
| bitweises ODER.
& bitweises UND.
~ bitweises negieren.
>>n rechtsshift um n Bits
<<n linksshift um n Bits
Falls du nicht genau weißt, was die machen:
http://de.wikipedia.org/wiki/Bitweiser_Operator
Das zweite Byte von rechts aus einer Variable x bekommst du dann z.B.
mit
uint8_t second_byte = (x & 0xFF00) >> 8.
Alternativ, nicht portabel aber evtl. schneller (Assembler-Code
kontrollieren!) geht das gleiche auch so:
uint8_t second_byte = ((uint8_t*)&x)[1].
Vielleicht muss der Index auch anders sein, aber das Prinzip sollte klar
werden.
Mit dem bitweisen ODER kannst du die übergebenen Werte in den
64-Bit-Wert "einfügen" (wenn du nicht weißt wie, würde ich dir raten
eine Skizze zu machen; so viele Möglichkeiten gibt es da nicht).
ir.c:48: warning: left shift count >= width of type
2
ir.c:50: warning: left shift count >= width of type
Natürlich könnte ich jetzt data und parity auch als 64 Bit-Variable
ausführen, aber dann kann ich mir das ja auch alles sparen und direkt
eine 64 Bit-Variable übergeben. Das will ich mir ja aber sparen. Wer
weiß Abhilfe?
Bei solchen Warnungen staune ich immer wieder über die
'Intelligenz' des GCC.
Das Problem sind in der Tat die Shifts:
Hier:
1
for(uint8_ti=0;i<48;i++){
und dann hier:
1
if(sequenz&(1<<i))
Die '1' ist vom Typ 'int', hat also beim AVR 16 Bits. Zu wenig
für die for-Schleife.
Ich würde es mit
1
if(sequenz&(1ULL<<i))
versuchen.
Hinter IR_LED_ON() steckt vermutlich ein PWM o. ä. der den Träger
erzeugt? Sowas wird nicht bei jedem Empfänger funktionieren, da
die tatsächliche Dauer eines Pulses so nur abgeschätzt wird.
Einige Geräte erwarten ziemlich genaue Zeiten und wenn man da
einen Puls mit einer Toleranz von +/-1 Periode sendet kann das
schnell mal schief gehen.
Also: Versuchen die Trägerfrequenz so genau wie möglich zu treffen
(davon hängt letztlich die Pulsdauer ab) und damit die erwartete
Anzahl Perioden senden.
Bedenke auch das das hantieren mit Datentypen > 8Bits auf einem
AVR mitunter recht lange dauern kann. Zumindest dann wenn es um
Mikrosekunden geht.
Hallo,
die Warnings liegen nicht in der Zeile, die du angesprochen hast,
sondern in den Zeilen 48 und 50, die ich mit entsprechenden
Zeilennummern versehen habe.
Die Modulationsfrequenz wird mit Timer2 im CTC Mode erzeugt. So erziele
ich eine Genauigkeit von 0,05kHz, was vollkommen ausreichend ist.
Ich möchte einfach erstmal gucken, ob mein Problem mit meinem Ansatz
lösbar ist. Anschließend denke ich nochmal über das mit dem Timer
nach...
Du hast übrigens noch mehr "ULL" Fehler in deinem Quellcode. Alle
Konstanten, die größer als int (0x7FFF bzw. 32767 beim AVR-GCC
normalerweise) sind, müssen mit U (für bis zu 0xFFFF, 65535), UL (für
bis zu 0xFFFFFFFF 32bit) oder ULL für 64bit postfixiert werden!
Damit die Shifts in 64Bit Breite ausgeführt werden, sollte die Variable
data bzw. parity vorher gecastet werden.
Du kannst übrigens auch data vorher in eine 64bit Temporär-Variable
kopieren und dann den Shift ausführen. Ich gehe mal stark davon aus,
dass der Compiler den gleichen Code oder einen ähnlichen Code generieren
wird.
Kannst du nochmal erläutern, was du mit dem folgenden meintest?
> Damit die Shifts in 64Bit Breite ausgeführt werden, sollte die Variable> data bzw. parity vorher gecastet werden.
> Kannst du nochmal erläutern, was du mit dem folgenden meintest?>> Damit die Shifts in 64Bit Breite ausgeführt werden, sollte die Variable>> data bzw. parity vorher gecastet werden.
Klar: Durch das (uint64_t) in den Klammern vor der Variable data bzw.
parity wird diese (16 Bit Variable) wie eine 64 bit Variable behandelt.
Der Compiler führt also den auf diesen Cast folgenden Shift in 64 Bit
Breite durch. Somit ist alles in Butter.
Folgende Zeilen wird vermutlich noch eine Warnung erzeugen:
Da das letzte Klammerpaar (durch das ULL Prefix) ein uint64_t Typ ist,
wird der gesamte Rechte Ausdruck auf uint64_t angehoben, bevor er durch
das XOR verknüpft und schlussendlich dem "parity" zugewiesen wird. Am
besten sollte man hier auch wieder einen Cast benutzen, wenn ich mich
jetzt nicht irre:
if(sequenz&((uint64_t)(1<<i)))//Ist das so richtig???
Die Zeile funktioniert so nicht wie gewünscht, da auch beim Casten immer
noch die Klammerregeln gelten. Sprich: Hier wird erst nach dem Shiften
gecastet.
Entweder:
1
if(sequenz&(1ULL<<i))
oder
1
if(sequenz&((uint64_t)1<<i))
Hoffe jetzt keinen Fehler gemacht zu haben. Ich verlass mich bei solchen
Geschossen immer auf die Warnungen vom Compiler ;)
Übrigens sind Shifts um ein i-faches auf dem AVR relativ langsam (Da der
Shift mithilfe eine Schleife durchgeführt wird). Am besten ist es, die
sequenz Variable durchzushiften und immer das niedrigste Bit zu prüfen.
und den Ausgang entsprechend zu setzen. Die Variable "sequenz" ist zwar
danach nicht mehr zu gebrauchen, aber man kann diese ja vorher sichern.
Wow, vielen Dank für die äußerst ausführlichen Ausführungen. Das mit den
Shifts um ein i-faches hab ich nach deinen Vorschlägen optimiert. Das
Ganze funktioniert jetzt wie gewünscht. Bei dem zu fernbedienenden Gerät
handelt es sich übrigens um einen DRA-700AE Stereoreceiver der Marke
Denon.
Sven S. wrote:
> Wow, vielen Dank für die äußerst ausführlichen Ausführungen. Das mit den> Shifts um ein i-faches hab ich nach deinen Vorschlägen optimiert. Das> Ganze funktioniert jetzt wie gewünscht. Bei dem zu fernbedienenden Gerät> handelt es sich übrigens um einen DRA-700AE Stereoreceiver der Marke> Denon.
Super! Auch wenn das Ganze nicht so wirklich schön aussieht ;)
Normalerweise (tm) macht man sowas mit einem Timer, aber wenn sonst
nichts nebenbei gemacht werden soll, was das Timing der Warteschleifen
zerreißt, ist es doch in Ordnung. :D
Du hast natürlich Recht. Eventuell muss ich bei der Implementierung das
Ganze nochmal umstricken, deswegen kann ich mir ja schonmal laut
Gedanken darüber machen:
1. Ich nehme zwei Timer; einer erzeugt die Modulation per Hardware
(CTC), der andere misst die Zeit. Das ganze würde dann per Interrupt
vonstatten gehen. Nachteil: Es werden zwei Timer benötigt.
2. Ich nehme einen Timer; er löst im Takt der Modulationsfrequenz aus
und zählt dabei die Zeit. Nachteil: Bei 40kHz Modulationsfrequenz wäre
die Zeit zwischen zwei Interrupts 25µs, das würde aber kaum für die
Befehle ausreichen, oder?
Was meint ihr?
@ Sven S. (schwerminator)
>Du hast natürlich Recht. Eventuell muss ich bei der Implementierung das>Ganze nochmal umstricken,
Vor allem ist es besonders auf einem 8 Bitter nicht sinnvoll, die
Datenpakete als 64 Bit Zahlen zu handhaben. Besser als Array von Bytes,
das ist viel schneller und übersichtlicher.
MFg
Falk
Hallo Falk,
darüber werde ich mal nachdenken. Ich finde es eigedlich mit der langen
Variablen übersichtlicher, aber schneller ist das Array natürlich -
obwohl es nicht so sehr auf Geschwindigkeit ankommt...
Hat jemand Rat auf die oben angesprochene Timerproblematik?
Falk Brunner wrote:
> @ Sven S. (schwerminator)>>>Du hast natürlich Recht. Eventuell muss ich bei der Implementierung das>>Ganze nochmal umstricken,>> Vor allem ist es besonders auf einem 8 Bitter nicht sinnvoll, die> Datenpakete als 64 Bit Zahlen zu handhaben. Besser als Array von Bytes,> das ist viel schneller und übersichtlicher.
Würde ich nicht unbedingt sagen. Wie kommst du darauf? Sind ja keine
Rechenoperationen, die hier durchgeführt werden.
Einzig beim Shiften wird der Compiler wohl immer alle 8 Bytes Shiften.
@ Simon K. (simon) Benutzerseite
>Würde ich nicht unbedingt sagen. Wie kommst du darauf?
Weil man ein Datenpakt per UART auch nicht als 1024 Bit Zahl behandelt,
wenn man 128 Bytes senden will ;-)
Daten in grosse Zahlen zu packen ist eigentlich nur dann notwendig, wenn
man direkt und ohne "von Hand" Überlaufrechung was berechnen will.
> Sind ja keine>Rechenoperationen, die hier durchgeführt werden.
EBEN!
>Einzig beim Shiften wird der Compiler wohl immer alle 8 Bytes Shiften.
Unter anderem. Auch wenn die Compiler schon bisweilen gut optimieren,
Brain 2.0 können sie nicht ansatzweise ersetzen.
>void JAPAN(uint16_t data){> uint64_t sequenz = 108016212ULL | ((uint64_t) data << 28);
Ok, das wird nen normale Initialisierung.
> uint8_t parity = ((sequenz & 0xFF0000UL) >> 16) ^ ((sequenz &> 0xFF000000UL) >> 24) ^ ((sequenz & 0xFF00000000ULL) >> 32);> sequenz |= ((uint64_t) parity << 40);
Ist der Compiler so clever, die Shifts rauszuschmeissen und einfachen
XOR der Bytes draus zu machen?
> if(temp_sequenz & 1)
Ist er hier so clever zu erkennen, dass er nur das untere Byte prüfen
muss?
> temp_sequenz >>= 1;
Der 64 Bit Shift, im aktuellen AVR-GCC eine Katastrophe. Siehe
Beitrag "Re: Frage zur C Syntax"
Habs mal ausprobiert.
Falk Brunner wrote:
>>Einzig beim Shiften wird der Compiler wohl immer alle 8 Bytes Shiften.
Bei 64 Bit Arithmetik ruft er sogar Funktionen dafür auf
> Unter anderem. Auch wenn die Compiler schon bisweilen gut optimieren,> Brain 2.0 können sie nicht ansatzweise ersetzen.
Natürlich nicht.
>> uint8_t parity = ((sequenz & 0xFF0000UL) >> 16) ^ ((sequenz &>> 0xFF000000UL) >> 24) ^ ((sequenz & 0xFF00000000ULL) >> 32);>> sequenz |= ((uint64_t) parity << 40);>> Ist der Compiler so clever, die Shifts rauszuschmeissen und einfachen> XOR der Bytes draus zu machen?
Normalerweie macht der Compiler das. Da aber (wie schon erwähnt) die
Shifts sofort durch einen Funktionsaufruf ersetzt werden, geht das
natürlich nicht mehr.. Hm, damit habe ich nicht gerechnet. Bei 32 Bit
Arithmetik versteht er das sehr gut.
>> if(temp_sequenz & 1)>> Ist er hier so clever zu erkennen, dass er nur das untere Byte prüfen> muss?
Ja.
>> temp_sequenz >>= 1;>> Der 64 Bit Shift, im aktuellen AVR-GCC eine Katastrophe. Siehe
Ja, das stimmt leider.
> ... code ...> Sinngemäss, ist jetzt nicht voll syntaktisch OK. Sieht für mich> wesenlich einfacher aus und ist garantiert kleiner und schneller.
Jep, diese Darstellung ist für das Datenpaket eher geeignet das stimmt.
Ich würde rein gefühlsmäßig bei sowas auch die Array-Variante vorziehen.
Dachte nur nicht, dass der GCC die 64 Bit Sachen so vermasselt. Wie
gesagt, bei 32 Bit sieht es definitiv besser aus.
Wieder was gelernt! Danke, die 8 Bit-Variante mit Array ist ja auch nur
unwesensentlich komplizierter, allerdings ist in dem obigen Code nicht
berücksichtigt, dass es 10 Datenbits gibt, naja ist auch nicht
wesentlich komplizierter. Ein bisschen shiften mit ner 16 Bit-Variable
sollte doch gehen...
Bleibt noch die Timerfrage (nein, ich lasse nicht locker ;) )...
@ Sven S. (schwerminator)
>Wieder was gelernt! Danke, die 8 Bit-Variante mit Array ist ja auch nur>unwesensentlich komplizierter,
Komplizierter?
>Bleibt noch die Timerfrage (nein, ich lasse nicht locker ;) )...
Du brauchst einen Soft-UART. Unter dem Stichwort wirst du viel finden.
Auch Application Notes von Atmel.
MFG
Falk
WOW!!! Der Code ist jetzt nur noch ein Drittel so groß wie vorher.
Wahnsinn. Ich habe noch eine andere Funktion, in der nur 12 Bit
übertragen werden müssen (SIRC). Bis jetzt habe ich die in einer 16
Bit-Variable gepackt, macht es da auch noch Sinn ein 8 Bit-Array zu
verwenden?
Zum Timerproblem:
Aber UART wird doch nicht moduliert oder seh ich das falsch? Wenn nein
wäre das doch nicht vergleichbar mit meiner Problemstellung.
Simon K. wrote:
> Falk Brunner wrote:>> Vor allem ist es besonders auf einem 8 Bitter nicht sinnvoll, die>> Datenpakete als 64 Bit Zahlen zu handhaben. Besser als Array von Bytes,>> das ist viel schneller und übersichtlicher.>> Würde ich nicht unbedingt sagen. Wie kommst du darauf? Sind ja keine> Rechenoperationen, die hier durchgeführt werden.
Das 64Bit-Paket wurde beim AVR-GCC mit der heißen Nadel dazugestrickt,
d.h. völlig ohne jegliche Optimierung.
Jeder 64Bit-Operator ruft ganze Orgien von MOV/PUSH/POP und sehr lange
Funktionscalls auf.
Ich hab mal testweise ein Programm von float auf long long umgestrickt.
Der Code wurde sogar noch größer statt kleiner.
Beim Umstellen auf Byte-Arrays sollte ein Eindampfen um mindesten 75%
möglich sein.
Ich könnte mir auch vorstellen, daß es zu Zeitproblemen bei den elend
langsamen 64Bit-Operationen kommt, so ein IR-Signal muß ja in Echtzeit
ausgegeben werden.
Peter
@ Sven S. (schwerminator)
>WOW!!! Der Code ist jetzt nur noch ein Drittel so groß wie vorher.>Wahnsinn.
Strike! ;-)
>übertragen werden müssen (SIRC). Bis jetzt habe ich die in einer 16>Bit-Variable gepackt, macht es da auch noch Sinn ein 8 Bit-Array zu>verwenden?
Bei 16 Bit kann man ein einzelne Variable nutzen, da ist der AVR-GCC
schon gut.
>Aber UART wird doch nicht moduliert oder seh ich das falsch?
Das siehst du rictig.
> Wenn nein wäre das doch nicht vergleichbar mit meiner Problemstellung.
Doch. Du must ja in einem festen Zeitraster Daten ausgeben. Der
Timer ist dein Freund.
MFG
Falk