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.
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.
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
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.
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 :/
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.
le x. schrieb:> Leider noch nichts zufriedenstellendes gefunden :/
Vielleicht hilft dir dieser Ansatz
1
structport_
2
{
3
uint8_tbit0:1;
4
uint8_tbit1:1;
5
uint8_tbit2:1;
6
uint8_tbit3:1;
7
uint8_tbit4:1;
8
uint8_tbit5:1;
9
uint8_tbit6:1;
10
uint8_tbit7: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
intmain()
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.
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'
>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.
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.
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....
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....
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.
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).
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:
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:>
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.
Vlad Tepesch schrieb:> bei deínem Kentnisstand rate ich dir dringend, die Finger von dem> ResetPin-Fuse-Bit zu lassen.
.... Mist
...sch...
keine Chance ?