Forum: Mikrocontroller und Digitale Elektronik C - Einer Funktion, eine Adresse eines Struktur Members übergeben?!


von Jan H. (janiiix3)


Lesenswert?

Hallo,

ich möchte gerne diese Funktion ein bisschen Variable gestalten.
Nun meine Frage. Bei meinem ATXMega gibt es beim Timer0 4 div. Compare 
Register ( CCA - CCD )

Hier die Struktur
1
/* 16-bit Timer/Counter 0 */
2
typedef struct TC0_struct
3
{
4
    register8_t CTRLA;  /* Control  Register A */
5
    register8_t CTRLB;  /* Control Register B */
6
    register8_t CTRLC;  /* Control register C */
7
    register8_t CTRLD;  /* Control Register D */
8
    register8_t CTRLE;  /* Control Register E */
9
    register8_t reserved_0x05;
10
    register8_t INTCTRLA;  /* Interrupt Control Register A */
11
    register8_t INTCTRLB;  /* Interrupt Control Register B */
12
    register8_t CTRLFCLR;  /* Control Register F Clear */
13
    register8_t CTRLFSET;  /* Control Register F Set */
14
    register8_t CTRLGCLR;  /* Control Register G Clear */
15
    register8_t CTRLGSET;  /* Control Register G Set */
16
    register8_t INTFLAGS;  /* Interrupt Flag Register */
17
    register8_t reserved_0x0D;
18
    register8_t reserved_0x0E;
19
    register8_t TEMP;  /* Temporary Register For 16-bit Access */
20
    register8_t reserved_0x10;
21
    register8_t reserved_0x11;
22
    register8_t reserved_0x12;
23
    register8_t reserved_0x13;
24
    register8_t reserved_0x14;
25
    register8_t reserved_0x15;
26
    register8_t reserved_0x16;
27
    register8_t reserved_0x17;
28
    register8_t reserved_0x18;
29
    register8_t reserved_0x19;
30
    register8_t reserved_0x1A;
31
    register8_t reserved_0x1B;
32
    register8_t reserved_0x1C;
33
    register8_t reserved_0x1D;
34
    register8_t reserved_0x1E;
35
    register8_t reserved_0x1F;
36
    _WORDREGISTER(CNT);  /* Count */
37
    register8_t reserved_0x22;
38
    register8_t reserved_0x23;
39
    register8_t reserved_0x24;
40
    register8_t reserved_0x25;
41
    _WORDREGISTER(PER);  /* Period */
42
    _WORDREGISTER(CCA);  /* Compare or Capture A */
43
    _WORDREGISTER(CCB);  /* Compare or Capture B */
44
    _WORDREGISTER(CCC);  /* Compare or Capture C */
45
    _WORDREGISTER(CCD);  /* Compare or Capture D */
46
    register8_t reserved_0x30;
47
    register8_t reserved_0x31;
48
    register8_t reserved_0x32;
49
    register8_t reserved_0x33;
50
    register8_t reserved_0x34;
51
    register8_t reserved_0x35;
52
    _WORDREGISTER(PERBUF);  /* Period Buffer */
53
    _WORDREGISTER(CCABUF);  /* Compare Or Capture A Buffer */
54
    _WORDREGISTER(CCBBUF);  /* Compare Or Capture B Buffer */
55
    _WORDREGISTER(CCCBUF);  /* Compare Or Capture C Buffer */
56
    _WORDREGISTER(CCDBUF);  /* Compare Or Capture D Buffer */
57
} TC0_t;
1
void timerSetCompareValue  ( TC0_t *tim0 , TC1_t *tim1 , register16_t ccx , uint16_t val )  
2
{
3
  if ( tim0 != NULL )
4
  {
5
    (( tim0->CCA ) + ccx ) = 0; // haut wohl so nicht hin..
6
  }
7
}

Kann ich der Funktion dann die Adresse des zu beschreibenden CCx 
Registers übergeben?

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Mach doch statt CCA-CCD ein Array "CCn[4]" und nutze "ccx" als Index.

PS: Der Bezeichner _WORDREGISTER in C der Standard-Bibliothek bzw. 
Compiler vorenthalten und nicht für User-Code erlaubt, da er mit 
Unterstrich+Großbuchstabe beginnt.

: Bearbeitet durch User
von Klaus Dieter D. (Gast)


Lesenswert?

Niklas G. schrieb:
> Mach doch statt CCA-CCD ein Array "CCn[4]" und nutze "ccx" als
> Index.
>
> PS: Der Bezeichner _WORDREGISTER in C der Standard-Bibliothek bzw.
> Compiler vorenthalten und nicht für User-Code erlaubt, da er mit
> Unterstrich+Großbuchstabe beginnt.

Okay.. aber wie komme ich dann z.b auf CCD?

von Klaus Dieter D. (Gast)


Lesenswert?

Also mich interessiert das auch mal..

von Niklas Gürtler (Gast)


Lesenswert?

_WORDREGISTER(CCn [4]);  /* Compare or Capture A */

tim0->CCn [ccx]

Je nachdem wie _WORDREGISTER definiert ist funktioniert das ggf nicht

von Jan H. (janiiix3)


Lesenswert?

Kann dir nicht ganz folgen...
Warst du so gut und könntest nen Pseudocode posten?

von Rolf M. (rmagnus)


Lesenswert?

Jan H. schrieb:
> (( tim0->CCA ) + ccx ) = 0; // haut wohl so nicht hin..

Das ergibt ja auch keinen Sinn. Verkürzt geschrieben wäre das ja das 
gleiche wie:
1
a + b = 0;

Auf was sollen da a und b gesetzt werden?
Die Klammern haben übrigens keinerlei Effekt.

Ich vermute, was du eigentlich meintest, wäre sowas:
1
> (&tim0->CCA)[ccx] = 0;

Das ist aber eher ziemlich hässlich.
Du versuchst, Elemente einer Struktur so anzusprechen, als seien sie 
Elemente eines Arrays. Deshalb der Vorschlag von Niklas Gürtler, diese 
gleich als Array anzulegen.

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Warum?
Sollte ich am besten für jeden Compare Match eine eigene Funktion 
schreiben um einen Wert zu setzen? Das soll später mal eine Timer 
Bibliothek werden, deshalb würde ich es gerne so machen wollen

von Rolf M. (rmagnus)


Lesenswert?

Jan H. schrieb:
> Warum?

Warum was?

> Sollte ich am besten für jeden Compare Match eine eigene Funktion
> schreiben um einen Wert zu setzen?

Nein, du sollst ein Array definieren. Und am besten ein gutes C-Buch 
kaufen und durcharbeiten. Ich habe den Eindruck, du hast keine Ahnung, 
wovon wir sprechen, obwohl das doch sehr grundlegende Dinge sind.

von A. S. (Gast)


Lesenswert?

offsetof()?

von Helmut S. (helmuts)


Lesenswert?


von Jan H. (janiiix3)


Lesenswert?

Rolf M. schrieb:
> Ich vermute, was du eigentlich meintest, wäre sowas:> (&tim0->CCA)[ccx]
> = 0;

Danke! Das hilft mir weiter ;)

von A. H. (ah8)


Lesenswert?

Rein technisch kannst Du den Offset eines Strukturelementes mit 
folgendem Ausdruck gewinnen:
1
(unsigned long) &((struct /*Structurname*/ *)0)->/*Elementname*/

also zum Beispiel:
1
struct X
2
{
3
        int     a;
4
        double  b;
5
        char    *c;
6
};
7
8
unsigned long offset_a = (unsigned long) &((struct X*)0)->a;
9
unsigned long offset_b = (unsigned long) &((struct X*)0)->b;
10
unsigned long offset_c = (unsigned long) &((struct X*)0)->c;

Ja, das funktioniert wirklich, da der Null-Pointer nie dereferenziert 
wird, er dient nur als Basis für die Adressrechnung. Diesen – oder jeden 
anderen Offset – kannst Du dann verwenden, um die Adresse des Elements 
in einer Struktur zu errechnen:
1
struct X x = { 5, 3.141, "hello world" };
2
3
unsigned long addr_x = (unsigned long) &x;
4
int    *addr_a = (int*)(addr_x + offset_a);
5
double *addr_b = (double*)(addr_x + offset_b);
6
char*  *addr_c = (char**)(addr_x + offset_c);

Das Casten der Adresse von X auf unsigned long ist notwendig, da Du 
mit den Binärwerten der Adresse rechnen willst. Dagegen wäre
1
&x + offset

äquvivalent zu
1
(struct X*) ((unsigned long) &x)+offset*sizeof(struct X))

berechnet also die Adresse einer Struktur mit gegebenen Offset in einem 
Array von Strukturen.

Vollständig sieht das dann so aus:
1
#include <stdio.h>
2
3
struct X
4
{
5
        int     a;
6
        double  b;
7
        char    *c;
8
};
9
10
unsigned long offset_a = (unsigned long) &((struct X*)0)->a;
11
unsigned long offset_b = (unsigned long) &((struct X*)0)->b;
12
unsigned long offset_c = (unsigned long) &((struct X*)0)->c;
13
14
int main()
15
{
16
        struct X x = { 5, 3.141, "hello world" };
17
18
        unsigned long addr_x   = (unsigned long) &x;
19
        int    *addr_a = (int*)(addr_x + offset_a);
20
        double *addr_b = (double*)(addr_x + offset_b);
21
        char*  *addr_c = (char**)(addr_x + offset_c);
22
  
23
        printf("%i\t%lf\t%s\n", x.a, x.b, x.c);
24
        printf("%lu\t%lu\t%u\n", offset_a, offset_b, offset_c);
25
        printf("%i\t%lf\t%s\n", *addr_a, *addr_b, *addr_c);
26
}
1
$ structOffset
2
5       3.141000        hello world
3
0       4       12
4
5       3.141000        hello world

Der Linux Kernel verwendet diese Technik, um auf die Linkelemente 
verketteter Listen in Strukturen zuzugreifen. Ob das schön ist, sei 
einmal dahin gestellt.


PS: Soweit ich mich erinnere ist unsigned long auf jeder Architektur 
garantiert groß genug, um einen Pointer aufzunehmen. Da bin ich mir aber 
nicht ganz sicher, im Zweifel müsste man das bitte nochmal im Standard 
nachlesen. Weiß das vielleicht jemand aus dem Kopf?

: Bearbeitet durch User
Beitrag #5529813 wurde vom Autor gelöscht.
von A. H. (ah8)


Lesenswert?

Jan H. schrieb:
> Rolf M. schrieb:
>> Ich vermute, was du eigentlich meintest, wäre sowas:> (&tim0->CCA)[ccx]
>> = 0;
>
> Danke! Das hilft mir weiter ;)

Das kann und wird auch in den meisten Fällen so funktionieren, ist aber 
nicht wirklich portabel. Der Compiler darf zwischen den Elementen einer 
Struktur Padding-Bytes einfügen, um sie an den physischen 
Speichergrenzen auszurichten und so einen schnelleren Zugriff zu 
ermöglichen. Zwischen Elementen eines Arrays gibt es Padding auf keinen 
Fall. Abhängig von Compiler und Architektur kann die Pointer-Arithmetik 
in diesem Fall also falsch rechnen, es sei denn, die Struktur wurde 
explizit als packed definiert, zum Beispiel:
1
struct TC0_struct
2
{
3
  ...
4
} __attribute((packed))__;

von Jan H. (janiiix3)


Lesenswert?

Okay. Das ist mir neu. Danke!

von Rolf M. (rmagnus)


Lesenswert?

A. H. schrieb:
> Ja, das funktioniert wirklich, da der Null-Pointer nie dereferenziert
> wird, er dient nur als Basis für die Adressrechnung.

Wird in der Praxis wohl meistens gehen, aber offiziell ist es nicht 
erlaubt.  Der offizielle Weg ist die Benutzung des Makros offsetof().

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Rolf M. schrieb:
> A. H. schrieb:
> Ja, das funktioniert wirklich, da der Null-Pointer nie dereferenziert
> wird, er dient nur als Basis für die Adressrechnung.
>
> Wird in der Praxis wohl meistens gehen, aber offiziell ist es nicht
> erlaubt.  Der offizielle Weg ist die Benutzung des Makros offsetof().

Wie sieht das denn dann mit der Funktion aus?

von Rolf M. (rmagnus)


Lesenswert?

Statt z.B.

A. H. schrieb:
1
> unsigned long offset_a = (unsigned long) &((struct X*)0)->a;

wäre besser:
1
size_t offset_a = offsetof(struct X, a);
und man bräuchte ein
1
#include <stddef.h>

Auch das würde ich so nicht machen:

A. H. schrieb:
> unsigned long addr_x   = (unsigned long) &x;
> int    *addr_a = (int*)(addr_x + offset_a);

Warum nicht einfach mit Zeigern arbeiten, wenn man mit Adressen 
hantiert?  Wenn schon ein Integer, dann intptr_t. Der ist garantiert 
groß genug, um einen Zeiger aufzunehmen.
Ich würd's aber so machen:
1
char* addr_x   = (char*)&x;
2
int    *addr_a = (int*)(addr_x + offset_a);

A. H. schrieb:
> PS: Soweit ich mich erinnere ist unsigned long auf jeder Architektur
> garantiert groß genug, um einen Pointer aufzunehmen.

Nein. Der ist auf so genannten IL32P64-Architekturen zu klein. Eine 
davon wäre z.B. die 64-Bit-Version von Windows.
Dafür ist unsigned long auf kleinen 8-Bittern oft eher unnötig groß, da 
er mindesten 32 Bit breit ist.

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

>
1
>> unsigned long offset_a = (unsigned long) &((struct X*)0)->a;
2
>
>
> wäre besser:
>
>
1
> size_t offset_a = offsetof(struct X, a);
2
>
Doofe Frage.. Jetzt habe ich zwar den Offset aber jedoch nicht die 
Adresse im an den der Position mit dem Offset zu schreiben?

von mh (Gast)


Lesenswert?

A. H. schrieb:
> Rein technisch kannst Du den Offset eines Strukturelementes mit
> folgendem Ausdruck gewinnen:
> (unsigned long) &((struct /*Structurname*/ *)0)->/*Elementname*/
Warum sollte man das machen, wenn es offsetof gibt?


A. H. schrieb:
> PS: Soweit ich mich erinnere ist unsigned long auf jeder Architektur
> garantiert groß genug, um einen Pointer aufzunehmen. Da bin ich mir aber
> nicht ganz sicher, im Zweifel müsste man das bitte nochmal im Standard
> nachlesen. Weiß das vielleicht jemand aus dem Kopf?
Nein, es ist nicht garantiert, dass unsigned long groß genug ist.

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.