Forum: Mikrocontroller und Digitale Elektronik Port Pins mit Struct ansprechen


von FireFox (Gast)


Lesenswert?

Ich hab gestern ein wenig mit Bitfeldern jongliert. Dabei hatte ich die
Idee, einen Port über ein Bitfeld in einzelne Bits aufzuteilen und dann
darauf zuzugreifen. Ich programmiere zur Zeit am ATMEGA8.

Beispiel:

Ich will auf PortB zugreifen:

struct {
   pin_b7:1;
   pin_b6:1;
   pin_b5:1;
   pin_b4:1;
   pin_b3:1;
   pin_b2:1;
   pin_b1:1;
   pin_b0:1;
} port_b;

Nun scheitere ich allerdinga am Versuch meine struct Variable port_b
mit dem Special Function Register Adresse port_b zu verknüpfen.

Kann man das irgendwie realisieren?
Dann könnte ich später mit:

port_b.pin_b5 = 1;

z.B. PINB5 einschalten.

von sven (Gast)


Lesenswert?

ehrlich gesagt verstehe ich den sinn des vorhabens nicht....

was ist gegen die zeile

PORTB |= (1<<PIN5);
bzw.:
PORTB |= 0x20;

für das setzen des pin5 (z.B.) einzuwenden???

oder zum löschen:

PORTB &= ~(1<<PIN5);
bzw.:
PORTB &= 0xdf;

?????

der compiler macht daraus letztendlich eh diese maskierung...

und nun noch eine kleinigkeit zu deiner struktur:
du solltest pragmatischer werden: den datentyp mit dazuschreiben!!!
struct {
   int pin_b7:1;
   int pin_b6:1;
   int pin_b5:1;
   int pin_b4:1;
   int pin_b3:1;
   int pin_b2:1;
   int pin_b1:1;
   int pin_b0:1;
} port_b;

jetzt weisst du schon mal, das du es mit einem integer zu tun hast, in
dem deine einzelbits abgelegt werden... dh. 16Bit sind möglich, das
17te veranlasst den compiler einen weiteren integer anzubrechen...

ps: für bitvariablen einer struktur werden, auch wenn du aus
sparsamkeit char davorschreibst, vom compiler integer eingesetzt!!!
der gcc wirft hier aber keine warnung :-P

nun willst du eine 16-bitvariable in ein 8-bit-I/Oregister prügeln...
dazu musst du rausfinden, wie den compiler die bits in der
integer-variablen ablegt.... fangt er bei bit15 an??? oder so wie du in
der struct beschreibst in der mitte??? bit7???? oder bit0???

hast du dir schon mal in der libc die makros angeschaut, die dir den
scheinbar direkten zugriff auf I/Obereiche ermöglichen??? da musst du
jetzt deine struktur reinprügeln (viel spass :-))

ich kann mich noch an gcc-portierungen der frühen generationen
erinnern, als man um den I/Obereich zu beschreiben sich der outb(char
xy) bedienen musste :-)

von FireFox (Gast)


Lesenswert?

jup, du hast recht.

oben hab ich natürlich das char vergessen. aber mir ist bisher
unbekannt, dass er einen int daraus macht.

Ich hab nichts gegen die Zeile: PORTB |= (1<<PIN5);

Ich bin nur Neugierig. Mir ist gerade nichts besseres eingefallen, wozu
ich sonst die Bitfelder benutzen soll. Aber war eigentlich fast klar,
dass das I/O Mapping nicht leicht wird.

von sven (Gast)


Lesenswert?

Bitvariablen sind tolle sachen, um sich abstact zustände zu merken...
sei es um eine Protokoll-engine auf basis einer state-machine zu
implementieren, oder sich einfach nur für eine software-routine ein
eigenes individuelles flag zu basteln :-)

ps:
 hinter dem doppelpunkt wird die anzahl der bits angegeben, die der
variablen davor zugewiesen werden sollen. du kannst als auch
2-bit-variablen, ...3-bit-variablen, ...n-bit-variablen generieren, die
dann einen zahlenbereich von 0 - ((2^n)-1) abdecken.

struct bsp{
  int  1bitter     :1; [0 - 1]
  int  3bitter     :3; [0 - 7]
}myTypes;

von Tom (Gast)


Lesenswert?

Hi

Die Idee finde ich sehr gut, weil die Standard-Befehle für einen
C-Anfänger enorm kryptisch sind. Dein Code wird durch diese Struktur
viel besser lesbar. Ich würde das mal so versuchen:

Der Struktur würde ich einen eigenen Typenbezeichner geben, z.B.
_IOPort8Bit. Dann kannst du einen Pointer für jeden Port anlegen. Diese
intialisierst du mit der Hardware-Adresse des Ports, die in deinem
uC-spezifischen Headerfile als Konstanten definiert sind:

struct _IOPort8Bit* pPortA = PORTA;
struct _IOPort8Bit* pPortB = PROTB;

Noch was: Das zum Port passende Direction-Register könntest du ja auch
gleich in der Struktur mitverlinken. Das würde etwa so aussehen:

struct _IOPort8Bit {
   unsigned char pin7:1;
   unsigned char pin6:1;
   unsigned char pin5:1;
   unsigned char pin4:1;
   unsigned char pin3:1;
   unsigned char pin2:1;
   unsigned char pin1:1;
   unsigned char pin0:1;
   struct _IOPort8Bit IODir;
};

Ob ein IOPort8Bit in der IOPort8Bit-Definition probleme macht kann ich
jetzt so nicht sagen. Ich hab aber auch schon solch abenteurliche
Konstrukte erfolgreich kompiliert.

Ich hab unsigned char genommen, weil das nur 8Bit sind. Ob das wie
gewollt rauskommt ist im Debugger zu prüfen. Die Porbezeichnung B hat
in den Pin-Namen nichts zu suchen, du willst ja dieselbe Struktur für
alle Ports benutzen!

Zudem würde ich nicht pPortA->pin1 = 1 schreiben, sondern den folgenden
Enummerator verwenden:

enum PinState { Off = 0, On };

pPortA->pin1 = On;

Das macht zwar keinen Unterschied, aber dein Code wird besser lesbar.
Das müsste dann aber eigneltlich als Integer abgelegt werden im Code.
Diese Variante könnte dir eventuell noch etwas Codespeicher spaaren:

#define Off 0x00
#define On  0x01

Gruss & HTH

Tom

von OldBug (Gast)


Lesenswert?

>struct _IOPort8Bit {

Den führenden Unterstrich solltet ihr besser weglassen, möglicherweise
kollidiert ihr mit Compilerintrnas oder der C-Standardlib.

von sven (Gast)


Lesenswert?

na da legt ihr den anfängern ja ein neues kryptisches ei :-D

maskierung sollte man als grundlage der digitaltechnik schon
draufhaben, bevor man an µC's denkt...

aber viel spass dabei, ich werde in zukünftigen libc-versionen auf eine
solche implementierung warten:-)
dann kann ich sagen, ich habe zur entstehung meinen senf geliefert :-P
;-) :-)

von OldBug (Gast)


Lesenswert?

Ich fürchte, diese Vorgehensweise wird einem Anfänger mehr schaden als
helfen. Darauf wollte ich aber eigentlich gar nicht eingehen. Besser
lesbar wird der Code dadurch nämlich gar nicht. Erfahrenere
Programmierer brauchen solche "Tricks" nicht und müssen, wenn der
Anfänger später nochmal ein Problem hat, erst mal nachvollziehen, wie
zum Teufel da auf einen Port oder was auch immer zugegriffen wird.
Da würd ich jetzt einfach mal bezweifeln, daß das überhaupt jemand
macht.

Also: lieber gleich an die "korrekte" Schreibweise gewöhnen. Einmal
verstanden gibts da auch nix Kompliziertes mehr dran... :)

von Tom (Gast)


Lesenswert?

@OldBug

Ich glaube eher weniger, dass das einen Unterschied macht. Hauptsache
ist, dass der Bezeichner nirgendwo sonst verwendet wird. Du kannst
natürlich auch einen anderen Präfix nehmen, beispielsweise ein s.

Ich mache das desshalb, weil in C immer struct [Typenbezeichner]
[Variablenname]und in C++ nur [Typenbezeichner] [Variablenname]
angegeben werden muss, um eine solche Struktur zu deklarieren. Um nicht
den ganzen Code bei einer Portierung umschreiben zu müssen, gehe ich
folgendermassen vor:

struct sIOPort8Bit {
....
};
#ifndef __cplusplus
  #define IOPort8Bit struct sIOPort8Bit
#else
  #define IOPort8Bit sIOPort8Bit
#endif

IOPort8Bit* pPortA = NULL;
IOPort8Bit* pPortB = NULL;
IOPort8Bit* pPortC = NULL;

Ein Test steht aber noch aus ;-)

Gruss

Tom

von OldBug (Gast)


Lesenswert?

Ich hätte mich da ein wenig an der stdint.h-Form orientiert:

struct
mystruct_s
{
  /* ... */
};

Bzw.:

typedef struct
mystruct_s
{
  /* ... */
} mystruct_t;

Das anführen von  oder _ ist laut Standard übrigens nicht erlaubt...

von Peter Dannegger (Gast)


Lesenswert?

Ich mag Bitfelder nicht, weil ich mir nie merken kann, ob die nun
rechtsbündig oder linksbündig zugewiesen werden.

Ist der erste Eintrag nun Bit 0 oder Bit 7 ?

Dagegen weiß ich bei (1<<7) ganz genau, daß es Bit 7 sein muß.


Für Flags nehme ich in der Regel ganze Bytes, da das Code spart und die
AVRs ja massig SRAM haben. Nur beim 8051 können ja Bits im SRAM direkt
angesprochen werden.


Peter

von JojoS (Gast)


Lesenswert?

Bitfelder und andere Methoden zum Portzugriff sind auch ausführlich im
[AVR-GCC-Tutorial] erklärt.

von Tom (Gast)


Lesenswert?

Nochmal @OldBug

Das mit der Lesbarkeit sehe ich entschieden anders! Bitmanipulationen
sind ganz klar ein fortgeschrittenes Thema, mit dem sich normalerweise
nur Elektroniker schon früh beschäftigen müssen, weil sie halt mit uCs
arbeiten. Frag mal einen C-Anfänger oder auch einen VB-Programmierer,
die haben noch nie von solchen Sachen gehört fg.

Zudem kann nicht jeder im Schlaf vom Hexadezimal- ins Binärsystem und
wieder zurückrechnen, da passieren schnell mal Fehler. Dass du das
kannst, weil du es dich gewohnt bist, glaube ich dir natürlich. Schwer
ist es ja nicht. Trotzdem kannst du mit der Struktur-Variante im Code
gleich lesen, was du bei dem normalen Weg erst noch umrechnen musst.
War 0x02 jetzt Pin 1 oder 6? Wenn du aber nur ein kleines Programm
schreiben musst, dann kannst du natürlich auch Lösungs-spezifische
Konstanten und Makros verwenden, die den Code lesbarer machen. Das
könnte so aussehen:

#define   SETPIN(port, pins) port |= pins
#define UNSETPIN(port, pins) port &= ~pins

#define LAMPE1 0x01
#define LAMPE2 0x02
#define LAMPE3 0x04

SETPIN(PORTA, LAMPE1 & LAMPE2 & LAMPE3);
UNSETPIN(PORTA, LAMPE2);

Jetzt schweife ich aber vom Thema ab. Ich finde die Diskussion sehr
spannend. Lesbarkeit ist mir wichtig, wie du unschwer erkennen kannst,
denn sie vermeidet auch logische Fehler und erleichtert das Debuggen.

Genau nachvollziehen, wie das Setzen von Pins genau funktioniert muss
man nur, wenn man genau dort den Fehler vermutet. Die Makros und
Strukturen sind ja selbstredend und schief laufen kann da nicht viel.

Die Struktur hat aber noch einen weiteren Vorteil, den ich auch sehr
praktisch finde: Der Code wird von der Hardware unabhängig. Dein
benutzer Ausgang wechselt von Port A nach B? Kein Problem, du musst nur
die Initalisierung des Strukturpointers anpassen. Die Pins sind auf
mehrere Ports verteilt? Auch kein Problem: Du speicherst die Strutur im
RAM und verteilst in einer periodisch aufgerufenen Routine die
einezelnen Bits auf die richtigen Ports und Pins. Den Code, der den
Port verwendet, musst du nicht an(f|p)assen. Das ist doch ein
entscheidender Vorteil! Ich bin auf eure Meinungen gespannt.

Gruss

Tom

von OldBug (Gast)


Lesenswert?

Wer hardwarenah Programmieren will, der sollte sich schnellstmöglich
an die Standardschreibweise mit Bit-Operanden gewöhnen. Die Ausrede
"ich kan nicht von Hex nach Dez und Bin" gilt hier nicht mehr!
Daß Code Grundsätzlich möglichst gut lesbar geschrieben werden sollte,
sehe ich auch so, aber ist diese Variante wirklich besseer Lesbar?
Ist On = On oder vielleicht doch Off? ;)
Portabel bekommt man seinen Code auch ohne solche tricks,
beispielsweise über eine Headerdatei, in der sämtliche Hardwarezugriffe
abgebildet werden.

von Tom (Gast)


Lesenswert?

Schonwieder ich

In der C Language Reference der MSDN steht bezüglich Bezeichner in C:

The first character of an identifier name must be a nondigit (that is,
the first character must be an underscore or an uppercase or lowercase
letter).

Somit ist der _ die Ausnahme in der Regel, dass Bezeichner keine
Sonderzeichen enthalten dürfen.

typedef ist natürlich schöner als ein #define, mit typedef hatte ich
jedoch des öfteren Probleme - wobei ich nicht ausschliessen will, dass
das an mir liegt ;-).

von OldBug (Gast)


Lesenswert?

Ich habe jetzt keinen direkten Verweis auf die Stelle im Standard, aber
diese Diskussion gab es schon öfters, u.a. hier:

  http://www.mikrocontroller.net/forum/read-2-162277.html#162412

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Ich habe nicht das geringste Verstaendnis dafuer wenn jemand wiederholt
Zeilen wie diese hier im Programm verwendet:

while (~PINB & (1 << 5))

Die Ausrede "an Bitoperatoren muss man sich halt gewoehnen" zaehlt
nicht; egal wie vertraut man mit den Operatoren ist, obige Zeile ist
immer fehleranfaelliger und schwerer zu lesen als folgendes:

while (bit_is_clear(PINB, 5))

von FireFox (Gast)


Lesenswert?

vielleicht als anmerkung:

die ganze verwendung von structs kommt aus einem sicherheitsrelevanten
projekt, welches portabel für mehrere controller sein soll.

außerdem verbietet der tüv auch den einsatz von globalen variablen, da
diese unbemerkt überschrieben werden können. bei structs ist durch den
structname:structvariable syntax dieses problem zwar immernoch
vorhanden, allerdings auf jedenfall unwahrscheinlicher.

von Tom (Gast)


Lesenswert?

Okay, dann lass uns doch mal einen direkten Vergleich starten. Die
Aufgabe: Eine Funktion soll die Lampe A ein und die Lampe B
ausschalten.

Hardwarenah:
PORTA |= 0x01;
PORTA &= 0xFD;  bzw.  PORTA &= ~0x02;

Damit hätten wir auch die IOs für die Aufgabe definiert. Zum lesen muss
ich wissen, dass |= Bits zusätzlich setzt, und dass z.B. 0x02 Pin1
selektiert. Damit kann ich lesen, dass auf PortA Pin0 gesetzt und Pin1
entfernt werden.

'Strukturiert':
pPortA->pin0 = On;
pPortA->pin1 = Off;

Hier lese ich, dass Pin0 und 1 an PortA geschaltet werden. Ich muss nur
noch wissen, wofür die gut sind.

'Makroriert':
SETPIN(PORTA, LAMP1);
UNSETPIN(PORTA, LAMP2);

Hier lese ich gleich, dass Lampe 1 ein und Lampe 2 ausgeschaltet
werden.


>> Wer hardwarenah Programmieren will, der sollte sich
schnellstmöglich
an die Standardschreibweise mit Bit-Operanden gewöhnen.
Das ist mir auch klar.

Noch einen Satz zur Hardwareabstraktion: Ich würde eher nicht über die
Hardware-spezifischen Headerfiles gehen, gerade weil diese Symbole
Standard sind und nicht abgeändert werden sollten. Die Auswirkungen
wären in einem komplexen Programm nur sehr schwer nachvollziehbar.
PORTA muss IMO immer PORTA bleiben.

Gruss

Tom

von OldBug (Gast)


Lesenswert?

>while (bit_is_clear(PINB, 5))

Und jetzt erzähl mir noch, daß das Portabel ist ;)
Genausowenig sind irgendwelche structs Portabel...

>außerdem verbietet der tüv auch den einsatz von globalen variablen,
>da diese unbemerkt überschrieben werden können.

Das kann genausogut mit lokalen Variablen passieren, und zwar mit
gleich hoher Wahrscheinlichkeit, siehe Stacküberlauf...

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Was ist an while (bit_is_clear(PINB, 5)) nicht portabel? Das Makro wird
einmal definiert, fertig.

von sven (Gast)


Lesenswert?

findet ihr nicht, dass es hier langsam etwas religiös zugeht???

---> jedem das, dass ihm am besten liegt...

von OldBug (Gast)


Lesenswert?

@sven:

Ich find die Diskussion eigentlich recht interessant und mich
interessieren die Meinungen anderer schon noch ab- und zu...

@Andreas:

Das ist natürlich richtig. Es ging mir darum, daß jemand, der noch nie
den avr-gcc benutzt hat, dieses Makro überhaupt nicht kennt...

von Peter Dannegger (Gast)


Lesenswert?

Leute Leute,

bevor man sich über was streitet, sollte es nicht vielleicht überhaupt
erstmal gehen ?

Keines der obigen Strukturbeispiele geht unter AVR-GCC !!!

Also hat sich die Diskussion doch wohl erledigt.


Peter

P.S.:
Übrigens legt AVR-GCC die Struktur rechtsbündig an, d.h. das erste Bit
ist 0 und nicht 7.

von Tom (Gast)


Lesenswert?

Hi Leute

struct _IOPort8Bit {
   unsigned char pin7:1;
   unsigned char pin6:1;
   unsigned char pin5:1;
   unsigned char pin4:1;
   unsigned char pin3:1;
   unsigned char pin2:1;
   unsigned char pin1:1;
   unsigned char pin0:1;
   struct _IOPort8Bit *IODir;

};

Mit einem Pointer auf das IODir kompiliert dieser Code fehlerfrei in
meinem VisualStudio 6.0. Einen uC-Kompiler hab ich leider grad nicht
da.

Die Makros hab ich so auch schon verwendet.

BTW: In Foren programmiere ich immer in C-ähnlichem Pseudocode wenn
nichts anderes angegeben ist ;-). Meine Konstanten haben natürlich
nichts mit AVR zu tun. Übereinstimmungen sind rein zufällig. Es ist wie
bei den elektrischen Schaltungen hier im Forum: 1:1 übernehmen geht eher
selten, man muss schon (fast) alles nachvollziehen können und verstehen.
Ich beiss mir da manchmal recht die Zähne aus.

Für meinen nächsten Prototypen, den ich im August nach den Ferien bauen
werde, werde ich mal die Makro-Methode verwenden und im praktischen
Einsatz untersuchen, ob sie wirklich was taugt.

von OldBug (Gast)


Angehängte Dateien:

Lesenswert?

>Keines der obigen Strukturbeispiele geht unter AVR-GCC !!!

Der Ansicht war ich bisher auch aber:
Ich habs nicht auf realer Hardware getestet, aber der Simulator sagt da
was anderes :-)

von FireFox (Gast)


Lesenswert?

also ich werde nachher das struct mal mit dem µc testen, sofern ich dazu
komme.

von OldBug (Gast)


Lesenswert?

Brauchst in meinem Makefile nur den Prozessor anzupassen, der steht im
Moment auf ATMega128...

von Tom (Gast)


Lesenswert?

@OldBug

Du sprichst jetzt von der Hardwareunabhängigkeit, oder? Wenn du nicht
die hardwareabhängigen Konstanten aus dem Headerfile, sondern eine
richtige Hardware-Abstraktion brauchst, dann kannst du auch die
Beschaltung mit gleichem Typ sehr einfach ändern. Also zum Beispiel
eine Relaiplatine von PortA nach PortC hängen oder die ersten vier
Relais auf PortB und die anderen an PortD und solche Spässe.

von Peter Dannegger (Gast)


Lesenswert?

@OldBug

"struct-io.tar.bz2"

Grrr, was ist denn das ?

Ich hab leider nicht soviel Zeit, erstmal 50 Entpacker anzuschmeißen.

Die 3 Zeilen hätste doch wohl in plain Text schreiben können.


Peter

von OldBug (Gast)


Angehängte Dateien:

Lesenswert?

Hm, sorry, ist halt Gewohnheitssache :\
Hier nochmal als PowerArch-Zip...

(paar Zeilen nicht als Plain, weil da das komplette Kompilat
drinsteckt)

von OldBug (Gast)


Lesenswert?

Wenn Du WinAVR hast geht das übrigens mit einer einzigen Zeile in dem
Verzeichnis Deines Vertrauens mit:

  $ tar -xjvf struct-io.tar.bz2

von Peter Dannegger (Gast)


Lesenswert?

Du hast recht, es geht, aber zu welchem Preis:

                porta_mirr->pin0 = 1;
  62:   e0 91 60 00     lds     r30, 0x0060
  66:   f0 91 61 00     lds     r31, 0x0061
  6a:   80 81           ld      r24, Z
  6c:   81 60           ori     r24, 0x01
  6e:   80 83           st      Z, r24

                porta_mirr->pin7 = 0;
  70:   e0 91 60 00     lds     r30, 0x0060
  74:   f0 91 61 00     lds     r31, 0x0061
  78:   80 81           ld      r24, Z
  7a:   8f 77           andi    r24, 0x7F
  7c:   80 83           st      Z, r24


Statt profanes SBI / CBI und zusätzlich noch 2 Bytes als Pointer je
Port.


Peter

von OldBug (Gast)


Lesenswert?

Daß das noch effektiv ist habe ich ja gar nicht behauptet gg

von Peter Dannegger (Gast)


Lesenswert?

Immerhin stimmt jetzt sogar die Reihenfolge der Bits.

Ist das irgendwo dokumentiert, oder hast Du das meinem Hinweis
entnommen.


Das tar habe ich unter utils gefunden (hätte ich noch dem Pfad
hinzufügen müssen), aber das -xjvf hätte mich wohl Stunden Man-Pages
gekostet.


Peter

von Unbekannter (Gast)


Lesenswert?

> Hardwarenah:
> PORTA |= 0x01;
> PORTA &= 0xFD;  bzw.  PORTA &= ~0x02;

VS.

> 'Makroriert':
> SETPIN(PORTA, LAMP1);
> UNSETPIN(PORTA, LAMP2);


Letzteres ist immer vorzuziehen, da die Intention des Programmieres
viel deutlicher wird, nämlich bestimmte Pins für bestimmte Lampen am
Port A setzen bzw. löschen.

Allerdings hätte ich nicht "UNSETPIN" sondern "CLEARPIN" o.ä.
verwendet.

Um Diskussionen vorzubeugen, ob nun "Set" ein High- oder Low-Pegel
bedeutet, könnten man die beiden Makros auch "PINHIGH" und "PINLOW"
oder "SETHIGH" und "SETLOW" etc. taufen.

Immer daran denken: Man programmiert nicht für den Controller oder den
Computer, sondern für Menschen. Der Compiler versteht alle
Formulierungen gleich gut und optimiert sie in gleicherweise zu einem
einzelnen Assemblerbefehl.

So macht es auch einen großen Unterschied, ob ich

   for(i=0; i<123; i++)

oder

   for(index=0; index<sizeof(xyz); i++)

Es ist wichtig, daß auch Anfänger sich gleich einen ordentlichen
Programmierstil aneignen, und nicht in den "Hack und Frimel Modus"
verfallen.

Bei dem Bit-rumgeschuppse passieren viel zu viele Fehler.

So hat Tom weiter oben (etwas gekürzt) geschrieben:


> #define SETPIN(port, pins) port |= pins
>
> #define LAMPE1 0x01
> #define LAMPE2 0x02
> #define LAMPE3 0x04
>
> SETPIN(PORTA, LAMPE1 & LAMPE2 & LAMPE3);

Schon hat sich in der Letzte Zeile ein Fehler eingeschlichen... Darf
jeder selbst überlegen welcher...

von Jens (Gast)


Lesenswert?

> Zudem kann nicht jeder im Schlaf vom Hexadezimal- ins Binärsystem
> und wieder zurückrechnen

Wozu auch. Jedes mir bekannte Betriebssystem verfügt über eine
Taschenrechnersoftware, die das problemlos kann.

Ich finde Bitfelder extrem angenehm, Microchip z. B. setzt beim C18 und
C30 grundsätzlich auf diese Methode. Ist doch besser, wenn ich
schreibe:

PORTBbits.PB7 = 1;

statt

PORTB |= (1<<7);

Ist halt meine Meinung.

Jens

von OldBug (Gast)


Lesenswert?

>Ist das irgendwo dokumentiert, oder hast Du das meinem Hinweis
>entnommen.

Ich hab Deinen Hinweis im Hinterkopf gehabt, aber erstmal die verdrehte
Version Kompiliert. Wenn man dann bei

  porta_mirr->pin0 ^= 1;

im Simulator PA7 Toggeln sieht, dann springt einem der Hinweis wieder
wie gerufen vors Auge :-)

von Volker (Gast)


Lesenswert?

Das ganze hatten wir doch schon mal in ähnlicher Form.

http://www.mikrocontroller.net/forum/read-2-158895.html#158965


Gruß Volker

von Tom (Gast)


Lesenswert?

@Unbekannter

Eigentlich kann man noch weiter abstrahieren. Meine neuen Makros sehen
so aus:

#define   ACTIVATE(port, sig) port |=  (sig)
#define DEACTIVATE(port, sig) port &= ~(sig)

Anstatt von PORTA verwendet man dann RELAIS_PORT, was wiederum PORTA
entspricht ;-). Damit bist du auf einer logischen Ebene, die auch von
Pins und Highs und Lows unabhängig ist. Overhead entsteht auch keiner.

Zu meinem Fehler:

SETPIN(PORTA, LAMPE1 & LAMPE2 & LAMPE3);

Ow Mann! Klar, die '&' sollten natürlich + oder | sein.

von Steff (Gast)


Lesenswert?

Eine frage zu struct wuerde micht interessieren. Ist es moeglich in dem
folgenden beispiel

struct {
   int pin_b7:1,
       pin_b6:1,
       pin_b5432:4,
       pin_b1:1,
       pin_b0:1;
} port_b;

mit einem increment befehl

port_b.pin_b5432++

einen aufwärtszähler an den portpin 5-4-3-2 realisiere der stetig von 0
bis 15 zählt?

von sven (Gast)


Lesenswert?

@steff: JA

einzige voraussetzung, die leuts hier schaffen es, das struct in die
I/O's zu mappen... :-)

von FireFox (Gast)


Lesenswert?

@sven

na das haben wir doch geschafft ... :)

von sven (Gast)


Lesenswert?

oh, hab ich was verpasst :-)
dann scroll ich gleich ma nach oben :-D

von Jörg B. (joerg-sh)


Lesenswert?

Auch wenn dieser Beitrag schon Uralt ist.
Ich möchte es gerne noch mal aufgreifen.

Die Macro Lösung finde ich als Anfänger in C sehr gut.


#define   SETPIN(port, pins) port |= pins
#define UNSETPIN(port, pins) port &= ~pins

#define LAMPE1 0x01
#define LAMPE2 0x02
#define LAMPE3 0x04

SETPIN(PORTA, LAMPE1 + LAMPE2 + LAMPE3);
UNSETPIN(PORTA, LAMPE2);



Könnte man dieses Macro jetzt noch so ändern, dass ich nur noch


SETPIN (LAMP1);
UNSETPIN (LAMPE1);


schreiben muss?


Mir persönlich ist es einfach hilfreicher, wenn ich einfach das an oder 
ausschalte was ich Namentlich kenne und nicht ein Port/ Portpin wo ich 
dann erst mal nachsehen muss wo meine LED etc dran hängt.

von holger (Gast)


Lesenswert?

>Könnte man dieses Macro jetzt noch so ändern, dass ich nur noch
>
>SETPIN (LAMP1);
>UNSETPIN (LAMPE1);
>
>schreiben muss?

#define LAMPE_AN   SETPIN(PORTA, LAMPE1);
#define LAMPE_AUS   UNSETPIN(PORTA, LAMPE1);

von Anja (Gast)


Lesenswert?

Hallo,

ich verwende folgendes struct:

(damit werden dann auch die sbi und cbi-Befehle des Prozessors 
verwendet)
1
    typedef struct
2
  {
3
     volatile bool B0: 1;
4
     volatile bool B1: 1;
5
     volatile bool B2: 1;
6
     volatile bool B3: 1;
7
     volatile bool B4: 1;
8
     volatile bool B5: 1;
9
     volatile bool B6: 1;
10
     volatile bool B7: 1;
11
  } Bits;
12
13
    #define LED (*(Bits *)(&PORTB)).B3
14
15
    LED = 1;
16
    /* delay */
17
    LED = 0;

Gruß Anja

von Klaus W. (mfgkw)


Lesenswert?

holger schrieb:
> ...
> #define LAMPE_AN   SETPIN(PORTA, LAMPE1);
> #define LAMPE_AUS   UNSETPIN(PORTA, LAMPE1);

die Semikolon am Ende sollte man besser löschen.

von Jörg B. (joerg-sh)


Lesenswert?

Das mit dem Semikolon war mein Fehler.

Das was Anja geschrieben hat ist natürlich noch vertrauter.

Leider meckert CodeVisionAVR den Syntax an

Er mag

 volatile bool B0: 1;

nicht.

von Anja (Gast)


Lesenswert?

Jörg B. schrieb:
> Er mag
>
>  volatile bool B0: 1;
>
> nicht.

sorry da fehlt noch ein:
    typedef uint8_t      bool;

was bei mir in den globalen Typdefinitionen mit drin steckt.

Gruß Anja

von Jörg B. (joerg-sh)


Lesenswert?

Hallo Anja,
danke für deine Hilfe!

Jetzt meckert er: Error: testdifine.c(28): ';' expected

bei

typedef uint8_t      bool;

von Klaus W. (mfgkw)


Lesenswert?

Anja schrieb:
> Hallo,
>
> ich verwende folgendes struct:
>
> (damit werden dann auch die sbi und cbi-Befehle des Prozessors
> verwendet)
>
>
1
>     typedef struct
2
>   {
3
>      volatile bool B0: 1;
4
>      volatile bool B1: 1;
5
>      volatile bool B2: 1;
6
>      volatile bool B3: 1;
7
>      volatile bool B4: 1;
8
>      volatile bool B5: 1;
9
>      volatile bool B6: 1;
10
>      volatile bool B7: 1;
11
>   } Bits;
12
> 
13
>     #define LED (*(Bits *)(&PORTB)).B3
14
> 
15
>     LED = 1;
16
>     /* delay */
17
>     LED = 0;
18
>
>
> Gruß Anja


Ja, so etwas finde ich auch wesentlich besser: sauberer, klarer, 
fehlerärmer.

Aber ein paar Stilvorschläge hätte ich dazu:
- man kann sich einen eigenen logischen Datentyp schaffen,
  der hat aber in einem Bitfeld nichts verloren.
  uint8_t macht auf einem AVR meistens Sinn für so einen Typ, aber
  in einem Bitfeld wird die Länge ja gezielt anderweitig vorgegeben.
  Deshalb gehört als Grundtyp da nur int oder unsigned hin, kein
  uintirgendwas_t oder char etc..
  Hier natürlich unsigned.
- bool würde ohnehin mit C++ kollidieren.
- das volatile gehört nicht an die Bits, weil sich nicht bei
  denen entscheidet, ob sie volatile sind oder nicht.
  Es gehört auch nicht in die struct, weil die ja nur dazu dient,
  ein Byte auf Bits aufzuteilen. Man könnte sie auch für
  nicht-volatile Elemente verwenden.
  Vielmehr ist die Anwendung im Zusammenhang mit dem Register
  die richtige Stelle für das volatile, weil es für PORTB
  angebracht ist.

Dann sieht es etwa so aus:
1
typedef struct
2
{
3
  unsigned bit0: 1;
4
  unsigned bit1: 1;
5
  unsigned bit2: 1;
6
  unsigned bit3: 1;
7
  unsigned bit4: 1;
8
  unsigned bit5: 1;
9
  unsigned bit6: 1;
10
  unsigned bit7: 1;
11
} byte_bits_t;
12
13
14
#define LED ((*(volatile byte_bits_t*)(&PORTB)).bit3)
15
16
...
17
  LED = 1;
18
  /* delay */
19
  LED = 0;

von Rolf M. (rmagnus)


Lesenswert?

Klaus Wachtler schrieb:
> - bool würde ohnehin mit C++ kollidieren.

Und mit C, wenn man #include <stdbool.h> nutzt.

von Jörg B. (joerg-sh)


Lesenswert?

Ich bin begeistert. Ausprobiert und im Simulator getestet. 1++

Vielen Dank

von H.Joachim S. (crazyhorse)


Lesenswert?

Jörg B. schrieb:
> Das mit dem Semikolon war mein Fehler.
>
> Das was Anja geschrieben hat ist natürlich noch vertrauter.
>
> Leider meckert CodeVisionAVR den Syntax an
>
> Er mag
>
>  volatile bool B0: 1;
>
> nicht.

Tja, CodeVision braucht das aber alles nicht...

#define Alarm_LED PortB.6

.
.
Alarm_LED=0;
Alarm_LED=1;

Selbsterklärend und garantiert kürzest mögliche Compilierung (cbi, sbi).
Nachteil: nicht ANSI, nicht portierbar.
Damit kann ich aber prima leben, habe noch nie ein Projekt von einer 
MC-Familie auf eine andere übertragen müssen/wollen. Allenfalls von 
einem kleineren auf einen grösseren AVR, seltener auch schon mal auf 
einen kleineren.
Hand aufs Herz: hat tatsächlich schon mal einer ein Programm, dass für 
nen AVR geschrieben, z.B. auf einen MSP430 portiert? Selbst wenn - die 
paar Sachen sind dann auch schnell geändert.

Ebenso sehr nützlich, Bitvariable als flags. Besonders effektiv bei 
Controllern mit GPIO-Register, z.B. Mega88

bit rec_complete;

rec_complete=1;     ->SBI  0x1E,0
if (rec_complete)   ->SBIS 0x1E,0
                RJMP _0xxx
   {
   }
Schneller und Codeeffektiver gehts nicht. Kann denn der gcc/WinAVR die 
GPIOs überhaupt sinnvoll nutzen?

von Jörg B. (joerg-sh)


Lesenswert?

Prima, da ich CodeVisonAVR auch nutze... Schade, dass ich so was nicht 
im Handbuch gefunden habe. Noch besser wäre es wenn so was gleich im 
CodeWizzard mit eingetragen werden könnte.

von Jörg B. (joerg-sh)


Lesenswert?

H.joachim Seifert schrieb:
> Tja, CodeVision braucht das aber alles nicht...
>
> #define Alarm_LED PortB.6
>
> .
> .
> Alarm_LED=0;
> Alarm_LED=1;


hmmm, so einfach geht es dann aber wohl doch nicht.

Ich benutze einen Xmega. Muss man es da denn noch anders definieren?

Es kommt die Fehlermeldung: Error: testdifine.c(601): undefined symbol 
'PortB'

von H.Joachim S. (crazyhorse)


Lesenswert?

Andreas Schwarz schrieb:
> Ich habe nicht das geringste Verstaendnis dafuer wenn jemand wiederholt
> Zeilen wie diese hier im Programm verwendet:
>
> while (~PINB & (1 << 5))
>
> Die Ausrede "an Bitoperatoren muss man sich halt gewoehnen" zaehlt
> nicht; egal wie vertraut man mit den Operatoren ist, obige Zeile ist
> immer fehleranfaelliger und schwerer zu lesen als folgendes:
>
> while (bit_is_clear(PINB, 5))

und das sieht dann so aus:

while (!PINB.5)
Für mich die übersichtlichste Variante.

@Jörg B.
Vom XMega habe ich keinen Schimmer. Und den werde ich auch nie anfassen 
:-)
Muss aber bestimmt PORTB heissen, also alles gross geschrieben. Mein 
Fehler.

von Jörg B. (joerg-sh)


Lesenswert?

H.joachim Seifert schrieb:
> Vom XMega habe ich keinen Schimmer. Und den werde ich auch nie anfassen
> :-)
> Muss aber bestimmt PORTB heissen, also alles gross geschrieben. Mein
> Fehler.

dann wird aus dem Fehler ein Error: testdifine.c(600): illegal symbol


Was hast du denn gegen den Xmega? Bietet doch einiges mehr für kleines 
Geld.

Der Xmega256A3B hat sogar eine Batterie gepuffert RTC eingebaut.

von H.Joachim S. (crazyhorse)


Lesenswert?

Naja, die I/O-Struktur der XMegas hat ja so gut wie nichts mehr mit der 
der normalen AVRs zu tun. Die Ports werden wohl nicht mehr mit in/out 
angesprochen, sondern memory mapped mit z.B. lds/sts. Damit entfallen 
auch die Einzelbit-Befehle auf die einzelnen Pins. Und damit erübrigt 
sich obiges Verfahren.

Warum ich sie nicht einsetzen werde: es bleibt ein 8bit-Controller, und 
er ist im Vergleich zum STM32 deutlich teuerer. Ausserdem gibts es bei 
keinem XMega einen oder gar mehrere CAN-Controller on Chip.
Atmel war bisher nicht gerade der zuverlässigste Hersteller, und ich 
denke, die XMega werden kein besonders langes Leben haben. Die 
Preispolitik von Atmel verstehe ich nicht.
Vorkenntnisse mit dem ATMega helfen nicht wirklich was, also wird es so 
oder so eine völlige Neueinarbeitung, und da habe ich persönlich auf den 
STM32 gesetzt.
Der billigste XMega in Einzelstückzahlen kostet bei Digikey um die 3€ 
(16D4), ein in etwa vergleichbarer STM32F101C4 1,70€, bringt aber 4k Ram 
statt 2k.
Rechenknechte brauchen aber inzwischen immer mehr Speicher, beim XMega 
ist bei 256k Flash Schluss, beim RAM bei mickrigen 16k. Das ist nicht 
mehr zeigemäss. Einzige für mich sichtbare Vorteile: der interne EEProm 
und bis zu 6 UARTs.

von Jörg B. (joerg-sh)


Lesenswert?

Ich beschäftige mich einfach zu selten mit der Materie, wollte jetzt 
aber mal endlich den Absprung von Bascom schaffen, da gerade ein etwas 
größeres Projekt ansteht.

Die Preise sind echt super günstig. Ich habe eben mal bei Ebay gestöbert 
und ein Eintwicklungsboard mit STM32F103RC MCU für umgerechnet 21 Euro 
frei Haus gefunden.  Da brauch ich dann nur noch einen Vorhandenen 
USB-Serial Adapter dran stecken und kann damit dank enthaltenen 
Bootloader programmieren...

von Andreas W. (geier99)


Lesenswert?

Klaus Wachtler schrieb:

das hier:
>>     #define LED (*(Bits *)(&PORTB)).B3

und noch besser das hier:
> #define LED ((*(volatile byte_bits_t*)(&PORTB)).bit3)


ist jetzt für einen Anfänger, der von C noch wenig Ahnung hat 
verständlicher als die Bit-Schieberrei?

Prosit Neues Jahr :-)

von Achim M. (minifloat)


Lesenswert?

Tom schrieb:
> #define   SETPIN(port, pins) port |= pins
> #define UNSETPIN(port, pins) port &= ~pins
>
> #define LAMPE1 0x01
> #define LAMPE2 0x02
> #define LAMPE3 0x04
>
> SETPIN(PORTA, LAMPE1 & LAMPE2 & LAMPE3);
> UNSETPIN(PORTA, LAMPE2);

Hallo,

SETPIN(PORTA, LAMPE1 & LAMPE2 & LAMPE3);

wird kein einziges Bit setzen.

0x01 & 0x02 & 0x04 ist immer noch 0x00.

mfg mf

von Klaus W. (mfgkw)


Lesenswert?

Andreas W. schrieb:
> ...
> ist jetzt für einen Anfänger, der von C noch wenig Ahnung hat
> verständlicher als die Bit-Schieberrei?


Das steht an einer Stelle einmal vorne.

Wenn man dagegen das ganze Programm mit Pblabla &= ~(1<<blubb) und 
Pblabla |= (1<<blubb) vollgekleistert ist und ich dagegen LED12 = 0 oder 
LED12 = 1 dagegen halte, weiß ich aber schon was fehlerträchtiger ist 
und was lesbarer.


Im ersten Fall hat schon ein paar Fehlerquellen mehr:
- ~ vergessen
- & und | verwechselt
- Port und Pinnummer passen nicht zusammen
- in komplexeren Ausdrücken Klammerung verbaselt
- ...

Das sind Sachen, die man zu knapp 100% in den Griff bekommt.
Aber eben nur knapp, und bei Verwendung an Dutzenden und Hunderten 
Stellen entfernt man sich schnell von den 100%, meist eher nach unten.
Ich muß auch noch öfter nachdenken, wie ich es denn nun mache.

Warum soll man sich das antun?
Für sowas habe ich einen Compiler.

Bei Zehnzeilern ist es natürlich wumpe, aber wenn ein Programm länger 
wird, ist man doch um jede Fehlerquelle froh, die man getilgt hat.

Es kann ja jeder glücklich werden wie er will, ich will niemandem etwas 
aufschwatzen.
Aber meine Meinung habe ich, und die ewige Bitschieberei in Massen kann 
mich nicht überzeugen.

von Klaus W. (mfgkw)


Lesenswert?

Mini Float schrieb:
> Tom schrieb:
>> #define   SETPIN(port, pins) port |= pins
> ...
> wird kein einziges Bit setzen.
>
> 0x01 & 0x02 & 0x04 ist immer noch 0x00.
>
> mfg mf

Mit LAMPE1 = LAMPE2 = LAMPE3 = 1; wäre das nicht passiert :-)

von Jörg B. (joerg-sh)


Lesenswert?

Ne brauchbare Lösung für den XMega habe ich jetzt aber immer noch nicht.

Also mal Zeit die Entwicklungsumgebung für den STM zu Installieren. Gibt 
hier ja ein gutes Tutorial dazu :)

von Jörg B. (joerg-sh)


Lesenswert?

Ne brauchbare Lösung für den XMega habe ich jetzt aber immer noch nicht.

Also mal Zeit die Entwicklungsumgebung für den STM zu Installieren. Gibt 
hier ja ein gutes Tutorial dazu :)

von Peter D. (peda)


Lesenswert?

Es ist eine schlechte Idee, uralte Leichen auszugraben.
Die Erfahrungen sind hoffnungslos veraltet.

Ich benutze inzwischen auch Bitstructs, das macht den Code deutlich 
besser lesbar und senkt drastisch die Fehleranfälligkeit.
Schieben sollte man nur dort, wo es gebraucht wird.

Hier mal aktuellere Links:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=318198#318198

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=102296

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=67368&start=all


Peter

von Achim M. (minifloat)


Lesenswert?

Klaus Wachtler schrieb:
> Port und Pinnummer passen nicht zusammen

PORTB |= (1<<PA3);

setzt immer noch das Bit3 in PORTB.

Ich hab mir angewöhnt, Makros und Inline-Funktionen für sowas zu 
schreiben.
In etwa folgendermaßen:
1
#define LEDPORT PORTB
2
#define LEDPIN  PINB
3
#define LEDDDR  DDRB
4
5
#define LED_RED (1<<PB0)
6
#define LED_GRN (1<<PB1)
7
8
//ab hier hab ich schon mal abstrahiert,
9
//wo der Kram angeschlossen ist
10
11
#define GREEN_ON()     LEDPORT&=~LED_GRN
12
#define GREEN_OFF()    LEDPORT|=LED_GRN
13
#define GREEN_TOGGLE() LEDPIN=LED_GRN
14
15
#define RED_ON()       LEDPORT&=~LED_RED
16
#define RED_OFF()      LEDPORT|=LED_RED
17
#define RED_TOGGLE()   LEDPIN=LED_RED
18
19
inline void LED_INIT(void)    
20
{
21
   LEDPORT|=LED_RED+LED_GRN;
22
   LEDDDR|=LED_RED+LED_GRN;
23
}
24
25
//ab hier "sieht" man die Hardware und das Bitgefummel nicht mehr
26
//jetzt kann ich im laufenden Code schreiben:
27
28
int main(void)
29
{
30
  LED_INIT();
31
  //...andere inits...
32
  for(;;)
33
  {
34
    //...tu irgend ein Kram
35
    GREEN_ON();
36
    //...tu anderen Kram
37
}
und der Code kommentiert sich selbst, da man sofort sieht, was gemeint 
ist.

mfg mf

von Klaus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich benutze inzwischen auch Bitstructs, das macht den Code deutlich
> besser lesbar und senkt drastisch die Fehleranfälligkeit.

Bei den PICs, die ich verwende, sind die Bitstructs für alle Prozessoren 
beim Compiler dabei. Das einzige, was ich selber machen muß, ist die 
Netznamen meiner Hardware den Portpins zuzuordnen, und das ist ein 
einfaches #define.
1
/* in meinem h-File */
2
#define EEPROM_CS PORTCbits.RC2

Und ab dann heißts
1
    EEPROM_CS = 0;
2
3
// oder
4
    if(EEPROM_CS) 
5
.
6
.

Und das verstehe ich auch nächstes (eher übernächstes) Jahr noch, wenn 
ich an den Code noch mal ran muß.

Prost Neujahr

Klaus

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.