Forum: Compiler & IDEs MSPGCC Bit setzen mit einem Befehl


von Gast (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Gast (Gast)


Lesenswert?

>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.

von Rolf Magnus (Gast)


Lesenswert?

>>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);

von Gast (Gast)


Lesenswert?

>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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> (...) 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.

von Gast (Gast)


Lesenswert?

>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.

von Rolf Magnus (Gast)


Lesenswert?

> 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.

von Christian R. (supachris)


Lesenswert?

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.

von Marcus Overhagen (Gast)


Lesenswert?

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

von Gast (Gast)


Lesenswert?

Hallo Marcus,

eine sehr edle Lösung.

von Andreas K. (a-k)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

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>

von Andreas K. (a-k)


Lesenswert?

> 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.

von Wolfgang-G (Gast)


Lesenswert?

Was spricht gegen:
   P2OUT &= ~BIT1;   // aus
   P2OUT |=  BIT1;   // an

von Gast (Gast)


Lesenswert?

>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.

von Christian R. (supachris)


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

> 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.

von Christian R. (supachris)


Lesenswert?

Wenn ich mir das Disassembly anschaue, steht da schon die Konstante 
drin. Muss natürlich zur Kompilierzeit feststehen.

von Simon K. (simon) Benutzerseite


Lesenswert?

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
Noch kein Account? Hier anmelden.