mikrocontroller.net

Forum: Compiler & IDEs Strings zur Compilezeit in ein ByteArray einfügen (und auffüllen)


Autor: Philipp Drewes (phili)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Liebe Leute,
ich möchte im Programmspeicher ein Bytearray ablegen, welches ich zur 
Compilezeit "zusammenstelle". Dazu habe ich einige #defines erstelle, 
über die die die Feldinhalte festlege:
#define NMEA2000_NMEADATABASEVERSION  1210        //  2Byte
#define NMEA2000_MFPRODUCTCODE    61258       //  2Byte
#define NMEA2000_MFMODELID    "Meine ID"  // 32Byte ASCII

Zur Compilezeit soll dann alles zusammengesetzt werden:
const prog_uint8_t NMEA2000_ProductInformation_Data[] = {
  (uint8_t)(NMEA2000_NMEADATABASEVERSION),
  (NMEA2000_NMEADATABASEVERSION>>8),
  (uint8_t)(NMEA2000_MFPRODUCTCODE),
  // hier soll jetzt das "Char-Array" eingefügt werden
  // und dann kommen noch weitere Bytes und Words ....
};

Das klappt auch bis auf den String "NMEA2000_MFMODELID". Hier weiß ich 
nicht, wie ich den zur Compilezeit geeignet zerlegen lassen kann, so 
dass die einzelnen Buchtaben sukkzessive in den Bytes von 
"NMEA2000_ProductInformation_Data" abgelegt werden.

Außerdem ist in der Spezifikation vorgeschrieben, das 
"NMEA2000_MFMODELID" 32 Byte lang zu sein hat und alle nicht vewendeten 
Bytes mit 0xFF aufgefüllt werden. Kann mann das auch irgendwie geschickt 
mit Präprozessor und Compiler lösen?

Ich hätte gern den fertigen Datensatz im Flash, damit ich im Programm 
dann nurnoch die Daten kopieren muss.

lG Phili

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Philipp Drewes schrieb:
>
> const prog_uint8_t NMEA2000_ProductInformation_Data[] = {
>   (uint8_t)(NMEA2000_NMEADATABASEVERSION),
>   (NMEA2000_NMEADATABASEVERSION>>8),
>   (uint8_t)(NMEA2000_MFPRODUCTCODE),
>   // hier soll jetzt das "Char-Array" eingefügt werden
>   // und dann kommen noch weitere Bytes und Words ....
> };
> 
>
> Das klappt auch bis auf den String "NMEA2000_MFMODELID".
Das klappt auch mit dem Rest nicht.

Wenn du NMEA2000_NMEADATABASEVERSION auf uint8_t castest, wird der Rest 
(obere 8 Bits) abgeschnitten und du hast nen falschen Wert drin.

Autor: Philipp Drewes (phili)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Sven,
danke für Deine Antwort.

>> Das klappt auch bis auf den String "NMEA2000_MFMODELID".
> Das klappt auch mit dem Rest nicht.
>
> Wenn du NMEA2000_NMEADATABASEVERSION auf uint8_t castest, wird der Rest
> (obere 8 Bits) abgeschnitten und du hast nen falschen Wert drin.

Mein System erwartet die Daten im Little-Endian Format. Somit müssen die 
Daten für das Byte 0 abgeschnitten werden. Für Byte 1 wird geeignet 
geshiftet.

Hast Du eine Idee für den String?
Geht das überhaupt so?

Phili

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
vielleicht sollte es so ähnlich aussehen:
#define NMEA2000_NMEADATABASEVERSION   1210        //  2Byte
#define NMEA2000_MFPRODUCTCODE        61258        //  2Byte
#define NMEA2000_MFMODELID            "Meine ID"                          \
                                      "\xff\xff\xff\xff\xff\xff\xff\xff"  \
                                      "\xff\xff\xff\xff\xff\xff\xff\xff"  \
                                      "\xff\xff\xff\xff\xff\xff\xff\xff"

const struct
{
  prog_uint16_t databaseversion;
  prog_uint16_t productcode;
  prog_uint8_t  modelid[32];
}
  NMEA2000_ProductInformation_Data  =
    {
      NMEA2000_NMEADATABASEVERSION,
      NMEA2000_MFPRODUCTCODE,
      NMEA2000_MFMODELID
    };

(bzw. die beiden 16-Bitwerte in je 8 Bit aufgeteilt und getrennt 
initalisiert mit cast bzw. Shift, wenn es sonst mit der Reihhenfolge
kneift)

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da wäre ich vorsichtig, die Schieberei und so wird m.W.n. arithmetisch 
durchgeführt. Schreibs lieber explizit dazu.

Ein Stringliteral à la "blablabla" zerfällt normalerweise in einen 
Zeiger. Lediglich im Kontext einer Vektorinitialisierung zerfällts in 
einzelne Zeichen:
char vektor[] = "abc";

Ich wüsste jetzt nicht, wie man dein Problem ohne gescheite Strukturen 
so lösen könnte :-(

Autor: Philipp Drewes (phili)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo ihr beiden,

vielen Dank für die Hinweise. So lässt es sich realisieren.

lG Phili

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ach und noch ein Tipp:

falls die ID von Fall zu Fall unterschiedlich lang ist, kann
man auch einfach soviele \xFF zum Auffüllen nehmen, daß es
auch für eine kurze ID sicher reicht (also am sichersten 32 hier).

Das erzeugt dann zwar eine Warnung, ist sonst aber nicht weiter 
schädlich.

Der Compiler weiß ja, daß das Feld nur 32 Byte hat und ignoriert
die überzähligen Bytes der Initialisierung.

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum eigentlich mit 0xFF auffüllen?
Bei einem String wird laut C immer ein 0x00 angehängt.
Die Initialisierung eines character Feldes kann auch kürzer sein, nur 
das abschließende 0x00 wird von C gebraucht.

Autor: Philipp Drewes (phili)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Falser schrieb:
> Warum eigentlich mit 0xFF auffüllen?
> Bei einem String wird laut C immer ein 0x00 angehängt.
> Die Initialisierung eines character Feldes kann auch kürzer sein, nur
> das abschließende 0x00 wird von C gebraucht.

Hallo Klaus,
die Daten werden über einen CAN-Bus versendet. Die Spezifikation des 
verwendeten Protokolls erwartet, dass mit 0xFF aufgefüllt wird

Autor: Philipp Drewes (phili)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> ach und noch ein Tipp:
>
> falls die ID von Fall zu Fall unterschiedlich lang ist, kann
> man auch einfach soviele \xFF zum Auffüllen nehmen, daß es
> auch für eine kurze ID sicher reicht (also am sichersten 32 hier).
>
> Das erzeugt dann zwar eine Warnung, ist sonst aber nicht weiter
> schädlich.
>
> Der Compiler weiß ja, daß das Feld nur 32 Byte hat und ignoriert
> die überzähligen Bytes der Initialisierung.

Und noch eine weitere Anmerkung / Frage hierzu. Damit die Strings auch 
im Flash landen, muss das folgende Vorgehen verfolgt werden:

Beitrag "[AVR-GCC] struct-Initialisierung. PROGMEM und Strings"

Da ich dann im Struct ja aber den Pointer habe, muss ich die Länge von 
32 Byte direkt in der String-Definition vorgeben. Hier der aktuelle 
Code-Ausschnitt
typedef struct {
  uint16_t    databaseversion;
  uint16_t    productcode;
  const char  *pmodelid;
  const char  *psoftwareversion;
  const char  *pmodelversion;
  const char  *pmodelserialcode;
  uint8_t     certificationlevel;
  uint8_t     loadequivalancy;  
} tNMEA2000_ProductInformation;

const char PROGMEM modelid[32] = NMEA2000_MODELID;
const char PROGMEM softwareversion[32] = NMEA2000_SOFTWAREVERSIONCODE;
const char PROGMEM modelversion[32] = NMEA2000_MODELVERSION;
const char PROGMEM modelserialcode[32] = NMEA2000_MODELSERIALCODE;

const tNMEA2000_ProductInformation PROGMEM NMEA2000_ProductInformation = {
    NMEA2000_DATABASEVERSION,
    NMEA2000_PRODUCTCODE,
    modelid,    
    softwareversion,
    modelversion,
    modelserialcode,
    NMEA2000_CERTIFICATIONLEVEL,
    NMEA2000_LOADEQUIVALENCY
};

Autor: Philipp Drewes (phili)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und dann ergibt sich damit das Problem, dass ich auf die uints mit Hilfe 
der "pgm_read_xxx"-Routinen zugreifen kann. Für die Strings benötige ich 
aber "geschachtelte" "pgm_read_word" und "pgm_read_char" Routinen um 
erst die Start-Adresse des Strings zu holen und dann den Inhalt lesen zu 
können.

Ich meiner Implementation würde ich später aber gern nur mit "memcpy_P" 
arbeiten und keine weiteren "i"-angängigen Entscheidungen treffen.
Hier ein Code-Ausschnitt.
for (uint8_t i=0; i<18; i++){
    message.data[0] = fast_packet_index++;
    memcpy_P(message.data+1, &NMEA2000_ClaimAddress+6+7*i, 7);
    mcp2515_send_message(&message);
}

Meine Frage ist also weiterhin:
Wie erhalte ich eine sequentielle Datenstruktur im Flash, die aus 
unsigned Integers verschiedener Größe und mehreren Strings besteht, zur 
Compilezeit zusammengebaut wird und später sukzessive mit Hilfe von 
"memcpy_P" gelesen werden kann??

Danke noch einmal für alle bisherigen Vorschläge, Nachfragen und 
Anregungen!!

Euer Phili

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.