Zusammenfügen von 2 mal 8 bit zu einem 16 bit Wert funktioniert nicht richtig

OP #3058734
Lesenswert?

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!
Angehängte Dateien:
#3060071
Lesenswert?

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?
OP #3060125
Lesenswert?

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.
#3060147
Lesenswert?

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.
#3060285
Lesenswert?

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.
Gast #3060307
Lesenswert?

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.
OP #3062775
Lesenswert?

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?
Gast #3063173
Lesenswert?

>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.
OP #3063183
Lesenswert?

@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.
Gast #3063190
Lesenswert?

>-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.
OP #3063194
Lesenswert?

@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?
OP #3063297
Lesenswert?

@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
OP #3063306
Lesenswert?

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.
Gast #3063311
Lesenswert?

>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?
OP #3063359
Lesenswert?

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.
#3063398
Lesenswert?

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.
#3063448
Lesenswert?

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.
OP #3063467
Lesenswert?

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!!!
Persönliche Seite #3069940
Lesenswert?

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
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren