[C] Eigene Register Definition schreiben

OP Persönliche Seite #6647048
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.
Moderator Persönliche Seite #6647059
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.
OP Persönliche Seite #6647123
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.
#6647147
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.
Moderator Persönliche Seite #6647154
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.
Gast #6647170
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;
OP Persönliche Seite #6647171
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 
:)
Gast #6647175
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.
#6647188
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.
#6647225
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
}
#6647262
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren