mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Problem mit STRUCT, UNION & CO


Autor: Marcel K. (viewer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Forumgemeinde,
ich habe ein Problem mit meine „struct’s“ und „union’s“ Ich erkläre kurz 
worum es geht. Ich habe einen µC der serielle Daten (UART) an einen 2. 
µC senden soll. Am 2. µC hängt ein G-LCD. Die Befehle des G-LCD’s haben 
unterschiedliche Übergabe-Parameter. Ich habe im 1. µC eine 
include-Datei mit dem Namen „Applikation“ In dieser Datei gibt es mehrer 
struct’s die die unterschiedlichen Übergabe-Parameter abbilden. In einer 
Union sind dann die erwähnten Funktionen für den 2. µC.



/****** Typ deklarationen  *****************************************/

typedef struct
{
  U8 dummy;
}no_data_t;

typedef struct
{
  U8 single;
}one_byte_t;

typedef struct
{
  U8 first;
  U8 second;
}two_byte_t;

typedef struct
{
  U16 single;
}one_int_t;

typedef struct
{
  U16 first_16;
  U16 second_16;
  U16 third_16;
  U8  first_8;
}three_int_one_byte_t;

typedef struct
{
  U16 first_16;
  U16 second_16;
  U16 third_16;
  U16 fourth_16;
  U8  first_8;
}four_int_one_byte_t;


typedef union
{
  two_byte_t            set_cursor;
  two_byte_t            set_offset;
  two_byte_t            set_address_xy;
  one_int_t             set_address;
  one_int_t             set_text;
  one_byte_t            text_area;
  one_byte_t            set_graphic;
  one_byte_t            graphic_area;
  one_byte_t            set_mode;
  one_byte_t            display_mode;
  one_byte_t            cursor_line;
  no_data_t             init;
  no_data_t             clear_text;
  no_data_t             clear_graph;
  no_data_t             put_char;
  four_int_one_byte_t   set_pixel;
  four_int_one_byte_t   line;
  three_int_one_byte_t  circle;
  three_int_one_byte_t  half_circle;
  four_int_one_byte_t   box;
}control_u;


typedef struct
{
  U16       sof;
  U8        laenge;
  U8        command;
  control_u u;
}puffer_t;


puffer_t buf;  // Erzeugung des Buffers

/***********************************************/

Mein Problem ist jetzt, ich schaffe es nicht eine Funktion zu erstellen, 
die byteweise den Buffer über UART versendet. Mir ist schon klar das man 
dies mit Zeiger machen muss, aber wie??
Es müsste so irgendwie aussehen: (ich weiß das die Funktion so nicht 
funktioniert)

//Sendefunktion//

void send_buffer(puffer_t *p,U8 laenge)
{
  U8 *pc = p;
  while(!(laenge == 0))
  {
    Uart_putchar(*pc);
    pc++;
    laenge--;
  }
}

//Funktionsaufruf//

send_buffer(buf,buf.laenge);


Ich suche eine Möglichkeit die Anfangsadresse des Buffers zu bekommen 
und dann mit einem Zeiger, byteweise die Daten über UART senden. Ich 
weiß nicht mehr weiter. Kann mir jemand weiter helfen??

Mit den besten Grüßen,
Marcel

Autor: Yagan Ζ. Dongobar (yagan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel,

wenn du buf vollständig in Bytes übertragen willst, sähe das so aus:

puffer_t buf;  // Erzeugung des Buffers

//Sendefunktion//

void send_buffer(U8 *pbyte, int laenge)
{
  while (laenge > 0)
  {
    Uart_putchar(*pbyte++);
    laenge -= 1;
  }
}

//Funktionsaufruf//

send_buffer((U8 *)buf,sizeof(puffer_t);

Ciao, Yagan

Autor: Marcel K. (viewer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Yagan,
danke für deine Antwort.
Ich habe jetzt die Funktion so umgeschrieben:

//////////////////////
void send_buffer(U8 *pbyte,U8 laenge)
{
  while(!(laenge == 0))
  {
    send_data(*pbyte++);
    laenge-=1;
  }
}
///////////////////////

Das funktioniert jetzt auch ohne Fehler!

Allerdings der Funktionsaufruf gibt mir einen Fehler! Ich übergebe für 
den 2. Parameter zum testen auch erst mal nur ne 1 damit dieser Fehler 
ausgeschlossen wird. Der Fehler liegt also beim 1. Übergabeparameter.

//////////////////////
send_buffer((U8 *)buf,1);
/////////////////////

Woran liegt das??
Viele Grüße, Marcel

Autor: Manuel Stahl (thymythos) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kleiner Tip, es sieht sauberer aus, wenn du ein enum für die 
Funktionsnamen manchst und ein struct für jede Funktion.

z.B. so:
typedef enum {
  RPC_set_cursor,
  RPC_set_offset,
  RPC_set_address_xy,
  RPC_set_address,
  RPC_set_text,
} RPC_function;

typedef struct {
  U8 x;
  U8 y;
} PARAM_set_cursor;

void call_rpc(RPC_function fkt, U8* param, U8 length) {
  Uart_putchar(fkt);
  while (length > 0) {
    Uart_putchar(*param++);
    laenge--;
  }
}

void foobar() {
  PARAM_set_cursor p = { 5, 2 };
  call_rpc(RPC_set_cursor, &p, sizeof(PARAM_set_cursor));
}

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
AAAACHTUNG: Enum deklarieren ist gut und schön, aber die Enum dann bitte 
nich als als Datentyp verwenden, weil die Standardmäßig (meine ich 
grübel) vier Bytes belegen.

Autor: Yagan Ζ. Dongobar (yagan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Allerdings der Funktionsaufruf gibt mir einen Fehler! Ich übergebe für
> den 2. Parameter zum testen auch erst mal nur ne 1 damit dieser Fehler
> ausgeschlossen wird. Der Fehler liegt also beim 1. Übergabeparameter.

> //////////////////////
> send_buffer((U8 *)buf,1);
> /////////////////////

> Woran liegt das??

Hm, wie sieht die Fehlermeldung aus?

Autor: Yagan Ζ. Dongobar (yagan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach ja, da fehlt noch eine Klammer:

send_buffer( (U8 *)buf, sizeof(puffer_t) );

So müsste es gehen.

Autor: Manuel Stahl (thymythos) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sven Pauli wrote:
> AAAACHTUNG: Enum deklarieren ist gut und schön, aber die Enum dann bitte
> nich als als Datentyp verwenden, weil die Standardmäßig (meine ich
> grübel) vier Bytes belegen.

Nicht beim AVR, so viel ich weiß. Zumindest gibts dafür ne 
Compiler-Option: -fshort-enums. Wichtig ist auch -fpack-struct.

Autor: Marcel K. (viewer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für eure Antworten!!!

@Manuel
im Funktionsaufruf:
call_rpc(RPC_set_cursor, &p, sizeof(PARAM_set_cursor));

stimmt doch der 3. Übergabewert nicht, oder??
Die Funktion erwartet doch ein U8??

@Yagan
ich bekomme bei der Funktion "send_buffer((U8 *)buf,1);" die 
Fehlermeldung: (Benutze IAR)

Error[Pe171]: invalid type conversion

Wieso übergibst du in deinem Funktionsaufruf als 2. Parameter: 
sizeof(puffer_t) wenn du in der Funktion allerdings ein U8 erwartest?

Hmm, ich denke da habe ich bei euch beiden etwas nicht so richtig 
verstanden!! (C- Verständnisproblem)

Autor: Manuel Stahl (thymythos) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sizeof(x) ist einfach eine Zahl die der Compiler erzeugt.

sizeof(U8) ist äquivalent zu 1. Da gibts null Unterschied.

Autor: Manuel Stahl (thymythos) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Yagan z. Dongobar wrote:
> Ach ja, da fehlt noch eine Klammer:
>
> send_buffer( (U8 *)buf, sizeof(puffer_t) );
>
> So müsste es gehen.

Nein! buf ist noch kein Zeiger.

send_buffer( (U8*)&buf, sizeof(puffer_t) );

Autor: Wolfgang Mües (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel,

ich möchte Dich bitten, von dieser Vorgehensweise gänzlich abzugehen.

Datenstrukturen in C sind in ihrem inneren Aufbau IMMER vom verwendeten 
Compiler abhängig. Ein Compiler kann solche lustigen Dinge machen wie:

- Datenstrukturen in ihrer Größe auf ganze Maschinenworte aufweiten.
- Elemente der Datenstrukturen umsortieren, damit sie sich besser packen 
lassen.
- Mehrbyte-Werte können Big Endian oder Little Endian sein.

Das macht C-Datenstrukturen grundsätzlich ungeeignet, um als Basis für 
die Kommunikation zu anderen Rechnern zu dienen. Ganz besonders lustig 
wird es, wenn Du z.B. Deinen Compiler updatest und der neue sortiert die 
Datenstrukturen anders.

Wen ich in meinem Bereich dabei erwische, wie er so programmiert, 
bekommt was auf die Finger!

Ich würde Dir raten, die Kommunikation in Einheiten von Bytes zu 
definieren, auch wenn es erstmal mehr Aufwand ist.

Gruß
Wolfgang

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sven Pauli wrote:
> AAAACHTUNG: Enum deklarieren ist gut und schön, aber die Enum dann bitte
> nich als als Datentyp verwenden, weil die Standardmäßig (meine ich
> grübel) vier Bytes belegen.

Enums verwenden standardmäßig den Datentypen int als Speicher. Der ist 
bei AVR-GCC 2 Byte groß. Die Sache mit fshort-enums wurde ja schon 
genannt.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wolfgang Mües wrote:
> Ich würde Dir raten, die Kommunikation in Einheiten von Bytes zu
> definieren, auch wenn es erstmal mehr Aufwand ist.

Da hat der gute Wolfgang nicht ganz unrecht. Es gibt ne Menge Dinge zu 
beachten bei sowas.

Autor: Yagan Ζ. Dongobar (yagan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel,

mit sizeof(puffer_t) berechnet der Compiler die Länge des Typs 
'puffer_t' in Bytes. Das Ergebnis ist eine konstante Zahl, die eventuell 
in ein 'U8' passt.

> send_buffer((U8 *)buf,1)
Wo hier eine 'invalid type conversion' sehe ich momentan nicht.

Autor: Manuel Stahl (thymythos) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wolfgang Mües wrote:
>
> Ich würde Dir raten, die Kommunikation in Einheiten von Bytes zu
> definieren, auch wenn es erstmal mehr Aufwand ist.

Schreib doch mal ein Codebeispiel.

Mit dem GCC und dem C166 hatte ich noch nie Probleme...

Autor: Manuel Stahl (thymythos) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Yagan z. Dongobar wrote:
> Marcel,
>
> mit sizeof(puffer_t) berechnet der Compiler die Länge des Typs
> 'puffer_t' in Bytes. Das Ergebnis ist eine konstante Zahl, die eventuell
> in ein 'U8' passt.
>
>> send_buffer((U8 *)buf,1)
> Wo hier eine 'invalid type conversion' sehe ich momentan nicht.

Hab ich doch schon geschrieben, buf ist kein Zeiger!

Autor: Yagan Ζ. Dongobar (yagan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Manuel,

du hast Recht, buf ist kein Zeiger.

Es muss 'send_buffer((U8 *)&buf,1)' lauten, daher die Fehlermeldung.

Ciao, Yagan

Autor: Marcel K. (viewer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch mal Danke für eure fleißigen Antworten!!!!

@Manuel
Also jetzt funktioniert alles! Ich bekomme über UART genau das gesendet 
was ich wollte! Danke ertsmal für deine Hilfe.

Jetzt komme ich zu Wolfgang. :o)

Also wenn ich dich richtig verstanden habe, meinst du dass ich alle 
Werte in ein Array von U8 anlegen soll. Auch die Int Werte sollten als 
high und low byte abgespeichert werden.
So richtig verstanden?

Viele Grüße, Marcel

Autor: Marcel K. (viewer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,
ich habe noch eine Frage wie ich den meinen „Buffer“ füllen kann. Ich 
weiß ich könnte z.B. schreiben:

buf.u.set_cursor.first = 4;

Das funktioniert auch. Ich würde allerdings gerne mit einem Zeiger auf 
den Buffer zugreifen. Es soll eine Funktion geben die byteweise in den 
Buffer schreibt und dann immer um eins erhöht wird. Ich habe es schon so 
probiert:

U8 *zeiger;
*zeiger = &buf;

Allerdings funktioniert das so nicht. Was mach ich den falsch?

Viele Grüße,
Marcel

Autor: Yagan Ζ. Dongobar (yagan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel,

wie Wolfgang schon dargelegt hat, ist die Methode eine Struktur in 
binärer Form byteweise über die serielle Schnittstelle zu übertragen, 
schlechter Programmierstil und kann problematisch werden.

Besser ist es ein 'Protokoll' zu definieren, das jedem übertragenen Byte 
eine bestimmte Funktion zuordnet, z.B. die Funktion 
'SetCursor(Zeile,Spalte)'
erzeugt drei Bytes:

1. Byte = Code für Kommando SetCursor,
2. Byte = Wert für Zeile,
3. Byte = Wert für Spalte.

etwa so:
#include <string.h>
#define SETCURSOR 0x01 // Kommandocode für SetCursor.

U8 buffer[20];  // String der Länge maximal 20 Bytes.

void SetCursor ( U8 zeile, U8 spalte )
    {
    buffer[0] = SETCURSOR;
    buffer[1] = zeile;
    buffer[2] = spalte;
    buffer[3] = 0;  // Endemarke für String setzen.
    send_buffer(buffer, strlen(buffer));
    }

// Beispiel: Setze Cursor in Zeile 1 und Spalte 2:

SetCursor(1,2);
Das ist jetzt ein ganz einfaches Beispiel um die Vorgehensweise 
anzudeuten.
Möglicherweise passt es nicht ganz zu dem bisherigen Konzept, lässt sich 
aber leicht anpassen.

Ciao, Yagan

Autor: Marcel K. (viewer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Yagan,
ja ich habe am Anfang das ganze auch so gehabt. Es gibt aber Funktionen 
die nicht nur zwei Byte benötigen (Cursor x,y) sondern auch Funktionen 
die integer benötigen. Z.B. „set_address(int address)“ Ich habe auch 
Funktionen die z.B. zwei Byte und zwei Integer erwarten. Deshalb wollte 
ich das ganze mit Strukturen machen da ich beim Empfangen des Buffers 
nur noch die Struktur als Maske über den Buffer legen muss und kann dann 
direkt die Werte über die Struktur heraus lesen. So wie du das erklärt 
hast, habe ich es am Anfang auch gehabt, aber gerade bei integer-Werten 
muss man dann mir zwischenvariablen arbeiten (int = empfang>>8) und dann 
(int = %256 empfang) (so irgendwie habe ich das gemacht, bin jetzt nicht 
zu Haus aber es hat funktioniert)
Auf jeden Fall dachte ich mir das es über Strukturen viel sauberer zu 
lesen ist.

Mein erstes Problem war ja, dass ich einen gefüllten Buffer byteweise 
senden wollte. Jetzt möchte ich empfangene Bytes in den Buffer 
schreiben. Allerdings mit einem Zeiger der auf die Anfangsadresse des 
Buffers zeigt.

Ich bin über die Antwort von Wolfgang dankbar. Und ich kann mir ganz gut 
vorstellen das es beim importieren in andere Compiler Probleme geben 
kann. Ich finde aber auch dass das byteweise senden wirklich nicht sehr 
gut für die Lesbarkeit zu erkennen ist.

Würdet Ihr wirklich das ganze byteweise versenden?? Gibt es denn keine 
andere, gut lesbare Möglichkeit?
Bin über jeden Vorschlag dankbar!

Schönen Tag noch und viele Grüße,
Marcel

Autor: Marcel K. (viewer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alles klar, ich hab's jetzt hinbekommen. Für diejenigen die es 
interessiert:

unsigned char *zeiger = (unsigned char*) &buf;

Danke für eure Mühe!!!

Marcel

Autor: Yagan Ζ. Dongobar (yagan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel,

gegen die Datenstrukturen ist nichts einzuwenden, problematisch ist nur 
die Art des 'streaming' über die serielle Schnittstelle.
Deine Methode funktioniert nur wenn auf Sender- und Empfängerseite 
absolut gleiche Bedingungen herrschen, also gleicher Compiler, gleiche 
Version, gleicher Prozessor.
Das macht die Angelegenheit unflexibel.
Wenn du z.B. zum Test auf der Empfängerseite zunächst einen PC benutzen 
willst, ist das Verfahren schon ungeeignet, wogegen die Datenübertragung 
nach einem festgelegten Protokoll immer (platformunabhängig) 
funktioniert.

Das Beispiel für set_address könnte dann so aussehen:
'set_address(Adresse)'
erzeugt drei Bytes:

1. Byte = Code für Kommando SetAdress,
2. Byte = unteres (Low) Byte von Adresse,
3. Byte = oberes (High) Byte von Adresse.

in C etwa so:

#define SETADDRESS 0x02 // Kommandocode für SetAddress.
#define SETADDRESS_FRAME 3 // Länge des SetAdresss-Frames.

U8 buffer[20];  // Sendepuffer der Länge maximal 20 Bytes.

void set_address ( U16 adresse )
    {
    buffer[0] = SETADRESS;
    buffer[1] = (U8)(adresse&0xFF);
    buffer[2] = (U8)(adresse>>8);
    send_buffer(buffer, SETADDRESS_FRAME);
    }

// Aufruf mit Union/Struktur-Element:

set_address( control_u.set_address.single );

Ciao, Yagan

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.