Forum: Mikrocontroller und Digitale Elektronik C > Struct > Pointer - Adresszuweisung


von Jan H. (janiiix3)


Lesenswert?

Hallo Bit Freunde,

ich möchte gerne weiter meine C Kenntnisse auffrischen..

Dieser Variable möchte ich gerne die Adresse zuordnen, jedoch zeigt der 
Online Compiler ein Fehler an (was vermutlich auch richtig ist..)

Was mache ich falsch?
1
  //GPIO->OutReg       = (uint32_t*)((0x0554 + (4 * 8)));
1
/******************************************************************************
2
3
                            Online C Compiler.
4
                Code, Compile, Run and Debug C program online.
5
Write your code in this editor and press "Run" button to compile and execute it.
6
7
*******************************************************************************/
8
9
#include <stdio.h>
10
#include <stdint.h>
11
12
#define GPIO_BASE 0x60004000ul
13
14
typedef struct
15
{
16
  volatile uint32_t *FuncOutCnfg;
17
  volatile uint32_t *OutReg;
18
  volatile uint32_t *EnableReg;
19
}sGPIO;
20
21
volatile sGPIO *GPIO;
22
23
24
25
int main()
26
{
27
  //GPIO->FuncOutCnfg  = (uint32_t*)(GPIO_BASE + 0x0004);
28
  //GPIO->EnableReg    = (uint32_t*)(GPIO_BASE + 0x0020);
29
  GPIO->OutReg       = (uint32_t*)(GPIO_BASE + (0x0554 + (4 * 8)));
30
  
31
  //GPIO->OutReg       = (uint32_t*)((0x0554 + (4 * 8)));
32
    
33
    
34
  printf( "%p", GPIO->OutReg);
35
36
    return 0;
37
}

von Ada J. Quiroz (inschnier)


Lesenswert?

*GPIO gibt es nicht. *GPIO zeigt ins Nichts.

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Warum soll GPIO ein Pointer sein?

Falls zwingend Pointer:
1
volatile sGPIO GPIO[1];
sonst . statt ->
1
volatile sGPIO GPIO;
2
... 
3
    GPIO.OutReg = (uint32_t*)(GPIO_BASE + (0x0554 + (4 * 8)))

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jan H. schrieb:
> jedoch zeigt der Online Compiler ein Fehler an (was
Sollen wir jetzt raten, welcher Compiler welchen Fehler anzeigt?

> ich möchte gerne weiter meine C Kenntnisse auffrischen..
Wozu einen neuen Thread für das exakt selbe Problem? Reicht der 
Beitrag "Mit dem ESP32C3 laufen lernen" nicht?

Jan H. schrieb:
> Dann bin ich mal auf deine Erklärung gespannt..
Denk mal da drüber nach:
- https://onlinegdb.com/HZ-zbCPMXY
1
#include <stdio.h>
2
3
#define GPIO_BASE 0x60004000ul
4
5
typedef struct
6
{
7
  volatile unsigned long *FuncOutCnfg;
8
  volatile unsigned long *OutReg;
9
  volatile unsigned long *EnableReg;
10
} sGPIO;
11
12
volatile sGPIO GPIO;
13
volatile sGPIO *pGPIO;
14
15
int main()
16
{
17
  pGPIO = (sGPIO*)(&GPIO);
18
19
  GPIO.OutReg    = (unsigned long*)(GPIO_BASE + 4);
20
  printf( "%p\n",  pGPIO->OutReg);
21
  printf( "%p\n\n", GPIO.OutReg);  
22
  
23
  pGPIO->OutReg  = (unsigned long*)(GPIO_BASE) + 4;
24
  printf( "%p\n",  pGPIO->OutReg);
25
  printf( "%p\n\n", GPIO.OutReg);  
26
    return 0;
27
}

: Bearbeitet durch Moderator
von Jan H. (janiiix3)


Lesenswert?

Und so würde ich dann dem "Register" (0x60004004) einen Wert zuweisen?
1
  GPIO.OutReg    = (unsigned long*)(GPIO_BASE + 4); // Reg.Addr: 0x60004004
2
  *GPIO.OutReg   = 0x80; << zugewiesener Wert

von Jan H. (janiiix3)


Lesenswert?

Ich versuche es mal wie folgt zu lösen..
1
//Mapping Address
2
//GPIO 0x6000_4000 to 0x6000_4FFF 
3
4
#define GPIO_BASE 0x60004000ul
5
6
typedef struct
7
{
8
  struct 
9
  {
10
    // mapped @GPIO_BASE
11
    uint32_t Bt_Select_Reg;
12
    uint32_t Out_Reg;
13
    uint32_t Out_W1TS_Reg;
14
    uint32_t Out_W1TC_Reg;
15
    uint32_t Enable_Reg;
16
    uint32_t Enable_W1TS_Reg;
17
    uint32_t Enable_W1TC_Reg;
18
    uint32_t Strap_Reg;
19
    uint32_t In_Reg;
20
    uint32_t Status_Reg;
21
    uint32_t Status_W1TS_Reg;
22
    uint32_t Status_W1TC_Reg;
23
    uint32_t PCPU_Int_Reg;
24
    uint32_t Status_Next_Reg;    
25
  }Cnfg_Reg;
26
  
27
28
29
  //mapped @GPIO_BASE + 0x554
30
  struct 
31
  {
32
    uint32_t Func0_Out_Sel_Cnfg;
33
    uint32_t Func1_Out_Sel_Cnfg;
34
    uint32_t Func2_Out_Sel_Cnfg;
35
    uint32_t Func3_Out_Sel_Cnfg;
36
    uint32_t Func4_Out_Sel_Cnfg;
37
    uint32_t Func5_Out_Sel_Cnfg;
38
    uint32_t Func6_Out_Sel_Cnfg;
39
    uint32_t Func7_Out_Sel_Cnfg;
40
    uint32_t Func8_Out_Sel_Cnfg;
41
  }Out_Func_Reg;
42
43
}GPIO_struct;
44
 
45
volatile GPIO_struct*    const sGPIO = (GPIO_struct*)GPIO_BASE;

Gibt es eine Möglichkeit die Struktur "Out_Func_Reg" an die Adresse 
"GPIO_BASE + 0x554" zu mappen also quasi das direkt schon bei der 
definition des Structs? Wie würde man sowas lösen?

von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> Gibt es eine Möglichkeit die Struktur "Out_Func_Reg" an die Adresse
> "GPIO_BASE + 0x554" zu mappen also quasi das direkt schon bei der
> definition des Structs?
Nein.

> Wie würde man sowas lösen?
mit einem Datenfeld entsprechender Größe dazwischen. unsigned char 
_dummy[0x554-4*14]; oder so

Oder 2 Structs. Wenn es zwei Bereiche sind, warum dann nicht auch zwei 
Strukturen?

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jan H. schrieb:
> Wie würde man sowas lösen?
Nachdem du zum Glück nicht der erste bist, der solche 
Registerdefinitionen in seinem C-Programmen verwednen will, wäre es 
sicher sinnvoll, mal zu schauen, wie seit vielen Jahren andere derartige 
Probleme lösen.

Bruno V. schrieb:
> mit einem Datenfeld entsprechender Größe dazwischen. unsigned char
> _dummy[0x554-4*14]; oder so
Ich finde diese verschachtelten Structs ungünstig, weil die jeder 
Compiler so interpretieren kann, wie es ihm gerade gefällt. Oder 
andersrum: es ist im Grunde Zufall wie diese Structs im Speicher 
abgelegt werden. Es kann sein, dass das mit den Dummy-Bytes zwischen den 
structs heute funktioniert. Es kann aber auch sein, dass es nach einem 
Compilerupdate oder nach dem Einschalten von Optimierungen nicht mehr 
funktioniert.

: Bearbeitet durch Moderator
von Jan H. (janiiix3)


Lesenswert?

Kann ich denn irgendwie sehen was der Compiler für Adressen als 
"Endergbnis" liefert?

Ich bin gerade dabei bei einem Board eine LED zum Leben zu erwecken aber 
ich habe das Gefühl das irgendwas mit den Adressen nicht ganz so 
hinhaut..
1
//Mapping Address
2
//GPIO 0x6000_4000 to 0x6000_4FFF 
3
4
#define GPIO_BASE 0x60004000ul
5
6
typedef struct 
7
{
8
    // mapped @GPIO_BASE
9
    uint32_t Bt_Select_Reg;
10
    uint32_t Out_Reg;
11
    uint32_t Out_W1TS_Reg;
12
    uint32_t Out_W1TC_Reg;
13
    uint32_t Enable_Reg;
14
    uint32_t Enable_W1TS_Reg;
15
    uint32_t Enable_W1TC_Reg;
16
    uint32_t Strap_Reg;
17
    uint32_t In_Reg;
18
    uint32_t Status_Reg;
19
    uint32_t Status_W1TS_Reg;
20
    uint32_t Status_W1TC_Reg;
21
    uint32_t PCPU_Int_Reg;
22
    uint32_t Status_Next_Reg;    
23
24
}GPIO_Cnfg_Reg_t;
25
 
26
typedef struct
27
{
28
  //mapped @GPIO_BASE + 0x554
29
    uint32_t Func0_Out_Sel_Cnfg;
30
    uint32_t Func1_Out_Sel_Cnfg;
31
    uint32_t Func2_Out_Sel_Cnfg;
32
    uint32_t Func3_Out_Sel_Cnfg;
33
    uint32_t Func4_Out_Sel_Cnfg;
34
    uint32_t Func5_Out_Sel_Cnfg;
35
    uint32_t Func6_Out_Sel_Cnfg;
36
    uint32_t Func7_Out_Sel_Cnfg;
37
    uint32_t Func8_Out_Sel_Cnfg;
38
39
}GPIO_Func_Sel_Reg_t;
40
41
volatile GPIO_Func_Sel_Reg_t*     const sGPIO_Func_Sel_Reg  = (GPIO_Func_Sel_Reg_t*)(GPIO_BASE + 0x0554);
42
volatile GPIO_Cnfg_Reg_t*         const sGPIO_Cnfg_Reg      = (GPIO_Cnfg_Reg_t*)GPIO_BASE;
43
44
void setup() 
45
{
46
  sGPIO_Func_Sel_Reg->Func8_Out_Sel_Cnfg |= 0x80;
47
  sGPIO_Cnfg_Reg->Enable_Reg |= 1<<8;
48
49
  // gpio8 auf low setzen
50
  sGPIO_Cnfg_Reg->Out_Reg &= ~(1<<8);
51
}

von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> sGPIO_Func_Sel_Reg->Func8_Out_Sel_Cnfg |= 0x80;
>   sGPIO_Cnfg_Reg->Enable_Reg |= 1<<8;
>   // gpio8 auf low setzen
>   sGPIO_Cnfg_Reg->Out_Reg &= ~(1<<8);

sollen die alle das gleiche bit beschreiben? Mal 0x80, mal 0x100.

von Bruno V. (bruno_v)


Lesenswert?

Lothar M. schrieb:
> Ich finde diese verschachtelten Structs ungünstig,
Ich auch. Deshalb war meine Antwort auch lakonisch

Bruno V. schrieb:
> Jan H. schrieb:
>> Gibt es eine Möglichkeit ...
> Nein.

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Bruno V. schrieb:
> Jan H. schrieb:
>> sGPIO_Func_Sel_Reg->Func8_Out_Sel_Cnfg |= 0x80;
>>   sGPIO_Cnfg_Reg->Enable_Reg |= 1<<8;
>>   // gpio8 auf low setzen
>>   sGPIO_Cnfg_Reg->Out_Reg &= ~(1<<8);
>
> sollen die alle das gleiche bit beschreiben? Mal 0x80, mal 0x100.

Nein natürlich nicht.
Habe mich hier an dieses Beispiel gehalten (mit der Struktur und den 
ganzen Registern)

https://blog.feabhas.com/2019/01/peripheral-register-access-using-c-structs-part-1/

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Jan H. schrieb:
> Kann ich denn irgendwie sehen was der Compiler für Adressen als
> "Endergbnis" liefert?

Fuer die Mutigen mit gcc/binutils toolchain, die sich durch den 
Assembler lesen wollen:
entweder gcc -save-temps ....
und dann im .s File stoebern
oder objdump -D bla.o | less
und dann g'schwind lesen.

Gruss
WK

Beitrag #7752281 wurde vom Autor gelöscht.
von Jan H. (janiiix3)


Lesenswert?

Jan H. schrieb:
> Bruno V. schrieb:
>> Jan H. schrieb:
>>> sGPIO_Func_Sel_Reg->Func8_Out_Sel_Cnfg |= 0x80;
>>>   sGPIO_Cnfg_Reg->Enable_Reg |= 1<<8;
>>>   // gpio8 auf low setzen
>>>   sGPIO_Cnfg_Reg->Out_Reg &= ~(1<<8);
>>
>> sollen die alle das gleiche bit beschreiben? Mal 0x80, mal 0x100.
>
> Nein natürlich nicht.
> Habe mich hier an dieses Beispiel gehalten (mit der Struktur und den
> ganzen Registern)
>
> 
https://blog.feabhas.com/2019/01/peripheral-register-access-using-c-structs-part-1/

Ich verstehe deine Antwort nicht.
Die Register haben jeweils unterschiedliche Werte.

von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> Ich verstehe deine Antwort nicht.
> Die Register haben jeweils unterschiedliche Werte.

Ich kenne Dein System nicht. Du schreibst nur

Jan H. schrieb:
> Ich bin gerade dabei bei einem Board eine LED zum Leben zu erwecken aber
> ich habe das Gefühl das irgendwas mit den Adressen nicht ganz so
> hinhaut..

aber nicht, was nicht funktioniert oder (noch wichtiger) was 
funktioniert.

Wenn Du Angst hast, dass die Adresse falsch sein könnte ... dann schreib 
sie direkt hin, probier es aus und wenn es funktioniert, dann nimm den 
Struct-Weg und probier erneut. Das brauchst Du ja nur ein Mal 
exemplarisch zu machen.

Oder Du hast einen Debugger am laufen, dann setze den Brakepoint davor, 
schaue Dir das Register an und geh im Singlestep drüber.

Ohne Debugger scheitert es an den unmöglichsten Dingen, angefangen von 
Konfigurationsbits und der Quarz läuft gar nicht los. Oder das Signal 
toggelt so schnell, dass Du es nicht siehst. Hast Du Debugger, LSA oder 
Oszi?

Die Struct-Adressfrage ist ja genügend beleuchtet. Das ist aber Kür wenn 
der direkte Weg funktioniert.

von Jan H. (janiiix3)


Angehängte Dateien:

Lesenswert?

Bruno V. schrieb:
> Ohne Debugger scheitert es an den unmöglichsten Dingen, angefangen von
> Konfigurationsbits und der Quarz läuft gar nicht los. Oder das Signal
> toggelt so schnell, dass Du es nicht siehst. Hast Du Debugger, LSA oder
> Oszi?

Für ATMEL habe ich alles da, ja. Oszi. habe ich auch. Bringt mir jetzt 
gerade nur nichts.

Es muss an meiner Initalisierung von dem Struct liegen bzw. der 
Adressen.
Leider kann ich mir nicht erklären was genau das Problem ist.

Wenn ich es mit fester Adresse teste, funktioniert es:
1
volatile uint32_t* Func_Reg = (uint32_t*)(GPIO_BASE + 0x0574);
2
volatile uint32_t* Enable_Reg = (uint32_t*)(GPIO_BASE + 0x0020);
3
volatile uint32_t* Out_Reg = (uint32_t*)(GPIO_BASE + 0x0004);
4
5
void setup() {
6
7
 *Func_Reg = 0x80; // set special index
8
 *Enable_Reg = 1<<8; // enable gpio 8
9
 *Out_Reg = 0; // set all @low level
10
11
}

Mache ich es mit der Struktur haut es aufeinmal nicht mehr hin..
Ich vermute mal das der Compiler? Da andere Adressen interpretiert als 
ich mir das vorstelle?!

Hier ist mein Zeiger auf die Basis Adresse des GPIO´s
1
volatile GPIO_Cnfg_Reg_t*         const sGPIO_Cnfg_Reg      = (GPIO_Cnfg_Reg_t*)GPIO_BASE;

So wie ich mir das jetzt vorstelle. Meine Struktur mit den ganzen 
Konfigurations Register:
1
typedef struct 
2
{
3
    // mapped @GPIO_BASE
4
    uint32_t Bt_Select_Reg;
5
    uint32_t Out_Reg;
6
    uint32_t Out_W1TS_Reg;
7
    uint32_t Out_W1TC_Reg;
8
    uint32_t Enable_Reg;
9
    uint32_t Enable_W1TS_Reg;
10
    uint32_t Enable_W1TC_Reg;
11
    uint32_t Strap_Reg;
12
    uint32_t In_Reg;
13
    uint32_t Status_Reg;
14
    uint32_t Status_W1TS_Reg;
15
    uint32_t Status_W1TC_Reg;
16
    uint32_t PCPU_Int_Reg;
17
    uint32_t Status_Next_Reg;    
18
19
}GPIO_Cnfg_Reg_t;
Halte ich mit der Maus jetzt auf den ersten Member so erhalte ich 
folgendes Bild (siehe Anhang).
Dort zeigt mir die Arduino IDE an das "Bt_Select_Reg" einen Offset von 0 
hat. Gehe ich mit der Maus jetzt auf den letzten Eintrag 
"Status_Next_Reg" sagt die IDE mir das es sich um einen Offset von 52 
Bytes handelt.
Das würde dann dem letzten Register (Adresse) vom Datenblatt entsprechen 
oder verstehe ich das falsch?

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Jan H. schrieb:
> Dort zeigt mir die Arduino IDE an das "Bt_Select_Reg" einen Offset von 0
> hat. Gehe ich mit der Maus jetzt auf den letzten Eintrag
> "Status_Next_Reg" sagt die IDE mir das es sich um einen Offset von 52
> Bytes handelt.

Das sind die Offsets der Strukturelemente relativ zum Beginn der 
Struktur.
Mit Deinen Registeradressen hat das, wenn überhaupt, nur rein zufällig 
zu tun.

Wozu soll bei Deinem Projekt eigentlich die Struktur dienen? Welches 
Problem meinst Du, damit lösen zu können?

Ich sehe hier nur eine zusätzliche Abstraktionsebene, die Du einziehst, 
und die Dich heftigst herausfordert.

von Jan H. (janiiix3)


Lesenswert?

Harald K. schrieb:
> Wozu soll bei Deinem Projekt eigentlich die Struktur dienen? Welches
> Problem meinst Du, damit lösen zu können?


Damit möchte ich einfach vermeiden das ich die ganzen Register Adressen 
von Hand in den Code tippen muss..
So wie hier..
1
volatile uint32_t* Func_Reg = (uint32_t*)(GPIO_BASE + 0x0574);

von Harald K. (kirnbichler)


Lesenswert?

Jan H. schrieb:
> Damit möchte ich einfach vermeiden das ich die ganzen Register Adressen
> von Hand in den Code tippen muss..

Musst Du ja auch nicht. Du machst an einer Stelle ein #define, und das 
war's dann.

So arbeitet der Rest der Welt, und so sind i.d.R. auch die von 
Controllerherstellern gelieferten Headerdateien für ihre µCs aufgebaut.

Und man kann so ein Macro auch so definieren, daß man es direkt als 
"Pseudovariable" verwenden kann.
1
#define Func_Reg (*((uint32_t *) (GPIO_BASE + 0x0574)))

Das dereferenziert die I/O-Adresse, so daß Du im Code einfach so etwas 
schreiben kannst:

1
Func_Reg = 4;
2
3
int x = Func_Reg;

Eine Struktur ist da nicht erforderlich, sondern höchstens ein besseres 
Namensschema (ich würde das Ding GPIO_FUNC_REG nennen, auch um zu 
zeigen, daß das ein Macro/#define ist).

von Jan H. (janiiix3)


Lesenswert?

Danke für den Vorschlag.
Meinen Ansatz hatte ich mal in einen Online C Compiler gehauen und da 
hatte es komischerweise gepasst mit den Adressen von daher bin ich 
ausgegangen das es so funktionieren kann. Auf einigen Webseiten wird es 
für diverse Atmels auch so praktiziert.

von Harald K. (kirnbichler)


Lesenswert?

Jan H. schrieb:
> Auf einigen Webseiten wird es
> für diverse Atmels auch so praktiziert.

Auf Webseiten wird viel praktiziert.

Es gibt im Zusammenhang mit HAL-Abstraktionen verschiedener 
µC-Hersteller  die Tendenz, statt einzelner Registerdefinitionen (wie 
oben) Pseudostrukturen zu definieren, die ein Peripherieobjekt abbilden.


Wenn bei einem Peripheriebaustein die zusammengehörenden Register 
monoton aufsteigend angeordnet sind, dann kann das so aussehen, wie hier 
diskutiert:
Beitrag "Ich verstehe diesen Code nicht"

Die Registerstruktur im ESP32, um den es Dir ja letztendlich geht, ist 
für diese Herangehensweise nicht geeignet, da die Register recht 
willkürliche Adressen haben (wie Dein Offset von 0x574 ja schon zeigt).

Da müsste man die Struktur mit sehr, sehr vielen "Füllregistern" für 
unbenutzte Registeradressen definieren):
1
typedef struct
2
{
3
    __IO uint32_t GPIO_BT_SELECT_REG; // 0x0000
4
    __IO uint32_t GPIO_OUT_REG;       // 0x0004
5
    __IO uint32_t GPIO_OUT_W1TS_REG;  // 0x0008
6
    __IO uint32_t GPIO_OUT_W1TC_REG;  // 0x000C
7
    __IO uint32_t UNUSED_BLOCK_0[4];
8
    __IO uint32_t GPIO_ENABLE_REG;    // 0x0020
9
   ... etc.
10
    __IO uint32_t GPIO_PIN21_REG      // 0x00C8
11
    __IO uint32_t UNUSED_BLOCK_x[35];
12
    __IO uint32_t GPIO_FUNC0_IN_SEL_CFG_REG // 0x0154
13
   ... etc.
14
15
16
} ESP32_GPIO_T;

(bezogen auf das Datenblatt, Seite 171, Tabelle mit GPIO-Registern)

Du siehst, daß das sehr unhandlich und vor allem fehlerträchtig wird.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Oder andersrum: es ist im Grunde Zufall wie diese Structs
> im Speicher abgelegt werden.

Natürlich ist es kein Zufall, sondern es wird im [E]ABI (Applicaton 
Binary Interface) festgelegt / beschrieben.

Nicht alle Features von C/C++ werden im Sprachstandard festgelegt; es 
gibt auch Dinge, die sind Implementation Defined.  Einige davon werden 
von der C/C++ Implmemtation festgelegt, für GCC etwa:

https://gcc.gnu.org/onlinedocs/gcc/C-Implementation.html
https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Implementation.html

andere werden im ABI festgelegt, welches idR Hardware- und OS-abhängig 
ist.  Und auch Eigenschaften des Binärformats (z.B. welche RELOCs im ELF 
wie festgelegt sind) sind Sache des ABIs.

von Jan H. (janiiix3)


Lesenswert?

Wieso funktioniert das mit den Registern dann bei z.B einem Xmega von 
ATMEL bzw. Microchip?
1
/* I/O Ports */
2
typedef struct PORT_struct
3
{
4
    register8_t DIR;  /* I/O Port Data Direction */
5
    register8_t DIRSET;  /* I/O Port Data Direction Set */
6
    register8_t DIRCLR;  /* I/O Port Data Direction Clear */
7
    register8_t DIRTGL;  /* I/O Port Data Direction Toggle */
8
    register8_t OUT;  /* I/O Port Output */
9
    register8_t OUTSET;  /* I/O Port Output Set */
10
    register8_t OUTCLR;  /* I/O Port Output Clear */
11
    register8_t OUTTGL;  /* I/O Port Output Toggle */
12
    register8_t IN;  /* I/O port Input */
13
    register8_t INTCTRL;  /* Interrupt Control Register */
14
    register8_t INT0MASK;  /* Port Interrupt 0 Mask */
15
    register8_t INT1MASK;  /* Port Interrupt 1 Mask */
16
    register8_t INTFLAGS;  /* Interrupt Flag Register */
17
    register8_t reserved_0x0D;
18
    register8_t REMAP;  /* I/O Port Pin Remap Register */
19
    register8_t reserved_0x0F;
20
    register8_t PIN0CTRL;  /* Pin 0 Control Register */
21
    register8_t PIN1CTRL;  /* Pin 1 Control Register */
22
    register8_t PIN2CTRL;  /* Pin 2 Control Register */
23
    register8_t PIN3CTRL;  /* Pin 3 Control Register */
24
    register8_t PIN4CTRL;  /* Pin 4 Control Register */
25
    register8_t PIN5CTRL;  /* Pin 5 Control Register */
26
    register8_t PIN6CTRL;  /* Pin 6 Control Register */
27
    register8_t PIN7CTRL;  /* Pin 7 Control Register */
28
} PORT_t;
29
.
30
.
31
.
32
.
33
.
34
#define PORTA    (*(PORT_t *) 0x0600)  /* Port A */
35
#define PORTB    (*(PORT_t *) 0x0620)  /* Port B */
36
#define PORTC    (*(PORT_t *) 0x0640)  /* Port C */
37
#define PORTD    (*(PORT_t *) 0x0660)  /* Port D */
38
#define PORTE    (*(PORT_t *) 0x0680)  /* Port E */
39
#define PORTF    (*(PORT_t *) 0x06A0)  /* Port F */
40
#define PORTH    (*(PORT_t *) 0x06E0)  /* Port H */
41
#define PORTJ    (*(PORT_t *) 0x0700)  /* Port J */
42
#define PORTK    (*(PORT_t *) 0x0720)  /* Port K */
43
#define PORTQ    (*(PORT_t *) 0x07C0)  /* Port Q */
44
#define PORTR    (*(PORT_t *) 0x07E0)  /* Port R */

von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> volatile uint32_t* Enable_Reg = (uint32_t*)(GPIO_BASE + 0x0020);

das ist ein offset von 32, also das 9te Element.

Jan H. schrieb:
> typedef struct
> {
>     // mapped @GPIO_BASE
>     uint32_t Bt_Select_Reg;
>     uint32_t Out_Reg;
>     uint32_t Out_W1TS_Reg;
>     uint32_t Out_W1TC_Reg;
>     uint32_t Enable_Reg;
>     uint32_t Enable_W1TS_Reg;
>     uint32_t Enable_W1TC_Reg;

Hier ist es das 5te Element.

Ein Problem ist, dass Du zwar mehrere Beispiele hast, aber niemals dabei 
sagst, ob es das gleiche oder was verschiedenes zeigt.

von Jan H. (janiiix3)


Lesenswert?

Ich werde

Bruno V. schrieb:
> Jan H. schrieb:
>> volatile uint32_t* Enable_Reg = (uint32_t*)(GPIO_BASE + 0x0020);
>
> das ist ein offset von 32, also das 9te Element.
>
> Jan H. schrieb:
>> typedef struct
>> {
>>     // mapped @GPIO_BASE
>>     uint32_t Bt_Select_Reg;
>>     uint32_t Out_Reg;
>>     uint32_t Out_W1TS_Reg;
>>     uint32_t Out_W1TC_Reg;
>>     uint32_t Enable_Reg;
>>     uint32_t Enable_W1TS_Reg;
>>     uint32_t Enable_W1TC_Reg;
>
> Hier ist es das 5te Element.
>
> Ein Problem ist, dass Du zwar mehrere Beispiele hast, aber niemals dabei
> sagst, ob es das gleiche oder was verschiedenes zeigt.

Ich werde leider nicht wirklich aus deinen Kommentaren schlau.
Was genau meinst Du? Es ist immer noch das gleiche Projekt was ich vor 
mir habe.

von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> Damit möchte ich einfach vermeiden das ich die ganzen Register Adressen
> von Hand in den Code tippen muss..
> So wie hier..volatile uint32_t* Func_Reg = (uint32_t*)(GPIO_BASE +
> 0x0574);

Ist zwar O.T., aber wenn die Adressen sowieso ungeordnet bzw. 
fragmentiert vorliegen, ist die Tipparbeit selbst mit Editoren aus dem 
letzten Jahrhundert in Summe geringer.

Du schreibst Die Namen und Adressen runter
1
Func_Reg      0x0574
2
Dings_Reg     0x0588
3
...

und machst den Pre- ("volatile uint32t* ")  Mid- (" = 
(uint32_t*)(GPIO_BASE +
 ") und Postfix (");") in wenigen Sekunden für alle.

Der Vorteil: Du siehst später sofort den echten Wert und musst ihn nicht 
abzählen. Hat zwar auch ein paar Nachteile, aber vorerst reicht das.

von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> Ich werde leider nicht wirklich aus deinen Kommentaren schlau.
> Was genau meinst Du? Es ist immer noch das gleiche Projekt was ich vor
> mir habe.

Du sagst

Jan H. schrieb:
> wenn ich es mit fester Adresse teste, funktioniert es:
> ...
> volatile uint32_t* Enable_Reg = (uint32_t*)(GPIO_BASE + 0x0020);

hast bei der "Struktur-Version" Enable_Reg aber an einer ganz anderen 
Adresse (GPIO_BASE + 20 statt +0x20)

Mag sein, dass das ein Tippfehler ist (0x20 statt 20), mag sein, dass 
Füll-Elemente fehlen.

von Jan H. (janiiix3)


Lesenswert?

Okay.
Also laut Datenblatt sind die Konfigurations Register 32 Bit lang, also 
4 Bytes und fangen bei der Adresse "0x0000".
Das "Enable" - Register liegt bei Adresse "0x0020".
Wenn ich jetzt eine Struktur habe wo alle Elemente 32 Bit lang sind also 
4 Bytes und das "Enable" Register an der richtigen Stelle steht, müsste 
es doch von der Adresse passen?!

von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> Wenn ich jetzt eine Struktur habe wo alle Elemente 32 Bit lang sind also
> 4 Bytes und das "Enable" Register an der richtigen Stelle steht, müsste
> es doch von der Adresse passen?!

Ja. Dann sähe dein Struct aber so aus. Mit, wie

Harald K. schrieb:
> sehr vielen "Füllregistern" für unbenutzte Registeradressen [..]
> typedef struct
>
1
> {
2
>     __IO uint32_t GPIO_BT_SELECT_REG; // 0x0000
3
>     __IO uint32_t GPIO_OUT_REG;       // 0x0004
4
>     __IO uint32_t GPIO_OUT_W1TS_REG;  // 0x0008
5
>     __IO uint32_t GPIO_OUT_W1TC_REG;  // 0x000C
6
>     __IO uint32_t UNUSED_BLOCK_0[4];
7
>     __IO uint32_t GPIO_ENABLE_REG;    // 0x0020
8
>    ... etc.

In Deinem Beispiel fehlen die Füllregister, hier UNUSED_BLOCK_0

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Oder, falls Du wirklich den Unterschied zwischen 0x20 und 20 nicht 
kennst: Das wievielte Element soll es ein? Das 5te oder das 9te?

von Jan H. (janiiix3)


Lesenswert?

Bruno V. schrieb:
> Oder, falls Du wirklich den Unterschied zwischen 0x20 und 20 nicht
> kennst: Das wievielte Element soll es ein? Das 5te oder das 9te?

Du hast natürlich vollkommen Recht! Ich habe den Wald voller Bäume nicht 
gesehen.
Habe gar nicht bemerkt das nach 0x000C nicht 0x0010 kommt^^

von Klaus (feelfree)


Lesenswert?

Jan H. schrieb:
> Habe gar nicht bemerkt das nach 0x000C nicht 0x0010 kommt

Erneuter Vertipper oder immer noch Verständnisschwierigkeiten?

von Jan H. (janiiix3)


Lesenswert?

Klaus schrieb:
> Jan H. schrieb:
>> Habe gar nicht bemerkt das nach 0x000C nicht 0x0010 kommt
>
> Erneuter Vertipper oder immer noch Verständnisschwierigkeiten?

Habe gerade im Datenblatt gesehen das es ab dem "GPIO_OUT_W1TC_REG" 
nicht mehr der Reihe nach geht. Das war mir vorher nicht bewusst.

von Harald (hasanus)


Lesenswert?

Bruno V. schrieb:
>> volatile uint32_t* Enable_Reg = (uint32_t*)(GPIO_BASE + 0x0020

Das ist wohl ein Tippfehler. Es sollte + 0x10 lauten, dann bist du 
wirklich am fünften Element.

von Harald (hasanus)


Lesenswert?

Jan H. schrieb:
> Mache ich es mit der Struktur haut es aufeinmal nicht mehr hin..

Ich war genauso blind, hab den Fehler einfach übersehen.
In solchen Fällen sind assertions überaus hilfreich. Dass und auch wo im 
code der Unterschied herkommt hat mir

    assert(&sGPIO_Cnfg_Reg->Out_Reg == Out_Reg);
    assert(&sGPIO_Cnfg_Reg->Enable_Reg == Enable_Reg);
    assert(&sGPIO_Func_Sel_Reg->Func8_Out_Sel_Cnfg == Func_Reg);

gezeigt.

von Jan H. (janiiix3)


Lesenswert?

Harald schrieb:
> Jan H. schrieb:
>> Mache ich es mit der Struktur haut es aufeinmal nicht mehr hin..
>
> Ich war genauso blind, hab den Fehler einfach übersehen.
> In solchen Fällen sind assertions überaus hilfreich. Dass und auch wo im
> code der Unterschied herkommt hat mir
>
>     assert(&sGPIO_Cnfg_Reg->Out_Reg == Out_Reg);
>     assert(&sGPIO_Cnfg_Reg->Enable_Reg == Enable_Reg);
>     assert(&sGPIO_Func_Sel_Reg->Func8_Out_Sel_Cnfg == Func_Reg);
>
> gezeigt.

Mit assert habe ich noch nicht gearbeitet. Muss ich mir mal anschauen.
Danke für deine Hilfe Harald! Einer von wenigen der nicht nur "ironisch" 
helfen will.

So funktioniert es ;)
1
//Mapping Address
2
//GPIO 0x6000_4000 to 0x6000_4FFF 
3
4
#define GPIO_BASE 0x60004000
5
6
/* Configuration Registers @DB Page 171 */
7
#define GPIO_BT_SELECT_REG      (*(volatile uint32_t*)(GPIO_BASE))
8
#define GPIO_OUT_REG            (*(volatile uint32_t*)(GPIO_BASE + 0x0004))
9
#define GPIO_OUT_W1TS_REG       (*(volatile uint32_t*)(GPIO_BASE + 0x0008))
10
#define GPIO_OUT_W1TC_REG       (*(volatile uint32_t*)(GPIO_BASE + 0x000C)) 
11
#define GPIO_ENABLE_REG         (*(volatile uint32_t*)(GPIO_BASE + 0x0020))
12
#define GPIO_ENABLE_W1TS_REG    (*(volatile uint32_t*)(GPIO_BASE + 0x0024))
13
#define GPIO_ENABLE_W1TC_REG    (*(volatile uint32_t*)(GPIO_BASE + 0x0028))
14
#define GPIO_STRAP_REG          (*(volatile uint32_t*)(GPIO_BASE + 0x0038))
15
#define GPIO_IN_REG             (*(volatile uint32_t*)(GPIO_BASE + 0x003C))
16
#define GPIO_STATUS_REG         (*(volatile uint32_t*)(GPIO_BASE + 0x0044))
17
#define GPIO_STATUS_W1TS_REG    (*(volatile uint32_t*)(GPIO_BASE + 0x0048))
18
#define GPIO_STATUS_W1TC_REG    (*(volatile uint32_t*)(GPIO_BASE + 0x004C)) 
19
#define GPIO_PCPU_INT_REG       (*(volatile uint32_t*)(GPIO_BASE + 0x005C))
20
#define GPIO_STATUS_NEXT_REG    (*(volatile uint32_t*)(GPIO_BASE + 0x014C))
21
22
#define GPIO_FUNC0_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0554)) 
23
#define GPIO_FUNC1_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0558)) 
24
#define GPIO_FUNC2_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x055C)) 
25
#define GPIO_FUNC3_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0560)) 
26
#define GPIO_FUNC4_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0564)) 
27
#define GPIO_FUNC5_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0568)) 
28
#define GPIO_FUNC6_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x056C)) 
29
#define GPIO_FUNC7_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0570)) 
30
#define GPIO_FUNC8_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0574)) 
31
#define GPIO_FUNC9_OUT_SEL_CFG_REG      (*(volatile uint32_t*)(GPIO_BASE + 0x0578)) 
32
#define GPIO_FUNC10_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x057C))  
33
#define GPIO_FUNC11_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x0580)) 
34
#define GPIO_FUNC12_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x0584)) 
35
#define GPIO_FUNC13_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x0588)) 
36
#define GPIO_FUNC14_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x058C)) 
37
#define GPIO_FUNC15_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x0590)) 
38
#define GPIO_FUNC16_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x0594)) 
39
#define GPIO_FUNC17_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x0598)) 
40
#define GPIO_FUNC18_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x059C)) 
41
#define GPIO_FUNC19_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x05A0)) 
42
#define GPIO_FUNC20_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x05A4)) 
43
#define GPIO_FUNC21_OUT_SEL_CFG_REG     (*(volatile uint32_t*)(GPIO_BASE + 0x05A8))

von Harald (hasanus)


Lesenswert?

Johann L. schrieb:
> Lothar M. schrieb:
>> Oder andersrum: es ist im Grunde Zufall wie diese Structs
>> im Speicher abgelegt werden.
>
> Natürlich ist es kein Zufall, sondern es wird im [E]ABI (Applicaton
> Binary Interface) festgelegt / beschrieben.

Zudem gibt es auch in C static_assert, sizeof() und Direktiven zum 
Ausrichten und Packen von structs. Damit kann man sicherstellen dass 
(oder zumindest prüfen ob) Daten z.B. auf einem avr genauso abgebildet 
werden wie auf x64.

von Jan H. (janiiix3)


Angehängte Dateien:

Lesenswert?

ich habe da nochmal eine Frage:
1
#define SPI_LL_GET_HW(ID) (((ID)==1) ? &GPSPI2 : NULL)

SPI_LL_GET_HW bekommt die Adresse von "GPSPI2", richtig?
1
/**
2
 * Initialize SPI peripheral (master).
3
 *
4
 * @param hw Beginning address of the peripheral registers.
5
 */
6
static inline void spi_ll_master_init(spi_dev_t *hw)
7
{
8
    //Reset timing
9
    hw->user1.cs_setup_time = 0;
10
    hw->user1.cs_hold_time = 0;
11
12
    //use all 64 bytes of the buffer
13
    hw->user.usr_miso_highpart = 0;
14
    hw->user.usr_mosi_highpart = 0;
15
16
    //Disable unneeded ints
17
    hw->slave.val = 0;
18
    hw->user.val = 0;
19
20
    hw->clk_gate.mst_clk_active = 1;
21
    hw->clk_gate.mst_clk_sel = 1;
22
23
    hw->dma_conf.val = 0;
24
    hw->dma_conf.tx_seg_trans_clr_en = 1;
25
    hw->dma_conf.rx_seg_trans_clr_en = 1;
26
    hw->dma_conf.dma_seg_trans_en = 0;
27
}
Hier wird mit (vermutlich) schon den Hardware Registern gearbeitet, sehe 
ich das richtig? Zumindest sehe ich sonst keine Funktion mehr die die 
Register behandelt.

Wo bekommt "*hw" bzw. "GPSPI2" die Basisadresse von der SPI Einheit her?
Die muss doch irgendwo zugewiesen werden?

Die "spi_struct.h" die sich im Anfang befindet definiert "GPSPI2" stellt 
jedoch noch keine "Basis Adresse" da, lediglich evtl. die Offsets für 
die einzelnen Register?

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Jan H. schrieb:
> SPI_LL_GET_HW bekommt die Adresse von "GPSPI2", richtig?

Das Makro wählt mittels ID zwischen verschiedenen Instanzen aus. Hier 
allerdings ist nur GPSPI2 für ID==1 vorhanden.

Solche Wrapper sollte man nur verwenden, wenn man sie braucht, also wenn 
der Code  mehrere Instanzen händelt.

Jan H. schrieb:
> Wo bekommt "*hw" bzw. "GPSPI2" die Basisadresse von der SPI Einheit her?

hw bekommt die Adresse beim Aufruf.
GPSPI2 beim Beispiel vorher ist bei seiner Definition an die richtige 
Adresse gelegt worden.

Es gibt eine ähnliche Zeile wie

> extern spi_dev_t GPSPI2;

Die heißt dann z.B.

> spi_dev_t GPSPI2 @0x10000;

wobei das @xxx, also die Adresszuweisung, compilerspezifisch ist.

von Jan H. (janiiix3)


Lesenswert?

Bruno V. schrieb:
>> spi_dev_t GPSPI2 @0x10000;
>
> wobei das @xxx, also die Adresszuweisung, compilerspezifisch ist.

Das wird dann aber ganz normal in einer .c oder .h gemacht oder im 
Linker?

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.