Forum: Mikrocontroller und Digitale Elektronik [C] Eigene Register Definition schreiben


von Paul G. (paul_g210) Benutzerseite


Lesenswert?

Hallo

Ich bin noch relativ neu im programmieren und hätte eine Frage.
Ich habe einen IC der per SPI (und STM32) durch verschiedene Register 
angesprochen werden kann. Die Register sind alle 32bit groß und können 
zum Teil beschrieben oder gelesen werden. Bis dato habe ich die Register 
immer direkt geschrieben:
1
                          reg  
2
                     R/W  addr <-------Daten-------->  
3
                      |   |  ||                      |  
4
Bsp.: 0x81000001    0b10000001000000000000000000000001  
5
6
uint32_t sendRegister = 0x81000001
Mit der Zeit wird das aber ziemlich kryptisch und ich würde viel lieber 
etwas in dieser Art schreiben:
1
uint32_t sendRegister = IC->CONFIG | IC_WRITE | DATA...

Was ich bis jetzt hinbekommen habe ist dieses:
1
#define IC            (uint32_t)(0x00000000)
2
#define IC_WRITE      (uint32_t)(1<<31)
3
#define IC_CONFIG     (uint32_t)(1<<24)
4
#define DATA          (uint32_t)(1<<0)
5
...
6
uint32_t sendRegister = (IC | IC_WRITE | IC_CONFIG | DATA);  // 0x81000001

Aber wie funktioniert das dass ich in meiner IDE (Eclipse) von dem "IC" 
als Child sozusagen "IC_CONFIG" auswählen kann, mit diesem 
"Rechtspfeil"? Ich weiß leider nicht die korrekte Bezeichnung.

Aber es soll dann so aussehen:
1
                           ?
2
                           |
3
uint32_t sendRegister = IC->IC_CONFIG | IC_WRITE | DATA;

Und statt IC_CONFIG sollen dann natürlich auch die ganzen anderen 
Register Adressen des IC ansprechbar sein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Paul G. schrieb:
> mit diesem "Rechtspfeil"?

Der "Rechtspfeil" ist nur eine Abkürzung.
1
foo -> bar

steht für
1
(*foo) . bar

Damit ist auch klar, dass "foo" ein Zeiger sein muss, der dereferenziert 
wird. Derartige Zugriffe auf irgendwelche Register funktionieren also 
nur dann, wenn diese memory mapped sind, d.h. die Hardware der CPU 
bildet sie auf eine Speicheradresse ab.

Für irgendwelche Geräte, die nicht direkt auf einen Speicherbus 
abgebildet werden (I²C, SPI, …) kann man diese Schreibweise folglich 
nicht benutzen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

p.s.: was du eher anstrebst, wären Namensräume (name spaces). Sowas 
gibt's aber in C nicht, C++ hat das. Dort könntest du das dann schreiben 
als:
1
uint32_t sendRegister = IC::IC_CONFIG | IC::IC_WRITE | IC::DATA;

von Paul G. (paul_g210) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> funktionieren also
> nur dann, wenn diese memory mapped sind

Okay, verstehe. Genau da ist das mir auch begegnet... In den tiefen der 
STM32 Firmware Header...

Jörg W. schrieb:
> C++ hat das.

:) Okay, das ist aber bei meinem STM32 Projekt nicht drinn. Ich will 
schon bei C bleiben.

Dann schreibe ich mir halt ein paar "defines" in den Header mit den 
häufigsten Register Adressen die ich dann im Code noch "bitmathen" kann.

von Simon (Gast)


Lesenswert?

Würde sowas gehen?
1
static const struct bla
2
{
3
  uint8_t x = 0x01,
4
  uint8_t y = 0x02,
5
  uint8_t z = 0x04,
6
  };
7
8
9
uint8_t reg = bla.x | bla.y | bla.z;

von Adam P. (adamap)


Lesenswert?

Paul G. schrieb:
> uint32_t sendRegister = (IC | IC_WRITE | IC_CONFIG | DATA);  //0x81000001

Paul G. schrieb:
> Dann schreibe ich mir halt ein paar "defines" in den Header mit den
> häufigsten Register Adressen

Warum erstellst du nicht Funktionen, die das erledigen, was sie sollen.
IC_WRITE ist ja ein Schreib-Befehl und DATA ist ja je Register 
unterschiedlich.

Dann könntest du es doch mittels Funktionen abstrahieren um nicht jedes 
mal alles zusammenzusetzen.

und wenn du das schreiben und lesen erstmal von den Daten getrennt hast, 
dann kannst du dir schon eine struct auf deine "Register" erzeugen.
1
struct my_ic_reg_data
2
{
3
   uint32_t reg_1;
4
   uint32_t reg_2;
5
   // ...
6
} my_ic_reg_data;
7
8
struct my_ic_reg_data ic_data;
9
10
spi_read_all_register(&ic_data);
11
12
if (ic_data.reg_1 == irgendwas)
13
   do_something();

Edit:
Ich verstehe deine Herangehensweise nicht so wirklich.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon schrieb:
> Würde sowas gehen?
>
1
> static const struct bla
2
> {
3
>   uint8_t x = 0x01,
4
>   uint8_t y = 0x02,
5
>   uint8_t z = 0x04,
6
>   };
7
> 
8
> 
9
> uint8_t reg = bla.x | bla.y | bla.z;
10
>

Jein.
1
static const struct
2
{
3
  uint32_t x, y, z
4
} bla = {
5
  .x = 0x01,
6
  .y = 0x02,
7
  .z = 0x04,
8
};
9
10
// ...
11
uint32_t reg = bla.x | bla.y | bla.z;

Die Definition des struct-Typs musst du schon von der Initialisierung 
der (unveränderlichen) Variablen trennen.

von Simon (Gast)


Lesenswert?

Jörg W. schrieb:
> Simon schrieb:
>> Würde sowas gehen?
>>> static const struct bla
>> {
>>   uint8_t x = 0x01,
>>   uint8_t y = 0x02,
>>   uint8_t z = 0x04,
>>   };
>>
>>
>> uint8_t reg = bla.x | bla.y | bla.z;
>>
>
> Jein.
> static const struct
> {
>   uint32_t x, y, z
> } bla = {
>   .x = 0x01,
>   .y = 0x02,
>   .z = 0x04,
> };
> // ...
> uint32_t reg = bla.x | bla.y | bla.z;
>
> Die Definition des struct-Typs musst du schon von der Initialisierung
> der (unveränderlichen) Variablen trennen.

Stimmt, das hatte ich mir etwas zu einfach vorgestellt:
1
typedef struct
2
{
3
  const uint8_t x;
4
  const uint8_t y;
5
  const uint8_t z;
6
}blub_t;
7
static const blub_t blub = {
8
  .x = 0x01,
9
  .y = 0x02,
10
  .z = 0x04,
11
};
12
13
uint8_t reg = blub.x | blub.y | blub.z;

von Paul G. (paul_g210) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Ich verstehe deine Herangehensweise nicht so wirklich.

Ich manchmal auch nicht. Ich wollte es mir halt einfacher machen indem 
ich nur IC. tippe und die IDE schlägt mir dann ein Dropdown aus 
vordefinierten Registern vor, so wie es auch bei der STM32 Hardware 
geht... RCC->CR ...

Dass das ganze aber nur bei bei Hardware Adressen geht wusste ich nicht 
:)

von Simon (Gast)


Lesenswert?

Jörg W. schrieb:
> Jein.
> static const struct
> {
>   uint32_t x, y, z
> } bla = {
>   .x = 0x01,
>   .y = 0x02,
>   .z = 0x04,
> };
> // ...
> uint32_t reg = bla.x | bla.y | bla.z;

Peinlich; du hast es ja schon richtig schön hingeschrieben.

von Simon (Gast)


Lesenswert?

Paul G. schrieb:
> Ich manchmal auch nicht. Ich wollte es mir halt einfacher machen indem
> ich nur IC. tippe und die IDE schlägt mir dann ein Dropdown aus
> vordefinierten Registern vor, so wie es auch bei der STM32 Hardware
> geht... RCC->CR ...
>
> Dass das ganze aber nur bei bei Hardware Adressen geht wusste ich nicht
> :)

Ich glaube soetwas ähliches macht das Microchip Studio auch; da fußt das 
Ganze tatsächlich auf einer struct-Struktur.

von Adam P. (adamap)


Lesenswert?

Paul G. schrieb:
> Dass das ganze aber nur bei bei Hardware Adressen geht wusste ich nicht
> :)

Jetz weiß ich was du wirklich meinst, jedoch würde ich das eher mit 
Funktionen kapseln und nicht so umständlich mit den einzelnen Bits immer 
zusammensetzen.

Bei der Hardware ist der struct Pointer auf eine Hardware-Adresse 
gelegt,
und hat z.B. mehrere uint32_t Elemente, hier SPI-Abschnitt bei mir:
1
/** \brief Spi hardware registers */
2
typedef struct {
3
  __O  uint32_t SPI_CR;        /**< \brief (Spi Offset: 0x00) Control Register */
4
  __IO uint32_t SPI_MR;        /**< \brief (Spi Offset: 0x04) Mode Register */
5
  __I  uint32_t SPI_RDR;       /**< \brief (Spi Offset: 0x08) Receive Data Register */
6
  __O  uint32_t SPI_TDR;       /**< \brief (Spi Offset: 0x0C) Transmit Data Register */
7
//...
8
} Spi;

Das Problem bei dir ist, dass du nur eine 32-Bit Variable hast.
Somit müsstest du deine struct mit Bit-Variablen anlegen, da du nicht 
auf mehrere 32-Bit Speicherstellen zugreifen willst, sondern nur auf 
eine 32-Bit Variable die du Bit-manipulieren willst.

von Adam P. (adamap)


Lesenswert?

Sowas würde gehen, aber ob es das besser macht?
1
typedef struct
2
{
3
  uint32_t rw : 1;
4
  uint32_t reserved : 7;
5
  uint32_t reg_addr : 4;
6
  uint32_t data : 20;
7
} foo_t;
8
9
void foo()
10
{
11
  foo_t my_reg;
12
13
  my_reg.rw = 1;
14
  my_reg.reg_addr = 15;
15
  my_reg.data = 123;
16
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Geht dann auch noch besser:
1
void foo()
2
3
{
4
  foo_t my_reg = { .rw = 1, .reg_addr = 15, .data = 123};
5
  // ...
6
}

Aber es ist davon halt erstmal nur im Code da und noch nicht bei der 
Hardware angekommen.

von Simon (Gast)


Lesenswert?

So hat es übrigens nxp für einen I2C Sensor gelöst:

https://mcuxpresso.nxp.com/api_doc/comp/101/mma865x_8h_source.html

von Adam P. (adamap)


Lesenswert?

Simon schrieb:
> So hat es übrigens nxp für einen I2C Sensor gelöst:
>
> https://mcuxpresso.nxp.com/api_doc/comp/101/mma865x_8h_source.html

Ja die haben ja die internen Register abgebildet,
das macht ja auch Sinn.

Aber der TO möchte ja nicht die internen Register abbilden, sondern 
sogar schon den Zugriff, lesen oder schreiben, welches Register usw.

Das find ich halt ein wenig "too much".

Deshalb sagte ich ja auch, Register abbilden ist ja OK, den Zugriff 
sollte man jedoch durch eindeutige Funktionen lösen.

von Paul G. (paul_g210) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Deshalb sagte ich ja auch, Register abbilden ist ja OK, den Zugriff
> sollte man jedoch durch eindeutige Funktionen lösen.

Ja, so mach ich das jetzt. Danke euch.

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.