Hallo! Ich habe hier ein IMU was laut Datenblatt dies ausspuckt: "16-bit 2's complement integers" Ich benutze diesen Wert um den 16bit Timer im CTC Mode von einem Atmega 8 anzusteuern. Hier das "Konstrukt": yupper=receive(); // Pitch "grösseres" Byte ylower=receive(); // Pitch "kleineres" Byte //yupper=255; //ylower=255; y=((int16_t)yupper<<8)|ylower; // Aus 2 8bit ein 16bit ocr[1]=y; Die "Symptome" sehen so aus: -Setze ich yupper und oder ylower selber wie im Kommentar oben als Beispiel mit jeweils 255 sieht das auf dem Oszi so aus wie es soll. -benutze ich nur yupper oder ylower die aus dem UART ausgelesen werden (receive) und setze den jeweils anderen wert selber sieht das Ergebnis auch gut aus. -benutze ich aber beide empfangenen Werte sieht das (gefühlt) aus wie ein permanenter Überlauf beim bewegen des IMU. Der Vollständigkeit halber das ist das gesammte Programm angehängt. Und wie immer schon mal vielen Dank!
Diesen Beitrag hatte ich bereits studiert auch einige andere zu dem Thema. (Beitrag "16 Bit Variable aus 2 8 Bit laden") Leider klappt es nicht. Ich habe probeweise mal den cast weggelassen das funktionierte auch nicht. Allerdings funktioniert das: y=yupper*255+ylower; Das KANN aber so nicht richtig sein. Wer von den Profis mag hier Licht ins Dunkel bringen?
receive() liefer ein int also signed zurück, damit gibt es probleme bei schieben.
Hallo Peter. Danke für den Hinweis. Allerdings verstehe ich ihn nicht. Magst Du das etwas genauer erläutern?
Attila Ciftci schrieb: > Hallo Peter. Danke für den Hinweis. Allerdings verstehe ich ihn nicht. > Magst Du das etwas genauer erläutern? es gab schon öfters probleme beim schieben wenn eine signed variabel beteiligt ist. Änder mal recieve so ab das es ein unsigned zurückliefert.
>Allerdings funktioniert das: > >y=yupper*255+ylower; ... es sollten eher 256 sein. y=yupper*256+ylower;
Jetzt bin ich baff! @Peter: Das hat nicht geklappt! @Zwoelf: Du hast recht und siehe da: y=yupper*256+ylower; macht den gleichen "Fehler" wie: y=yupper<<8|ylower Was läuft da über und warum?
Mach die Variablen unsigned:
1 | unsigned char yupper = 0; |
2 | unsigned char ylower = 0; |
und den Cast auf int16_t nach außen:
1 | y = (int16_t) ((yupper << 8) | ylower); |
In Deinem Fall könntest Du den Cast sogar weglassen, weil y ja schon int16_t ist.
upper muß signed sein, lower muß unsigned sein. Ist upper auch unsigned, klappt es nur auf 16Bit-CPUs, nicht aber auf 32Bittern.
Fabian O. schrieb: > y = (int16_t) ((yupper << 8) | ylower); last mal den cast weg. da wird es doch wieder signed, außerdem wird in C immer mit 16 gerechnet damit ist dieser nicht notwendig.
Danke Fabian. Geht leider auch nicht :-(
Das ist ein Paradebeispiel, warum Bit-Operation auf signed-Value verpönnt sind. Caste deine Werte explizit vorheit auf dein Zieldatentyp.
// convert negative two's complement values to native negative value: int temp_value = (data > 0x7fff) ? data - (0xffff + 1) : data;
Ich schätze mal, Dein Problem liegt wo anders. Du schreibst den Wert ja letztlich in das OCR1A-Register. Da machen negative Werte aber keinen Sinn. Vielleicht willst Du zuerst noch prüfen, ob der gelieferte Wert größer/gleich Null ist bzw. auf einen positiven Bereich skalieren?
PS: Bei Signed Werten: Beachte beim shiften immer das Vorzeichen!
Vielen Dank für die Hinweise ich glaube das Problem liegt tatsächlich ganz wo anders: Zuallerst mal: Ich habe mich bislang nicht so sehr darum gekümmert ob etwas signed ist oder nicht da ich erstmal nur möchte das das IMU mit mir spricht und sobald es das tut erstmal nur eine Neignung nach Rechts betrachten möchte. Mulitpliziere ich mit 255 passiert auch ungefähr was passieren soll. Kippe ich das IMU nach rechts habe ich einen Peak von 6-7 ms länge und nach links von 40-800µs. Ich habe probeweise den Wert von y fix vorgegeben. Wenn es kleiner ist als 0x0037 "spinnt" der µC was aber meines Erachtens auch richtg ist. Bei Multiplikation mit 256 und auch bei der shift konstruktion kann ich auf meine Oszi sehen wie 8 bit durchlaufen werden und bei kleiner als 0x37 der Timer "spinnt" Mir ist das ein Rätsel, vielleicht hilft diese Beschreibung des Problems mehr.
Ach so: Die 8 bit werden immer wieder durchlaufen bei einer konstanten Drehung des IMU
Peter Dannegger schrieb: > upper muß signed sein, lower muß unsigned sein. > Ist upper auch unsigned, klappt es nur auf 16Bit-CPUs, nicht aber auf > 32Bittern. Eher im Gegenteil:
1 | int receive (void) //Funktion um ein Zeichen vom UART zu empfangen |
2 | {
|
3 | while (!(UCSRA&(1<<RXC))) //Die Schleife prüft ob das Empfangsbit gesetzt ist |
4 | ; // ist es nicht gesetzt passiert nix. |
5 | return UDR; //Empfangenes Zeichen wird zurückgegeben |
6 | }
|
7 | |
8 | int main (void) |
9 | {
|
10 | int yupper=0; |
11 | unsigned int ylower=0; |
12 | int y; |
13 | |
14 | // ...
|
15 | yupper=receive(); // Pitch "grösseres" Byte |
16 | ylower=receive(); // Pitch "kleineres" Byte |
17 | y=(yupper<<8)|ylower; |
18 | // ...
|
19 | }
|
Das funktioniert nur auf 16-Bit-Plattformen, nicht auf 32 Bit.
Nach einer Weile des Nachdenkens: "yupper" scheint immer 0 zu sein beim Shiften wie auch beim Multiplizieren mit 256. Bei Multiplikation mit 255 aber nicht. Was kann das sein?
Erklär doch mal, was das Programm überhaupt machen soll. Diese Statemaschine in der Timer-ISR mit Neuladen des OCR1A-Register sieht mir ziemlich suspekt aus ... Also: - Welchen Wertebereich liefert der Sensor (laut Datenblatt) bei welcher Position? - Was soll bei welcher Position bzw. welchem Wert passieren? Am besten wäre, wenn Du erstmal sicherstellst, was da überhaupt für Zahlen ankommen. Hast Du irgendeine Möglichkeit, die Zahl menschenlesbar auszugeben, z.b. über die TX-Leitung des UART? Wenn nicht, dann mach das Programm erstmal so einfach wie möglich. Schreib den empfangenen Wert direkt in OCR1A und lass den Timer einen Pin toggeln. Auf dem Oszi kannst Du dann anhand der Frequenz auf den Wert schließen.
Attila Ciftci schrieb: > y=((int16_t)yupper<<8)|ylower; // Aus 2 8bit ein 16bit Attila Ciftci schrieb: > Nach einer Weile des Nachdenkens: Oh nein. IHR ALLE !!!! habt überhaupt nicht nachgedacht und stattdessen verzweifelt in der Gegend herumgecastet. Einer schöner als der andere. Also: Casten ist mal ganz salopp gesagt etwa so, daß man dem Compiler sagt "Das soll jetzt aber mal ein ... sein." Also zum Beispiel if((unsigned long)MyPointer >= 0xA0100000) {.....} Also: dein yupper ist und bleibt das was es ist, nämlich 8 Bit. An den Daten selbst kannst du nämlich durch Casten nix ändern. Guck dir mal den entstandenen Code an oder probiere es mal ohne cast: y = yupper; y = (y<<8) | ylower; Falls dein y ne lokale variable ist, steht sie ohnehin in einem Register und falls nicht (und auch nicht volatile), hofft man, daß der Compiler sich das Zwischenspeichern einspart. W.S.
Hallo W.S.: Das hier ALLE falsch liegen ist recht unwahrscheinlich auch wenn die Vorschläge, genau so wie deiner, nicht das gewünschte Ergebnis erbrachten. Fabian: Ich habe deinen Vorschlag umgesetzt im Ergebnis bleib alles beim Alten. Allerdings wurde die Fehlersuche vereinfacht da ich zumindest keine ISR hatte die rumspinnt. Ich habe das Rätsel dennoch im Trial und Error Verfahren gelöst und bitte um Rat der Profis: Ich habe versuchsweise die Optimierung , die auf -Os war ausgeschaltet und siehe da: "y=(int16_t)((yupper<<8)|ylower)" bring das gewünschte Ergebnis. An die Profis: Wie muss/soll ich das in Zukunft machen? -Os habe ich nur benutzt 1) Weil vor einiger Zeit die Ansteuerung eines Displays ohne optimierung nicht ging 2) Weil in diesem Artikel http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung die Optimierung -Os empfohlen wird. Was ist das korrekte Verfahren?
nimmst du ein union!
@Dummschwätzer: Ist dein Alias Programm? Ich kann nämlich mit deiner Aussage nichts anfangen. Ich bin in meinem C Buch noch nicht so weit fortgeschritten das ich wüsste was ein "union" ist.
Welches C-Buch? In der Regel im Kapitel Strukturen, unions, Aufzählungen. Siehe auch Register oder Inhaltsverzeichnis des Buches.
Dummschwaetzer schrieb: > nimmst du ein union! Was hat das mit dem Problem zu tun? Das bring bestenfalls neue Probleme (undefined behavior etc.) Dies Forum ist voll mit Erklärungen und Hinweisen, warum.
Bei dem pillepalle kann man mit einer union nicht viel falsch machen, es soll ja nur int und char[2] übereinanderliegen.
>Bei dem pillepalle kann man mit einer union nicht viel falsch machen, es >soll ja nur int und char[2] übereinanderliegen. Mit Kanonen auf Spatzen schiessen;) Aus int receive (void) macht er ganz schnell mal uint8_t receive (void). Warum? Ein UART überträgt 8Bit. Der weiss einen Scheiss von signed, das ist dem sowas von egal. Somit sind dann auch int yupper=0; int ylower=0; als signed Variablen ganz schlecht gewählt. uint8_t yupper=0; uint8_t ylower=0; Dann noch die Variante aus dem von Johann als erstes gepostetem Link: y = (int16_t)(((uint16_t)yupper << 8) + ylower); Und alles sollte in Ordnung sein.
Der Hase wird irgendwo da im Pfeffer liegen, wo die Daten für 'y' erzeugt werden (vermutlich hat das was mit receive(..) zu tun), so dass der Zugriff auf 'y' bei jeder eingeschalteten Optimierung wegoptimiert werden wird. Irgendwas vernalasst den Compiler anzunehmen, dass sich 'ylower' und 'yupper' niemals ändern.
@Dummschwätzer: -C von Helmut Erlenkötter -Ich bin noch nicht mal im Kapitel 9 (Der Präprozessor) angekommen. Das union befindet sich im Kapitel 14 (Komplexe Datentypen) Es wird also noch ein weilchen dauern. -Wer mag denn mir nun vorsagen was zu tun ist bis ich kapiert habe was die Optimierung genau bedeutet und macht? Keine Optimierung? Eine bestimmte Optimierung? Oder egal, hauptsache es läuft, ausprobieren welche läuft? Vielen Dank Ach ja: Was dem einen pillepalla ist für den anderen schon recht kompliziert.
>-Wer mag denn mir nun vorsagen was zu tun ist bis ich kapiert habe was >die Optimierung genau bedeutet und macht? Hast du meinen Post gelesen? Hör auf für jeden Mist signed Variablen zu nehmen. Schiebe und verodere signed Variablen besser nicht. Dann ist es Wurst welche Optimierung du nimmst.
@holger: Ich hatte zwischendurch für yupper und ylower unsigned char genommen das hat aber auch nichts gebracht. Genau genommen sehe ich es in meinem Anfänger-leichtsinn ähnlich wie du: Da werden 8 bit ausgespuckt (UART) und ob die signed sind oder nicht ist erstmal völlig wurscht. @Patrick: Die Funktion receive kannst Du Dir gerne in der Codeansicht in meinem ersten Posting ansehen. Ich persönlich wüsste nicht wo da jetzt ein Problem sein sollte? Ich habe spasseshalber mal andere Optimierungen ausprobiert und bei manchen lief es und bei anderen nicht (Hab es nicht mehr so genau im Kopf) Vielleicht hilft der nochmalige Hinweis: "yupper*255" brachte das gewünschte Ergebnis, "yupper<<8" nicht (Bei optimierung -Os) Es läuft ja jetzt ohne Optimierung. Es bleibt die Frage: Wie gehe ich damit in Zukunft um?
@Holger: Alles klar! Ich werde das ausprobieren. Unsere Posts hatten sich überschnitten!
Ich muss gestehen: ich habe gerade erst in das im Eingangspost angehängte Sourcefile geguckt: Du darfst natürlich nur Zuweisungen mit receive() machen, wenn Du tatsächlich Daten empfangen hast. Ansonsten passiert nämlich nicht 'nichts' (wie in dem Kommentar erklärt), sondern 'return UDR;' gibt "0" zurück.
Quatsch! Falsch gelesen...
@Holger: Ich hab das jetzt genau so gemacht wie Du es vorgeschlagen hast. Leider klappt das nicht. Und zwar sind jetzt yupper, ylower, und receive() uint8_t. Dies: y = (int16_t)(((uint16_t)yupper << 8) + ylower); läuft nur bei ausgeschalteter Optimierung. Dies: y=(int16_t)((yupper<<8)|ylower); läuft bei -O0, -O2, -O3 aber nicht bei -O1 und -Os
Ich habe jetzt nochmal zum Ausprobieren yupper,ylower und receive() als int deklariert. Jetzt laufen beide Varianten also: y=(int16_t)((yupper<<8)|ylower); und y = (int16_t)(((uint16_t)yupper << 8) + ylower); mit: -O0 -O2 und -O3 Aber nicht mit: -O1 und -Os Ich habe den Eindruck das das Deklarieren der Variablen als signed oder unsigned hier nicht die Wurzel des Übels ist.
>Ich hab das jetzt genau so gemacht wie Du es vorgeschlagen hast. Leider >klappt das nicht. Ich denke das dein Problem die ganzen signed Sachen sind. OCR1A interessiert sich einen Scheiss für signed, genau wie der UART. Und ocr[] ist auch unsigned. Wieso muss y dann ein int16_t sein?
holger schrieb: > Wieso muss y dann ein int16_t sein? Weil sein Sensor einen signed-Wert liefert, siehe allerersten Satz im ersten Post: > Ich habe hier ein IMU was laut Datenblatt dies ausspuckt: "16-bit 2's > complement integers" @Attila: Poste mal den aktuellen Code.
Weil ich mit y noch weiterrechne was übrigens auch sehr gut klappt.
Ich habe es grade ausprobiert. Alles uint_8 repektive u_int16t. Es klappt nicht.Bei -Os. Ohne Optimierung läuft es. Einziger Unterschied ist das die Rechnung mit y nicht mehr so ist wie gewünscht. (Logischerweise)
Bei welchem AVR ist eigentlich OCR1A & OCR1B 16Bit breit? Sicher, dass das stimmt? Das würde jedenfalls erklären weshalb die Multiplikation mit 255 zu gehen scheint, aber mit 256 nicht (niederwertiges Byte ist da immer 0)
Es ist ein Atmega8. Ich fahre ein Modelbauservo an (daher y+14300 ergibt ungefähr 1,5 ms Mittelstellung des Servos) Sieht recht hübsch aus! Anbei wie gewünscht der aktuelle Code
Beim Atmega8, sind OCR1A&OCR1B 8Bit, dein Fehler liegt also an einer ganz anderen Stelle
Seite 76 das Atmega8 Datenblatts, Zitat: "The 16-bit Timer/Counter unit allows accurate program execution timing (event management), wave generation, and signal timing measurement. The main features are: • True 16-bit Design (i.e., allows 16-bit PWM)" Ausserdem läuft es ja ohne Optimierung ich kann mir die 16bit Auflösung sogar auf dem Oszi anschauen.
Der Vollständigkeit halber, Seite 79: "The TCNT1, OCR1A/B, and ICR1 are 16-bit registers......."
Ach so Fabian, Du fragtest auch mal nach dem Sensor. Es ist ein CHR6-dm.
hab wohl A/B mit L/H verwechselt...
@Maik: Ach so! Ich war schon etwas verunsichert jetzt. Naja, sowas pasiert!
Bei Deinem letzten Code sehe ich jetzt keine Fehlerquellen mehr. Solange die Werte des Sensors zwischen -14300 und 18467 liegen, sollte es keine ungewöhnlichen Sprünge/Überläufe geben. Welche Compilerversion benutzt Du denn? Vor ein paar Wochen gabs mal ein ähnliches Thema, da war es letztlich ein Fehler im avr-gcc aus AVR-Studio 5.
Ok auf die Gefahr hin dass ich hier geschlachtet werde: Wo kann ich beim Studio5 ersehen welche Compilerversion ich habe?
Im Studio 6 steht es unter Help -> About Atmel Studio und da im Dropdown AVRGCC. Aber der Hinweis auf Studio 5 reicht eigentlich schon. Versuchs mal mit Studio 6 oder zumindest der aktuellen Version der Toolchain.
Ok, auch wenn das etwas off topic ist jetzt: Unterstützt das Studio 6 denn das STK500? Mein AVRGCC hat die Version 3.3.1.27 wie ich anhand deiner Anleitung herausfinden konnte.
Attila Ciftci schrieb: > Ok, auch wenn das etwas off topic ist jetzt: Unterstützt das Studio 6 > denn das STK500? Sollte gehen. > Mein AVRGCC hat die Version 3.3.1.27 wie ich anhand deiner Anleitung > herausfinden konnte. Das dürfte das Problem sein. Siehe hier: Beitrag "Umwandlung von zwei uint8_t in uint16_t erzeugt falschen Code?" Ach ja, weil ich gerade gelesen habe, dass Du einen Modellbauservo ansteuerst: Das geht mit dem 16-Bit-Timer ganz ohne Interrupt. Du musst einfach den Top-Wert so einstellen, dass die Periode 20 ms beträgt. Dann reicht es, in das Compare-Register 1/20 bis 1/10 des Top-Werts als Duty-Cycle reinzuschreiben. Von der Auflösung her sollte das dank 16 Bit genau genug für einen Servo sein.
Oh wow! Jetzt wird mir einiges klar. Ich wäre allerdings nie darauf gekommen nach so einem Fehler im Forum zu suchen. Wie soll man denn auf so etwas kommen? Ich lade mir mal das Studio 6 runter. Klar im Moment läuft es ja auch ohne interrupt. Die state maschine in meiner ISR soll aber bis zu 7 Servos steuern. Irgendwann. Mal sehen. Auf jeden Fall: Vielen Dank für die Hilfe!!!
Der mögliche Fehler hat die Nummer PR46779. Er ist behoben in 4.5.4, 4.6.2 und neueren Versionen. Und das ist die GCC-Version, nicht die "Atmel-Version". Die GCC-Version wird angezeigt mit avr-gcc --version
Johann L. schrieb: > Er ist behoben in 4.5.4, 4.6.2 Und wie kommt man an die Versionen für Windows feertig einsetztbar? Ich meine ohne sich das Zeug selber zu kompilieren? Danke
Hmmm schrieb: > Und wie kommt man an die Versionen für Windows feertig einsetztbar? Ich > meine ohne sich das Zeug selber zu kompilieren? einfach bei Atmel downloaden (nennt sich toolchain). Dann bekommt man zumindest die 4.6.2
Helmut Herold: C Compakt Referenz ISBN 3-8273-1480-1, 1.Auflage 1999 Kapitel 2.41 auf Seite 122, für dich interessant: 2.41.2 Overlay-Technik ... Seite 124f
Dummschwaetzer schrieb: > Welches C-Buch? In der Regel im Kapitel Strukturen, unions, > Aufzählungen. Siehe auch Register oder Inhaltsverzeichnis des Buches. Dummschwaetzer schrieb: > Helmut Herold: C Compakt Referenz > ISBN 3-8273-1480-1, 1.Auflage 1999 > Kapitel 2.41 auf Seite 122, für dich interessant: 2.41.2 Overlay-Technik > ... Seite 124f Wenn das wirklich meint: Byte-Komponenten setzen und lesen einer 16-Bit Variablen aus der Union, dann entsorg das Buch zum Altpapier. Der C-Standard sichert nur zu, die zuletzt geschriebene Komponente einer Union auch wieder auszulesen. Alle anderen Komponente der Union erhalten durch Zuweisung undefinierte Werte (mit Ausnahme eines ersten, allen Komponenten gemeinsamen Elements, i.d.R Verwendnet zur Identifikation des Union-Inhalts). Konkret: In
1 | unsigned compose (char a, char b) |
2 | {
|
3 | union
|
4 | {
|
5 | char byte[2]; |
6 | unsigned word; |
7 | } u; |
8 | |
9 | u.byte[0] = a; |
10 | u.byte[1] = b; |
11 | |
12 | return u.word; |
13 | }
|
Wird die Komponente .word nie geschrieben. Ein Konformer Compiler darf das also genau so übersetzen wie:
1 | unsigned compose (char a, char b) |
2 | {
|
3 | unsigned word; |
4 | |
5 | return word; |
6 | }
|
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.