Forum: Compiler & IDEs Präprozessor Ausgabe in Datei ausgeben


von Andreas M. (schnitzeltony)


Lesenswert?

Hallo,

ich brauche für mein Projekt mehrere SPIs, so dass ich mindestens einen 
'von Hand' relaisieren muss. Hierfür habe ich nach lektüre der 
hardwaremäßigen Implemntierung folgendes für den Preprozessor 
ausgedacht:
1
enum enSPIDataMode
2
{              //    Clk On at Idle    Leading Edge Sample
3
  SPI_MODE0,  //         0                      0
4
  SPI_MODE1,  //        0                      1
5
  SPI_MODE2,  //        1                      0
6
  SPI_MODE3,  //        1                      1
7
};
8
9
enum enSPIDataDirection
10
{
11
  SPI_MSB_FIRST = 0,
12
  SPI_LSB_FIRST
13
};
14
15
#define SPI_CREATE_SHIFT_FUNCTION(  iSPIInstance,  /* instance of SPI        */\
16
                                    eSPIDataMode,                              \
17
                                    eDataDirection,                            \
18
                                    CLK_PORT,      /* port for clock out     */\
19
                                    CLK_PINNO,    /* pin no clock out        */\
20
                                    DO_PORT,      /* port data out (MOSI)    */\
21
                                    DO_PINNO,      /* pin no data out (MOSI)  */\
22
                                    DI_PIN,        /* port data in (MISO)    */\
23
                                    DI_PINNO      /* pin no data in (MISO)  */\
24
                                    )                                          \
25
  uint8_t SPIBitBangedShiftData##iSPIInstance(uint8_t ui8DataSend)             \
26
  {                                                                            \
27
    uint8_t ui8DataReceived = 0;                                              \
28
    uint8_t ui8BitMask;                                                        \
29
    for(ui8BitMask = eDataDirection==SPI_MSB_FIRST ? 0x80 : 0x01;              \
30
        ui8BitMask != 0;                                                      \
31
        ui8BitMask = (eDataDirection==SPI_MSB_FIRST) ?                         \
32
                        (ui8BitMask>>1) : (ui8BitMask<<1))                    \
33
    {                                                                          \
34
      /*////////////////////////////////////////////////////////////////////*/\
35
      /* leading edge  */                                                      \
36
                                                                              \
37
      /* clk */                                                                \
38
      if(eSPIDataMode == SPI_MODE2 || eSPIDataMode == SPI_MODE3)              \
39
        /* rising  */                                                          \
40
        CLK_PORT |= (1<<CLK_PINNO);                                            \
41
      else                                                                    \
42
        /* falling */                                                          \
43
        CLK_PORT &= ~(1<<CLK_PINNO);                                          \
44
      /* data */                                                              \
45
      if(eSPIDataMode == SPI_MODE0 || eSPIDataMode == SPI_MODE2)              \
46
      {                                                                        \
47
        /* sample */                                                          \
48
        if(DI_PIN & (1<<DI_PINNO))                                            \
49
          ui8DataReceived |= ui8BitMask;                                       \
50
      }                                                                        \
51
      else                                                                    \
52
        /* setup */                                                            \
53
        (ui8DataSend & (1<<ui8BitMask)) ?                                     \
54
            (DO_PORT |= (1<<DO_PINNO)) :                                       \
55
            (DO_PORT &= ~(1<<DO_PINNO));                                       \
56
                                                                              \
57
      /*asm volatile(  "nop\n\t" */                                            \
58
      /*              "nop\n\t" */                                            \
59
      /*              ::);*/                                                  \
60
                                                                              \
61
      /*////////////////////////////////////////////////////////////////////*/\
62
      /* trailing edge */                                                      \
63
                                                                              \
64
      /* clk */                                                                \
65
      if(eSPIDataMode == SPI_MODE0 || eSPIDataMode == SPI_MODE1)              \
66
        /* rising */                                                          \
67
        CLK_PORT |= (1<<CLK_PINNO);                                            \
68
      else                                                                    \
69
        /* falling */                                                          \
70
        CLK_PORT &= ~(1<<CLK_PINNO);                                          \
71
                                                                              \
72
      /* data */                                                              \
73
      if(eSPIDataMode == SPI_MODE1 || eSPIDataMode == SPI_MODE3)              \
74
      {                                                                        \
75
        /* sample */                                                          \
76
        if(DI_PIN & (1<<DI_PINNO))                                            \
77
          ui8DataReceived |= ui8BitMask;                                       \
78
      }                                                                        \
79
      else                                                                    \
80
        /* setup */                                                            \
81
        (ui8DataSend & (1<<ui8BitMask)) ?                                     \
82
            (DO_PORT |= (1<<DO_PINNO)) :                                       \
83
            (DO_PORT &= ~(1<<DO_PINNO));                                       \
84
    }                                                                          \
85
    return ui8DataReceived;                                                    \
86
  }

Die Verwendung dieht dann z.B so aus

1
SPI_CREATE_SHIFT_FUNCTION(0,            /* instance of SPI         */
2
                          SPI_MODE0,    /* bit shift mode          */
3
                          SPI_MSB_FIRST,/* data direction          */
4
                          PORTA,        /* port for clock out      */
5
                          0,            /* pin no clock out        */
6
                          PORTA,        /* port data out (MOSI)    */
7
                          1,            /* pin no data out (MOSI)  */
8
                          PINA,          /* port data in (MISO)    */
9
                          2              /* pin no data in (MISO)  */)                  
10
11
12
13
////////////////////////////////////////////////////////////////////////////
14
// Main entry
15
int main(void)
16
{
17
  // TODO remove oscillator calibration
18
  OSCCAL = 0xB1;
19
  // get defaults from EEPROM
20
  LOAD_EEPROM_SETTINGS();
21
  // setup event handling
22
  InitEvents();
23
  // Port directions
24
  InitPorts();
25
  
26
  SPIBitBangedShiftData0(0xAA);

WinAVRs gcc schluckt das ganze soweit.

Warum die Funktion als Makro?

Schon mit niedriger Optimierung weiss gcc, dass er gewisse Dinge nicht 
wirklich kompilieren muss und die Bit-Schieberein selbst erledigen kann. 
Es kommt geschmeidig kleiner footprint raus.
Vergeben tue ich auch nichts, weil ich die Port- und 
Kommunikations-Einstellung nicht gedenke, während Laufzeit zu verändern.

Das ganze hat nur einen kleinen Haken: Der Debugger kennt die Funktion 
SPIBitBangedShiftData0 nur als Ganzes und auf dem Simulator sehen 
gewisse Dinge Totschleifen erschreckend ähnlich.

Um das zu testen, würde ich gerne die Ausgabe des Präprozessors in eine 
Datei umleiten. Das kopiere ich mir für verschiedene Fälle heraus und 
lasse das kompilieren.
In der Doku von gcc hab ich folgendes gefunden:
1
-o file Write output to file. This is the same as specifying file as the second non-option
2
argument to cpp. gcc has a different interpretation of a second non-option
3
argument, so you must use ‘-o’ to specify the output file.

Hm.. Das konfliktet mit dem Parameter für die Object-Datei.

Ich habe wirklich ehrlich gesucht, jedoch nichts brauchbares gefunden. 
Also hat jemand einen Link oder..

von Ekschperde (Gast)


Lesenswert?

$ avr-cpp blub.c > blub.out
$ cat blub.out
1
# 1 "blub.c"
2
# 1 "<built-in>"
3
# 1 "<command-line>"
4
# 1 "blub.c"
5
enum enSPIDataMode
6
{
7
  SPI_MODE0,
8
  SPI_MODE1,
9
  SPI_MODE2,
10
  SPI_MODE3,
11
};
12
13
enum enSPIDataDirection
14
{
15
  SPI_MSB_FIRST = 0,
16
  SPI_LSB_FIRST
17
};
18
# 89 "blub.c"
19
uint8_t SPIBitBangedShiftData0(uint8_t ui8DataSend) { uint8_t ui8DataReceived = 0; uint8_t ui8BitMask; for(ui8BitMask = SPI_MSB_FIRST==SPI_MSB_FIRST ? 0x80 : 0x01; ui8BitMask != 0; ui8BitMask = (SPI_MSB_FIRST==SPI_MSB_FIRST) ? (ui8BitMask>>1) : (ui8BitMask<<1)) { if(SPI_MODE0 == SPI_MODE2 || SPI_MODE0 == SPI_MODE3) PORTA |= (1<<0); else PORTA &= ~(1<<0); if(SPI_MODE0 == SPI_MODE0 || SPI_MODE0 == SPI_MODE2) { if(PINA & (1<<2)) ui8DataReceived |= ui8BitMask; } else (ui8DataSend & (1<<ui8BitMask)) ? (PORTA |= (1<<1)) : (PORTA &= ~(1<<1)); if(SPI_MODE0 == SPI_MODE0 || SPI_MODE0 == SPI_MODE1) PORTA |= (1<<0); else PORTA &= ~(1<<0); if(SPI_MODE0 == SPI_MODE1 || SPI_MODE0 == SPI_MODE3) { if(PINA & (1<<2)) ui8DataReceived |= ui8BitMask; } else (ui8DataSend & (1<<ui8BitMask)) ? (PORTA |= (1<<1)) : (PORTA &= ~(1<<1)); } return ui8DataReceived; }
20
# 103 "blub.c"
21
int main(void)
22
{
23
24
  OSCCAL = 0xB1;
25
26
  LOAD_EEPROM_SETTINGS();
27
28
  InitEvents();
29
30
  InitPorts();
31
32
  SPIBitBangedShiftData0(0xAA);

von Andreas M. (schnitzeltony)


Lesenswert?

Seehr schön :-))

von Andreas M. (schnitzeltony)


Lesenswert?

Und schwupps ist auch klar, wor meine fast-Totschleife vergraben war:
1
        /* setup */                                                            \
2
        (ui8DataSend & (1<<ui8BitMask)) ?                                     \

war wohl keine so gute Idee..

Es gibt nicht noch einen schönen Trick, der mir erspart die 
Zeilenumbrüche von Hand zu tippern?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Anstatt Makro ginge doch auch static inline?

Mit Debug-Stufe -g3 werden auch Infos für Makrodebugging erzeugt, aber 
AFAIR unterstützen das nicht alle Debugger.

Um das Präcompile zu schreiben (%.i für C bzw. %.ii für C++) gibt man 
gcc ein -save-temps und erhält Präprozess und Assembler-Ausgabe vom 
Compiler im aktuellen Verzeichnis (anstatt als tmp-Dateien).

Das %.i kannst du abenso compilieren wie ein %.c, allerdings stehen das 
Line-Direktiven drin, die auf die ursprüngliche Quelle verweisen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas Müller wrote:

> Hm.. Das konfliktet mit dem Parameter für die Object-Datei.

Nein, -o file gibt immer die primäre Ausgabedatei an.  Wenn du mit
-c compilierst, ist das eine Objektdatei, wenn du mit -S compilierst,
ist es eine Assemblerdatei, und wenn du mit -E ,,compilierst'', dann
ist das eine Datei, die den präprozessierten C-Code enthält.  Der dafür
übliche Suffix ist .i.  (-E ohne -o file gibt den Code nach stdout
aus, dort kann man ihn mit "> dateiname" umlenken.  avr-gcc -E ist
damit effektiv das gleiche wie das Kommando avr-cpp, was dir schon
genannt worden ist.)

von Andreas M. (schnitzeltony)


Lesenswert?

>Anstatt Makro ginge doch auch static inline?

1. Mit welchem Typ soll ich PORTA übergeben?
2. Inline hilft in meinem Fall nicht, da ich Funktionspointer auf die 
Funktionen anlegen möchte. Dann hat gcc keine Chance und bläht die 
Funktion auf, da er nicht weiss, welche Werte die Parameter haben 
werden.

@Jörg: Danke: jetzt fange ich an zu begreifen, was ich in der Doku zu 
Beginn des Abschnitts über Präprozessor Optionen gelesen habe.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas Müller wrote:
>>Anstatt Makro ginge doch auch static inline?
>
> 1. Mit welchem Typ soll ich PORTA übergeben?

DU müsstest dann die Adresse von PortA übergeben:
1
foo (uint8_t volatile * port)
2
{
3
    *port |= (1 << 3);
4
}
5
6
foo (& PORTA);

Dann muss die Funktion aber geinlinet werden, ansonsten gibt das 
hässlichen und vor allem nicht-atomaren Code.

> 2. Inline hilft in meinem Fall nicht, da ich Funktionspointer auf die
> Funktionen anlegen möchte.

Kannst du mit Makros doch auch nicht.

Momentan verwendest du das Makro, um die Implementierung der Funktion zu 
erzeugen.

Du könntest aber auch anstatt eine Implementierung zu erzeugen, eine 
"allgemeine" Implementierung machen und in den Code einfügen lassen, 
statisch verwendet wird das ja wohl nur an wenigen Stellen in der 
Endanwendung.

Falls jedoch F-Zeiger gebraucht werden, dann ist eine Erzeugung der 
Implementierung via Makro wohl nicht schlecht, sofern man nicht den 
Überblick verliert :-)

von Andreas M. (schnitzeltony)


Lesenswert?

>Kannst du mit Makros doch auch nicht.

hat doch mit dem Makro nichts zu tun. Ich habe lediglich eine Funktion 
erzeugt. Ich sehe kein Problem, darauf einen Pointer zeigen zu lassen.

Ich habe das Makro noch ein bischen überarbeitet und bin sehr mit dem 
Ergebnis zufrieden. Es ist schon genial was der Compiler alles so beim 
Optimieren erkennt: In der oben angegebenen Version hatte ich als 
Endekriterium verwendet, dass das letzte Bit aus der Maske rausgeschoben 
wird (==0). Der Compiler erkennt, dass diese Schleife 8* durchlaufen 
wird und legt dafür einen Schleifenzähler an. Dummerweise legt er diesen 
Schleifenzähler 16Bit breit an. Daher habe ich noch einen 8Bit 
Schleifenzähler angelegt, der von 8 runterzählt. Lässt man das mit -O3 
kompilieren liegt das Ergebnis nur unweit von dem, was in Assembler 
möglich gewesen wäre!

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
Noch kein Account? Hier anmelden.