Hallo, ist es möglich, mit dem mspgcc ein einzelnes Bit zu setzen? Z.B. möchte ich definieren, dass OUT auf Port 2.1 liegt, sodass ich den Portpin mit OUT=1 setzen und OUT=0 löschen kann. M.E. nach geht das im avrgcc.
Der AVR kennt eine Bit-Adressierung, die kennt der MSP430 nicht. Deine Operation lässt sich dennoch in einer Anweisung verpacken: P2OUT &= ~0x02; // aus P2OUT |= 0x02; // an Statt der für manche nicht selbsterklärenden Schreibweise 0x02 kann man auch die gerade bei AVR-Programmierern sehr beliebte Bitshift-Schreibweise verwenden: P2OUT &= ~(1 << 2); // aus P2OUT |= 1 << 2; // an Allerdings sollte man sich nicht dazu verleiten lassen, diese Notation mit den Bitkonstanten aus den mit msp430-C-Compilern gelieferten Headerdateien zu verwenden: im Gegensatz zur AVR-Konvention, bei der eine Bitkonstante die Nummer des Bits enthält, enthält sie bei der msp430-Konvention den Wert des Bits.
Naja, wirklich ,,beliebt'' sind diese 1<<-Konstrukte auch bei den AVR-Programmierern eher nicht... Sie sind eben nur in häufiger Benutzung dort, weil Atmel die Bits als Bitnummern statt -masken vorgegeben hat, was im Wesentlichen ein Tribut an die Mitnutzung der Headerdateien durch Assemblerprogrammierer (innerhalb von SBI/CBI/SBIC/SBIS-Befehlen) sein dürfte.
>P2OUT &= ~0x02; // aus >P2OUT |= 0x02; // an Diese "Krücken" benutze ich gerade. Sind zwar nicht übersichtlich, aber wenigstens verwendet der Compiler dafür die Bit-Set- und -Clear-Befehle.
>>P2OUT &= ~0x02; // aus >>P2OUT |= 0x02; // an > > Diese "Krücken" benutze ich gerade. Sind zwar nicht übersichtlich, aber > wenigstens verwendet der Compiler dafür die Bit-Set-und -Clear-Befehle. Wieso "Krücken"? Das ist die normale Schreibweise, um das in C zu machen. Wenn dir das lieber ist, kannst du ja auch sowas machen: #define SET(port, val) (port |= val) #define CLR(port, val) (port &= ~val) CLR(P2OUT, 0x02); SET(P2OUT, 0x02);
>Wieso "Krücken"? Das ist die normale Schreibweise, um das in C zu >machen. Das mag für ANSI-C zu sein, für C für µC ist es mehr als unpraktisch. Beispielsweise sei hier der HITECH-Compiler für PIC genannt, der sogar den Datentyp Bit kennt. z.B.
1 | static bit KEY @ ((unsigned)&PORTA*8+4); |
oder der Keil-Compiler
1 | #define DATA P1_1
|
2 | #define SCK P1_0
|
3 | |
4 | SCK = 1; |
5 | Data = 0; |
oder eben auch der besagte avrgcc. Bei vielen µC-Compilern ist eben auch der C-Dialekt recht assemblernah. Wünschenswert wäre eine Schreibweise, die genau das sichbar macht, was es machen soll, nämlich den Portpin setzen oder löschen. Vielleicht ist das ja durch ein makro machbar.
> (...) der sogar den Datentyp Bit kennt. Das mag bei µCs sinnvoll sein, die Bitadressierung unterstützen, wie MCS-51, AVRs oder PICs. Der msp430 hat so etwas nicht, und daher wäre ein Bit-Datentyp in C kaum sinnvoll zu implementieren.
>Der msp430 hat so etwas nicht, und daher wäre ein Bit-Datentyp in C kaum >sinnvoll zu implementieren. Das erklärt dann einiges. Schade eigentlich.
> static bit KEY @ ((unsigned)&PORTA*8+4); Äußerst skurrile Syntax. > oder eben auch der besagte avrgcc. Der kennt keine Bittypen. > Bei vielen µC-Compilern ist eben auch der C-Dialekt recht assemblernah. Ich bin eigentlich eher dagegen, irgendwelche speziellen Dialekte zu verwenden, sondern mag es lieber standardmäßig. Dann muß man nicht für jeden Compiler wieder eine andere Syntax lernen, und falls das Programm mit mehr als einem Compiler übersetzbar sein muß, muß man nicht massenweise Fallunterscheidungen mit #define einbauen. > Wünschenswert wäre eine Schreibweise, die genau das sichbar macht, was > es machen soll, nämlich den Portpin setzen oder löschen. Vielleicht ist > das ja durch ein makro machbar. In C++ kann man sich eine Klasse dafür schreiben. Das geht z.B. mit avr-g++ ganz prima.
Der Bitweise AND bzs. OR Befehl braucht doch auch nur einen Takt, wo liegt also das Problem? Um ein bestimmtes Bit zu setzen oder rückzusetzen tun´s doch ganz prima Präprozessor-Makros.
Hallo, mit C kannst du auch Bitfelder definieren, und kannst den einzelnen Bits auch eigene Namen geben, hier z.B. "onoff". Dann definierst du einen Pointer auf die Adresse die das Byte enthält das du ändern willst, und kannst dann auf das Byte zugreifen. Wenn du anstatt eines Pointer ein Makro verwendest spart du den Speicherplatz für den Pointer ein. Dann kannst du Sachen schreiben wie PA.onoff = 1; typedef struct { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; unsigned char onoff : 1; unsigned char bit4 : 1; unsigned char bit5 : 1; unsigned char bit6 : 1; unsigned char bit7 : 1; } _attribute_ ((packed)) porta_t; #define PA (*(volatile porta_t *)&PORTA) PA.bit6 = 1; PA.onoff = 0; PA.onoff = 1; oder nicht dereferenziert: #define PB ((volatile porta_t *)&PORTB) PB->onoff = 1; (die Reihenfolge der Bits im Bitfeld ist compilerabhängig, also ggf. ausprobieren) Gruss Marcus
Blöd an Bitfeldern ist nur, dass man die Arbeit doppelt hat, wenn man im Header sowohl die Bitfelder als auch die Bitmasken/Nummern deklariert. Denn wenn man nur die Bitfelder deklariert (wie z.B. Microchip das macht), dann findet man im Code doch wieder öfter die klassische Kurzform XY1CON = 0xD425; für XY1CONbits.EN = 1; XY1CONbits.MODE = 1; XY1CONbits.HAPPY = 0; XY1CONbits.WIDE = 1; ... weil der Programmierer es unlustig fand, beliebig viele Bits einzeln zu setzen und dafür zig Bytes Code zu verschwenden. Denn weil der Kram mit gutem Grund volatile ist, darf der Compiler das nicht zusammenfassen.
Andreas Kaiser wrote: > Denn weil der Kram mit > gutem Grund volatile ist, darf der Compiler das nicht zusammenfassen. Theoretisch geht das hier:
1 | const struct xy1con xy1con_initializer = { |
2 | .EN = 1, .MODE = 1, .HAPPY = 0, .WIDE = 1 |
3 | };
|
4 | XY1CONbits = xy1con_initializer; |
Habe ich mal mit einer angedachten Lösung für Bitfelder in der avr-libc probiert. GCC 4.x hat hier nur einen Bug (*) wodurch er das beim Auftreten von mehr als einem Bitfeld nicht mehr durch eine konstante Zahl ersetzt. Das funktionierte aber unter GCC 3.x noch, und man sollte annehmen, dass das wieder repariert wird. (*) http://gcc.gnu.org/bugzilla/show_bug.cgi?id=32901
Habe ich mir auch schon überlegt. Ist zwar mittlerweile standardkonform möglich nachdem das lange Zeit auf GCC beschränkt war, aber wieviele Microcontroller-Compiler kriegen sonst noch gebacken? C99 scheint mir da nicht allzu weit verbreitet zu sein. Und wenn - wie sieht dann der Code aus?
Naja, da die avr-libc inherent zum AVR-GCC gehört, mache ich mir da keine zu großen Gedanken. ;-) Aber das alte byteorientierte Interface wird's natürlich auch noch geben.
Dir kann's egal sein. Aber wenn ich mir für den MCP2515 oder ENC28J60 ein Headerfile bastele, dann möglichst nicht noch für jeden Compiler anders.
In Ada könnte man zum Beispiel schreiben:
1 | xy1con := (en => 1, mode => 1, wide => 1, foobar => 5, others => 0); |
2 | ... |
3 | xy1con.en := 0; |
...und aus der Typdefinition von xy1con weiß der Compiler, wie die einzelnen Elemente zu einem Byte (oder auch zu einem ganzen Speicherblock!) zusammenzufügen sind. Eigentlich schade dass sich C überall durchgesetzt hat. Wenn man es genau betrachtet ist C völlig ungeeignet für µC-Programmierung: es gibt keine binären Literale, das Typen-/Speichermodell ist zu inflexibel, Bitfelder sind kaum nutzbar weil die Abbildung auf den Speicher nicht definiert ist, usw... und für High Level Programmierung ist es genauso ungeeignet, weil das Speicherhandling jedes Programm zum Minenfeld macht. </rant>
> Eigentlich schade dass sich C überall durchgesetzt hat. Wenn man es > genau betrachtet ist C völlig ungeeignet für µC-Programmierung: Ack. Mir wäre Ada auch lieber. Aber da ist nix mehr zu machen.
Was spricht gegen: P2OUT &= ~BIT1; // aus P2OUT |= BIT1; // an
>Was spricht gegen: > P2OUT &= ~BIT1; // aus > P2OUT |= BIT1; // an Die Schreibweise. BIT1 = 0; oder BIT1 = 1; ist sofort ersichtlich. Es soll genau das gemacht werden, was es darstellt und nicht das, was der Compiler anschließend daraus macht. In dem bekannten Fall würde erst das Bit invertiert und dann anschließend mit dem Port UND-Verknüpft werden. Diese Art ist von hinten durch die Brust.
Das Invertieren des Bits macht schon der Präprozessor. Es ist am Ende auch nur ein Befehl der ausgeführt wird. Und die Gründe dafür sind ja nun schon mehrfach erläutert: Keine Bitweise Adressierung am MSP.
> Das Invertieren des Bits macht schon der Präprozessor.
Verbreiteter Irrtum. gcc -E zeigt dir was der Präprozessor draus macht.
Rechnen tut er jedenfalls nicht, das macht der Compiler selbst.
Wenn ich mir das Disassembly anschaue, steht da schon die Konstante drin. Muss natürlich zur Kompilierzeit feststehen.
Christian Richter wrote: > Wenn ich mir das Disassembly anschaue, steht da schon die Konstante > drin. Trotzdem macht es nicht der Preprozessor... > Muss natürlich zur Kompilierzeit feststehen. Eben. Der Compiler rechnet den konstanten Ausdruck aus.
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.