Forum: Mikrocontroller und Digitale Elektronik Einzelne Bits in C setzen


von Maxim (Gast)


Lesenswert?

Heute in der Schule haben wir wiedermal 8051er programmiert. Die IDE ist 
RIDE. Da kann man mit dem Befehl "sbit at x y" ein Bit an der Adresse x 
als y nennen und es dann z.B. mit y=1 setzen oder analog löschen, 
abfragen u.s.w ...

Kann man das auch beim AVR in AVR Studio machen?

von yalu (Gast)


Lesenswert?

Das Problem hat fast jeder, der anfängt µCs in C zu programmieren.
Entsprechend viel gibt es darüber nachzulesen:

http://www.mikrocontroller.net/articles/Bitmanipulation

http://www.mikrocontroller.net/search?query=%2Bbits+%2Bsetzen&from=opensearch

von Maxim (Gast)


Lesenswert?

Dann gibt es bei den AVRs kein "sbit at"-ähnlichen Befehl?

von D. W. (dave) Benutzerseite


Lesenswert?

Ich glaube nicht, dass "sbit at x y" ein Standard-C Befehl ist.

von Peter D. (peda)


Lesenswert?


von Klaus (Gast)


Lesenswert?

@Peter

Super Lösung.
Aber leider absolut nicht geeignet, damit jemand "C" lernt.


Besser wären Vorschläge wie ...

  PORTB  =  PORTB | 0x01 ;   // setze Bit 0 des Port B

oder

  PORTB  =  PORTB & ~0x01 ;  // lösche Bit 0 des Port B

von Maxim (Gast)


Lesenswert?

Na die bitweise Logikoperationen kenne ich gut. Mich hat es nur 
gewundert, dass man beim 8051 z.b. einfach bit=1 schreiben kann.

von Karl H. (kbuchegg)


Lesenswert?

Maxim wrote:
> Na die bitweise Logikoperationen kenne ich gut. Mich hat es nur
> gewundert, dass man beim 8051 z.b. einfach bit=1 schreiben kann.

Mal Hand aufs Herz.
Was bringt einem das?

Deswegen wird programmieren auch nicht einfacher, wenn man durch
eine Zuweisung ein einzelnes Bit setzen kann. Wenns denn unbedingt
sein muss, kann man das mit einem Makro, inline-Funktionen oder
mit der struct Variante auch mit Standard-C abbilden.


Meine Meinung: Die Möglichkeit bei einigen Compilern mit einer
speziellen Syntax Bits einzeln ansprechen zu können, wird bei
weitem überschätzt. Ist ungefähr so wichtig wie es beim Auto
ein beleuchteter Tankdeckel ist. Nett wenn mans hat, aber
nicht wirklich wichtig.

von Johannes M. (johnny-m)


Lesenswert?

Maxim wrote:
> Na die bitweise Logikoperationen kenne ich gut. Mich hat es nur
> gewundert, dass man beim 8051 z.b. einfach bit=1 schreiben kann.
Das hat zunächst nichts mit "8051" oder "AVR" zu tun, sondern allein mit 
dem Compiler. Kommerzielle AVR-Compiler wie CodeVision oder IAR oder... 
besitzen Erweiterungen, die ebenfalls eine "Pseudo-Bitadressierung" 
ermöglichen, mit einer Syntax ähnlich der des Keil51-Compilers. Das sind 
aber plattformspezifische Erweiterungen, die nichts mit dem 
ANSI-C-Standard zu tun haben und die die Portierbarkeit von Code 
praktisch unterbinden, da auch die unterschiedlichen Compiler-Hersteller 
z.T. unterschiedliche Erweiterungen benutzen. Der AVR-GCC-C-Compiler, 
der in der WINAVR-Distribution enthalten ist, ist jedoch ein "echter" 
und nicht-kommerzieller ANSI-C-Compiler, dem diese Erweiterungen fehlen. 
Den Code mit den Bitschiebereien kann aber im Unterschied zu dem anderen 
"Murks" jeder C-Compiler verstehen.

Einen kleinen echten Unterschied in der Hardware gibt es aber 
tatsächlich: Die 8051er besitzen eine größere Anzahl an 
bitadressierbaren Special Function-Registern, die dem AVR in der Art 
fehlen. Daher existieren auch in der internen Handhabung Unterschiede.

von yalu (Gast)


Lesenswert?

Ach, jetzt habe ich's auch kapiert. Dir geht es also vorrangig um die
sbit-Erweiterung des C-Standards.

Da schließe ich mich voll und ganz der Aussage von Karl heinz an:

Erstens tritt dieses Feature den ISO-Standard mit Füßen. Das sbit ist
hauptsächlich auf 8051-Compilern verbreitet. Wenn sich die
Compiler-Hersteller wenigstens auf einen Pseudo-Standard einigen
könnten. Aber vergleiche mal das sbit des Keil-Compilers mit dem in
dinem RIDE verwendeten: Völlig unterschiedliche Syntax.

Zweitens braucht kein Mensch das sbit. Jeder, der schon mehr als ein
paar Stunden C auf Nicht-8051-µCs programmiert, erkennt bei einer
bitweisen Logikoperation sofort die dahintersteckende Absicht.

Wenn man mit aller Gewalt trotzdem direkten Zugriff auf einzelne Bits
per Zuweisungsoperator möchte, gibt es immer noch die von Peter
vorgestellte, standardkonforme Methode mit Unions und Makros. Die
Bitdeklarationen sehen anders aus als bei der sbit-Erweiterung, aber
die Zugriffsoperationen haben die gleiche Syntax. Der Compiler erzeugt
daraus auch tatsächlich die gewünschten sbi- und cbi-Instruktionen.

Hier ist übrigens der Thread, wo das alles schon einmal bis ins Detail
diskutiert wurde:

  Beitrag "sbit macro für avr-gcc"

von Peter D. (peda)


Lesenswert?

yalu wrote:

> Wenn man mit aller Gewalt trotzdem direkten Zugriff auf einzelne Bits
> per Zuweisungsoperator möchte, gibt es immer noch die von Peter
> vorgestellte, standardkonforme Methode mit Unions und Makros.


Ich sehe darin nichts vergewaltigendes (hast Du sie nicht zuerst hier 
veröffentlicht?). Seit ich sie kenne, benutze ich sie nur noch.

Ich hab sogar ein altes Programm mit vielen IOs und logischen 
Verknüpfungen dahingehend umgeschrieben und das Sourcefile wurde 
deutlich kleiner und wesentlich besser lesbar!

Da die IOs per Schieberegister realisiert wurden und quasi im SRAM als 
Schattenregister lagen, konnte ich auch das volatile rausnehmen (die 
alten &=,|= Konstrukte arbeiteten ja auch ohne volatile auf den SRAM).
Und ulkiger Weise wurde auch das Binärfile kleiner!

Der GCC konnte die Strukturzugriffe besser zusammenfassen, als die alten 
Maskierungen.

Ich kann daher nur jedem empfehlen, auch diese Methode zu benutzen.

Immerhin ist durch die bessere Lesbarkeit ja auch die Gefahr von 
Schreibfehlern geringer.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Tja,
Programmieren ist immernoch eine Kunst. Der eine findet abstrakte Kunst 
besser, der andere steht auf anderes.

von yalu (Gast)


Lesenswert?

Peter schrieb:

> Ich sehe darin nichts vergewaltigendes

Nee, vergewaltigt wird dadurch natürlich nichts. Vielleicht hätte ich
statt "mit aller Gewalt" besser "unbedingt" schreiben sollen, das hört
sich nicht so brutal an :-)

> (hast Du sie nicht zuerst hier veröffentlicht?). Seit ich sie kenne,
> benutze ich sie nur noch.

Der eigentliche Trick, nämlich die Darstellung eines Bytes als
Bitfeld, stammt von jemandem namens Volker. Von mir kam lediglich die
benutzerfreundlichere Verpackung der etwas kryptischen Ausdrücke in
ein Makro. Ich habe das damals aber nur gepostet um anderen eine
Freude zu machen :-), selber benutzen tu ich's nicht.

> Der GCC konnte die Strukturzugriffe besser zusammenfassen, als die
> alten Maskierungen.

Das klingt interessant. Lag der Grund für die Verbesserung in der
Verwendung der Bitfelder oder im Weglassen des volatile? Oder anders
gefragt: Hätte die Maskiermethode gleich guten Code ergeben, wenn man
bei den Ports das volatile ebenfalls weggelassen hätte? Für
schnelleren oder kleineren Code bin ich natürlich sofort bereit, meine
Gewohnheiten über Bord zu schmeißen :-)

Und überhaupt: Jetzt, wo alle so begeistert von der Methode mit den
Einzelbitzugriffen sind, komme ich doch etwas ins Grübeln. Immerhin
hat die Methode noch einen anderen Vorteil: Für ein bestimmtes I/O-Pin
werden Port-Name und Bitnummer in einem einzigen Makro-Symbol
gekapselt. Das bringt dann Vorteile, wenn man einen Algorithmus
unabhängig von verwendeten I/O-Pin gestalten will (was eigentlich
immer guter Stil ist). Mit der klassischen Maskierungsmethode müssen
dann immer zwei Symbole durch's Programm geschleppt werden, nämlich
eines für den Port und eines für die Bitnummer.

Ich glaube fast, ich werde beim nächsten Programm, das ich schreibe,
auch die neue Methode anwenden ;-)

von Peter D. (peda)


Lesenswert?

yalu wrote:

> Das klingt interessant. Lag der Grund für die Verbesserung in der
> Verwendung der Bitfelder oder im Weglassen des volatile? Oder anders
> gefragt: Hätte die Maskiermethode gleich guten Code ergeben, wenn man
> bei den Ports das volatile ebenfalls weggelassen hätte?

Es waren ja nur virtuelle Ports, d.h. Bytes im SRAM, die dann nach 
Durchlauf des Steuerprogramms immer zyklisch auf echte Schieberegister 
ausgegeben wurden. Damit ist es egal, wenn die Bits nicht sofort 
geupdatet werden und zwischendurch verbotene Zustände haben.
D.h. sowohl bei der Maskierung als auch bei der Struktur hat der 
Compiler einige Bitbefehle im gleichen Byte zusammengefaßt, bei der 
Struktur aber besser.


> Immerhin
> hat die Methode noch einen anderen Vorteil: Für ein bestimmtes I/O-Pin
> werden Port-Name und Bitnummer in einem einzigen Makro-Symbol
> gekapselt. Das bringt dann Vorteile, wenn man einen Algorithmus
> unabhängig von verwendeten I/O-Pin gestalten will (was eigentlich
> immer guter Stil ist). Mit der klassischen Maskierungsmethode müssen
> dann immer zwei Symbole durch's Programm geschleppt werden, nämlich
> eines für den Port und eines für die Bitnummer.

Genau das ist auch mein Hauptgrund, wenn man viel Bytes und Bits hat, 
verliert man leicht den Überblick, welches Bit in welchem Byte sitzt.
Man müßte also in jedem Bit das Byte mitführen, z.B.:
1
unsigned char reg_in[8], reg_out[8];
2
3
#define REG_IN_3_END_LEFT_2    (1<<2)
4
#define REG_OUT_6_MOTOR_LEFT_2 (1<<7)
5
6
if( reg_in[3] & REG_IN_3_END_LEFT_2 )
7
  reg_out[6] &= ~(REG_OUT_6_MOTOR_LEFT_2);

Ist viel Schreibarbeit und nicht gut leserlich.
Besser lesbar ist:
1
#define END_LEFT_2   SBIT( reg_in[3], 2 )
2
#define MOTOR_LEFT_2 SBIT( reg_out[6], 7)
3
4
if( END_LEFT_2 )
5
  MOTOR_LEFT_2 = 0;

Und wenn man mal das Register ändern muß, kein Problem, nur das Define 
muß geändert werden und nicht alle |=,&= Zeilen, wo das Bit vorkommt.


Peter

von Durchblicker (Gast)


Lesenswert?

Der 8051 kann Variablen mit der Länge 1 Bit ansprechen. Warum sollte man 
dieses nicht von C aus unterstützen, wenn es

1. Den Quelltext lesbarer macht (C ist eine Hochsprache, es darf 
lesbar sein)

2. Der Code kompakter und schneller wird, da die vorhandenen 
Maschinenbefehle verwendet werden können?

Man muss es ja nicht verwenden. Die Portierbarkeit leidet zwar 
erheblich, ist aber nicht unlösbar. Man portiert ja nicht hauptsächlich.

von Gast (Gast)


Lesenswert?

Niemand ist verpflichtet, den C-Standard einzuhalten. Es wird zwar immer 
das Argument der Portierbarkeit rausgekramt, praktisch tritt es eher 
selten auf.  Alleine die Portierung des AVR-Codes auf einen PIC 
erfordert derartig viele defines und #if-Anweisungen, dass man es 
praktisch neu schreibe kann. Das fängt schon damit an, wenn man PORTx 
schreibt, eine unsinnige Anweisung, die die Lesbarkeit bei einer 
Portierung unmöglich macht, wenn der µC statt eines PORTB eine Port P2 
hat. Wenn man schon Argumente der Portierbarkeit aufgreift, sollte man 
die Ports den Funktionen entsprechend nach beschreiben, also z.B. 
LCD_PORT, wobei es schon wieder schwierig ist, wenn der Port für ein 
Lesen nicht umgeschalten werden muss (glaube ist beim Keil so. DATA=1 
und  i=DATA sind dort legitime Anweisungen, ohne dass auf Lesen 
umgeschalten werden muss.)

C für µC ist schon sehr hardwarenah. Nur Standardfunktionen können 
portiert werden. Alles, was auf die Hardware zugreift, und dazu zählt 
oftmals auch das setzen eines einzelnen Bits, ist sowieso schlecht 
portierbar.

Das ist auch der Grund, warum man mit spezifischen Compilern wie IAR, 
HITECH, CVAVR effektiver programmieren kann, als z.B. mit dem 
Universalcompiler gcc.

von yalu (Gast)


Lesenswert?

> Niemand ist verpflichtet, den C-Standard einzuhalten. Es wird zwar
> immer das Argument der Portierbarkeit rausgekramt, praktisch tritt
> es eher selten auf.

Das ist schon richtig. µC-Software ist von Natur aus meist so schlecht
portierbar, dass solche Dinge wie die sbits keine große Rolle mehr
spielen.

Trotzdem ist es manchmal nervig, wenn man, um fremden Quellcode
verstehen zu können, erst das entsprechende Compilerhandbuch
durchlesen muss, auf das man u.U. gar keinen Zugriff hat.

> Das ist auch der Grund, warum man mit spezifischen Compilern wie
> IAR, HITECH, CVAVR effektiver programmieren kann, als z.B. mit dem
> Universalcompiler gcc.

Den Universalcompiler einzusetzen ist vor allem dann angenehm, wenn
man ständig mit mehreren unterschiedlichen Prozessoren zu tun hat. Man
braucht dann Compiler-Optionen, Konfigurationsmöglichkeiten usw. nur
einmal zu lernen. Und das gilt nicht nur für den Compiler selbst,
sondern für die gesamte Tool-Chain. Ich habe den GCC schon für etwa 7
unterschiedliche Prozessorfamilien benutzt und war jedesmal froh, dass
ich mir um die Tool-Chain keine großen Gedanken mehr machen musste und
sofort mit der eigentlichen Arbeit beginnen konnte.

von Gast (Gast)


Lesenswert?

>Trotzdem ist es manchmal nervig, wenn man, um fremden Quellcode
>verstehen zu können, erst das entsprechende Compilerhandbuch
>durchlesen muss, auf das man u.U. gar keinen Zugriff hat.

Ist schon richtig. Das ist aber für den Programmierer uninteressant.

>Den Universalcompiler einzusetzen ist vor allem dann angenehm, wenn
>man ständig mit mehreren unterschiedlichen Prozessoren zu tun hat.

Die Compiler haben weniger einen eigenen Syntax, als dass sie 
hardwarenähere Dialekte bevorzugen (z.B. HITECH mit dem TRISB-Befehl, 
in asm bekannt, in C nicht).

Oder auch dir Unterschiede beim GCC, eine ISR beim AVR entweder mit 
interrupt (vector)... oder einfach nur mit isr(vector). Der mspgcc kennt 
sowas nicht. Letztendlich wird das auch nur ein anderes define sein. 
Aber man sieht ganz klar, wer einen gewissen Syntaxstandard vorgibt und 
das ist IAR. Hardwarespezifische Befehle wie EINT() z.B. kennt man vom 
IAR her.

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.