C und Register

Gast #6847591
Lesenswert?

Hallo Forengemeinde,

ich suche nach einer Lösung wie ich das am besten mache. Ich wollte mir 
eine Registertabelle anlegen für einen IC. Das ganze mache ich mit 
#define um dann in den Registern Bits setzen oder Löschen können.
Das #define im Code nachhinen nicht mehr verändert werden kann, habe ich 
auch schon rausgefunden, aber wie machen das den die Profis?

Wenn ich in der Lib vom Atmel AVR reinschaue steht dort auch
#define DDRB
#define DRB1

Trotzdem ist sowas:
DDRB |= (1<<DRB1)
problemlos machbar.

Mein Code siet wie folgt aus:
1
//Mode Register
2
#define W5500_MR   (uint16_t)0x0000
3
#define W5500_MR_RST  (uint8_t)0x07
4

5
W5500_MR |= (1 << W5500_MR_RST);

Wie geht das von statten?

MfG
Matt
#6847603
Lesenswert?

Matt schrieb:

> Trotzdem ist sowas:
> DDRB |= (1<<DRB1)
> problemlos machbar.

Das ist machbar weil da nicht

> #define DDRB

steht sondern z.B.
1
#define DDRB  (uint_8*)(0x1234)

Defines sind TEXTERSETZUNGEN. Sonst nichts. Bitte merken.
Und wenn der Text durch eine Variable oder einen Pointer ersetzt wird, 
kann ich damit tun was ich sonst auch ohne das define damit hätte tun 
können.
Gast #6847606
Lesenswert?

Du bist längst nicht der erste mit dieser Idee.

Leider hat Programmiersprache C keine Befehle für einzelne Bits. 
Deswegen lässt es sich damit nicht ordentlich umsetzen, ganz egal was 
für ausgefuchste Strukturen du dir ausdenkst. Irgendwo ist immer ein 
Haken.

Deswegen nutze die Zeit lieber für andere Sachen die Spaß machen oder 
Geld einbringen.
#6847608
Lesenswert?

Matt schrieb:
> Mein Code siet wie folgt aus://Mode Register
> #define W5500_MR   (uint16_t)0x0000
> #define W5500_MR_RST  (uint8_t)0x07
> W5500_MR |= (1 << W5500_MR_RST);

Der Präprozessor ist ein Textersetzer.

Der macht dadraus folgendes:
> 0x0000 |= (1 << 0x07)

Und das geht nicht, du kannst in einer Null nicht einfach ein paar Bits 
setzen. Probiere mal folgendes:

> #define W5500_MR *(uint16_t*)0x0000

Dann geht das, weil deine Null dann eine Adresse ist und keine 
einfache Zahl. Aber in dem Fall ist das ein Nullpointer (weil Adresse 
0x0000), darauf schreiben tut ziemlich sicher nicht das, was du willst.

Das ganze System funktioniert sowieso nur, wenn deine Register 
memory-mapped sind, also direkt per Adresse ansprechbar. Wenn da ein 
Bussystem (I2C, SPI, CAN, ...) dazwischen liegt, geht das alles nicht.
Gast #6847612
Lesenswert?

In den Headers von Atmel steht sicher mehr als nur
#define DDRB
#define DRB1

Der Präprozessor mit seinem #define macht nur Textersetzung: mach das 
doch mal im Kopf und schau was aus deinem Beispiel wird:
1
//Mode Register
2

3
#define W5500_MR   (uint16_t)0x0000
4

5
#define W5500_MR_RST  (uint8_t)0x07
6

7
W5500_MR |= (1 << W5500_MR_RST);

wird zu
1
(uint16_t)0x0000 |= (1 << (uint8_t)0x07);

Was soll das also sein? Du weißt einer Zahl einen Wert zu, das kann 
natürlich nicht gehen.

Was du in den Atmel-Headern gesehen hast ist ähnlich zu dem hier:
1
#define W5500_MR (*(uint16_t*)0x0000)

das wird dann bei einem Schreibzugriff zu:
1
(*(uint16_t*)0x0000) = 42;

Du hast also Links (LValue) eine Adresse und keine Zahl. Der Code würde 
die Zahl 42 an die Adresse 0x0000 schreiben. So kann man auf 
AVR-Register zugreifen weil die direkt im Adressraum des AVRs liegen 
(memory mapped). Das geht also bei Chips die über SPI, I2C oder sowas 
angebunden sind natürlich nicht. Einzige Ausnahme wären z.B. parallele 
LCDs die man z.B. beim Mega128 memory mapped anschließen könnte.

Ich würde das so machen:
1
#define MYCHIP_REG_A 0x00
2
#define MYCHIP_REG_B 0x01
3

4
#define MYCHIP_CFG_BIT0 0
5
#define MYCHIP_CFG_BIT1 1
6

7
mychip_write_reg(MYCHIP_REG_A, 1<<MYCHIP_CFG_BIT0);

in der Funktion mychip_write_reg ist dann das Schreiben auf den Chip.

Lg
Bauteiltöter
Gast #6847614
Lesenswert?

Gerade bei Anfängern führen #define immer wieder zu Problemen. Deshalb 
sollten Anfänger niemals #define verwenden. (Ausnahme Include-Guards)

Das meiste lässt sich wunderbar über richtige Funktionen, enums und 
static const int  etc erschlagen.
Der Compiler optimiert sowieso noch.

Aber der Anfänger bekommt weniger Probleme mit Code, den er nicht 
versteht.
Gast #6847616
Lesenswert?

Matt schrieb:
> Trotzdem ist sowas:
> DDRB |= (1<<DRB1)
> problemlos machbar.

Natürlich.

Da wird ja nicht geändert, WO sich das Datenrichtungsregister für Port B 
im Adressraum des Chips befindet, sondern sein Inhalt bekommt ein Bit 
mehr gesetzt.

Matt schrieb:
> #define W5500_MR   (uint16_t)0x0000
> #define W5500_MR_RST  (uint8_t)0x07
> W5500_MR |= (1 << W5500_MR_RST);

Hier wird ja nicht W5500_MR statt als 0x0000 nun zu 0x0080 umdefiniert, 
sondern IN Speicherplatz 0000 das Bit 0x0080 hinzugesetzt.
Gast #6847635
Lesenswert?

Matt schrieb:
> aber was macht (uint16_t*)?

Das sagt dem Compiler, dass der Zeicher auf ein unsigned 16 bit integer 
zeigt. So weiß der Compiler wie groß die Speicherzelle bzw. das Register 
ist.

Aber wie mehrfach gesagt wurde geht das so nicht. Du kannst auf dem 
Mikrocontroller die Register eines anderen externen Chips nicht einfach 
so ansprechen, wie internes RAM/Register. Jeder Zugriff muss eine SPI 
Kommunikation auslösen. Das macht der Compiler nicht von alleine.
#6847636
Lesenswert?

Matt schrieb:
> Ich habe jetzt einen Zeiger mit dem Namen W5500_MR und der Zeigt auf die
> Adresse 0x0000, aber was macht (uint16_t*)?

(uint16_t*)0x0000 ist ein Cast und bedeutet, dass 0x0000 ein Zeiger auf 
die Adresse 0x0000 ist (und nicht der Wert 0x0000).

*(uint16_t*)0x0000 sagt, dass dieser Zeiger dereferenziert werden soll, 
also der Wert an der Adresse 0x0000 (und nicht die Adresse 0x0000).

Und das uint16_t in der Mitte besagt, dass der Wert an der Adresse 
0x0000 ein 16 Bit Unsigned Integer ist, gibt also sowohl den Datentypen 
des Wertes und damit auch den Adresstypen (Typ des Zeigers) an.
Gast #6847650
Lesenswert?

Super also das mit dem Register habe ich verstanden und auch warum das 
über SPI nicht funktionieren kann. In der Adresse von dem 
Mikrocontroller halt irgendwas drinstehen kann und ich dann mit dem 
Inhalt von der Adresse über SPI in irgendein Register schreiben würde.

Wie wäre den eure Lösung um das am besten zu Ordnen?
Ist der Vorschlag von Bauteiletöter eine gute Variante oder ist das mal 
wieder so eine glaubensfrage?

MfG
Matt
Beitrag #6847651 wurde von einem Moderator gelöscht.
Gast #6847658
Lesenswert?

Matt schrieb:
> Wie wäre den eure Lösung um das am besten zu Ordnen?

Schreibe dir Funktionen für die SPI Kommunikation mit sprechenden Namen. 
Meinetwegen eine Funktion für jedes Register und dann nochmal eine 
generische, die davon aufgerufen wird (mit der Adresse des Registers).

w5500_write_common_mode(uint16_t value);
ruft auf: w5500_write(0x0000, uint16_t value);

uint16_t w5500_read_common_mode();
ruft auf: w5500_read(0x0000);

Oder besser: Nutze die Bibliotheken der Chip-Herstellers. Ich kann mir 
nicht vorstellen, dass man dieses Rad neu erfinden muss.
Gast #6847659
Lesenswert?

Erich schrieb:
> Stefan ⛄ F. schrieb im Beitrag #6847651:
>> Linux Distributionen

Beitrag #6847651 wurde vom Autor gelöscht.

Ok, er hat's gemerkt.

>einfach schnell irgendwas loszublubbern.

ist nicht immer sinnvoll.

Etwas Zurückhaltung tut Not.

Erst denken.
Dann schreiben.

Gruss
Gast #6847668
Lesenswert?

Jup also das Rad muss ich auch nicht neu erfinden aber um die vorgänge 
wirklich zu verstehen ist es ganz praktisch mal eins selbstzubauen. Rad 
ist dafür nicht das beste Beispiel.
1
void W5500_Daten_Senden(W5500 *_w5500, uint16_t _adresse, uint8_t _controll, uint8_t *_p_daten, uint8_t _size_daten)
2
{
3
  W5500_Adresse_Umwandeln(_w5500, _adresse);
4

5
  uint8_t buffer[_size_daten + 3];
6

7
  buffer[0] = _w5500->sW5500_adresse.Adresse_High;
8
  buffer[1] = _w5500->sW5500_adresse.Adresse_Low;
9
  buffer[2] = _controll;
10

11
  for(uint16_t i = 3; i < _size_daten; i++)
12
  {
13
    buffer[i] = *_p_daten++;
14
  }
15

16
  HAL_SPI_Transmit(&hspi1, buffer, _size_daten + 3, SPI_Time_Out);
17
}

Eine funktion dafür, habe ich mir auch schon gecshrieben.

MfG
Matt
#6847677
Lesenswert?

Vorsicht bei Register mit Spezialfunktionen.

Z.B.
- UART Sende- und Empfangsregister sind meist auf der gleichen  Adresse
- manche Registerbits dienen zum Quittieren bzw. Rückstellen und können 
nur geschrieben werden. Eine '1' löst diesen Vorgang aus. Wird das 
komplette Register, zwecks Manipulation einzelner Bits gelesen und 
zurückgeschrieben, dann kann es sein, dass diese Spezialfunktionen 
unbeabsichtigt mit ausgelöst werden.

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