Forum: Mikrocontroller und Digitale Elektronik Bitstrukturen mal wieder


von Jojo (Gast)


Lesenswert?

Hallo Forum!

Ich hab jetzt in letzter Zeit viel auf TI-DSPs programmiert und finde da 
den Zugriff auf einzelne Bits über die vorgefertigten Strukturen sehr 
komfortabel. Zum beispiel kann ich da ein Bit im ADC so setzen:
1
AdcRegs.ADCTRL3.bit.ADCPWDN = 1;
oder so löschen:
1
AdcRegs.ADCTRL3.bit.ADCPWDN = 0;

Wenn ich zugriff auf alle Bits haben will schreibe ich z.B.:
1
AdcRegs.ADCTRL3.all = 0xA05B;

Abgefragt wird dann einfach mit:
1
if(AdcRegs.ADCTRL3.bit.ADCPWDN)
oder
1
if(!AdcRegs.ADCTRL3.bit.ADCPWDN)

Jetzt bin ich nach einiger Zeit mal wieder auf die AVRs umgeschwenkt. Da 
finde ich den Zugriff auf einzelne Bits jetzt aber sehr unkomfortabel.
Man könnte ja solche Strukturen auch für die Register der AVRs anlegen, 
oder zumindest für eigene Variablen.

Jetzt ist nur meine Frage: ist das ratsam ? Kann der AVR mit dieser 
Art der Programmierung gut umgehen/wandelt der Compiler das geschickt 
um? Was ist der Grund, warum das so selten/nie gemacht wird?

Danke schonmal,
Gruß,

Jojo

von heinzhorst (Gast)


Lesenswert?

Warum denn nicht? Schreib dir ein paar Headerdatein dafür und los 
geht's. Beim MCC18 für den PIC gib's das gleich ab Werk. Weiß nur nicht 
jeder weil's meiner Meinung nach nicht vernünftig dokumentiert ist.

von Jojo (Gast)


Lesenswert?

@heinzhorst:
jo, danke schonmal für die Antwort. Aber so richtig beantworten tut das 
meine Frage(n) nicht :) .
Wie das geht weiß ich ja. Ich möchte aber eigentlich wissen, worin der 
Grund liegt, daß das eigentlich keiner so macht (bei AVRs). ICh meine... 
unkomfortabler als
1
if( !(PORTD & (1<<PD1)) )
oder
1
PORTD &= ~(1<<PD1)
geht es ja kaum noch!

Und da wundert es mich einfach, daß das alle so hinnehmen. Das muß ja 
nen Grund haben, oder?

Gruß

von Klaus W. (mfgkw)


Lesenswert?

> Ich möchte aber eigentlich wissen, worin der Grund liegt,...

Das hat mein Opa schon so gemacht, das hat mein Vater so  gemacht, dann 
mache ich das auch so.

von Tobias K. (kurzschluss81)


Lesenswert?

Also für die MSP430 er habe ich mir solche Family Headerdateien für CCS 
angelegt
war zwar ein Haufen Aufwand aber das Programmieren geht sehr 
komfortabel.
IAR hat solche Headerdateien für MSP auch im Angebot.

von Jojo (Gast)


Lesenswert?

@Klaus:
hihi... ja genau, das alte Leid... "Das war immer schon so!"

Aber sag doch mal ernsthaft: liegt es am Compiler? ODer an der Art, wie 
der Controller Bitzugriffe handhabt? Daß die Bedeutung von
1
GPIORegs.PortA.bit.PA1 = 1;
und
1
PORTA |= (1<<PA1);
die gleichen sind, darüber sind wir uns ja relativ einig. Nur kommt der 
Controller mit beiden Ausdrücken gleich gut zurecht (im Bezug auf 
Verarbeitungszeit)?

Warum so unglaublich unkomfortabel?

von Karl H. (kbuchegg)


Lesenswert?

Jojo schrieb:

> Aber sag doch mal ernsthaft: liegt es am Compiler? ODer an der Art, wie
> der Controller Bitzugriffe handhabt? Daß die Bedeutung von
>
1
GPIORegs.PortA.bit.PA1 = 1;
> und
>
1
PORTA |= (1<<PA1);
> die gleichen sind, darüber sind wir uns ja relativ einig. Nur kommt der
> Controller mit beiden Ausdrücken gleich gut zurecht (im Bezug auf
> Verarbeitungszeit)?


Wenn der Compiler halbwegs etwas auf dem Kasten hat, kommt in beiden 
Fällen hinten der gleiche Code raus.

> Warum so unglaublich unkomfortabel?

Da kann ich jetzt nur für mich sprechen:
* Weil in der 'unkomfortablen' Version ersichtlich ist, dass das
  unter Umständen eine teure Operation ist.
* Weil es auch für den Fall verallgemeinert, wenn mehr als 1 Bit
  in einem Aufwasch zu setzen und zu löschen ist.


Im übrigen, wenn dir

   PORT |= ( 1 << Bit );

zu unkomfortabel ist, kannst du

* dir selbst eine struct/union bauen, mit der du den Zugriff
  nach deinen Wünschen gestalten kannst

* dir ein Makro schreiben, welches die Details verbirgt
1
#define SET_BIT( where, which )   ( (where) |= ( 1 << (which) ) )
2
3
4
int main()
5
{
6
  ...
7
8
  SET_BIT( PORTB, 4 );
9
}

zusammen mit anderen Makros
1
#define LED_PORT   PORTB
2
#define LED_RED    6
3
4
#define TURN_ON( which )   ( SET_BIT( LED_PORT, which ) )
5
6
int main()
7
{
8
9
   ...
10
11
  TURN_ON( LED_RED );
12
}

hat das dann auch noch eine zusätzliche ganz andere Qualität der 
Programmdokumentation als
1
   PORTB.6 = 1;

d.h. viel Lärm um nichts. Denn sinnvollerweise wird man dieses Detail 
wie jetzt konkret ein Bit gesetzt wird sowieso an einer Stelle im Code 
'verstecken' und im Code einen Mechanismus benutzen, der mir dort viel 
besser durch die Schreibweise schon im Code dokumentiert was hier 
passiert. Und damit relativiert sich dann der Ausdruck 'komplizierte 
Schreibweise' schon ganz gewaltig.

von Jojo (Gast)


Lesenswert?

@ Karl Heinz!

Danke für deine Ausführung! Toll, daß du dir immer so viel Mühe bei 
deinen Posts gibst :) .

Stimmt schon, bei der Sache mit den structs wird der Zugriff auf mehrer 
Bits immer etwas "weitschweifiger". Dadurch wird es aber auf der anderen 
Seite auch imemr sehr übersichtlich (finde ich). Wenn jedes Bit immer 
einzelnd gesetzt/gelöscht/abgefragt werden muß kann man immer genau 
sehen, was passiert.
Wenn man die "gängige" Methode nimmt kommt man schon mal mit den ganzen 
logischen Verknüpfungen durcheinander.
Aus dem Ausdruck
1
if( !(PORTA & ((1<<PA3) | (1<<PA0)) )
kann man ja wirklich nicht auf Anhieb erkennen, daß eigentlich "nur" 
abgefragt wird, ob das 0. und das 3. Bit von Port A gelöscht sind.
Durch
1
if(    !GPIORegs.PORTA.bit.PA0
2
       && !GPIORegs.PORTA.bit.PA3)

würde man ja recht deutlich sehen, was passiert. Natürlich mit dem 
Nachteil, daß zwei Abfragen stattfinden müssen.

Dann frag ich dich (Karl Heinz) einfach mal: wie machst du das? Gibst 
du dich mit der "normalen" vorgehensweise ab? Oder schreibst du dir 
lustige Makros?

Gruß

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Eine Alternative zum bei C-auf-AVRs-Programmierern beliebten Bitschieben 
ist die Definition der Bitkonstanten nicht als Bitnummer, sondern als 
Bitwert, wie es bei der Programmierung von MSP430 üblich ist.

Da heißt es dann:
1
if (!(PORTA & (PA3 | PA0)))

anstelle von
1
if (!(PORTA & ((1 << PA3) | (1 << PA0))))

von Karl H. (kbuchegg)


Lesenswert?

Jojo schrieb:

> Dann frag ich dich (Karl Heinz) einfach mal: wie machst du das? Gibst
> du dich mit der "normalen" vorgehensweise ab? Oder schreibst du dir
> lustige Makros?


Solange es sich um µC-spezifische Dinge handelt, wie zb ADC Bits oder 
Timer Konfiguration, nehm ich die "normale" Schreibweise ala

   ADMUX |= ....

schon aus dem Grund, weil ich es überischtlicher finde, wenn ich 
zusammengehörende Bits (Einstellung der Referenzspannung) auch in einem 
Statement beisammen habe.

Für Peripherie (LED, Taster, etc) seh ich immer zu, dass ich die 
konkreten Portbits nur an einer Stelle (einem #define) habe und im 
eigentlichen C-Code dann nur noch diese auftauchen.

So was
1
  ...
2
3
  PORTB &= ~( 1 << PB5 );    // rote Fehlerled einschalten
4
  PORTC |= ( 1 << PC3 );     // Summer einschalten

wirst du bei mir im Code nie finden.


Das schon eher:
1
  ...
2
3
  ERROR_LED_PORT &= ~( 1 << ERROR_LED );

(und da wirds dann mit deinen Bitzugriffen schon gar nicht mehr so 
einfach, aber es ist machbar :-) Die Bitzugriffe haben hauptsächlich 
einen Vorteil: In einer Angabe ist sowohl Port als auch Bit codiert.

oder aber tatsächlich so
1
  TURN_ON( ERROR_LED );

Mein oberste Maxime ist nämlich: Gestalte den Code so, dass du keine 
Kommentare brauchst ausser wenn der Kommentar in groben Zügen 
beschreiben soll, was im nächsten Abschnitt passiert und warum. Aber 
Einzelstatements will ich nach Möglichkeit niemals kommentieren müssen. 
Wenn ich mir also beim Einschalten einer LED im Kommentar dazuschreiben 
muss, welche LED das ist, dann ist das für mich ein deutlicher Hinweis, 
dass ich am Code arbeiten muss.

von Jojo (Gast)


Lesenswert?

@Rufus:

aha, das versteh ich aber gerade nicht. Was verbirgt sich dahinter?

von Peter D. (peda)


Lesenswert?

Jojo schrieb:
> Jetzt ist nur meine Frage: ist das ratsam ?

Ja

> Kann der AVR mit dieser
> Art der Programmierung gut umgehen/wandelt der Compiler das geschickt
> um?

Ja.

Ich arbeite auch gerne mit Bits, bins vom Keil C51 so gewohnt.
Ich benutze dazu ein Macro, in Anlehnung an den Keil habe ich es SBIT() 
genannt.
Hier ein Beispiel:

http://www.mikrocontroller.net/attachment/30300/lcd_drv.zip

An der Schieberei-Schreibweise stört mich vor allem, daß ich dann immer 
2 Ausdrücke mitschleppen muß, das Byte und die Bitnummer.


> Was ist der Grund, warum das so selten/nie gemacht wird?

Vielleicht Angst vor was neuem?


Peter

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Unterschiedliche Definitionen der Konstanten in den Headerdateien.

Bei C-Compilern für AVRs steht da i.d.R.

1
#define PA0 0
2
#define PA1 1
3
#define PA2 2
4
...
5
#define PA7 7

währenddem bei C-Compilern für MSP430 folgende Konvention verwendet 
wird:
1
#define PA0 1
2
#define PA1 2
3
#define PA2 4
4
...
5
#define PA7 128

Der von AVR-Anwendern genutzte Teilausdruck

  (1 << PA0)

erzeugt aus der Bitnummer PA0 den korrespondierenden Bitwert.

von Jojo (Gast)


Lesenswert?

@Rufus:
das ist ja abgefahren ;) ... So naheliegend, und trotzdem nicht drauf 
gekommen! Aber dann müßte ich ja ALLE Hader-Datein umschreiben! Aber 
vielleicht wäre es mal einen Versuch wert...

@Peter:
danke, zur Kenntniss genommen. Das werd ich mir mal angucken :)

@Karl Heinz
so ähnlich hab ich das auch intuitiv gemacht. Meine #defines sahen aber 
immer noch wilder aus:
1
#define schalter_eingaenge PINC
2
#define schalter1 PC0
3
#define schalter1_gedrueckt ( !(schalter_eingaenge & (1<<schalter1)) )
4
#define schalter1_nicht_gedrueckt (schalter_eingaenge & (1<<schalter1))

Wenn ich dann nach 63 Wochen mit definieren fertig war konnte ich aber 
bequem schreiben:
1
if(schalter1_gedrückt)
 =)


Also dann danke ich euch erstmal!!
Dann kann ich mir jetzt eine Lösung ausdenken/aussuchen, die gerade zu 
meiner Laune oder zum Wetter passt :)

Gruß
Jojo

von Karl H. (kbuchegg)


Lesenswert?

Jojo schrieb:
> @Rufus:
> das ist ja abgefahren ;) ... So naheliegend, und trotzdem nicht drauf
> gekommen! Aber dann müßte ich ja ALLE Hader-Datein umschreiben!

Ganz so schlimm ist es auch wieder nicht :-)
Mit ein wenig C-Erfahrung kann man das mit relativ geringem Aufwand 
managen. Überhaupt wenn man im Umgang mit dem Präprozessor und Makros 
geübt ist.

> Wenn ich dann nach 63 Wochen mit definieren fertig war

:-)
Wenn es jedesmal 63 Wochen dauern würde, würde ich es auch lassen. Aber 
Gott sei Dank gehts ja schneller.

Ausserdem kann man ja die Basisideen von einem Projekt ins andere 
retten. Und da das immer der gleiche Makro-Code ist, kommt der in ein 
Include File und wird laufend wiederverwendet :-)

Und dann sind die 63 Wochen runter auf 63 Sekunden und nichts mehr, was 
mir Kopfzerbrechen machen würde.

von Bernhard R. (barnyhh)


Lesenswert?

Für die AVR-Methode spricht noch etwas:
- Die gesamten IO-Variablen sind als volatile definiert; der Compiler 
darf also nicht zwei aufeinanderfolgende Zugriffe (bei Struktur-Zugriff 
erforderlich) in eine Maschineninstruktion umsetzen.
- U.U. ist es erforderlich, mehrere Bits einer IO-Variablen 
in__einer__Instruktion zu modifizieren. Hier scheitert die 
Struktur-Methode, sobald die Bits nicht nebeneinander liegen.

Im übrigen ist das Ganze eine Frage der Gewohnheit. Probleme ergeben 
sich höchstens dann, wenn man als "Wanderer zwischen den Welten" einmal 
so und einmal anders herum zu denken hat.

Bernhard

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.