Forum: Mikrocontroller und Digitale Elektronik "Zeiger" auf struct-Element


von Marcel P. (Gast)


Lesenswert?

Hallo, ich hab da ein kleines Problem.
Für das auslesen mehrerer Motor-Sensoren habe ich mir folgende Struktur 
an Variablen angelegt:
1
typedef struct{
2
  uint16_t ui16_sensor_val;
3
  char description_string[20];
4
} SENSOR;
5
6
struct sensors {
7
  SENSOR oil_pressure;
8
  SENSOR oil_temp;
9
  SENSOR coolant_pressure;
10
  SENSOR coolant_temp;
11
  SENSOR ambient_temp;
12
} datafields;
13
14
uint8_t ui8_active_screen = 0;

Jetzt möchte ich diese Sensorwerte auf einem Display ausgeben, abhängig 
vom Wert der in ui8_active_screen  der gewählten Bildschirmseite soll 
der entsprechende Sensor angezeigt werden.
also statt so für einen einzelnen Wert:

1
char Buffer[5];
2
itoa(datafields.oil_pressure.ui16_sensor_val, Buffer, 10);
3
lcd_string(Buffer);

eher sowas in der art:

1
char Buffer[5];
2
itoa(datafields[ui8_active_screen].ui16_sensor_val, Buffer, 10);
3
lcd_string(Buffer);

das frisst der compiler natürlich nicht. Wie kann ich das 
bewerkstelligen ?
Vielen dank schonmal...

von mr. mo (Gast)


Lesenswert?

Mal folgenden Operator nachschlagen "->".

von Daniel A. (daniel-a)


Lesenswert?

Mit einem Array.
1
#define SENSOR_COUNT 3
2
3
struct sensors {
4
  SENSOR oil_pressure;
5
  SENSOR oil_temp;
6
  SENSOR coolant_pressure;
7
  SENSOR coolant_temp;
8
  SENSOR ambient_temp;
9
} datafields[SENSOR_COUNT];

Oder noch besser:
1
// Headerfile
2
#define SENSOR_COUNT 3
3
4
struct sensor { // nur declaration
5
  SENSOR oil_pressure;
6
  SENSOR oil_temp;
7
  SENSOR coolant_pressure;
8
  SENSOR coolant_temp;
9
  SENSOR ambient_temp;
10
};
...
1
// c file
2
struct sensor sensors[SENSOR_COUNT]; // definition
...
1
char Buffer[6]; // 6 statt fünf, weil das Nullbyte auchnoch Platz braucht
2
itoa(sensors[ui8_active_screen].ui16_sensor_val, Buffer, 10);
3
lcd_string(Buffer);

: Bearbeitet durch User
von Marcel P. (Gast)


Lesenswert?

@mr.mo

danke, werde ich mal ansehen...

@Daniel

hätte ich dann aber nicht 3 mal das komplette struct ? Also 3 mal den 
Ölsensor, 3 mal den Kühlmittelsensor, usw ?
1
char Buffer[6]
stimmt, an die maximale 65535 muss ja als String noch ne '\0' ran... 
ganz vergessen, danke ^^

von aSma>> (Gast)


Lesenswert?

Marcel P. schrieb:
> typedef struct{
>   uint16_t ui16_sensor_val;
>   char description_string[20];
> } SENSOR;
>
> struct sensors {
>   SENSOR oil_pressure;
>   SENSOR oil_temp;
>   SENSOR coolant_pressure;
>   SENSOR coolant_temp;
>   SENSOR ambient_temp;
> } datafields;

Servus,
du willst ja mit Pointer arbeiten. Ein großes Thema ist es immer wieder, 
wenn es um dynamische Speicherverwaltung geht...

Hier ein hardcoded bsp.:
1
typedef struct{
2
   uint16_t ui16_sensor_val;
3
   char description_string[20];
4
} SENSOR_t;
5
6
typedef struct sensors {
7
   SENSOR_t oil_pressure;
8
   SENSOR_t oil_temp;
9
   SENSOR_t coolant_pressure;
10
   SENSOR_t coolant_temp;
11
   SENSOR_t ambient_temp;
12
 } DATAFIELD_t;
13
14
//gloabal var
15
DATAFIELD_t[] DATA

von Marcel P. (Gast)


Lesenswert?

@aSma

irgendwie stehe ich gerade auf dem Schlauch was du mir genau sagen 
möchtest.
Liegt vielleicht aber auch daran, das ich gerade versuche den -> 
Operator zu verstehen o.O

Im Grunde möchte ich folgende Funktion erreichen, nur eben sehr viel 
einfacher, da die Sensoren mehr werden sollen und ich nicht für jeden 
Sensor ein extra case-Block schreiben möchte.
1
      char Buffer[5];
2
      
3
      switch(ui8_active_screen) {
4
        
5
        case 0:
6
        itoa(datafields.oil_temp.sensor_val, Buffer, 10);
7
        break;
8
        case 1:
9
        itoa(datafields.oil_pressure.sensor_val, Buffer, 10);
10
        break;
11
        case 2:
12
        itoa(datafields.coolant_temp.sensor_val, Buffer, 10);
13
        break;
14
        case 3:
15
        itoa(datafields.coolant_pressure.sensor_val, Buffer, 10);
16
        break;
17
        case 4:
18
        itoa(datafields.ambient_temp.sensor_val, Buffer, 10);
19
        break;
20
        default:
21
        
22
      }
23
      lcd_setcursor(0,0);
24
      lcd_string(Buffer);

von aSma>> (Gast)


Lesenswert?

Marcel P. schrieb:
> typedef struct{
>   uint16_t ui16_sensor_val;
>   char description_string[20];
> } SENSOR;
>
> struct sensors {
>   SENSOR oil_pressure;
>   SENSOR oil_temp;
>   SENSOR coolant_pressure;
>   SENSOR coolant_temp;
>   SENSOR ambient_temp;
> } datafields;

Servus,
du willst ja mit Pointer arbeiten. Ein großes Thema ist es immer wieder, 
wenn es um dynamische Speicherverwaltung geht...

Hier ein hardcoded bsp.:
1
//in header
2
#define      MAX_SENSOR   3    
3
4
typedef struct{
5
   uint16_t ui16_sensor_val;
6
   char description_string[20];
7
} SENSOR_t;
8
9
typedef struct{
10
   SENSOR_t oil_pressure;
11
   SENSOR_t oil_temp;
12
   SENSOR_t coolant_pressure;
13
   SENSOR_t coolant_temp;
14
   SENSOR_t ambient_temp;
15
 } DATAFIELD_t;
16
17
DATAFIELD_t* read_data(); //fkt in header
18
19
20
//gloabal var
21
DATAFIELD_t DATA[MAX_SENSOR];  
22
23
//fkt mit einen pointer als rückgabetyp
24
DATAFIELD_t* read_data(){
25
26
static uint8_t sensor_counter=0;  // No malloc use. Hard coded.
27
28
if (sensor_counter == MAX_SENSOR)  // Max sensor arrived. 
29
    return NULL;
30
31
//hier ist dein pointer
32
DATAFIELD_t* p = (DATAFIELD_t*)&DATA[sensor_counter++]; //pointer zeigt auf sensor i!
33
34
p->oil_pressure.ui16_sensor_val = xxx;
35
36
...
37
return p;
38
}
39
40
//main
41
extern DATAFIELD_t* read_data();
42
DATAFIELD_t *sensor1_p, *sensor2_p ,*sensor3_p;
43
44
int main(){
45
46
sensor1_p = read_data();
47
sensor2_p = read_data();
48
sensor3_p = read_data();
49
}

Wenn du
>   uint16_t ui16_sensor_val;
>   char description_string[20];
nicht gleichzeitig brauchst, dann kannst du daraus auch eine union 
machen.

mfg

von aSma>> (Gast)


Lesenswert?

Marcel P. schrieb:
> @aSma
>
> irgendwie stehe ich gerade auf dem Schlauch was du mir genau sagen
> möchtest.

sry, habe noch nicht fertig gehabt. Bin auf den enter botton drauf 
gekommen.

von aSma>> (Gast)


Lesenswert?

Marcel P. schrieb:
> char Buffer[5];
>
>       switch(ui8_active_screen) {
>
>         case 0:
>         itoa(datafields.oil_temp.sensor_val, Buffer, 10);
>         break;
> ...

Dein case ist nicht ausgagekräftig. Du brauchst hier keinen Pointer, 
wenn du alles global hälst. Aber du kannst natürlich einen nutzen. Wie 
zuvor von mir gezeigt.
1
typedef enum{
2
   oil_temp = 0,
3
   cool_pres,
4
   cool_temp,
5
   amb_temp,
6
} SENSOR_SWITCH_t;
7
8
SENSOR_SWITCH_t sensor_e;
9
10
switch(sensor_e) {
11
12
      case oil_temp:
13
                        itoa(datafields.oil_temp.sensor_val, Buffer, 10);
14
                        break;
15
      case cool_pres:

usw

von Daniel A. (daniel-a)


Lesenswert?

Marcel P. schrieb:
> @Daniel
>
> hätte ich dann aber nicht 3 mal das komplette struct ? Also 3 mal den
> Ölsensor, 3 mal den Kühlmittelsensor, usw ?

Ups, ich hatte nicht nachgesehen, was im Struct drin steht. Neuer 
vorschlag: (c11)
1
typedef struct{
2
  uint16_t ui16_sensor_val;
3
  char description_string[20];
4
} SENSOR;
5
6
struct sensors {
7
  union {
8
    SENSOR sensor_list[1];
9
    struct {
10
      SENSOR oil_pressure; // Müssen alle vom typ SENSOR sein
11
      SENSOR oil_temp;
12
      SENSOR coolant_pressure;
13
      SENSOR coolant_temp;
14
      SENSOR ambient_temp;
15
    };
16
  };
17
} datafields;
18
19
uint8_t ui8_active_screen = 0;
20
21
...
22
23
char Buffer[6];
24
itoa(datafields.sensor_list[ui8_active_screen].ui16_sensor_val, Buffer, 10);
25
lcd_string(Buffer);

von Marcel P. (Gast)


Lesenswert?

@aSma

ok, ich versuche mal zu verstehen, wie ich das dann anwende. Momentan 
stehe ich gerade immernoch auf den Schlauch, was aber eher an mir liegt 
^^ .

Ich hab mal kommentiert, wie ich das verstanden habe... und ab wo ich 
auf dem schlauch stehe...
1
//in header
2
#define      MAX_SENSOR   3                      // meine Anzahl an sensoren (eigentlich 5)...
3
4
typedef struct{
5
   uint16_t ui16_sensor_val;
6
   char description_string[20];
7
} SENSOR_t;
8
9
typedef struct{
10
   SENSOR_t oil_pressure;
11
   SENSOR_t oil_temp;
12
   SENSOR_t coolant_pressure;
13
   SENSOR_t coolant_temp;
14
   SENSOR_t ambient_temp;
15
 } DATAFIELD_t;
16
17
DATAFIELD_t* read_data(); //fkt in header        
18
19
20
//gloabal var
21
DATAFIELD_t DATA[MAX_SENSOR];                        // indiesem Array stehen meine jeweiligen Sensorwerte aus ui16_sensor_val
22
23
//fkt mit einen pointer als rückgabetyp
24
DATAFIELD_t* read_data(){
25
26
static uint8_t sensor_counter=0;  // No malloc use. Hard coded.                             //fängt bei 0 an, klar
27
28
if (sensor_counter == MAX_SENSOR)  // Max sensor arrived.                                  //wenn alle Sensoren durch, fertig
29
    return NULL;
30
31
//hier ist dein pointer
32
DATAFIELD_t* p = (DATAFIELD_t*)&DATA[sensor_counter++]; //pointer zeigt auf sensor i!        // Hier fängt es an für mich unverständlich zu werden
33
34
p->oil_pressure.ui16_sensor_val = xxx;                                                         // Hier steig ich ganz aus ? das ist ja nur der Öldruck-Sensor.. was ist mit den anderen ? Entsprechend so ?:
35
36
p->oil_temp.ui16_sensor_val = xxx;                       // =xxx wäre dann etwa mein = ADC_Read_avg(channel, samples); für jeden sensor ?
37
38
...
39
return p;
40
}
41
42
//main
43
extern DATAFIELD_t* read_data();
44
DATAFIELD_t *sensor1_p, *sensor2_p ,*sensor3_p;                  // verstehe ich nicht ganz genau, aber das sind dann 3 pointer für das struct DATAFIELD_t ?
45
46
int main(){
47
48
sensor1_p = read_data();
49
sensor2_p = read_data();
50
sensor3_p = read_data();
51
}

Sorry, das ich mich doof anstelle, aber pointer sind für mich irgendwie 
ein rotes Tuch....

@Daniel

mache ich mir durch deinen Vorschlag nicht den Vorteil des struct 
zunichte wenn alle vom Typ SENSOR sein müssen ? Weil dann könnte ich 
doch auch ein Array draus machen ? Also statt dem hier:
1
typedef struct{
2
  uint16_t sensor_val;
3
  char description_string[20];
4
} SENSOR;
5
6
struct sensors {
7
  union {
8
    SENSOR sensor_list[1];
9
    struct {
10
      SENSOR oil_pressure; // Müssen alle vom typ SENSOR sein
11
      SENSOR oil_temp;
12
      SENSOR coolant_pressure;
13
      SENSOR coolant_temp;
14
      SENSOR ambient_temp;
15
    };
16
  };
17
} datafields;

dann so:
1
enum sensors {
2
OIL_PRES_e = 0,
3
OIL_TEMP_e,
4
COL_TEMP_e,
5
EOL_PRES_e,
6
AMB_PRES_e
7
};
8
9
SENSOR sensors[5];
10
11
itoa(sensors[OIL_PRES].sensor_val, Buffer, 10);

oder ?

Ich habe das "Datafields" als struct angelegt, weil da in Zukunft noch 
andere sachen ausser SENSOR-typen dazu kommen sollen. Diese anderen 
enthalten zwar auch immer die Felder "sensor_val" und 
"sensor_descriptor" aber eben auch noch zusätzliche Felder.

von Daniel A. (daniel-a)


Lesenswert?

Marcel P. schrieb:
> mache ich mir durch deinen Vorschlag nicht den Vorteil des struct
> zunichte wenn alle vom Typ SENSOR sein müssen ? Weil dann könnte ich
> doch auch ein Array draus machen ? Also statt dem hier:

Ja. In solchen fällen nutze ich Basisklassen.
1
enum sensor_type {
2
  ST_PRESSURE,
3
  ST_TEMP
4
};
5
6
struct sensor {
7
  enum sensor_type type;
8
  uint16_t ui16_sensor_val;
9
  char description_string[20];
10
};
11
12
struct pressure_sensor {
13
  struct sensor super;
14
  bool other_value;
15
};
16
17
struct temp_sensor {
18
  struct sensor super;
19
  int bla;
20
};
21
22
struct pressure_sensor oil_pressure = {
23
  .type = ST_PRESSURE
24
};
25
26
struct temp_sensor oil_temp = {
27
  .type = ST_TEMP
28
};
29
30
struct pressure_sensor coolant_pressure = {
31
  .type = ST_PRESSURE
32
};
33
34
struct temp_sensor coolant_temp = {
35
  .type = ST_TEMP
36
};
37
38
struct temp_sensor ambient_temp = {
39
  .type = ST_TEMP
40
};
41
42
struct sensor* sensors[] = {
43
  &oil_pressure.super,
44
  &oil_temp.super,
45
  &coolant_pressure.super,
46
  &coolant_temp.super,
47
  &ambient_temp.super
48
};
49
50
...
51
52
char Buffer[6];
53
itoa(sensors[ui8_active_screen]->ui16_sensor_val, Buffer, 10);
54
lcd_string(Buffer);

: Bearbeitet durch User
von aSma>> (Gast)


Lesenswert?

Marcel P. schrieb:
> Sorry, das ich mich doof anstelle, aber pointer sind für mich irgendwie
> ein rotes Tuch....

Siehe Überschrift: "Zeiger" auf struct-Element

Dann lasse einfach die Zeiger und Unions weg.

Wichtig ist, dass man aussagekräftig programmiert, denn dann brauchst du 
auch kaum zu kommentieren (Stichwort: real programmer).

Schreibe jetzt ein header und .c Datei und poste hier.

mfg

von Marcel P. (Gast)


Lesenswert?

Ich werde versuchen das sinnig in mein Code zu setzen und dann poste ich 
das ganze Ding mal hier sobald es passt.

Vielen vielen Dank aber schon mal. Die Codebeispiele haben mir aber auch 
noch viele andere Kleinigkeiten gezeigt, die den Programmierstil viel 
besser machen.

Grüße, Marcel

von aSma>> (Gast)


Lesenswert?

Daniel A. schrieb:
> Daniel Abrecht

Gar nicht so schlecht. Damit ist das Programm nicht redudant und lässt 
sich prima erweitern. Daumen hoch.

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.