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
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.
@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ß
> 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.
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.
@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?
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
#define TURN_ON( which ) ( SET_BIT( LED_PORT, which ) )
5
6
intmain()
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.
@ 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ß
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:
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.
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
@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:
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
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.
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