Forum: Mikrocontroller und Digitale Elektronik Programmier Arten


von Volland S. (volland_s)


Lesenswert?

puhh als Frischling im Bereich Arduino und Atmel hat mans gar nicht 
soooo einfach.  Ich Programmier über Arduino ISP ein ATtiny85

mal freut man sich über Schritte die man geschafft hat und beim nächsten 
Punkt fällt das Kartenhaus wieder zusammen:

Was ist Vor  und Nachteil von dieser Art zu programmieren:

DDRB |= ~(1<<PB2);

und dieser:

const int switchPin = 0;
const int ledPin1 = 2;
const int ledPin2 = 3;

int state;
int lastState;
int ledState;

void setup()
{
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(switchPin, INPUT);

}

void loop()
{
  state = digitalRead(switchPin);

  if (state != lastState)
  {


ich dachte ursprünglich das ist das selbe; aber Unterschiede muss es 
doch geben. (also nocht nur die Übersichtlichkeit und Kompaktheit)

verwirrt bin ich gerade auch dadurch das die Pegel mit der ersten 
Variante nicht zu 100% definiert sind, also Floaten können.

und mit der unübersichtlicheren Programmierung sind die Pegel definiert.

bei beiden Varianten hab ich Pull-downs, Pull-ups hin und her getauscht.

Mein Denkfehler ist wohl noch woanders.

von Peter (Gast)


Lesenswert?

Volland S. schrieb:
> DDRB |= ~(1<<PB2);

Hier arbeitest du halt direkt mit den Registern und musst dich mit dem 
jeweiligen Datenblatt des Controllers beschäftigen.


> pinMode(ledPin1, OUTPUT);

Mit dieser Funktion hast du eine Abstraktionsebene dawzischen. Gerade 
für Einsteiger ist sowas leichter zu verstehen.

Auch im professionellen Umfeld wird so etwas häufig gemacht. Dort ist 
die Software auch in ein Bios und die reine Applikation aufgeteilt. Das 
hat den Vorteil, dass der Applikationsingenieur sich auf seine Regelung 
o.ä. begrenzen kann und sich keinen Kopf darüber machen muss, wie genau 
das einlesen eines ADCs funktioniert bzw. er muss sich nicht mit den 
Registern beschäftigen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Nur als Anmerkung:

> DDRB |= ~(1<<PB2);

ist meist ziemlich unsinnig.

Damit sagst Du nämlich: Schalte ALLE Pins von Port B auf Ausgang... 
AUSSER PB2.

Sinnvoller ist

  DDRB |= (1<<PB2);     // Schalte PB2 auf Ausgang

oder

  DDRB &= ~(1<<PB2);    // Schalte PB2 auf Eingang

von nobi (Gast)


Lesenswert?

Ja, der Vorteil der 2. schreibweise ist der, dass Du dir Routinen 
pinmode, in einer anderen Datei anlegen und deffinierne kannst.
Mann bildet also einen sogenannte HAL (Harware abstraction Layer), der 
die Hardware abbildet, von der Deine eigentliche Applikation dann gar 
nichts mehr wissen muß. (Nur pinmode weis an welchem PORT die LED 
eigentlich liegt).
-> Du kannst die eigentliche Applikation auf einen kommplett anderen 
Controller übertragen, und mußt nur die Hardware Abstraktion ändern.

In der Softwareentwicklung werden solche Hardwarezugriffe meist nur noch 
über eine Abstarktionsebene gemacht.

Nachteil:
Nur wenns auf µs oder schneller ankommt bist Du vieleicht etwas 
langsamer, und benötigst evtl. ein paar Byte meht Code.

von Le X. (lex_91)


Lesenswert?

Grade bei I/O-Operationen wie in deinem Beispiel bringt Methode 2 leider 
einiges an Overhead.

Sowohl Programmspeicher als auch Ausführungszeit.

Ich überlege auch schon lange wie ich von den Ports des AVRs, vor allem 
seine Aufteilung in (oft) vier 8-Bit-Gruppen abstrahieren kann.
Leider noch nichts zufriedenstellendes gefunden :/

von akaDem (Gast)


Lesenswert?

le x. schrieb:
> Grade bei I/O-Operationen wie in deinem Beispiel bringt Methode 2 leider
> einiges an Overhead.
>
> Sowohl Programmspeicher als auch Ausführungszeit.
>
> Ich überlege auch schon lange wie ich von den Ports des AVRs, vor allem
> seine Aufteilung in (oft) vier 8-Bit-Gruppen abstrahieren kann.
> Leider noch nichts zufriedenstellendes gefunden :/

wenn man weiß, was compiler tun, könnte man auf den gedanken kommen, 
dass so manches framework ziemlich gut ist, obwohl es die 
registeradressierung leichter verständlich formuliert.

von Karl H. (kbuchegg)


Lesenswert?

le x. schrieb:


> Leider noch nichts zufriedenstellendes gefunden :/

Vielleicht hilft dir dieser Ansatz
1
struct port_
2
{
3
  uint8_t bit0 : 1;
4
  uint8_t bit1 : 1;
5
  uint8_t bit2 : 1;
6
  uint8_t bit3 : 1;
7
  uint8_t bit4 : 1;
8
  uint8_t bit5 : 1;
9
  uint8_t bit6 : 1;
10
  uint8_t bit7 : 1;
11
};
12
13
14
#define PortA  (*((volatile struct port_*)&PORTA))
15
#define PortB  (*((volatile struct port_*)&PORTB))
16
17
#define ERROR_LED    (PortA.bit0)
18
19
int main()
20
{
21
   ....
22
23
   ERROR_LED = 1;
24
}

Wechselt die LED von Port A - Bit 0 auf Port B - Bit 5, dann genügt es 
das #define zu ändern
1
#define ERROR_LED    (PortB.bit5)

Der Trick besteht darin, den Pointer, der hinter der WinAVR Definition 
von PORTA steht, auf einen Strukturpointer umzucasten, der (in diesem 
Fall) über ein Bitfeld den Zugriff auf die Einzelbits ermöglicht, so 
dass sich der Compiler um das Bitgeschiebe bzw. Maskieren kümmert.
Nachteil: mehrfach-Bitoperationen in 'einem Aufwasch' sind so erst mal 
nicht mehr möglich.

Theoretisch kann man das auch mit allen anderen Registern machen (Timer, 
ADC), wobei man auch erreichen kann, dass man sich dann zb beim Timer 
nicht mehr darum kümmern müsste, in welchem der beiden TCCR Register 
jetzt eigentlich welches WGM Bit enthalten ist.
Ist aber zugegebenermassen viel Aufwand.

von Viktor N. (Gast)


Lesenswert?

Es gibt nochmals eine Methode, die ich vorziehe :


DDRB = 0x03;  // 1= out, 0 = in

von Karl H. (kbuchegg)


Lesenswert?

Viktor N. schrieb:
> Es gibt nochmals eine Methode, die ich vorziehe :
>
>
> DDRB = 0x03;  // 1= out, 0 = in

Jep - die Methode gibt es. Die schlimmste von allen.
Sozusagen ein 'Anti-HAL'

von Viktor N. (Gast)


Lesenswert?

>Jep - die Methode gibt es. Die schlimmste von allen.
Sozusagen ein 'Anti-HAL'

Es besteht auch kein Anspruch auf Portierbarkeit. Man sollte nicht allzu 
weit weg im Code festhalten welcher Pin welche Funktion hat, sonst 
wird's muehsam.

von Karl H. (kbuchegg)


Lesenswert?

Es geht nicht nur um Portierbarkeit. Es geht auch um Fehlervermeidung 
und Fehlersuche

Ein simples

   PORTB |= ( 1 << ERROR_LED );

erzählt mir schon mehr darüber, was an dieser Stelle passiert, als ein

   PORTB |= 0x02;

und mit einem

   ERROR_LED = LED_ON;

kann ich quer durchs Programm formal überhaupt nichts mehr falsch 
machen. Ich kann weder irrtümlich den falschen Port erwischen, noch das 
falsche Bit. An dieser Stelle im Programm beschäftige ich mich als 
Programmierer dann nur noch damit, was passieren soll (die Error LED 
soll aufleuchten), den technischen Hintergrund wie das passieren soll, 
hab ich komplett in meinem HAL versteckt. Sowas kommt dann auch der 
Verständlichkeit des Codes zu gute. Je mehr ich die eigentliche Logik 
des Programms im Code betonen kann, desto besser.

Und der Aufwand dafür ist nicht wirklich groß. Das sind einmalig 5 
Minuten Tippslerei, die man sogar für viele Projekte nur einmalig machen 
muss, weil man sich den zugrundeliegenden Mechanismus in ein Header File 
legt. Ein bischen AUfwand für Codeorganisation hat sich noch immer 
gelohnt.

von Volland S. (volland_s)


Lesenswert?

man mann,  irgendwie hätte ich dies lieber studiert in meinem Leben als 
der Allrounder den ich bisher abgebe.

das die längere Variante mehr Speicher braucht hab ich gleich gesehen, 
jetzt verstehe ich auch warum das so ist.  nuja und deutlich länger 
braucht das wirklich.

schade aber ich werd mich wohl mit der längeren Methode erstmal befassen 
müssen.

will eigentlich eine ganz simple schaltung haben....  seufz

1 Eingang wo beim ersten Impuls Ausgang 1 ansteuert und beim 2. Impuls 
Ausgang 1 abschaltet und dafür Ausgang 2 schaltet

und am 2 Eingang das selbe mit 2 anderen Ausgängen........

Die Ausgangszeit einfach über eine Wartezeit ein stellen....

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

Volland S. schrieb:
> nuja und deutlich länger braucht das wirklich.

hast du denn eine zeitkritische Anwendung, wo das wichtig ist?

Volland S. schrieb:
> Die Ausgangszeit einfach über eine Wartezeit ein stellen....

nö, anscheinend doch nicht....

von Karl H. (kbuchegg)


Lesenswert?

Volland S. schrieb:

> irgendwie hätte ich dies lieber studiert in meinem Leben

Ist noch lange nicht zu spät.
Ein gutes C-Buch wirkt da Wunder. Damit du erst mal den vollen Überblick 
darüber hast, welche 'Werkzeuge' du überhaupt in deinem "Werkzeugkasten" 
zur Verfügung hast. Solange du nur einen Hammer kennst, wirds eben 
nichts mit dem Aufbau der Kuckucksuhr.

> das die längere Variante mehr Speicher braucht hab ich gleich gesehen,
> jetzt verstehe ich auch warum das so ist.  nuja und deutlich länger
> braucht das wirklich.

das liegt hauptsächlich daran, dass die Arduino Entwickler die Existenz 
der einzelnen Ports vor dem Programmierer verstecken wollten. Daraus 
resultiert der nicht unerhebliche Overhead, den die digitalRead, 
digitalOutput, etc. Funktionen nach sich ziehen.

Gib die Forderung auf, dass auf deinen externen Anschlüssen Zahlen von 0 
bis 16 stehen und du die im Programm auch mit diesen Zahlen ansprechen 
willst und der ganze Overhead fällt in sich zusammen.

> schade aber ich werd mich wohl mit der längeren Methode erstmal befassen
> müssen.

Das ist jetzt aber billig.
ALles was du wissen musst ist, welcher externe Anschluss liegt an 
welchem Pin. Und schon kannst du die in der nicht Arduino-Welt übliche 
Programmierung benutzen.

> will eigentlich eine ganz simple schaltung haben....  seufz

Was hat das damit zu tun?
Die Schreibweise, wie man auf einen Portpin zugreift, hängt ja nicht 
davon ab, wie simpel die Schaltung ist, sondern welchen Komfort man 
dabei haben will.

> 1 Eingang wo beim ersten Impuls Ausgang 1 ansteuert und beim 2. Impuls
> Ausgang 1 abschaltet und dafür Ausgang 2 schaltet
>
> und am 2 Eingang das selbe mit 2 anderen Ausgängen........

Und?
Was hindert dich jetzt daran?

> Die Ausgangszeit einfach über eine Wartezeit ein stellen....

Welche Ausgangszeiten? In deiner Beschreibung kommen gar keine Zeiten 
vor. Und Wartezeiten hört sich sowieso schon mal nach einem verkorksten 
Ansatz an.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Gib die Forderung auf, dass auf deinen externen Anschlüssen Zahlen von 0
> bis 16 stehen und du die im Programm auch mit diesen Zahlen ansprechen
> willst und der ganze Overhead fällt in sich zusammen.

Wobei .... auch das liese sich beibehalten, wenn man die Bezeichnungen 
ein klein wenig abwandelt.
Anstelle von

   digitalOutput( 0, HIGH );

   PIN_0 = HIGH;

dazu der richtige Unterbau und die Sache ist geritzt. Aber dazu muss man 
eben dann schon C können um den Unterbau dafür hinzukriegen. Und da ist 
dann eben der Trugschluss, dass man die Programmiersprache nicht lernen 
müsse, weil mit dem Arduino ohnehin alles so einfach ist.

Aber so wie ich das sehe, ist dein Hauptproblem im Moment ohnehin nicht, 
mit welcher Syntax man jetzt konkret auf einen Portpin zugreift. Das ist 
nun leider mal die Krux in der Programmierung, dass man als Neuling 2 
Dinge gleichzeitig lernen muss: die Programmiersprache (das Werkzeug) 
und Algorithmen bzw. Programmier-Techniken (wie setze ich mein Werkzeug 
ein).

von Vlad T. (vlad_tepesch)


Lesenswert?

Viktor N. schrieb:
> Es besteht auch kein Anspruch auf Portierbarkeit. Man sollte nicht allzu
> weit weg im Code festhalten welcher Pin welche Funktion hat, sonst
> wird's muehsam.

Richtig mühsam wirds, wenn du das zu schaltende Element an einen anderen 
Ausgang hängst. In KHBs Version ändert man ein Define. Bei dir musst du 
erst aus allen Zuweisungenn an den Port dein altes Bit rausfummeln und 
das neue reinfummeln.

Karl Heinz Buchegger schrieb:
>> DDRB = 0x03;  // 1= out, 0 = in
>
> Jep - die Methode gibt es. Die schlimmste von allen.

Du irrst es geht noch schlimmer:
1
DDRB = 175; // 1= out, 0 = in
oder noch schlimmer:
1
DDRB = 0175; // 1= out, 0 = in

von Klaus (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ist aber zugegebenermassen viel Aufwand.

Es sei denn, der Chiphersteller liefert das gleich mit.

MfG Klaus

von Volland S. (volland_s)


Lesenswert?

Leute leute.......

sehr verwirrend.   ich lern mich wohl über Ostern weiter irgendwie ein.

Kuckuksuhr und Vorschlaghammer

von Uwe (de0508)


Lesenswert?

Hallo Volland,

Vlad hat Dir noch zwei weitere Beispiele zusammen gestellt.
A) in Dezimalschreibweise und
B) in Oktalschreibweise.

Vlad Tepesch schrieb:
> Du irrst es geht noch schlimmer:
>
1
> DDRB = 175; // 1= out, 0 = in
2
>
> oder noch schlimmer:
>
1
> DDRB = 0175; // 1= out, 0 = in
2
>

von Volland S. (volland_s)


Lesenswert?

haha  mein Programm funktioniert tatsächlich

....
const int Takt1 = 0;
const int Takt2 = 1;
const int REL1set1 =  2;
const int REL1set2 =  3;
const int REL2set1 =  4;
const int REL2set2 =  5;
.....

Proplem:

Der Reset Pin brauch ich als Ausgangspin,    wie kriege ich den ebenso 
aktiviert ?

klar zum Programmieren brauch ich den Pin, aber wenn das Programm dann 
läuft dann müsst der doch auch wieder als Ausgang funktionieren.

von Vlad T. (vlad_tepesch)


Lesenswert?

bei deínem Kentnisstand rate ich dir dringend, die Finger von dem 
ResetPin-Fuse-Bit zu lassen.

von Volland S. (volland_s)


Lesenswert?

Vlad Tepesch schrieb:
> bei deínem Kentnisstand rate ich dir dringend, die Finger von dem
> ResetPin-Fuse-Bit zu lassen.

.... Mist
...sch...

keine Chance ?

von Volland S. (volland_s)


Lesenswert?

muss das IC nur einmal Programmieren können.....   :-)

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.