www.mikrocontroller.net

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


Autor: Andreas Müller (schnitzeltony)
Datum:

Bewertung
0 lesenswert
nicht 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:
enum enSPIDataMode
{              //    Clk On at Idle    Leading Edge Sample
  SPI_MODE0,  //         0                      0
  SPI_MODE1,  //        0                      1
  SPI_MODE2,  //        1                      0
  SPI_MODE3,  //        1                      1
};

enum enSPIDataDirection
{
  SPI_MSB_FIRST = 0,
  SPI_LSB_FIRST
};

#define SPI_CREATE_SHIFT_FUNCTION(  iSPIInstance,  /* instance of SPI        */\
                                    eSPIDataMode,                              \
                                    eDataDirection,                            \
                                    CLK_PORT,      /* port for clock out     */\
                                    CLK_PINNO,    /* pin no clock out        */\
                                    DO_PORT,      /* port data out (MOSI)    */\
                                    DO_PINNO,      /* pin no data out (MOSI)  */\
                                    DI_PIN,        /* port data in (MISO)    */\
                                    DI_PINNO      /* pin no data in (MISO)  */\
                                    )                                          \
  uint8_t SPIBitBangedShiftData##iSPIInstance(uint8_t ui8DataSend)             \
  {                                                                            \
    uint8_t ui8DataReceived = 0;                                              \
    uint8_t ui8BitMask;                                                        \
    for(ui8BitMask = eDataDirection==SPI_MSB_FIRST ? 0x80 : 0x01;              \
        ui8BitMask != 0;                                                      \
        ui8BitMask = (eDataDirection==SPI_MSB_FIRST) ?                         \
                        (ui8BitMask>>1) : (ui8BitMask<<1))                    \
    {                                                                          \
      /*////////////////////////////////////////////////////////////////////*/\
      /* leading edge  */                                                      \
                                                                              \
      /* clk */                                                                \
      if(eSPIDataMode == SPI_MODE2 || eSPIDataMode == SPI_MODE3)              \
        /* rising  */                                                          \
        CLK_PORT |= (1<<CLK_PINNO);                                            \
      else                                                                    \
        /* falling */                                                          \
        CLK_PORT &= ~(1<<CLK_PINNO);                                          \
      /* data */                                                              \
      if(eSPIDataMode == SPI_MODE0 || eSPIDataMode == SPI_MODE2)              \
      {                                                                        \
        /* sample */                                                          \
        if(DI_PIN & (1<<DI_PINNO))                                            \
          ui8DataReceived |= ui8BitMask;                                       \
      }                                                                        \
      else                                                                    \
        /* setup */                                                            \
        (ui8DataSend & (1<<ui8BitMask)) ?                                     \
            (DO_PORT |= (1<<DO_PINNO)) :                                       \
            (DO_PORT &= ~(1<<DO_PINNO));                                       \
                                                                              \
      /*asm volatile(  "nop\n\t" */                                            \
      /*              "nop\n\t" */                                            \
      /*              ::);*/                                                  \
                                                                              \
      /*////////////////////////////////////////////////////////////////////*/\
      /* trailing edge */                                                      \
                                                                              \
      /* clk */                                                                \
      if(eSPIDataMode == SPI_MODE0 || eSPIDataMode == SPI_MODE1)              \
        /* rising */                                                          \
        CLK_PORT |= (1<<CLK_PINNO);                                            \
      else                                                                    \
        /* falling */                                                          \
        CLK_PORT &= ~(1<<CLK_PINNO);                                          \
                                                                              \
      /* data */                                                              \
      if(eSPIDataMode == SPI_MODE1 || eSPIDataMode == SPI_MODE3)              \
      {                                                                        \
        /* sample */                                                          \
        if(DI_PIN & (1<<DI_PINNO))                                            \
          ui8DataReceived |= ui8BitMask;                                       \
      }                                                                        \
      else                                                                    \
        /* setup */                                                            \
        (ui8DataSend & (1<<ui8BitMask)) ?                                     \
            (DO_PORT |= (1<<DO_PINNO)) :                                       \
            (DO_PORT &= ~(1<<DO_PINNO));                                       \
    }                                                                          \
    return ui8DataReceived;                                                    \
  }

Die Verwendung dieht dann z.B so aus

SPI_CREATE_SHIFT_FUNCTION(0,            /* instance of SPI         */
                          SPI_MODE0,    /* bit shift mode          */
                          SPI_MSB_FIRST,/* data direction          */
                          PORTA,        /* port for clock out      */
                          0,            /* pin no clock out        */
                          PORTA,        /* port data out (MOSI)    */
                          1,            /* pin no data out (MOSI)  */
                          PINA,          /* port data in (MISO)    */
                          2              /* pin no data in (MISO)  */)                  



////////////////////////////////////////////////////////////////////////////
// Main entry
int main(void)
{
  // TODO remove oscillator calibration
  OSCCAL = 0xB1;
  // get defaults from EEPROM
  LOAD_EEPROM_SETTINGS();
  // setup event handling
  InitEvents();
  // Port directions
  InitPorts();
  
  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:
-o file Write output to file. This is the same as specifying file as the second non-option
argument to cpp. gcc has a different interpretation of a second non-option
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..

Autor: Ekschperde (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
$ avr-cpp blub.c > blub.out
$ cat blub.out
# 1 "blub.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "blub.c"
enum enSPIDataMode
{
  SPI_MODE0,
  SPI_MODE1,
  SPI_MODE2,
  SPI_MODE3,
};

enum enSPIDataDirection
{
  SPI_MSB_FIRST = 0,
  SPI_LSB_FIRST
};
# 89 "blub.c"
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; }
# 103 "blub.c"
int main(void)
{

  OSCCAL = 0xB1;

  LOAD_EEPROM_SETTINGS();

  InitEvents();

  InitPorts();

  SPIBitBangedShiftData0(0xAA);

Autor: Andreas Müller (schnitzeltony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Seehr schön :-))

Autor: Andreas Müller (schnitzeltony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und schwupps ist auch klar, wor meine fast-Totschleife vergraben war:
        /* setup */                                                            \
        (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?

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

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

Bewertung
0 lesenswert
nicht 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.)

Autor: Andreas Müller (schnitzeltony)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
foo (uint8_t volatile * port)
{
    *port |= (1 << 3);
}

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 :-)

Autor: Andreas Müller (schnitzeltony)
Datum:

Bewertung
0 lesenswert
nicht 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!

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.