Hallo, ich möchte in einem Register eines Mikrocontrollers auf einzelne Bits des Registers zugreifen und diese setzen. Ich möchte dabei aber auf unschönes Shiften und Maskieren von Bits verzichten. Ich stelle mir eher z.B. so etwas vor: Register_Name.Bit4 =0xFF; Wie kann man so etwas am besten realisieren (am liebsten mit einem kleinen Beispiel)? Super, wenn Ihr mir helfen könntet. Dnake und schönen Gruß Thomas
> Register_Name.Bit4 =0xFF; Sicher, dass Du weißt, was Du willst??? Seit wann kann ein einzelnes Bit 0xFF sein??? Abgesehen davon: Schau mal hier ins AVR-GCC-Tutorial. Da steht eine nette Möglichkeit drin, wie man so was lösen kann (Stichwort struct-Variable)
Ohne maskieren oder shiften wird das schwierig, ein Byte ist nunmal die kleineste adressierbare Einheit. Und kleiner als ein char geht eben nicht. Es gibt in Assembler (aber ich glaube du sprichst eher von C) aber einige die Instruktionen sbi und cbi. Ein Blick ins Instruction set hilft weiter. Setzen in C ist doch nicht so schwer, oder? Register |= (1<<Bitnr) Und löschen ist auch nicht so schwer: Register &= ~(1<<Bitnr) und über das Maskieren musst du dir nichtmal mehr Gedanken machen. Gruß Andreas
ist immer vom Chip und Assembler abhängig. Bein 8051 kann man zB. Set Px.x schreiben. Dann wird bit X im Port x gesetzt. mit CPL Px.x wird das Bit negiert. Mann kann SetB Bit Adr. setzt ein Bit an Adresse nehmen. Man kann einem Bit auch einen "Namen" geben und dann setzten oder zurücksetzen. zB. LED EQU P3.1 setb LED ; LED an clr LED ; LED aus Beides wirkt auf das Pin Port 3.1. Anstelle von Ports können auch RAM adressen dienen.
> Register_Name.Bit4 =0xFF;
Das deutet eigentlich eindeutig auf C hin (auch wenn die Zuweisung
unsinnig ist).
Hatte grad gesehen, dass die Frage sich nur auf Register bezog. Da geht
das mit ner struct natürlich nicht. Das haut nur bei Variablen hin. In
einer Hinsicht hat AndreasB aber recht: So schlimm ist das mit dem
shiften gar nicht. Und man kann den Code hinterher noch lesen. Und es
ist C-konform, was wichtig sein kann für die Portierbarkeit des Codes.
OK, so wie ich das jetzt verstanden habe, finktioniert das wie folgt: ich definiere ein struct struct { unsigned char bit_1:1; unsigned char bit_2:1; unsigned char bit_3:1 ... } x; anschließend weise ich x der physikalischen Adresse meines Registers zu, z.B. #define Reg *((volatile unsigned char *)0x8002300) Dann bin ich in der Lage mit x.bit_1 = 1 das erste Bit des Registers zu setzen. Ist das richtig ? Wenn ja, wäre das so eine Lösung, die ich suche. Klar funktioniert das auch mit Shiften, aber diese Lösung (vorausgesetz sie funktioniert...) finde ich schon eleganter und ist auch irgendwie übersichtlicher. Wie sieht es mit dem Speicherbedarf aus, entstehen dadurch irgendwelche Nachteile ?
Nein, das funktioniert nicht. Schon mal deshalb nicht, weil du einer Variablen die der Compiler anlegt, nicht einfach eine andere Adresse zuweisen kannst.
Wie wärs denn einfach mit einem Makro (Setbit,Clearbit) was du dir selber machst, und was deine von dir als "unschönes Shiften und Maskieren von Bits" bezeichnete Vorgehensweise weitgehend verdrängt.
Naja, mit einem Pojnter auf eine derartige Struktur lässt sich das schon hinbekommen: struct RegisterBitfeld { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; //etc. }; struct RegisterBitfeld *pRegister; pRegister = (struct RegisterBitfeld *) ((void *) 0xDEADFACE); pRegister->bit0 = 1; pRegister->bit1 = 1; pRegister->bit2 = 0; Wie effizient der vom Compiler erzeugte Code in diesem Falle ist, das gilt es allerdings zu untersuchen. Da Bitfelder an sich vom Typ int sind, wäre obendrein zu untersuchen, ob der gegebene Compiler "struct RegisterBitfeld" so interpretiert, daß sizeof (struct RegisterBitfeld) == 1 ist. Ist das nicht der Fall, so sollte man weitere Versuche einstellen. Oder sich auf überaus erfreuliche Seiteneffekte einstellen ... Tandaradei, singt der Debugger
Schade, dass das nicht so einfach funktioniert. Aber wie darf man das Beispiel in AVR-GCC-Tutorial verstehen ? @Simon Küppers: Kannst du mir kurz ein Beispiel für ein solches Makro geben (sorry, bin Anfänger...) Gruß Thomas
@Rufus: Vielleicht wäre es sogar sinnvoll, eine Union statt struct zu nehmen. Man könnte dann auch die 8 bit in einem Rutsch schreiben, wenn man möchte.
ich mach es so .DSEG ;Reserviert jeweils 1 Byte im SRAM um Register zu sparen Steuerbyte1: .byte 1 lds temp, Steuerbyte1 ;Lade temp mit SRAM) sbr temp, 0b00001000 ;Setze Bit 3 sts Steuerbyte1, temp ;Sichere temp im SRAM dauert zwar ein paar Takte länger aber ich spare mir dadurch einige Register, früher haben die mir immer nicht gereicht, weil z.b. alleine für die 8 ADC Kanäle die meisten schon belegt hatte. Mit der Equ-Möglichkeit kann es manchmal zu Fehlern beim Assemblieren kommen wenn das in der Includedatei anderst beschrieben ist. Das auslesen mach ich dann auch durch eine UND-Maskierung
Mit Macros geht das beim AVR-GCC z.B. so:
1 | #define RESET(x) _XRS(x)
|
2 | #define SET(x) _XS(x)
|
3 | |
4 | #define _XRS(x,y) x &= ~(1<<y)
|
5 | #define _XS(x,y) x |= 1<<y
|
6 | |
7 | #define LED_I2C PORTD,5
|
8 | |
9 | #define LED_ERR PORTC,3
|
10 | |
11 | // Code:
|
12 | |
13 | if( Error ) |
14 | RESET( LED_ERR ); |
15 | else
|
16 | SET( LED_ERR ); |
Die Verschachtelung ist nötig, damit die Aufteilung in 2 Argumente keinen Fehler erzeugt (wird nur bei der ersten Macroexpansion geprüft). Peter
@Peter wenn ich _XRS(LED_ERR) aufrufe, dann ist doch mein y undefiniert!
Der springende Punkt ist, dass du eben nicht das Makro _XRS direkt benutzt. Ambesten vergist du gleich wieder, dass dieses Makro existiert. Als Programmierer benutzt du immer nur die Makros SET und RESET
Ui... kann mir bitte jemand Rufus's Zeile pRegister = (struct RegisterBitfeld *) ((void *) 0xDEADFACE); erläutern?
Wo liegt das Problem? 0xDEADFACE ist zunächst mal einfach nur eine Zahl. Allerdings in hexadezimaler Schreibweise. Der Rest ist einfach nur casting. (void*) 0xDEADFACE weist den Compiler an, er möge doch bitte so tun, als ob die Zahl eine Adresse in den Speicher wäre. Das castet also den int um in einen Pointer. Noch ist keine Aussage darüber gefallen was man sich denn im Speicher unter der Adresse 0xDEADFACE vorstellen soll. Liegt dort ein int, oder ein double oder ... niemand weis es. Bisher ist nur von einem Pointer die Rede. Aber das aendert sich jetzt: (struct RegisterBitFeld*) ( ... ) Das sagt jetzt dem Compiler, dass er mal davon ausgehen soll, dass er an der angegebenen Speicheradresse eine struct RegisterBitfeld vorfinden wird. Hier wird also eine konkrete Aussgae darüber getroffen wie die Bytes die an der angegebenen Adresse zu finden sind zu interpretieren sind.
Wird denn auch an Adresse 0xDeadFace (wohl nen AVR mit ordentlich RAM;-) ein struct zu finden sein oder zeigt z.B. pRegister->bit0 wildlings auf Speicher? ;-) So z.B.: struct RegisterBitfeld { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; //etc. }; struct RegisterBitfeld *pRegister; struct RegisterBitfeld MyRegister; //neu pRegister = &MyRegister; // pRegister->bit0 = 1; pRegister->bit1 = 1; pRegister->bit2 = 0; wär ich als nicht so routinierter nicht über ein absturzträchtiges Beispiel (was mir ja nicht in den Sinn kam) ins Grübeln gekommen... PS. Wäre Rufus Version gekürzt zu pRegister = (struct RegisterBitfeld *) 0xDEADFACE; nicht genausogut? Testcompilierung läuft jedenfalls anstandslos durch.
Nachtrag zum PS: Wohin als auf Speicher sollte auch ein Zeiger zeigen können?
> pRegister = (struct RegisterBitfeld *) 0xDEADFACE; > nicht genausogut? Testcompilierung läuft jedenfalls anstandslos > durch. Ja, klar, ginge auch. Ein cast ist die Anweisung an den Compiler: "Vergiss jetzt mal dein Typsystem. Ich als Programmierer sag die jetzt wo's langgeht und du hältst dich da raus. Wenn ich ich dir sage, dass das so ist, dann ist das so. Aus, ende, basta!" -> Ein cast ist eine Waffe! > Wird denn auch an Adresse 0xDeadFace :-) DEADFACE benutzt man in Beispielen wenn man eine Hex-Zahl braucht, bei der der Wert an sich keine Rolle spielt, sondern man ein Prinzip illustrieren möchte. Ein anderes Beispiel wäre zb. foo. foo wird als Funktionsname genommen, wenn man einfach nur eine Funktion braucht, wobei es auf die Funktion an sich nicht ankommt. Bsp. Funktionspointer. Die eigentliche Funktion ist völlig irrelevant. Alles was man braucht ist irgendeine Funktion: typedef void (*Fnct)( void ); void foo() { } Fnct FunctionPointer = foo; 0xDEADFACE hat natürlich den Charme ein Wort zu ergeben und trotzdem eine gültige 32 Bit Zahl zu sein :-)
@Der springende Punkt ist, dass du eben nicht das Makro _XRS direkt benutzt. Ambesten vergist du gleich wieder, dass dieses Makro existiert. Als Programmierer benutzt du immer nur die Makros SET und RESET! Ne - ich mag nicht vergessen das es dieses Makro gibt, denn es wird vom Makro Reset verwendet - und eben da komme ich nicht weiter. Wenn ich mitteles Reset XRS aufrufe, dann fehlt mir da irgendwo der zweite Parameter.
> Ne - ich mag nicht vergessen das es dieses Makro gibt Das sollst du aber. Alle Makros die mit '_' beginnen, gehen den Programmierer nichts an :-) '_' ist reserviert für den Compiler workspace. > und eben da komme ich nicht weiter Dann musst du mal nachlesen, nach welchen Regeln der Präprozessor Makro-Expansionen macht. Vor allen Dingen nicht vergessen: Makro Expansionen sind reine Textersetzungen! Sonst steckt da nichts dahinter: SET( LED_ERR ) expandiert durch das Makro #define SET(x) _XS(x) zu _XS( LED_ERR ) danach werden die Argumente selbst expandiert und LED_ERR ersetzt _XS( PORTC,3) somit ist das SET Makro (inkl.Argumente) selbst komplett ersetzt und es wird versucht ob es weitere Makro-Ersetzungen mit diesem (Zwischen-)Ergebnis gibt. Gibt es, _XS ist selbst ein Makro #define _XS(x,y) x |= 1<<y wird es angewendet, so ergibt sich PORTC |= 1 << 3
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.