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:
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.
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.
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.
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:
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.
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.
PittyJ schrieb:> (Ausnahme Include-Guards)
Ich habe angefangen, durchgängig "#pragma once" in Headern zu benutzen.
Allerdings habe ich nur mit einem C++-Compiler zu tun, auch wenn ich
"eher C" programmiere.
Bauteiltöter schrieb:> Ich würde das so machen:> [c]> #define MYCHIP_REG_A 0x00> #define MYCHIP_REG_B 0x01>
Bei mir wäre das
enum
{
MYCHIP_REG_A = 0x00,
MYCHIP_REG_B = 0x01,
}
Vielen Dank svenska,
Jetzt geht es. Ich muss es nur noch verstehten, also
Ich habe jetzt einen Zeiger mit dem Namen W5500_MR und der Zeigt auf die
Adresse 0x0000, aber was macht (uint16_t*)?
MfG
Matt
Ok wärend ich den Text geschireben habe sind schon soviele neu
Nachrichten gekommen das der Text auch wieder als veraltet angesehen
werden kann.
MfG
Matt
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.
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.
Stefan ⛄ F. schrieb:> Du bist längst nicht der erste mit dieser Idee.
Und du hast die Frage des TE gar nicht verstanden. Weil du so damit
beschäftigt bist, einfach schnell irgendwas loszublubbern.
Cyblord -. schrieb:> Und du hast die Frage des TE gar nicht verstanden. Weil du so damit> beschäftigt bist, einfach schnell irgendwas loszublubbern.
Danke, dass du meine Augen geöffnet hast. Die Welt braucht mehr solcher
Kommentare von dir.
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
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.
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
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.
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.
Namen, die mit _ beginnen, sollte man übrigens nicht unnötig für eigenen
Kram verwenden.
Die werden nämlich für viele interne Geschichten in Libs und Headern
genutzt, um Kollisionen mit den Namen des Anwendungsprogramms zu
vermeiden.
Stefan ⛄ F. schrieb:> Deswegen lässt es sich damit nicht ordentlich umsetzen, ganz egal was> für ausgefuchste Strukturen du dir ausdenkst. Irgendwo ist immer ein> Haken.
Der Haken ist Plattformabhängigkeit. Ein Makel, der bei einer
Plattform akzeptabel ist.