Forum: Compiler & IDEs Konzept Zugriff auf Variablen und Modbus


von Falko J. (spacefrog)


Lesenswert?

Hallo zusammen!

Dumme Frage zu folgendem Fall:

Ich habe ein Gerät das jetzt Modbus lernen soll. D.h. alle Parameter und 
daten sollen per Modbus ausgelesen und beschrieben werden. Die 
eigentliche Modbus Kommunikation läuft schon.

Aktuell habe ich z.B. solche Variablen
1
typedef struct
2
{
3
  uint8_t   batt_nr;           // ausgewählter Batterie Typ  
4
  uint8_t   batt_cells;            // Batterie Zellen
5
  uint16_t  capacity;             // in A/h
6
  int16_t    temp;
7
}  selected_batt_t;
8
9
  typedef struct {       
10
     ext_output_mode_t ext_output_mode;    
11
    uint8_t v_high;
12
    uint8_t v_low;
13
    uint8_t inverted;
14
    uint8_t hold_time;
15
  } ext_output_config_t;
16
17
uint16_t act_pwm;
18
float voltage_batterie;
19
float current_batterie;
20
selected_batt_t selected_batterie;
21
ext_output_config_t output_config[4];

Damit die Variablen jetzt immer an eine feste Adresse liegen, und ich 
diese einfach per Modbus ausgeben kann, habe ich angefangen alles in ein 
großes Array zu machen:
1
#define ADR_ACT_PWM 0
2
#define ADR_VOLTAGE_BATTERIE_H 1
3
#define ADR_VOLTAGE_BATTERIE_L 2
4
#define ADR_CURRENT_BATTERIE_H 3
5
#define ADR_CURRENT_BATTERIE_L 4
6
7
uint16_t system_data[5];

Jetzt kann ich einfach die Werte übertragen wenn die Adressen vom Modbus 
Master abgefragt werden.

Nur was mache ich mit den Strukturen? z.B. die output config Struktur, 
in der die Konfiguration der Ausgänge gespeichert ist. Davon habe ich 
vier Stück mit jeweils 5 Parametern. Ich könnte das jetzt aufdrösseln, 
ins große Array packen und die 20 Adressen mit #define definieren. Aber 
ich denke das wird unübersichtlich. Außerdem habe ich im programm sowas 
stehen:

[c]
for (x=0;x <= 3; x++)
{

  switch (ext_output_config[x].ext_output_mode)
  {
    case EXT_OUTPUT_MODE_DISABLED:
    break;

    case EXT_OUTPUT_MODE_CHARGER_STATE:
    if (charger_mode==charger_mode_float_charging)
    {
      switch_output_on(x);
    }
    else
    {
      switch_output_off(x);
    }
    break;
  }
}

Das würde dann ja gar nicht mehr gehen, wenn ich keine Struktur für die 
Konfiguration eines Ausgangs habe....

Jetzt steh ich irgendwie auf den Schlauch, wie man das am cleversten 
machen kann... irgendwelche Vorschläge?

Gruß
Falko

von Oliver (Gast)


Lesenswert?

Falko J. schrieb:
> Jetzt steh ich irgendwie auf den Schlauch, wie man das am cleversten
> machen kann... irgendwelche Vorschläge?

Ja. Wie du an der Vielzahl der eingegangenen Antworten erkennst, wäre es 
vielleicht sinnvoll, die Frage(n) so zu stellen, daß irgend jemand das 
Problem überhaupt versteht (dich eingeschlossen ;)

Oliver

von Falko J. (spacefrog)


Lesenswert?

:-)
okay ich versuchs mal

Also um alle Daten des Gerätes einfach via Modbus zu übertragen habe ich 
ein uint16_t Array angelegt. Jetzt kann ich einfach die angefragte 
Adressen zurücksenden, indem ich die angefragte Adresse als index für 
das Array verwende.

Die verwendeten Strukturen kann ich aber nicht einfach in das Array 
verschieben, da diese ja keine uint16_t ist....

ich hoffe das hat die Sache klarer gemacht...


Gruß
Falko

von Oliver (Gast)


Lesenswert?

Falko J. schrieb:
> Also um alle Daten des Gerätes einfach via Modbus zu übertragen habe ich
> ein uint16_t Array angelegt. Jetzt kann ich einfach die angefragte
> Adressen zurücksenden, indem ich die angefragte Adresse als index für
> das Array verwende.

Damit löst du ein Problem, was du überhaupt nicht hast.

Was spricht gegen ein Array direkt mit den Strukturdaten? Oder ein Array 
mir Pointer auf die Daten, oder eine (linked)Liste, oder, oder...

Die Zuordnung der Modbusadresse zur jeweilige Datenstruktur ist doch 
nebensächlich und trivial.

Oliver

von Falko J. (spacefrog)


Lesenswert?

Oliver schrieb:

> Die Zuordnung der Modbusadresse zur jeweilige Datenstruktur ist doch
>
> nebensächlich und trivial.
Nicht wenn man auf dem Schlauch steht :-)

> Was spricht gegen ein Array direkt mit den Strukturdaten?
Wie meinst du das...mach mal ein Stück c-code bitte


>Oder ein Array
> mir Pointer auf die Daten,

Das würde aber zusätzlich Ram Speicher verbrauchen. oder? Davon ist 
nicht mehr viel da...

Gruß
Falko

von Oliver (Gast)


Lesenswert?

Du hast eine interne Datenstruktur, die du nach aussen auf 
Modbusadressen abbilden willst. Die internen Daten müssen aber doch 
nicht so abgelegt werden, daß sie der Mosbusstruktur entsprechen. Die 
"Übersetzung" kann ja eine Funktion übernehmen.

Aber um das konkreter zu lösen, braucht es konkreteren Input. Schreib 
doch mal ein Beispiel auf, wie die internen Datenstrukturen auf die 
Modbusadressierung abgebildet werden sollen.

Oliver

von Falko J. (spacefrog)


Lesenswert?

Hallo

also z.B. sollen diese Daten
1
uint16_t act_pwm;
2
float voltage_batterie;
3
float current_batterie;
4
selected_batt_t selected_batterie;
5
uint8_t charger_mode;
6
uint8_t output_mode
7
ext_output_config_t output_config[4];


einfach der reihe Modbus adressen bekommen.
act_pwm Adresse 0
voltage_batterie Adresse 1 und 2
current_batterie Adresse 3 und 4
charger_mode Adresse 5
output_mode Adresse 6
output_config dann Adresse 7-11 für die erste Struktur 12 - 17 für die 
zweiten usw.

im obigen Beispiel währe es vielleicht noch möglich charger_mode und 
output_mode auf eine Adresse zu legen (Highbyte und Lowbyte) für zwei 
Byte sparen... aber das währe dann wohl nicht modbus konform....

Die Schreib und lese Funktion sollen auf den gleichen Speicherbereich 
sein.


Gruß
Falko

von Tom M. (tomm) Benutzerseite


Lesenswert?

Falko J. schrieb:
> einfach der reihe Modbus adressen bekommen.

Wenn ich wikipedia richtig verstehe, kennt Modbus Adressen und 
Funktionen. Es scheint naheliegend, jedem Modul eine (1) Adresse und 
jedem Konfig-Parameter einen Funktionscode zuzuweisen. Wahrend der 
Funktionscode nur 8 bit (RTU Modus) bzw. 2 Dezimalstellen (ASCII Modus) 
umfasst, kann das Datum zu jedem Funktionscode beliebig lang sein.

Dein Bus läuft im RTU Mode?

Falko J. schrieb:
> Nur was mache ich mit den Strukturen?

Wie wär's mit:
Jeder Struktur bekommt einen Funktionscode.
Im Datum wird das ganze Struct hinterlegt.

von Karl H. (kbuchegg)


Lesenswert?

Kann es sein, dass du die (eigentlich verbotene, de facto aber überall 
benutzte) Missverwendung einer union suchst?
1
struct abc
2
{
3
  uint16_t a;
4
  float    b;
5
  uint16_t c;
6
};
7
8
9
union store
10
{
11
  struct abc  data;
12
  uint16_t    data_mod[ sizeof( struct abc )/sizeof(uint16_t) ];
13
};
14
15
union store storage;


Auf deine Daten kannst du auf die 'schöne' Art mit sprechenden Namen 
zugreifen
1
     storage.data.b = 5.0;

und für den Zugriff als uint16_t Array benutzt du
1
     Transfer( storage.data_mod[3] );

Warnung:
Eigentlich ist diese Verwendung einer union streng genommen nicht 
erlaubt. Allerdings gibt es keinen Compiler, der das falsch (= nicht so 
wie erwartet) macht, so dass sich diese Variante als eine Art 
'inoffizieller De Facto Standard' etabliert hat.

Warnung 2:
Ein mögliches Struktur Padding kann dir da noch einen Strich durch die 
Rechnung machen. Also darauf aufpassen und zur Not in der Compiler-Doku 
nachsehen, wie man das Padding für eine einzelne Struktur gezielt 
abschalten kann.

von Falko J. (spacefrog)


Lesenswert?

Jap! Genau das hab ich gesucht!!! Vielen Dank!

Eine Frage noch:
Was versteht man den unter "Struktur Padding " ?

Gruß
Falko

von Karl H. (kbuchegg)


Lesenswert?

Falko J. schrieb:

> Eine Frage noch:
> Was versteht man den unter "Struktur Padding " ?

Das der Compiler zwischen Strukturmember Dummybytes einfügen darf um 
Alignment-Restriktionen des Prozessors zu erfüllen.

Gilt zb die Restriktion, dass alle Zugriffe nur auf geraden 
Speicher-Adressen stattfinden dürfen, dann gibt es hier

struct x
{
  char a;
  char b;
};

ein Problem, wenn ein char 1 Byte gross ist. Es ist einfach, dafür zu 
sorgen, dass bei einer struct x Variablen das x.a auf einer geraden 
Speicheradresse zu liegen kommt. Aber es ist so erst mal nicht möglich 
im selben Aufwasch dann auch x.b auf eine gerade Speicheradresse zu 
legen. Der Compiler hat nun 2 Möglichkeiten: entweder er baut etwas 
komplizierteren Code um auf x.b zugreifen zu können (in dem er nicht 
einen Bytezugriff macht sondern einen Wortzugriff und dann das b 
entsprechend mit UND/ODER Operationen rausmaskiert) oder aber er schiebt 
einfach ein Dummybyte zwischen a und b rein. Dann liegt auch b wieder an 
einer geraden Speicheradresse und alles ist wieder paletti.

Ob Structure Padding notwendig ist oder nicht, hängt im wesentlichen von 
der Prozessorarchitektur ab und welche Speicherzugriffsrestriktionen sie 
mitbringt.

Man merkt es daran, dass zb
sizeof( struct x )
eine 4 ergibt und keine 2, wie man eigentlich anhand der beiden 1-Byte 
Variablen annehmen würde.

von Oliver (Gast)


Lesenswert?

Ein kurzer Blick auf die "Endinaness" des Systems und des Modbusses 
sollte man aber schon gemacht haben, bevor die Lösung mit der union zum 
Einsatz kommt. Ansonsten gibts Datensalat...

Oliver

von Falko J. (spacefrog)


Lesenswert?

Hallo Oliver, Hallo Karl Heinz,

Danke für die ausführliche Erklärung zum Structure Padding.

Ich hab jetzt angefangen das Programm umzuschreiben und es funktioniert. 
Auch ohne Probleme mit "Structure Padding", musste also nichts am 
compiler umstellen (Das System ist übrigens ein avr und gcc).

Der Modbus Master Simulator für den PC "Modbus Poll" hat auch die 
nacheinander abgelegten float Werte wieder zu einer richtigen float zahl 
zusammengesetzt.

Vielen Dank nochmal!
Gruß
Falko

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.