Forum: Mikrocontroller und Digitale Elektronik void Pointer auf struct


von Jan K. (jan_k)


Lesenswert?

Hallo Leute,

folgendes Problem habe ich momentan :

Ich baue ein kleines Programm auf einem uC, das über die serielle 
Schnittstelle Befehle bekommt. So soll bspw. ein bestimmter Modus 
eingestellt werden (z.B. Logging Modus 1). Das geht mit einem Befehl. 
Dieser wird dann durch eine Zeichenfolge bestätigt. Dann kann ein 
zweiter Befehl empfangen werden, der z.B. einen anderen Parameter setzt. 
Dieser Befehl wird ebenfalls bestätigt. Und so weiter.

Es soll aber gewartet werden, bis alle Parameter gesetzt worden sind, 
bevor der Modus umschaltet.

Ich habe momentan zwei Modi, die ich beeinflussen möchte :
1
typedef struct usartMode
2
{
3
  uint16_t mode; // aktueller Modus
4
  uint8_t  confirmed;
5
} usartModeStruct;
6
7
typedef struct pidMode
8
{
9
  uint16_t mode;    // aktueller Modus : Tabelle / Funktion
10
  uint8_t function;  // welche Funktion bzw Tabelle?
11
  uint16_t amplitude; // Amplitude in steps
12
  float   frequenz;    // Frequenz in Herz
13
  uint8_t  confirmed;   // aktiviert?
14
} pidModeStruct;
15
16
volatile typedef struct activateMode
17
{
18
  void *newMode;
19
  void *activeMode;
20
} activateModeStruct;
21
22
23
...
24
usartModeStruct usartLogMode = {USART_LOG_OFF_READ,1};  
25
usartModeStruct newUsartLogMode = {USART_LOG_OFF_READ,1};
26
27
pidModeStruct pidMode = {PID_OFF_READ,0,0,0,1};      // aktueller Modus
28
pidModeStruct newPidMode = {PID_OFF_READ,0,0,0,1};   // nächste Modus  
29
  
30
activateModeStruct activateMode = {NULL, NULL};  // Beinhaltet die Modestructs..

Je nachdem, was von außen kommt, steht in 'mode' etwas anderes drin. In 
einer switch/case Anweisung wird dann geprüft, um welchen Befehl es sich 
handelt.

Ausschnitt:
1
switch(action)    // action
2
      {
3
        case USART_LOG_COUNTER:         // Stelle ein : log Mode 1 - counter
4
            newUsartLogMode.mode = USART_LOG_COUNTER; // noch nicht freigegeben -> usartLogMode.confirmed = 0;
5
            newUsartLogMode.confirmed = 0;
6
            activateMode.newMode = (usartModeStruct*) &newUsartLogMode;
7
            activateMode.activeMode = (usartModeStruct*) &usartLogMode;
8
          break;
9
10
        case USART_LOG_COUNTER_ABS:    // Stelle ein : log Mode 2 - counter + abs
11
            newUsartLogMode.mode = USART_LOG_COUNTER_ABS; // noch nicht freigegeben -> usartLogMode.confirmed = 0;
12
            newUsartLogMode.confirmed = 0;
13
            activateMode.newMode = (usartModeStruct*) &newUsartLogMode;
14
            activateMode.activeMode = (usartModeStruct*) &usartLogMode;
15
          break;
16
...
17
        case PID_SET_FREQ:  // Frequenz einstellen            
18
            newPidMode.frequenz = freq;
19
            newPidMode.confirmed = 0;   
20
            activateMode.newMode = (pidModeStruct*) &newPidMode;
21
            activateMode.activeMode = (pidModeStruct*) &pidMode;  
22
...
23
        case CONFIRM:          // Führe aus.  C
24
          if(activateMode.newMode != NULL && (uint16_t)data == lastAction)
25
          {            
26
            activateMode.activeMode = activateMode.newMode;  
27
            activateMode.activeMode -> confirmed = 1;  // neuen Modus setzen        
28
          }          
29
          break;

action ist dabei die von außen kommende Variable.

Vermutlich geht die Zuweisung unten nicht, im Notfall kopiere ich halt 
die Inhalte Elementweise. Das ist aber jetzt erst einmal nicht das 
Hauptproblem.

Ich habe gedacht, dass ich dem Problem, dass ich eben nicht weiß, um 
welche struct es handelt, aus dem Weg gehen kann, indem ich den void 
Pointer nehme und dann jeweils caste.

Nun habe ich gelesen, dass nicht bei der Zuweisung, sondern bei der 
Dereferenzierung gecastet werden muss. Damit bin ich wieder beim alten 
Problem. Ich weiß nicht, ob es eine usartModeStruct oder eine 
pidModeStruct ist. Eventuell werden es noch mehr als diese beiden Typen.

Ich hoffe ihr konntet das Problem nachvollziehen, ich wäre euch sehr 
dankbar, wenn einige Tips/Anregungen kämen. Danke :)

von Peter II (Gast)


Lesenswert?

Jan K. schrieb:
> Ich weiß nicht, ob es eine usartModeStruct oder eine
> pidModeStruct ist. Eventuell werden es noch mehr als diese beiden Typen.

anders geht es nicht. Dann musst du irgendwie dafür sorgen das du es 
weißt.

von Karl H. (kbuchegg)


Lesenswert?

Mit etwas Bauchweh kann man tricksen.

Die Zuweisung
  activateMode.activeMode = activateMode.newMode;
geht sowieso nicht, weil da ein paar Dereferenziersterne fehlen.

Allerdings: Du musst ja nicht mit einer direkten Zuweisung operieren. 
memcpy kopiert Bytefelder und alles was es dazu braucht ist die Länge 
des zu kopierenden Bytefeldes. Und die kann man sich ja zusätzlich zu 
den Pointern merken.

Das Problem, dass das confirmed Feld irgendwo in der Struktur steckt, 
kann man damit umgehen, indem man ihn als ersten Member in die Struktur 
packt. Dann weiß man immer, egal bei welcher STruktur, wo dieser Member 
liegt.

Allerdings: Damit ist jegliche Sicherheit dahin! Wenn auf deiner 
Schnittstelle Kommandos reinkommen, die so nicht erlaubt sind, dann geht 
das enorm in die Hose!

von Ralf R. (rrascher)


Lesenswert?

Peter II schrieb:
> Jan K. schrieb:
>> Ich weiß nicht, ob es eine usartModeStruct oder eine
>> pidModeStruct ist. Eventuell werden es noch mehr als diese beiden Typen.
>
> anders geht es nicht. Dann musst du irgendwie dafür sorgen das du es
> weißt.

ufuer so etwas gibt es in C den Datentyp UNION.

[c}
typedef struct usartMode
{
  uint16_t mode; // aktueller Modus
  uint8_t  confirmed;
} usartModeStruct;

typedef struct pidMode
{
  uint16_t mode;    // aktueller Modus : Tabelle / Funktion
  uint8_t function;  // welche Funktion bzw Tabelle?
  uint16_t amplitude; // Amplitude in steps
  float   frequenz;    // Frequenz in Herz
  uint8_t  confirmed;   // aktiviert?
} pidModeStruct;

volatile typedef struct activateMode
{
  void *newMode;
  void *activeMode;
} activateModeStruct;

[/c]

pidModeStruct enthaelt ja praktisch die gleichen
Variablen die auch in usartModeStruct drin sind.
Dann definier dir doch noch eine zusaetzliche
Variable im pidModeStruct, z. B. mode, die dir
die verwendeten Variablen defieniret, bzw. welche
Art von Struktur gerade gueltig ist.

Ralf

von Jan K. (jan_k)


Lesenswert?

Danke für die Antworten :)

Ich habe mir sowas in der Art auch überlegt, Peter. Aber das fühlte sich 
dann auch nach zu sehr Gefrickel an.

Die Eingaben werden gefiltert, viel mehr Möglichkeiten gibt es momentan 
auch noch nicht. Ist ein kleines Programm, aber schadet ja nicht, das 
vernünftig zu designen.

Ich denke ich werde eine zusätzliche Variable nehmen, die sich merkt, 
was zuletzt gesetzt werden sollte..

Ist denn so eine Aufgabe so ungewöhnlich? Ging jetzt davon aus, dass 
häufiger in der Art auf Benutzereingaben reagiert wird?

Danke + viele Grüße!

edit: Deinen Beitrag hab' ich zu spät gesehen, Ralf :) Mit Unions habe 
ich noch nie gearbeitet, werde mir die aber auch angucken, danke.

von Pako (Gast)


Lesenswert?

Jan K. schrieb:
1
activateMode.activeMode -> confirmed = 1;  // neuen Modus setzen

Das kann nicht gehen.
Du hast einen void* und versuchst auf das Element "confirmed" 
zuzugreifen. Der C-Compiler kann nicht wissen, was der Inhalt des void* 
bedeuten soll, daher kann er auch keinen Zusammenhang zu usartModeStruct 
oder pidModeStruct herstellen.

Durch das Zuweisen der Adresse einer Struktur auf einen void* ändert 
sich am Typ des void* nichts, es ist nach wie vor ein void*.

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Lesenswert?

Die Struktur:
typedef struct pidMode
{
  uint16_t mode;    // aktueller Modus : Tabelle / Funktion
  uint8_t function;  // welche Funktion bzw Tabelle?
  uint16_t amplitude; // Amplitude in steps
  float   frequenz;    // Frequenz in Herz
  uint8_t  confirmed;   // aktiviert?
} pidModeStruct;

würd ich so umformen;

typedef struct pidMode
{
  uint8_t  confirmed;   // aktiviert?
  uint8_t function;  // welche Funktion bzw Tabelle?
  uint16_t mode;    // aktueller Modus : Tabelle / Funktion
  float   frequenz;    // Frequenz in Herz
  uint16_t amplitude; // Amplitude in steps
} pidModeStruct;

kostet dich nix, bringt 5 Bytes mehr Speicher.

Grüsse

von Jan K. (jan_k)


Lesenswert?

@ Pako: Ja, ich dachte halt, es würde bei der Zuweisung reichen, zu 
casten. Tatsächlich muss aber beim Auslesen gecastet werden. Habe jetzt 
in der struct "activateMode" ein weiteres Feld angelegt, das den letzten 
Modus gespeichert hält..

@ Gebhard, danke für den Tip, gehe davon aus, es liegt am alignment. Der 
Compiler selbst packt die Reihenfolgen nicht an?

von Klaus W. (mfgkw)


Lesenswert?

Und warum nicht eine struct, die den Typ enthält (als enum) sowie eine 
union mit dem einen oder dem anderen mode?
Für sowas ist eine union doch da.

Den Typ außerhalb der Daten (also in activateMode) zu speichern, geht 
zwar, ist aber weniger elegant. Je näher bei den Daten, desto klarer und 
zuverlässiger.

Wenn man void* verwendet, hat man meistens etwas falsch gemacht - so 
auch hier.

Oder: wie du es schon hast in activateMode den Typ merken, und darin 
dann eine union mit einem Zeiger auf den einen oder den anderen mode.

von Stefan W. (dl6dx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das Problem, dass das confirmed Feld irgendwo in der Struktur steckt,
> kann man damit umgehen, indem man ihn als ersten Member in die Struktur
> packt. Dann weiß man immer, egal bei welcher Struktur, wo dieser Member
> liegt.

Ich verwende in solchen Fällen gern ein Konstrukt aus einer Struktur, 
die als erstes Element einen Typdeskriptor (kleiner uint-Typ) und ggf. 
weitere immer vorhandene Werte enthält, gefolgt von einer Union aus 
mehreren, zu den  einzelnen funktionalen Typen gehörenden Structs. Für 
den Einsatzzweck des OP könnte ich mir das so vorstellen:
1
// Message-Typen
2
#define MSGT_UNDEF 0
3
#define MSGT_USART 1
4
#define MSGT_PID   2
5
// etc...
6
7
// Die typabhängigen Teilstrukturen
8
typedef struct
9
  {
10
  uint16_t mode;       // aktueller Modus
11
  } usartModeStruct;
12
13
typedef struct
14
  {
15
  uint16_t mode;       // aktueller Modus : Tabelle / Funktion
16
  uint8_t function;    // welche Funktion bzw Tabelle?
17
  uint16_t amplitude;  // Amplitude in steps
18
  float   frequenz;    // Frequenz in Hertz
19
  } pidModeStruct;
20
21
// Jetzt die "Superstruktur"
22
// Für alle gemeinsame Elemente zuerst
23
// und in der Union die typabhängigen Elemente
24
25
typedef struct {
26
  uint8_t  type;              // Typ der Message
27
  uint8_t  confirmed;         // confirmed?
28
  union
29
    {
30
    usartModeStruct usartmode; // Daten, falls USART-MSG
31
    pidModeStruct   pidmode;   // Daten, falls PID-MSG
32
    } daten;
33
  } messageStruct;
34
35
36
void beispielfunktion(messageStruct InMsg)
37
  {
38
  uint8_t  confirmed;
39
40
  uint16_t usartmode;
41
42
  uint16_t pidmode;
43
  uint8_t  pidfunction;
44
  uint16_t pidamplitude;
45
  float    pidfrequenz;
46
47
  switch(InMsg.type)
48
    {
49
    case MSGT_USART:
50
      if(InMsg.confirmed)
51
        {
52
        usartmode = InMsg.daten.usartmode.mode;
53
        }
54
      // weiteres...
55
      break;
56
57
    case MSGT_PID:
58
      if(InMsg.confirmed)
59
        {
60
        pidmode      = InMsg.daten.pidmode.mode;
61
        pidfunction  = InMsg.daten.pidmode.function;
62
        pidamplitude = InMsg.daten.pidmode.amplitude;
63
        pidfrequenz  = InMsg.daten.pidmode.frequenz;
64
        }
65
      // weiteres...
66
      break;
67
68
    default: // unbekannter Typ
69
      // panic();
70
      break;
71
  }

Durch die Schachtelung ist es natürlich etwas mehr "Schreibarbeit", bis 
man am gewünschten Element ist. Und man muss darauf achten, je nach Typ 
die richtige Union anzusprechen, dafür gibt es in C natürlich keine 
Automatik.

Grüße

Stefan

PS: Ich hab den Beispielcode nur "auf die Schnelle" in die Eingabemaske 
des Forums gehackt. Tippfehler möchte ich daher nicht restlos 
ausschließen.

von Jan K. (jan_k)


Lesenswert?

Danke für die zusätzlichen Antworten!

In etwa so, wie im letzten Beitrag habe ich das jetzt, nur ohne union. 
Werde die union aber auch ausprobieren, dankeschön!

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.