mikrocontroller.net

Forum: Compiler & IDEs sbit macro für avr-gcc


Autor: wolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich hab zur zeit ein Problem und zwar hab ich ein Programm welches mit 
dem keil-Compiler geschrieben wurde, ich benütze allerdings das 
Avr-Studio, welches ja bekannter massen den Befehl "sbit" nicht kennt.
Hat zu fällig jemand ein Macro fürs AVR Sudio so das ich den befehl 
verwenden kann?
oder kann mir jemand sagen in welcher form ich das Macro in meine 
Headerdatei schreiben muss?
Viele Dank schon einmal im vorraus

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was soll sbit denn machen?

Autor: wolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ups stimmt wäre clever gewesen des gleich dazu zuschreiben

sbit led = 0xb0;

gibt dem entsprechendem Pin den Namen led, sprich man kann den Pin dann 
direkt mit "led = 1" bzw "led=0" an/ausschalten

kann ich mit dem avr Studio auch irgendwie so einfach bestimmten Pins 
einen namen geben und die dann mit "=1" an bzw "=0" ausschalten?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit AVR Studio hat das alles nichts zu tun.

Der AVR kennt erstmal keine Bitvariablen.  Man kann das mit Bitfeldern
in C realisieren, wenn du das unbedingt willst, aber die AVR-Ports
sind natürlich erst einmal von Natur aus 8-bittig.

Vielleicht hilft es dir ja einfach, einen Wrapper-Makro zu schreiben?
#define MYLEDPORT PORTB
#define MYLEDBIT (1 << 0)
#define LED(onoff) do {\
   if (onoff) \
      MYLEDPORT &= ~MYLEDBIT; \
   else \
      MYLEDPORT |= MYLEDBIT; \
} while(0)

Autor: wolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja ich denke des hilf mir weiter
vielen Dank

Autor: Volker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das geht über Bitfelder


struct test {
  byte b0:1;
  byte b1:1;
  byte b2:1;
  byte b3:1;
  byte b4:1;
  byte b5:1;
  byte b6:1;
  byte b7:1;
} __attribute__((_packed_));

#define LED_rot (*(volatile struct test*)&PORTD).b3
#define LED_gruen (*(volatile struct test*)&PORTD).b2

Im Programm kannst dann schreiben

LED_rot = 1;
LED_gruen = 0;
usw...

Gruß Volker

Autor: wolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wow Danke!
Sehe ich es richtig das ich des auf jeden Port anwenden kann nur halt 
immer den Port Buchstaben dem entsprechen anpassen muss?
Also  für Led_rot an Port a und grün an Port d:
#define LED_rot (*(volatile struct test*)&PORTA).b3
#define LED_gruen (*(volatile struct test*)&PORTD).b2

Autor: Volker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, das siehst du richtig.

Im obigen Beitrag fehlt noch:

typedef unsigned char byte;

Ich hatte das selbe Problem mit alten C51 Codes und keine Lust
alles zu ersetzen.

Ich hab dir noch nen Link aus dem Forum rausgesucht (die letzten 3 
Beiträge).

Beitrag "#define in C"


Aufpassen musst du beim Einlesen eines Pins, da muss eben PINX und nicht
PORTX stehen.

Gruß Volker

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hi
hier steht:
#define LED_rot (*(volatile struct test*)&PORTD).b3

in dem beitrag auf dem der link verweißt steht:
#define LED1 ((volatile struct test*)&PORTD)->b0

muss es jetzt ".b3" oder "->b3" heißen? oder ist des Jacke wie Hose?
Grüße

Tom

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
-> statt . ist ja nicht der einzige Unterschied.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...und »a->b« ist einfach eine Kurzform für »(*a).b«.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht packst du die coole Lösung von Volker noch in ein Makro:
#define BIT(r,n) (((volatile struct test *)&r)->b##n)
Dann wird die Deklaration der einzelnen Bits noch übersichtlicher:
#define LED_rot     BIT(PORTD,3)
#define LED_gruen   BIT(PORTD,2)
#define LED_gelb    BIT(PORTC,0)

  

Autor: Andreas Paulin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh, DANKE, danke!
Ich vermisse die Bitadressierung beim AVR auch sehr, bin bis dato aber 
auch nicht auf was genialeres gekommen....

Clever!

Nur über

(((volatile struct test *)&r)->b##n)

muss ich erstmal ne Nacht lang schlafen :)

Autor: Volker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@yalu,

Kompliment, so ists wirklich noch übersichtlicher, habs zwar in der 
Praxis noch nicht getestet, müsste aber gehen.

@Andreas

getreu mach dem Motto: Was nicht passt wird passend gemacht.

Das schönste an der Sache finde ich ist, dass es C-Konform ist, also 
keine
Kompiler Spezialerweiterung, die nicht portierbar ist.

Gruß Volker

Autor: Günter R. (galileo14)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Jörg:

Die Zeile

>       MYLEDPORT &= ~MYLEDBIT;

in Deinem Beitrag ist recht interessant; sie erinnert mich an eine 
Passage im avr-libc Manual (Punkt 7.3.21, S.197 im Manual Version 
1.4.3), in der empfohlen wird, solche bitwise-Operationen sinngemäß wie 
folgt zu schreiben:

>       MYLEDPORT &= (unsigned char)~MYLEDBIT;

damit der Compiler 8-Bit-Operationen erzeugt, was ja genügt; andernfalls 
(ohne den Cast) würde er 16-Bit-Operationen erzeugen ("promotion to an 
int"); kannst Du das bestätigen? Und ist die Verwendung des Casts 
vorteilhaft, oder ist es egal (bzw. würde die Optimierung -Os letztlich 
das gleiche Ergebnis bringen - hab's jetzt nicht getestet)?

Günter

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tatsache. interessant!

8.3.21 Why does the compiler compile an 8-bit operation that uses 
bitwise operators into a 16-bit operation in assembly? Bitwise 
operations in Standard C will automatically promote their operands to an 
int, which is (by default) 16 bits in avr-gcc. To work around this use 
typecasts on the operands, including literals, to declare that the 
values are to be 8 bit operands. This may be especially important when 
clearing a bit:
var &= ~mask; /* wrong way! */

The bitwise "not" operator (~) will also promote the value in mask to an 
int. To keep it an 8-bit value, typecast before the "not" operator:
var &= (unsigned char)~mask;

{quelle: avr-libc-user-manual-1.4.5.pdf S.211}


pumpkin

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
edit:

im manual ist diese methode zwar auf der "Obsolete IO macros" liste, 
aber funktionieren tut es auch.

#define sbi(port, bit) (port) |= (1 << (bit))

{quelle: avr-libc-user-manual-1.4.5.pdf S.48}

ich verstehe nur die begründung nicht ganz warum dieses makro entfernt 
wurde:

"These macros became obsolete, as reading and writing IO ports can be 
done by simply using the IO port name in an expression, and all bit 
manipulation (including those on IO ports) can be done using generic C 
bit manipulation operators."

nichts anderes geschieht in diesem makro, oder sehe ich hier was falsch? 
allerdings sollte man, um die linie zu einzuhalten, folgendes schreiben:

#define sbi(port, bit) (port) |= (unsigned char)(1 << (bit))

bin gespannt was ihr dazu sagt...

pumpkin

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Interresant, habs mal ausprobiert aber bin noch nicht ganz zufrieden.

Ich schreibe häufig code für den 8x51 und auf die hier beschriebene 
Weise kann man sich in der Tat ne Menge Arbeit sparen.

Folgendes funktioniert aber leider nicht. Beispiel: Habe einen I/O als 
Data definiert und shreibe:

DATA = meinbyte & 0x80;

Verwendet man ja des öfteren wenn man seriell etwas Aussenden will. Beim 
MSP sowie 8x51 kein Problem. Mit dem hier beschriebenen Konstrukt gehts 
in die Hose.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
keiner ne Idee ?

DATA = meinbyte & 0x80;

Warum gehts mit dem GCC nicht ?

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> DATA = meinbyte & 0x80;

Wenn "DATA" für ein Einzelbit steht, geht alles andere als Bit 0 über 
die Wupper. Alternative:

DATA = (meinbyte & 0x80) != 0;
   oder, äquivalent:
DATA = (meinbyte & 0x80) ? 1 : 0,

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich hätte noch ne anmerkung:
(port) |= (1 << (bit))

ist ja die kurzschreibweise von
(port) = (port) | (1 << (bit))

wenn man jetzt
(port) |= (unsigned char)(1 << (bit))

schreibt, wird das doch vermutlich (!) in
(port) = (port) | (unsigned char)(1 << (bit))

'übersetzt'. wenn ich mit meiner vermutung richtig liege, dann ist es 
doch hinfällig, dass man nur "<<" castet: "[...] promote their operands 
[...]" - also beide operanten.

castet der compiler den anderen operanten automatisch genauso?


pumpkin

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> castet der compiler den anderen operanten automatisch genauso?

Bei (1<<n) kann ein Compiler ohne Kenntnis von n nicht wissen, dass das 
Ergebnis in ein Byte passt, wird also ohne cast die normgerechte 
16bit-Rechnung durchführen.

Er kann jedoch wissen, dass a|b nicht mehr Bits braucht als a oder b. 
Das nützt ihm aber nur etwas, wenn er weiss das b ein Byte ist.

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich will mal davon ausgehen dass 'n' zum compile-zeitpunkt definiert und 
<=7 ist. '(port)' soll auch acht bit haben. ansonsten würde das 
gedankenexperiment nichtig sein.

pumpkin

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich mich recht entsinne, bezog sich die Bemerkung in der FAQ
nicht auf zur Compilezeit bekannte Konstanten, sondern auf eine
variable Schiebweite.  Ja, auch sowas soll zuweilen vorkommen. ;-)

Ob und wie sich das Problem eventuell mit GCC 4.x entschärft hat,
hab' ich mir aber auch noch nicht angesehen.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab die Beiträge hier gerade mit großem Interesse gelesen und bin etwas 
bestürzt das der AVR GCC so großzügig mit den AVR Ressourcen um sich 
wirft wenn man nicht etliche Sachen beachtet.

Ich bin generell kein Fan von den Bitschiebereien in C und der 
Lösungsansatz von yalu gefällt mir. Vielleicht könnte man das so weit 
ausbauen das man damit Bits bearbeiten kann und der Compiler kein 
Integer daraus macht um Ressourcen zu schonen und das ganze dann mal zum 
AVR-GCC Standard. Ich bin leider nicht fit genug in C sonst würde ich 
das selbst machen

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Hab die Beiträge hier gerade mit großem Interesse gelesen und
> bin etwas bestürzt das der AVR GCC so großzügig mit den AVR
> Ressourcen um sich wirft wenn man nicht etliche Sachen beachtet.

Und ich bin bestürzt, wie bereitwillig viele Leute allerlei propriertäre 
und hoffnungslos unportable Erweiterungen der Sprache C verwenden, um 
sich momentan etwas Arbeit zu ersparen. Zur Strafe bleiben sie dann für 
ewig an ihrem Compiler festgenagelt, weil sie später merken, dass sie 
nicht in C sondern in irgendwas entfernt ähnlichem programmiert haben.

GCC wurde für Workstations entwickelt und der C-Standard definiert 
minimal 16bit Arithmetik. AVR ist überhaupt die einzige 
8bit-Architektur, für die es den GCC gibt. Dafür ist er dann eigentlich 
nicht übel. Und wenn andere Compiler den Standard ignorieren und auch 
dann 8bittig rechnen, wenn der Standard 16bit vorschreibt - dafür kann 
GCC nichts.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich weiß gar nicht was du willst. So wie ich das verstanden hab ich die 
Lösung von yalu doch C-Konform, sollte also auch auf anderen Plattformen 
laufen.

Außerdem glaube ich nicht das es mir viel hilft wenn ich auf Biegen und 
Brechen alles überall schön ausführlich und C-Konform programmiert habe 
und später das ganze auf eine andere Prozessorarchitektur portieren 
will. Die IO Register auf einem ARM sehen anders aus und heißen auch 
anders als auf einem AVR. Die Protierung eines AVR Programms auf einen 
anderen Core ist IMHO IMMER mit viel Umschreiberei verbunden. Da könnte 
so ein Makro am Ende sogar mehr helfen als schaden, oder? Außerdem wird 
(zumindest für mich) der Code wesentlich besser lesbar wenn ich LED=1 
schreiben kann statt dieses (1<<n) Bitgeschiebe.

Und wenn das Makro, so ganz nebenbei, auch noch dafür sorgt das der 
Compiler ressourcenschonend mit Byte statt Int arbeitet bin ich 
glücklich und zufrieden.

Und selbst wenn ich "propriertäre und hoffnungslos unportable 
Erweiterungen" verwenden will ist das doch mein Bier, solange ich andere 
damit nicht konfrontiere sondern nur mir selbst damit schade

Autor: Volker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Clifford,

volle Zustimmung!

Gruß Volker

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bezog mich da eher auf die im Thema genannten "sbit"-Spielchen und 
Notationen wie "port.3". Und dieser ganze Thread wurde überhaupt nur 
gestartet, eben weil sich jemand in einer proprietärer Erweiterung 
verheddert hatte.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn man "portabel" programmieren will muß man sowieso von vorn herein 
die eigentliche Programmlogik und die Hardwarezugriffe streng 
voneinander trennen (und wer macht sowas schon). Wenn man das Programm 
dann tatsächlich mal protieren muß dann muß man sowieso den kompletten 
Hardwarelayer neu programmieren. Ich bezweifle das es für einen AVR, 
8051, PIC, ARM ... zb einheitliche UART Librarys gibt die alle das 
gleiche Interface haben.

Außerdem finde ich Makros wie sbi/cbi durchaus auch legitim, auch wenn 
SetBit/ClrBit vielleicht allgemeiner wären. sbi/cbi stammen ja aus dem 
AVR Assembler Wortschatz. Solche Makros lassen sich doch in 5 Minuten in 
jedem C Compiler nachbilden. Ich kenne Bitfelder (noch) nicht und weiß 
nicht in wie weit die "C-Konform" sind und ob die bei allen C Compilern 
zu finden sind. Falls nicht wären auch in dem Fall Makros die bessere 
Wahl, die gibt es definitiv bei jedem Compiler.

Und was das Verhalten des Compilers angeht alles in Integer umzuwandeln: 
ich kann den Grund dafür durchaus verstehen warum das so ist, aber auf 
einem Ressourcenarmen System wie einem 8 Bit Controller sollte man doch 
eher darauf Rücksicht nehmen die Ressourcen zu schonen. Aber da finde 
ich es schon fast unzumutbar bei jeder Bitoperation "unsigned char" oder 
von mir aus auch eine der Kurzformen hinschreiben zu müssen.

Und es ist doch auch nicht Sinn der Sache wenn ich nur wegen diesem 
Compilerverhalten einen größeren AVR nehmen muß als eigentlich nötig. 
Das mag im Hobbybereich vielleicht gehen, aber in der Industrie würde 
dem Compiler der Vorzug gegeben der den kleineren Code produziert, auch 
wenn der 1000€ kostet.

Da liegen die Prioritäten halt eher auf Ressourcenoptimierung und gut 
lesbarer und wartbarer Code und weniger auf Portierbarkeit und 
C-Konformität um jeden Preis. Den Mehraufwand zahlt einem nämlich keiner

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du erstmal mit der Bitmanipulation in C vertraut bist, gibt's
keinen richtigen Grund mehr für die "simplen" Makros: Generationen von
C-Programmierern vor dir haben es verstanden, damit Bits zu verwürfeln
(schließlich war sowas von vornherein von K&R mit eingeplant).
Gejammer darüber, ach wie umständlich das doch sei (auch wenn am Ende
die beliebten CBI- und SBI-CPU-Befehle generiert werden) kommt da
lediglich aus dem Lager der AVR-Programmierer.

Wenn du den Schritt logisch vernünftig weitergehst (siehe yalu),
kommst du eher bei einer Art HAL (hardware abstraction layer) heraus
und abstrahierst auf dem Niveau des Zugriffs auf die tatsächlich
angeschlossene Peripherie.

Ich kann dir aus eigener Erfahrung auch sagen, dass der wesentliche
Nachteil von Dingen wie cbi- und sbi-Makros (abgesehen davon, dass
damit ein nicht-AVR-C-Programmierer schlicht nichts anfangen kann)
darin besteht, dass die Leute sie, sowie sie einmal da sind, dann nur
gedankenlos benutzen.  Statt ein control register auf einmal mit all
seinen 8 Bits einzurichten, werden dann blindlings 8 cbi- und sbi-
Aufrufe draus gemacht...

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Außerdem finde ich Makros wie sbi/cbi durchaus auch legiti

Wo ist da ein Macro:
  sbit led = 0xb0;
Das ist ein Compiler-Konstrukt.

cbi/sbi wie in früherer avrlibc sind ok, ebenso bitfields. Ich selber 
mag bitfields nicht so, weil man dann doch immer wieder auch die 
Möglichkeit benötigt, alles oder mehrere Steuerbis gleichzeitig zu 
setzen, und dann gewöhnlich bei doppelter Definitionsarbeit landet: 
einmal als bitfield, und einmal als Latte von #defines.

Statt dessen neige ich eher zu Jörgs Variante: Passend benannte 
macros/inlines verstecken die Portoperation.

> bei jeder Bitoperation "unsigned char" oder
> von mir aus auch eine der Kurzformen hinschreiben zu müssen.

Dann nimm -mint8. Jeder Compiler mit Repekt vor Standards wird den Typ 
von 1<<n bei unbekanntem n als "int" ansetzen müssen. Soweit liegt es 
fest. Wenn int dann 8bittig ist, wird es trivial.

Inwiefern er dann in der Lage ist, festzustellen, dass es letztlich egal 
ist, ist Sache der Optimierung. GCC ist hinsichtlich Optimierung an sich 
nicht übel. Aber dass die angenommenen 16bittigen Grundoperationen von 
der 8bittigen Zielmaschine weiter aufgedröselt werden müssen, das ist 
nicht so sein Ding. Willst du einen Compiler der auf Fisematentchen von 
8bittern gut eingerichtet ist, dann lass die Finger weg von GCC, das ist 
einfach nicht seine Baustelle.

Ich habe obigen Code letzthin mal durch GCC 4.1 laufen lassen. Der Shift 
war bei unbekanntem n immer 16bittig, egal wie gecastet, aber danach 
blieb es 8bittig. Nicht ideal, aber auch nicht schlecht.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich kenn die normalen Bitmanipulationen von C, aber ich mag sie einfach 
nicht und kann (und vielleicht auch will, das geb ich zu) mich einfach 
nicht daran gewöhnen. Die Schreibweise in C ist der Mathematik entnommen 
und ich war nie gut in Mathe. Ich bin auch kein reiner AVR Programmierer 
sondern ich habe schon zu C64 Zeiten angefangen zu programmieren und hab 
seitdem diverse Prozessoren und Mikrocontroller programmieren müssen. 
Aber meistens in Assembler. Vielleicht auch deshalb die Probleme mit den 
Bitmanipulationen in C.

Wenn andere Leute damit nicht umgehen können ist das eine andere Sache. 
Ich bin durchaus in der Lage in Bits, Bytes, HEX oder was auch immer zu 
denken und das auch so zu programmieren. Für mich wäre ein

#define LED Bit(PortB, PB5);

LED=1;

einfach logischer, lesbarer und schlicht und ergreifend hilfreicher als 
ein

#define LED_PORT PortB;
#define LED PB5;

LED_PORT |= (1 << (LED);

Ich kann ja das Bestreben, C-Konformen Code programmieren zu wollen, in 
gewissen Grenzen verstehen, aber es hilft mir einfach nicht. Die 
Begründung, das die Programme dann portierbar sind, finde ich nämlich 
hinfällig weil die Unterschiede zwischen den Architekturen einfach zu 
groß sind. Wie gesagt, wer wirklich protierbaren Code erreichen will 
kommt um einen HAL nicht drumrum.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.
Ich werd die Variante von Jörg und die von Volker/yalu einfach mal in 
der Praxis ausprobieren und mir dann das raussuchen was für mich besser 
funktioniert.

Wie muß ich denn die beiden Varianten modifizieren das alles als Byte 
angesehen wird? Da steh ich irgendwie auf dem Schlauch. Könnt Ihr mir da 
grad mal helfen?

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wobei die lesbarkeit von
LED = 1;

imho zu wünschen übrig lässt wenn man gerade verdrängt hat dass 'LED' 
ein einzelnes bit ist. dann doch lieber:
bla |= (1 << LED);

meinetwegen auch mit cast, aber dann ist klarer was da wie, wo und wann 
passiert. man muss sich am ende doch nur merken dass '|' setzt und '& ~' 
löscht - mir ist unklar wie man sich dagegen sträuben kann.

>> Wie gesagt, wer wirklich protierbaren Code erreichen will
>> kommt um einen HAL nicht drumrum.

das halte ich für ein gerücht. das macht das portieren bloss leichter 
weil nur einzelne codeabschnitte bearbeitet werden müssen.


pumpkin

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@pumpkin
Es gibt Situationen wo die übliche Schreibweise sicher Vorteile bringen 
kann, aber für mich wäre "meine" Variante in den meisten Fällen 
einfacher. Ich tue mich mit einer Wortsprache (zb ASM) einfach leichter 
als mit der Symbolik von C. Ich hab viel in ASM programmiert und hab am 
PC später auch lieber in Pascal/Delphi programmiert. Aber auf 
Mikrocontrollern hat man halt kaum eine Wahl

>> Wie gesagt, wer wirklich protierbaren Code erreichen will
>> kommt um einen HAL nicht drumrum.

>das halte ich für ein gerücht. das macht das portieren bloss leichter
>weil nur einzelne codeabschnitte bearbeitet werden müssen.

Dann hast du aber keinen portierbaren Code. Portierbarer Code hat keine 
Hardwarezugriffe sondern ruft nur Funktionen auf die die 
Hardwarezugriffe übernehmen. Und diese Funktionen müssen auf allen 
Plattformen die gleiche Schnittstelle haben, also die gleichen Parameter 
entgegennehmen und zurückgeben. Ein Code ist erst dann "portabel" wenn 
man auf dem neuen System keine Änderungen vornehmen muß sondern einfach 
in den Compiler packt und compilieren kann. Und aufgrund der teilweise 
massiven Unterschiede in manchen Architekturen kann es selbst mit einem 
HAL manchmal Probleme geben. Allein der Zugriff auf ganz normale IO 
Ports ist auf einem ARM vollkommen anders als auf einem AVR und macht 
einer Portierung schon schwierig.

Wenn ich an einem Programm stundenlang rumdoktorn muß bis es auf einem 
neuen System läuft ist es für mich nicht portabel. Und da helfen mir die 
C-Konformen Bitschiebereien auch nicht wirklich.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die hier diskutierte Lösung ist aus meiner Sicht sehr sinnvoll und ich 
werde Sie übernehmen.

Warum kann ein IAR, Keil, SDCC so etwas ohne probleme ? Weils kein 
Problem ist. Nur beim AVR ist es umständlich weil er kein Einzelbit 
kennt.

Diese Lösung hat immerhin dazu geführt das ich nun identischen Code bei 
den MSP's, 8x51 und AVR's verwenden kann.

Danke dafür ;-))

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Joe:
Welche der Lösungen meinst du jetzt?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> LED = 1;

Mach's doch gleich zu

LED(on);

Du sollst schließlich beim Lesen nicht erst drüber nachdenken müssen,
ob die LEDs in diesem System low-aktiv (häufig der Fall) oder
high-aktiv sind.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es geht ja nicht spezifisch um LEDs sondern allgemein um Bits. Das 
gleiche könnte man auch für Eingänge machen. Die können auch "activ low" 
oder "active high" sein. Außerdem sind bei mir sowieso alle LEDs und 
Taster active high

Mir wäre einfach mal wichtig eine "für mich" verständliche Lösung zu 
finden die im Idealfall für eine einfache Bitoperation wirklich nur die 
nötigen ASM Befehle erzeugt. Ich programmiere häufig für kleinere AVRs 
und bin jetzt etwas verunsichert ob die gelegentliche Speicherknappheit 
nur daran lag das der Compiler überflüssige Sicherheitsreserven 
eingebaut hat.

Aus dem Grund möchte ich gerne die Version von Volker/yalu für mich 
etwas anpassen so das gleich alles so eingebaut ist das auch wirklich 
nur Bytes bearbeitet werden.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Clifford, na diese hier:
struct test {
  byte b0:1;
  byte b1:1;
  byte b2:1;
  byte b3:1;
  byte b4:1;
  byte b5:1;
  byte b6:1;
  byte b7:1;
} __attribute__((__packed__));

#define BIT(r,n) (((volatile struct test *)&r)->b##n)

#define LED_rot     BIT(PORTD,3)

und dann im Code: LED_rot = 1; oder =0;

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, genau. Die will ich auch verwenden. Ich möchte jetzt nur noch wissen 
ob in der Variante auch 16 Bit Schiebeoperationen erzeugt werden und 
wenn ja wie man die vermeiden kann.

Autor: Volker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also wenn man im Assembler-Listing nachschaut findet man mit den 
verwendetet Macros

#define sclad (*(volatile struct test*)&PORTD).b3
...     sclad = 1;
        sclad = 0;
...

     602:  93 9a         sbi  0x12, 3  ; 18
     604:  93 98         cbi  0x12, 3  ; 18

wird also so umgesetzt, wie es sein soll. Von einer 16-Bit 
Schiebeoperation
ist da nichts zu finden.

Gruß Volker

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Perfekt. Nur was war das dann mit den 16 Bit Schiebeoperationen? Wo 
kamen die her?

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die können entstehen, wenn in den veralteten cbi/sbi-Macros die 
Bitnummer dem Compiler nicht bekannt ist, siehe pumpkins Beiträge.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Vielen Dank. Das hilft mir jetzt denke ich echt weiter.

Aber mir ist noch was anderes aufgefallen. Wie kann bei einem so 
einfachen Programm auf 152 Byte (ohne Optimizer. Mit sind es 144) kommen 
(WinAVR 20070122)?
int main(void)
{
    while(1);
}

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
- Interruptvektoren
- C-Startup-Code (Löschen von .bss, Laden von .data)

Regel #1: degenerierte Testfälle werden dir nie eine sinnvolle
Aussage bringen.  Ein leeres main() ist halt das typische
Beispiel dafür.

Autor: Clifford (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es ist ja nicht "leer" ;)
Interruptvektoren ist klar. Wird da immer die komplette Tabelle 
eingetragen? Vermutlich.

C-Startup-Code is mal ne Info die ich noch nicht hatte. Ohne solche 
"degenerierten Testfälle" wäre ich vermutlich nie darauf gekommen das es 
sowas gibt und würde mich immer nur über große Programme wundern. Man 
muß ja lernen

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja, auf einem Controller mit nur 128 Bytes ROM wirst du kaum C
benutzen. ;-)  Auf allen anderen fällt der Startup-Code im Vergleich
zum Rest nicht mehr groß ins Gewicht, da desse Größe ja praktisch
konstant ist.

Ja, die Vektoren werden immer alle eingebunden.  Sind auf einem
ATmega1281 auch schon mal (56 + 1) Stück zu je 4 Bytes, also 228
Bytes allein für die Vektortabelle.  Würde in den fiktiven 128-Byte-
Controller also schon gar nicht mehr passen...

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Etwas offtopic vielleicht, aber ich kam darauf, weil in diesem Thread
viel über vom Compiler generierte 16-Bit-Operationen gescholten wurde
und weil A.K. weiter oben den Vorschlag machte, -mint8 zu verwenden.

Irgendwo (kann sein im K&R, vielleicht auch im C-Standard) stand, dass
die Größe von int die "natürliche" Wortlänge des Zielprozessors sein
sollte. Bei manchen Prozessoren (wie dem MC68000) kann diese
"natürliche" Wortlänge je nach Geschmack unterschiedlich sein (16 oder
32 Bit), aber beim AVR kann sie eindeutig auf 8 Bit festgelegt werden.

Deshalb sollte eigentlich ein int 8 Bit lang sein, was durch die
Option -mint8 erreicht werden kann. Damit gehören alle ungewollten
16-Bit-Operationen der Vergangenheit an, auch ohne Type-Casts oder
Umstellung der Auswertereihenfolge.

(Mögliche) Nachteile bei der Verwendung von -mint8:

- ein Integertyp (und sein Unsigned-Pendant) geht verloren, bei
  älteren GCCs der 32- bei neueren der 64-Bit-Typ. Aber wer macht auf
  dem AVR schon etwas mit 64-Bit?

- Ich bin mir nicht sicher (und habe gerade nicht die Zeit
  nachzuschauen, aber Jörg weiß es aus dem Kopf ;-)), ob die avrlibc
  "mint8-aware" ist. Möglicherweise genügt es, sie mit -mint8 neu zu
  bauen.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@yalu:
> ...aber beim AVR kann sie eindeutig auf 8 Bit festgelegt werden.
Nein, kann sie nicht. Der Standard besagt (als Einschränkung zu der von 
Dir ein paar Zeile weiter oben wiedergegebenen Aussage), dass int und 
short mindestens 16 Bit lang sein müssen.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
yalu wrote:

> - Ich bin mir nicht sicher (und habe gerade nicht die Zeit
>   nachzuschauen, aber Jörg weiß es aus dem Kopf ;-)), ob die avrlibc
>   "mint8-aware" ist. Möglicherweise genügt es, sie mit -mint8 neu zu
>   bauen.

Ist sie nicht, und kann sie nicht sein.  Daher kann man sie auch nicht
,,einfach'' mit -mint8 neu übersetzen.

Der Grund, dass sie es nicht sein kann, liegt wieder in den
Mindestforderungen des C-Standards.  Um nur einfach ein Beispiel
zu nennen, das mir sofort einfällt: die Behandlung des Typs "char"
in den Standardfunktionen erwartet außer den 256 Zeichen, die da
reinpassen außerdem noch, dass es einen out-of-band-Wert für EOF
gibt.  Daher übergeben/-nehmen diese Funktionen ein int, das
folglich mindestens 257 Werte unterscheiden können muss.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Option -mint8 geht nur, wenn man auf die avrlibc weitgehend 
verzichtet. Was machbar ist, vor allem bei eher kleinen Projekten.

Aber auch wenn sie nicht einsetzbar ist, kann sie in einer Hinsicht 
trotzdem nützlich sein: um rauszukriegen, wieviel man dabei sparen 
würde. So katastrophal ist das nämlich nicht ;-).

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Irgendwo (kann sein im K&R, vielleicht auch im C-Standard) stand, dass
> die Größe von int die "natürliche" Wortlänge des Zielprozessors sein
> sollte.

sollte, ja. Sie muß aber mindestens 16 Bit sein.

>  die Behandlung des Typs "char" in den Standardfunktionen erwartet
> außer den 256 Zeichen, die da reinpassen außerdem noch, dass es einen
> out-of-band-Wert für EOF gibt.  Daher übergeben/-nehmen diese
> Funktionen ein int, das folglich mindestens 257 Werte unterscheiden
> können muss.

Das ist kein wirklich passendes Beispiel, denn es wäre durchaus erlaubt, 
wenn char und int beide 16 Bit breit wären und EOF ein gültiger 
char-Wert wäre. Deshalb gibt's z.B. auch die Funktion feof(). Mit einem 
Check des Rückgabewertes von fgetc() auf EOF alleine ist das Erkennen 
eines Dateiendes nicht 100% portabel.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Daher übergeben/-nehmen diese Funktionen ein int, das folglich
> mindestens 257 Werte unterscheiden können muss.

Jetzt ist mir halbwegs klar, woher die Forderung nach mindestens 16
Bit für ein int kommt. Vielen Dank.

> Das ist kein wirklich passendes Beispiel, denn es wäre durchaus
> erlaubt, wenn char und int beide 16 Bit breit wären und EOF ein
> gültiger char-Wert wäre. Deshalb gibt's z.B. auch die Funktion
> feof(). Mit einem Check des Rückgabewertes von fgetc() auf EOF
> alleine ist das Erkennen eines Dateiendes nicht 100% portabel.

Gibt es denn passendere Beispiele?

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Jörg Wunsch
@Rolf Magnus

Nochmal offtopic, weil mich die Sache mit den unterschiedlichen
int-Typen jetzt einfach interessiert:

Ich habe mir gerade den Standard

  ISO/IEC 9899 Second edition 1999-12-01
  Programming languages - C

reingezogen.

Da habe ich keine Informationen darüber gefunden, dass ein int
mindestens 16 bit groß sein muss. Habe ich da etwas übersehen, oder
gab es diese Forderung nur in älteren Standards?

Natürlich leuchtet nach Jörg's Argumentation ein, dass ein int größer
als ein char sein sollte (wobei dann bei einer char-Größe von 8 bit
aber auch 9 bit reichen würden), aber vielleicht ist auch Rolf's
Aussage richtig, dass der EOF-Rückgabewert von fgetc() für sich
alleine kein hinreichendes Kriterium für das Erreichen des Dateiendes
(bzw. einen Fehler) darstellt.

Ich habe jedenfalls für beide "Theorien" keine Hinweise gefunden und
bin jetzt etwas verwirrt.

Vielleicht könnt ihr mir da heraus helfen :-)

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
    -- minimum value for an object of type int
       INT_MIN                     -32767 // -(215-1)

    -- maximum value for an object of type int
       INT_MAX                     +32767 // 215-1

    -- maximum value for an object of type unsigned int
       UINT_MAX                     65535 // 216-1

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.

Ah, nicht die minimale Bitzahl, sondern der minimale Wertebereich ist 
angegeben. Da hätte ich wohl alles etwas genauer durchlesen müssen :-)

Danke für den Hinweis! Habe auf jden Fall wieder was dazu gelernt.

Autor: Andreas Paulin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Volker & Yalu:

Bin richtig gespannt und probier das jetzt mal aus :-D

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.