mikrocontroller.net

Forum: Compiler & IDEs Union Problem


Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Forumsgemeinde,

folgendes System:
AVR Mega 64,
Schrittmotorsteuerung,

Der Schrittmotortreiber ist ein Trinamic mit SPI. Es wird ein 12 Bit 
Wert übergeben, der die Werte von Spule A und Spule B beinhaltet. Diese 
Werte stehen in einer Tabelle
IAR Compiler (jetzt bitte nicht die Augen rollen, es ist ein C-Problem)

struct
{
 unsigned int sollwert;
 unsigned int istwert;
 unsigned char richtung;
 ...
 unsigned int spulenwert;
 ...
} dimmer;

Folgende Konstruktion funktioniert:

dimmer.spulenwert = sinus_tab[dimmer.mikro_schritt];

cs_dimmer_low;
SPDR = dimmer.spulenwert >> 8; //Highbyte ausmaskieren u. senden
while (!(SPSR & (1 << SPIF)));

SPDR = (unsigned char) dimmer.spulenwert;  //Lowbyte senden
while (!(SPSR & (1 << SPIF)));
cs_dimmer_high;

folgendes aber nicht:

struct
{
 unsigned int sollwert;
 unsigned int istwert;
 unsigned char richtung;
 ...
 union
 {
  unsigned cvhar low_byte;
  unsigned char dimmer;
  unsigned int spulenwert;
 }
 motor;
 ...
} dimmer;


dimmer.motor.spulenwert = sinus_tab[dimmer.mikro_schritt];
cs_dimmer_low;   //Dimmer-CS einschalten
SPDR = dimmer.motor.high_byte;
while (!(SPSR & (1 << SPIF)));

SPDR = dimmer.motor.low_byte;
while (!(SPSR & (1 << SPIF)));
cs_dimmer_high;  //Dimmer-CS ausschalten

Das High und Lowbyte habe ich schon getauscht, aber das war der Fehler 
nicht. Der Motor läuft unsauber und ruckelig. Die Tabelle mit den 
Spulenwerten ist die gleiche.

Hat einer von euch eine Idee?

MW

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast du hier nicht einen Tippfehler gemacht?

>  union
>  {
>   unsigned cvhar low_byte;
>   unsigned char dimmer;
>   unsigned int spulenwert;
>  }
>  motor;

die union enthält kein high_byte.

Bitte mach immer ein Copy&Paste um Quelltext
zu posten. Sonst suchen wir uns die Hacken nach
Fehlern ab, die in deinem wirklichen Program gar
nicht vorhanden sind.

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Richtig,sollte low und highbyte sein. Sorry.

MW

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Trotzdem. Ich rate mal:

Deine union sieht in Wirklichkeit so aus:

 union
 {
   unsigned char low_byte;
   unsigned char high_byte;
   unsigned int spulenwert;
 }
 motor;

Dann ist klar, dass das nicht gehen kann.
high_byte und low_byte teilen sich ein und
denselben Speicher. Dazu kommt dann noch
spulenwert, der auch noch drüber liegt.

Im Speicher spielt sich also folgendes ab:


     +---+---+
     |   |   |
     +---+---+

      ^
      hier liegt low_byte

      ^
      hier liegt high_byte

      ^     ^
      hier liegt spulenwert (2 Bytes)


Ansonsten bleibt nur noch das Problem der Endianess.
Abhängig vom Prozessor kann der int mit dem
High Byte oder mit dem Low Byte zuerst im Speicher
abeglegt sein. Es ist also Prozessorbahängig, welches
Byte vom int du tatsächlich über low_byte zu Gesicht
bekommst.


Du könntest folgendes machen:

  struct Bytes {
    unsigned char low;
    unsigned char high;
  }

  ...

  struct {
     ...
     union {
       struct Bytes bytes;
       unsigned int spulenwert;
     }
     motor;

  } dimer;

Der Zugriff wäre dann:

 dimmer.motor.spulenwert = ....

 SPDR = dimmer.motor.bytes.low

 SPDR = dimmer.motor.bytes.high

Jetzt muss nur noch der Compiler mitspielen:

  * Ob die Bytes tatsächlich in der Reihenfolge
    low / high im Speicher liegen

  * Ob der Compiler nicht zwischen Bytes.high und Bytes.low
    im Speicher noch sog. 'Padding Bytes' unterbringt.
    Kein Mensch zwingt den Compiler nämlich, dass struct
    Member im Speicher direkt aufeinanderfolgend sein müssen :-)



Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Karl Heinz,
das war der entschidende Wink. So gehts:
  unsigned char speed;
  unsigned char richtung;
  union
  {
    unsigned int spulenwert;
    struct
    {
      unsigned char low_byte;
      unsigned char high_byte;
    }bytes;
  }motor;
}  dimmer;

  dimmer.motor.spulenwert = sinus_tab[dimmer.mikro_schritt];
      cs_dimmer_low;                    //Dimmer-CS einschalten
      SPDR = dimmer.motor.bytes.high_byte;
      while (!(SPSR & (1 << SPIF)));

      SPDR = dimmer.motor.bytes.low_byte;
      while (!(SPSR & (1 << SPIF)));
      cs_dimmer_high;             //Dimmer-CS ausschalten

Beim Copy und Paste ist die Formatierung nicht so dolle. Liegt aber an 
den Einstellungen meines Editors.

MW

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael Wilhelm wrote:

> Beim Copy und Paste ist die Formatierung nicht so dolle. Liegt aber an
> den Einstellungen meines Editors.
>
> MW

Hm, sorry, aber das sieht immer n och nicht nach C&P aus:
   unsigned char speed;
   unsigned char richtung;
   union
   {
     unsigned int spulenwert;
     struct
     {
       unsigned char low_byte;
       unsigned char high_byte;
     }bytes;
   }motor;
 }  dimmer;
 
   dimmer.motor.spulenwert = sinus_tab[dimmer.mikro_schritt];
       cs_dimmer_low;                    //Dimmer-CS einschalten
       SPDR = dimmer.motor.bytes.high_byte;
       while (!(SPSR & (1 << SPIF)));
 
       SPDR = dimmer.motor.bytes.low_byte;
       while (!(SPSR & (1 << SPIF)));
       cs_dimmer_high;             //Dimmer-CS ausschalten

...da ist ne Klammer zu viel.
Highlighting macht die Sache angenehmer zu lesen ;)

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Na, den oberen Teil der Grundstruktur habe ich weggelassen. Ausserdem, 
im Editorfenster hier sieht es komplett anders aus als nachher im 
Thread.

MW

Autor: Wolfram (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Kein Mensch zwingt den Compiler nämlich, dass struct
>Member im Speicher direkt aufeinanderfolgend sein müssen :-)
doch zumindest beim gcc mit __attribute__((_packed_)) für die Struktur
bei anderen compilern meist mit irgendwelchen #pragma
Solange du beim AVR bleibst wirst du ueber padding bytes nicht stolpern, 
nur denk dran wenn du den Code wo anders verwnedest.

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gemäss meinem C-Buch dürfen keine Padding Bytes vom Compiler eingesetzt 
werden. Wenn doch, wäre die Union als solche nicht zu gebrauchen, 
jedenfalls nicht mit dem Merkmal, dass sich verschiedene Datentypen 
einen Speicherbereich teilen.

MW

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine union fuegt keine padding bytes ein, eine struct kann das aber
schon.  Die gewuenschte union war ja eine aus struct + int.

Allerdings duerfen selbst hier nur dann vom Compiler padding bytes
eingefuegt werden, wenn "unsigned char" groeßer als 8 bits waere
(was beim AVR nicht der Fall ist).  Abhilfe waere die Benutzung
von uint8_t (aus <stdint.h>), da es diesen Datentyp auf einer
Maschine einfach nicht gibt, die keine 8-Bit-Integers beherrscht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Allerdings duerfen selbst hier nur dann vom Compiler padding bytes
> eingefuegt werden, wenn "unsigned char" groeßer als 8 bits waere
> (was beim AVR nicht der Fall ist).

Klar. Beim AVR kommt das nicht vor.
Bei anderen Prozessoren (ab 16 Bit) ist es aber durchaus
nicht ungewöhnlich, dass der Compiler zwischen 2 unsigned
char ( mit je 8 Bit) ein Padding Byte einfügt.
Wenn ich mich recht erinnere war es doch zb. bei einer 68000
so, dass Speicherzugriffe auf ungerade Adressen zu einer
Exception führt. Also hat der Compiler speziell unsigned
char so ausgerichtet, dass jeder der beiden auf jeweils
einer geraden Adresse zu liegen kommt.

Bei einer
struct xyz {
  unsigned char a;
  unsigned char b;
};

führt daher der Zugriff auf xyz.b ohne Padding Byte zu
einem kräftigen Penalty. Mit einem Padding Byte ist
der Zugriff aber 'straight forward'.


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wenn doch, wäre die Union als solche nicht zu gebrauchen,
> jedenfalls nicht mit dem Merkmal, dass sich verschiedene Datentypen
> einen Speicherbereich teilen.

Wenn wir ganz streng C programmieren, dann ist obige
Verwendung der union eigentlich gar nicht erlaubt. Na, ja
nicht erlaubt ist der falsche Ausdruck. Es ist undefiniert
was passiert. Laut C Standard darf man aus einer union
nur über denselben Member lesen über den der letzte Schreib-
zugriff erfolgt ist.
Damit ist aber das reinschreiben in einen int und das
rauslesen über 2 unsigned char schon im Bereich undefiniertes
Verhalten.
Klar: Ich weiss auch, dass das in der Praxis überall funktioniert.
Aber ganz streng genommen muss es das nicht.

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So Leute,

noch einmal Dank an alle, die sich mit dem Problem befasst haben.

Simulation im AVR-Studio:

Version 1 mit der ganz normalen Struktur und dem "hinschieben" des 
INT-Wertes für die Motorausgabe: 92 Taktzyklen.

Version 2 mit der union 90 Taktzyklen.

Also, ich hatte mir mehr versprochen.

Aber, Erkenntnisse hat es trotzdem gbracht.

MW

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
D.h. Dir geht es um die Geschwindigkeit?

Die ist doch hauptsächlich durch das Warten auf den SPI gegeben.
Wenn Du etwas Zeit sparen willst, dann lade während der ersten Wartezeit 
den 2. Wert schonmal vor:
cs_dimmer_low;
SPDR = dimmer.spulenwert >> 8; //Highbyte ausmaskieren u. senden
local_spule_low = (unsigned char) dimmer.spulenwert;  //Lowbyte senden
while (!(SPSR & (1 << SPIF)));

SPDR = local_spule_low;
while (!(SPSR & (1 << SPIF)));
cs_dimmer_high;
Statt faul auf SPIF zu warten, kann der mc ruhig schonmal vorarbeiten 
...

Übrigens setzt gcc diesen Ausdruck meines Wissens nicht mit 8*Shiften 
um, sondern kopiert das High- ins Low-Byte:
SPDR = dimmer.spulenwert >> 8;
Einziger Unterschied zum Byte-Zugriff per union: es wird ein 16-Bit-Wert 
geladen und das High- ins Low-Byte kopiert, anstatt direkt das 
8-Bit-Lowbyte zu verwenden. Das kommt auch gut an Deine 2 Zyklen 
Unterschied hin.

Schau mal in den Assembler-Output.

Viele Grüße, Stefan

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gute Idee Stefan, obwohl die SPI mit 4MHz arbeitet, ist die Wartezeit 
lästig. Ich werde deine Idee aufgreifen und berichten.

MW

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
P.S.
Ich habe keine Ahnung von Assembler.

MW

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und Dein ATmega hat einen 16Mhz-Quarz? Dann wartest Du min. 64 Takte auf 
den SPI von den gemessenen 90.

>P.S.
>Ich habe keine Ahnung von Assembler.

Musst Du auch nicht. Schau einfach mal den Quellcode bei verschiedenen 
Programmversionen an. Ob z.B. irgendwo geshiftet wird, siehst Du auch 
ohne viel Assembler-Ahnung. Aber Du erhälst mit der Zeit ein Gefühl 
dafür, welcher C-Code aufwendigen und welcher Code effizienten Output 
erzeugt. Allein die Länge des Assembler-Outputs ist ein guter 
Anhaltspunkt.

Gruß, Stefan

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Na ja, was ich im Studio simuliert und getestet habe, waren die 
Taktzyklen und nicht die Zeit. AVR-Studio löst die Stoppuhr ja nur mit 
0,5 µs auf und das ist bei taktgenauer Analyse doch etwas grob.
Ich kopiere mal den schnellsten C-code rein und das, was der Assembler 
macht. Du wirst staunen, wie der mit den X,Y und Z Registern am 
jonglieren ist.

Wenn du Lust hast, werf einen Blick drauf.

C-Code:
      dimmer.spulenwert = sinus_tab[dimmer.mikro_schritt];

      cs_dimmer_low;                        //Dimmer-CS einschalten
      SPDR = dimmer.spulenwert >> 8;         //Highbyte ausmaskieren u. 
senden
      while (!(SPSR & (1 << SPIF)));

      SPDR = (unsigned char) dimmer.spulenwert;  //Lowbyte senden
      while (!(SPSR & (1 << SPIF)));
      cs_dimmer_high;                   //Dimmer-CS ausschalten
      dimmer.daten_berechnet = 0;


Assembler-Code:

    402                dimmer.motor.spulenwert = 
sinus_tab[dimmer.mikro_schritt];
   \                     ??timer_3_over_isr_12:
   \                     ??timer_3_over_isr_11:
   \   000000F8   ....                       LDI     R30,LOW(sinus_tab)
   \   000000FA   ....                       LDI     R31,(sinus_tab) >> 
8
   \   000000FC   9696                       ADIW    R27 : R26,38
   \   000000FE   911C                       LD      R17,X
   \   00000100   9796                       SBIW    R27 : R26,38
   \   00000102   E002                       LDI     R16,2
   \   00000104   9F10                       MUL     R17,R16
   \   00000106   0DE0                       ADD     R30,R0
   \   00000108   1DF1                       ADC     R31,R1
   \   0000010A   9105                       LPM     R16,Z+
   \   0000010C   9114                       LPM     R17,Z
   \   0000010E   9699                       ADIW    R27 : R26,41
   \   00000110   930D                       ST      X+,R16
   \   00000112   931C                       ST      X,R17
   \   00000114   979A                       SBIW    R27 : R26,42
    403                cs_dimmer_low;                    //Dimmer-CS 
einschalten
   \   00000116   981F                       CBI     0x03,0x07
    404                SPDR = dimmer.motor.bytes.high_byte;
   \   00000118   969A                       ADIW    R27 : R26,42
   \   0000011A   910C                       LD      R16,X
   \   0000011C   979A                       SBIW    R27 : R26,42
   \   0000011E   B90F                       OUT     0x0F,R16
    405                while (!(SPSR & (1 << SPIF)));
   \                     ??timer_3_over_isr_13:
   \   00000120   9B77                       SBIS    0x0E,0x07
   \   00000122   CFFE                       RJMP 
??timer_3_over_isr_13
    406
    407                SPDR = dimmer.motor.bytes.low_byte;
   \   00000124   9699                       ADIW    R27 : R26,41
   \   00000126   910C                       LD      R16,X
   \   00000128   9799                       SBIW    R27 : R26,41
   \   0000012A   B90F                       OUT     0x0F,R16
    408                while (!(SPSR & (1 << SPIF)));
   \                     ??timer_3_over_isr_14:
   \   0000012C   9B77                       SBIS    0x0E,0x07
   \   0000012E   CFFE                       RJMP 
??timer_3_over_isr_14
    409                cs_dimmer_high;             //Dimmer-CS 
ausschalten
   \   00000130   9A1F                       SBI     0x03,0x07
    410                dimmer.daten_berechnet = 0;
   \   00000132   E000                       LDI     R16,0
   \   00000134   965F                       ADIW    R27 : R26,31
   \   00000136   930C                       ST      X,R16
   \   00000138   975F                       SBIW    R27 : R26,31

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Na ja, was ich im Studio simuliert und getestet habe, waren die
>Taktzyklen und nicht die Zeit. AVR-Studio löst die Stoppuhr ja nur mit
>0,5 µs auf und das ist bei taktgenauer Analyse doch etwas grob.

Ich habe wegen dem Teiler-Verhältnis für den SPI nach dem Takt gefragt, 
4 CPU-Takte -> ein SPI-Clock.

>Ich kopiere mal den schnellsten C-code rein und das, was der Assembler
>macht. Du wirst staunen, wie der mit den X,Y und Z Registern am
>jonglieren ist.

Der Code sieht doch garnicht so schlecht aus. Viel besser schafft man es 
auch von Hand nicht.

Viele Grüße, Stefan

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die allerschnellste Variante geht nicht über ein union
sondern durch direkten Byte-Zugriff:

   int i;

davon wollen wir jetzt das low Byte:

   unsigned char low = *((unsigned char*)&i);

und das High Byte

   unsigned char high = *(((unsigned char*)&i)+1);

Also nichts mit vorher umkopieren oder dergleichen.
Man nimmt einfach die Adresse der Speicherstelle an
der der Wert gespeichert ist und teilt dem Compiler
mit, das mal als die Adresse eines Bytes aufzufassen und
das Byte zu holen. Dann dieselbe Adresse, wieder als
Adresse auf ein Byte umgecastet, 1 dazu (weil ja das nächste
Byte gefragt ist) und eben dieses Byte geholt.


Wenn du also den Spulenwert eigntlich überhaupt
nicht brauchst, dann probiere mal

   unsigned char* sinTab;

...

       cs_dimmer_low;                    //Dimmer-CS einschalten

       sinTab = ( unsigned char* )sinus_tab + dimmer.mikro_schritt;

       //
       // High Byte
       //
       SPDR = *( SinTab + 1 );
       while (!(SPSR & (1 << SPIF)));
 
       //
       // Low Byte
       //
       SPDR = * SinTab;
       while (!(SPSR & (1 << SPIF)));

       cs_dimmer_high;             //Dimmer-CS ausschalten

  

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl Heinz, deine Lösung benötigt 118 Takte. Deine Idee mit der union 
ist um 28 Takte schneller.

Trotzdem danke für die Mühe.

MW

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Karl Heinz, deine Lösung benötigt 118 Takte.

Ehrlich? (Ich habs nicht ausprobiert).
Das hätte ich nicht gedacht.

Welchen Code hast du konkret probiert.
Das interessiert mich jetzt, möchte in bischen
mit dem Compiler spielen. Die Vorgabe von 90
Takten steht ja.

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also bei meinen Versuchen kommt mit einer
Optimierung von -Os bei der Pointervariante durchaus
was kürzeres heraus:
void foo()
{
       unsigned char* SinTab = ( unsigned char* )sinus_tab + dimmer.mikro_schritt;
  ca:  80 91 06 01   lds  r24, 0x0106
  ce:  e8 2f         mov  r30, r24
  d0:  ff 27         eor  r31, r31
  d2:  e0 50         subi  r30, 0x00  ; 0
  d4:  ff 4f         sbci  r31, 0xFF  ; 255

       //
       // High Byte
       //
       SPDR = *( SinTab + 1 );
  d6:  81 81         ldd  r24, Z+1  ; 0x01
  d8:  8f b9         out  0x0f, r24  ; 15
       while (!(SPSR & (1 << SPIF)));
  da:  77 9b         sbis  0x0e, 7  ; 14
  dc:  fe cf         rjmp  .-4        ; 0xda <foo+0x10>
 
       //
       // Low Byte
       //
       SPDR = * SinTab;
  de:  80 81         ld  r24, Z
  e0:  8f b9         out  0x0f, r24  ; 15
       while (!(SPSR & (1 << SPIF)));
  e2:  77 9b         sbis  0x0e, 7  ; 14
  e4:  fe cf         rjmp  .-4        ; 0xe2 <foo+0x18>
  e6:  08 95         ret

000000e8 <main>:

}

Vor allem die Zuweisungen an SPDR kriegt man nicht mehr
kürzer hin.

Die Variante über die union ist deutlich länger (und komplizierter).
Bedingt durch das rel. komplizierte Holen des Wertes in
den Zwischenspeicher der union.
void foo()
{
  dimmer.motor.spulenwert = sinus_tab[dimmer.mikro_schritt];
  ca:  80 91 06 01   lds  r24, 0x0106
  ce:  e8 2f         mov  r30, r24
  d0:  ff 27         eor  r31, r31
  d2:  ee 0f         add  r30, r30
  d4:  ff 1f         adc  r31, r31
  d6:  e0 50         subi  r30, 0x00  ; 0
  d8:  ff 4f         sbci  r31, 0xFF  ; 255
  da:  80 81         ld  r24, Z
  dc:  91 81         ldd  r25, Z+1  ; 0x01
  de:  90 93 08 01   sts  0x0108, r25
  e2:  80 93 07 01   sts  0x0107, r24

  SPDR = dimmer.motor.bytes.high_byte;
  e6:  80 91 08 01   lds  r24, 0x0108
  ea:  8f b9         out  0x0f, r24  ; 15
  while (!(SPSR & (1 << SPIF)));
  ec:  77 9b         sbis  0x0e, 7  ; 14
  ee:  fe cf         rjmp  .-4        ; 0xec <foo+0x22>

  SPDR = dimmer.motor.bytes.low_byte;
  f0:  80 91 07 01   lds  r24, 0x0107
  f4:  8f b9         out  0x0f, r24  ; 15
  while (!(SPSR & (1 << SPIF)));
  f6:  77 9b         sbis  0x0e, 7  ; 14
  f8:  fe cf         rjmp  .-4        ; 0xf6 <foo+0x2c>
  fa:  08 95         ret

000000fc <main>:
}

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Karlheinz,

die Sinustabelle scheint im Flash zu liegen. Michael benutzt den 
IAR-Compiler, nicht den gcc, deswegen sind seine Ergebnisse nicht ganz 
vergleichbar.

Gruß, Stefan

Autor: FBI (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Karlheinz,

die Pointer Variante ist nicht immer die günstigste, da das immer übers 
Ram abgewickelt wird. Besser ist meist ein direkter Cast auf eine Union, 
den kann der Compiler dann unter Umständen direkt über Register 
optimieren.

z.B. so:
typedef union {
   uint16_t w;
   struct {
     uint8_t h,l;
   };
} uint16_t_u;

uint16_t i;

unsigned char low = ((uint16_t_u)i).l;
unsigned char high = ((uint16_t_u)i).h;

siehe auch folgenden Thrread: Beitrag "GCC inline assembler"

CU Frank

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So meine Struktur:

[c]
volatile struct
{
  unsigned int soll_wert;
  unsigned int ist_wert;
  unsigned char analog_wert;
  unsigned char vorteiler;
  unsigned char dmx_wert;
  unsigned char tabellen_wert;
  unsigned char vorhanden;
  unsigned char daten_berechnet;
  unsigned int differenz;
  unsigned char ziel_erreicht;
  unsigned char modus;
  unsigned char zaehler;
  unsigned char warte_zaehler;
  unsigned char mikro_schritt;
  unsigned int spulenwert;
  unsigned char speed;
  unsigned char richtung;
  unsigned char * zeiger;
/*  union
  {
    unsigned int spulenwert;
    struct
    {
      unsigned char low_byte;
      unsigned char high_byte;
    }bytes;
  }motor;*/
}  dimmer;

[/]

und die Motorausgabe:
[c]

      dimmer.zeiger = (unsigned char *)sinus_tab[dimmer.mikro_schritt];
      cs_dimmer_low;                    //Dimmer-CS einschalten
      SPDR = *(dimmer.zeiger + 1);

      while (!(SPSR & (1 << SPIF)));

      SPDR = * dimmer.zeiger;

      while (!(SPSR & (1 << SPIF)));
      cs_dimmer_high;             //Dimmer-CS ausschalten

      dimmer.daten_berechnet = 0;
[/]

Die Takte im Simulator habe ich gemessen von dimmer.zeiger = (unsigned 
char *)sinus... bis dimmer.daten_berechnet = 0;

Und das ergibt 118 Takte. Auch wenn ich den Zeiger global und ausserhalb 
der Struktur definiere, sind es 118 Takte. Bei Interesse kann ich das 
Ass-Listing mal posten.

MW

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Michael benutzt den IAR-Compiler, nicht den gcc,

Ah, ok. Das habe ich verpennt.

> die Pointer Variante ist nicht immer die günstigste, da das immer
> übers Ram abgewickelt wird. Besser ist meist ein direkter Cast auf
> eine Union, den kann der Compiler dann unter Umständen direkt über
> Register optimieren.

Netter kleiner Trick. Muss ich mir merken.
Danke

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.