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?
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
@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
Na die bitweise Logikoperationen kenne ich gut. Mich hat es nur gewundert, dass man beim 8051 z.b. einfach bit=1 schreiben kann.
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.
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.
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"
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
Tja, Programmieren ist immernoch eine Kunst. Der eine findet abstrakte Kunst besser, der andere steht auf anderes.
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 ;-)
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
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.
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.
> 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.
>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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.