Forum: Mikrocontroller und Digitale Elektronik Magnetic Loop Steuerung (Arduino)


von Kilo S. (kilo_s)


Lesenswert?

Guten Morgen.

Wie hier im beitrag 
angekündigt:Beitrag "Re: Wie ein schlecht dokumentierte Arduino Library nutzen?"

An und für sich Funktioniert das ganze.

Nur folgende Mankos:
Encoder zählt nicht richtig.
Arduino "hängt" sich beim schnellen drehen des encoder ab und zu auf und 
der stepper dreht "Unendlich" in die letzte drehrichtung. (Das 
"Unendlich" muss ich erst noch testen!)

Nicht gleich erschlagen, Die Variablen sind noch absolut "Nichtssagend" 
und chaotisch weil ich nur das Nötigste angepasst habe damit es erst ein 
mal läuft.
1
//ML Steuerung (Versuch 1)
2
#include <Wire.h>
3
#include <LiquidCrystal_I2C.h>
4
#include <Stepper.h>
5
6
int encoderPinA = 2;
7
int encoderPinB = 3;
8
int encoderButC = 4;
9
int buttonState = 0;
10
int buttonDState = 0;
11
int encoderButD = 5;
12
int encoderButDLast = LOW;
13
int encoderPos = 0;
14
int encoderPinALast = LOW;
15
int encoderPinANow = LOW;
16
int encoderButCLast = LOW;
17
int RelPin = 13;
18
int RelPinStat = LOW;
19
int Rel1Pin = 7;
20
int Rel1PinStat = LOW;
21
int StepperPos = 0;
22
int LastStepperPos = 1;
23
int s = 1;
24
const int stepsPerRevolution = 48;
25
boolean A_set = false;
26
boolean B_set = false;
27
static boolean rotating = false;
28
29
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);
30
LiquidCrystal_I2C lcd(0x27, 20, 4);
31
32
void setup() {
33
  myStepper.setSpeed(10);
34
  pinMode (encoderPinA, INPUT_PULLUP);
35
  pinMode (encoderPinB, INPUT_PULLUP);
36
  pinMode (encoderButC, INPUT);
37
  pinMode (encoderButD, INPUT);
38
  attachInterrupt(0, doEncoderA, CHANGE); //pin 2
39
  attachInterrupt(1, doEncoderB, CHANGE); //pin 3
40
41
  lcd.init();
42
  lcd.backlight();
43
  lcd.clear();
44
  lcd.setCursor(0, 0);
45
  lcd.print("Pos:");
46
  lcd.setCursor(10, 0);
47
  lcd.print("Max:+/-700");
48
  lcd.setCursor(0, 1);
49
  if ( RelPinStat == HIGH ) {
50
    lcd.setCursor(0, 1);
51
    lcd.print("Relais:AN ");
52
  } else {
53
    lcd.setCursor(0, 1);
54
    lcd.print("Relais:AUS");
55
  }
56
  if ( Rel1PinStat == HIGH ) {
57
    lcd.setCursor(11, 1);
58
    lcd.print("Motor:AN ");
59
  } else {
60
    lcd.setCursor(11, 1);
61
    lcd.print("Motor:AUS");
62
  }
63
}
64
65
void doEncoderA() {
66
  // debounce
67
  if ( rotating );  // wait a little until the bouncing is done
68
  // Test transition
69
  if (digitalRead(encoderPinA) != A_set ) { // debounce once more
70
    A_set = !A_set;
71
    // adjust counter - if A leads B
72
    if ( A_set && !B_set )
73
      StepperPos = -s;  //change the 1 to steps to take when encoder turned
74
    rotating = false;  // no more debouncing until loop() hits again
75
  }
76
}
77
78
void doEncoderB() {
79
  if ( rotating );
80
  if ( digitalRead(encoderPinB) != B_set ) {
81
    B_set = !B_set;
82
    //  adjust counter + 1 if B leads A
83
    if ( B_set && !A_set )
84
      StepperPos = s; //change the 1 to steps to take when encoder turned
85
    rotating = false;
86
  }
87
}
88
89
void loop() {
90
91
  buttonState = digitalRead(encoderButC);
92
  buttonDState = digitalRead(encoderButD);
93
  encoderPinANow = digitalRead(encoderPinA);
94
95
  if ((encoderPinALast == HIGH) && (encoderPinANow == LOW)) {
96
    if (digitalRead(encoderPinB) == HIGH) {
97
      encoderPos++;
98
    } else {
99
      encoderPos--;
100
    }
101
    lcd.clear();
102
    lcd.setCursor(0, 0);
103
    lcd.print("Pos:");
104
    lcd.setCursor(4, 0);
105
    lcd.print(encoderPos);
106
    lcd.setCursor(10, 0);
107
    lcd.print("Max:-/700");
108
    if ( RelPinStat == HIGH ) {
109
      lcd.setCursor(0, 1);
110
      lcd.print("Relais:AN ");
111
    } else {
112
      lcd.setCursor(0, 1);
113
      lcd.print("Relais:AUS");
114
    }
115
    if ( Rel1PinStat == HIGH ) {
116
      lcd.setCursor(11, 1);
117
      lcd.print("Motor:AN ");
118
    } else {
119
      lcd.setCursor(11, 1);
120
      lcd.print("Motor:AUS");
121
    }
122
  }
123
124
  encoderPinALast = encoderPinANow;
125
126
  if ( buttonState != encoderButCLast ) {
127
    if ( buttonState == HIGH ) {
128
      if ( RelPinStat == LOW ) {
129
        digitalWrite(RelPin, HIGH);
130
        RelPinStat = HIGH;
131
        lcd.setCursor(0, 1);
132
        lcd.print("Relais:AN ");
133
      } else {
134
        digitalWrite(RelPin, LOW);
135
        RelPinStat = LOW;
136
        lcd.setCursor(0, 1);
137
        lcd.print("Relais:AUS");
138
      }
139
    }
140
  }
141
142
  encoderButCLast = buttonState;
143
144
  if ( buttonDState != encoderButDLast ) {
145
    if ( buttonDState == HIGH ) {
146
      if ( Rel1PinStat == LOW ) {
147
        digitalWrite(Rel1Pin, HIGH);
148
        Rel1PinStat = HIGH;
149
        lcd.setCursor(10, 1);
150
        lcd.print("Motor:AN ");
151
      } else {
152
        digitalWrite(Rel1Pin, LOW);
153
        Rel1PinStat = LOW;
154
        lcd.setCursor(10, 1);
155
        lcd.print("Motor:AUS");
156
      }
157
    }
158
  }
159
160
  encoderButDLast = buttonDState;
161
  rotating = true;
162
163
  if (LastStepperPos != StepperPos) {
164
    LastStepperPos = StepperPos;
165
    myStepper.step(StepperPos);
166
    StepperPos = 0;
167
  }
168
169
}

Umgebung:
Arduino IDE 1.8.19, alle Libs sind aus dem IDE eigenen Manager und auf 
dem aktuellsten stand. OS ist Xubuntu 20.04.1.

Hardware:
Arduino Nano. (China, Old Bootloader.)
ULN2003 Board. (LB1233, der Stepper kommt aus einem Scanner. Da hab ich 
auch das Darlington array her das pinkompatibel zum ULN2003 ist.)
Mitsumi M35SP-7NP 50Ohm/24V Stepper. 
https://datasheetspdf.com/pdf-file/580641/MitsumiElectronics/M35SP-7/1
LCD: HD44780 , 20x4 mit PCF.
Encoder: 
https://www.amazon.de/degree-encoder-compatible-Arduino-Raspberry/dp/B09Q8RSK6T 
(Optisch und von der Funktion gleich, allerdings nicht dieses Modul 
sondern ausgeschlachtet aus einer alten Stereoanlage.)

Für die Taster habe ich externe Pulldown widerstände, am encoder 
zusätzlich 1nF kerkos.

Sooo, ich hoffe ich habe euch alle infos geliefert die ihr braucht um 
mir eventeull bei dem problem mit dem encoder zu helfen.

Im Prinzip zählt er nur dann richtig wenn ich schnell genug drehe das
ich eine Rastposition überspringe und im Prinzip an der Rastposition
lande an der er bereits den zweiten zählen sollte. Es wäre allerdings 
sehr schön wenn jede Rastposition vernünftig erkannt wird.

Vielen Lieben dank und bis später. ;)

von Stefan F. (Gast)


Lesenswert?

Reduziere das Programm auf eine möglichst kleine Variante, die das 
Problem so gerade eben noch hat. Zuerst mal gehören da die Bibliotheken 
vom LCD und Stepper entfernt. Gebe die Variablen seriell aus, damit man 
sehen kann, welche davon unerwartete Werte hat.

Sollte sich das Probleme ohne diese beiden Bibliotheken nicht 
nachvollziehen lassen, dass ist auch das eine sehr nützliche Erkenntnis. 
Finde in diesem Fall heraus, welche der beiden Bibliotheken den Fehler 
auslöst.

Was mir da direkt negativ auffällt ist:

a) Ich sehe keinen Code zum Entprellen der Kontakte
b) Du machst zwischen den Abfragen der Kontakte Display-Ausgaben die mit 
Sicherheit zu viel Zeit in Anspruch nehmen. Deswegen bekommst du bei 
schnellem Drehen Probleme.
c) Du ruft immer wieder lcd.clear() auf. Dein Display wird daher beim 
Drehen flackern und einen schlechten Kontrast zeigen. Notfalls kannst du 
zuerst Leerzeichen ausgeben und dann mit den gewünschten Zahlen 
überschreiben. Aber eigentlich ist das nicht nötig, da printf() Zahlen 
bereits passend formatieren kann.

von J. S. (jojos)


Lesenswert?

TL;DR, aber die Stepper Klasse ist sehr minimalistisch und 
step(StepperPos) ist blockierend, was sich auf das Einlesen des Encoders 
und die Reaktion auf die Tasten auswirkt bei großen Sprüngen in der 
Position.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Die Interrupt Handler doEncoderA() und doEncoderB() fragen den Encoder 
mit Entprellung ab,  aber das Hauptprogramm (loop) fragt die Eingänge A 
und B erneut direkt (ohne Entprellung) ab.

Generell wird davon abgeraten, Interrupthandler zu benutzen, weil die 
Kontakte prellen und dabei beliebig viele Interrupts auslösen können. 
Das Hauptprogramm kann dadurch stark ausgebremst werden. Solche Probleme 
fallen eventuell erst viele Jahre später auf, wenn der Encoder nicht 
mehr perfekt schaltet.

> if ( rotating );  // wait a little until the bouncing is done

Diese Zeile tut nicht das, was der Kommentar daneben sagt. Ich denke, 
diese Zeile wird vom Compiler komplett entfernt.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Reduziere das Programm auf eine möglichst kleine Variante, die das
> Problem so gerade eben noch hat. Zuerst mal gehören da die Bibliotheken
> vom LCD und Stepper entfernt. Gebe die Variablen seriell aus, damit man
> sehen kann, welche davon unerwartete Werte hat.
>
> Sollte sich das Probleme ohne diese beiden Bibliotheken nicht
> nachvollziehen lassen, dass ist auch das eine sehr nützliche Erkenntnis.
> Finde in diesem Fall heraus, welche der beiden Bibliotheken den Fehler
> auslöst.
>
> Was mir da direkt negativ auffällt ist:
>
> a) Ich sehe keinen Code zum Entprellen der Kontakte
> b) Du machst zwischen den Abfragen der Kontakte Display-Ausgaben die mit
> Sicherheit zu viel Zeit in Anspruch nehmen. Deswegen bekommst du bei
> schnellem Drehen Probleme.
> c) Du ruft immer wieder lcd.clear() auf. Dein Display wird daher beim
> Drehen flackern und einen schlechten Kontrast zeigen. Notfalls kannst du
> zuerst Leerzeichen ausgeben und dann mit den gewünschten Zahlen
> überschreiben. Aber eigentlich ist das nicht nötig, da printf() Zahlen
> bereits passend formatieren kann.

OK, also muss ich noch nachlesen wie ich das entprellen in software 
richtig hinbekomme.

Deinen vorschlag umsetzen hat nur wenige minuten gedauert: 
(Minimalversion)
1
//ML Steuerung (Versuch 1)
2
#include <Wire.h>
3
4
int encoderPinA = 2;
5
int encoderPinB = 3;
6
int encoderPos = 0;
7
int encoderPinALast = LOW;
8
int encoderPinANow = LOW;
9
int StepperPos = 0;
10
int LastStepperPos = 0;
11
int s = 1;
12
13
boolean A_set = false;
14
boolean B_set = false;
15
static boolean rotating = false;
16
17
18
void setup() {
19
  Serial.begin(9600);
20
  pinMode (encoderPinA, INPUT_PULLUP);
21
  pinMode (encoderPinB, INPUT_PULLUP);
22
  attachInterrupt(0, doEncoderA, CHANGE); //pin 2
23
  attachInterrupt(1, doEncoderB, CHANGE); //pin 3  
24
}
25
26
void doEncoderA() {
27
  // debounce
28
  if ( rotating );  // wait a little until the bouncing is done
29
  // Test transition
30
  if (digitalRead(encoderPinA) != A_set ) { // debounce once more
31
    A_set = !A_set;
32
    // adjust counter - if A leads B
33
    if ( A_set && !B_set )
34
      StepperPos = -s;  //change the 1 to steps to take when encoder turned
35
    rotating = false;  // no more debouncing until loop() hits again
36
  }
37
}
38
39
void doEncoderB() {
40
  if ( rotating );
41
  if ( digitalRead(encoderPinB) != B_set ) {
42
    B_set = !B_set;
43
    //  adjust counter + 1 if B leads A
44
    if ( B_set && !A_set )
45
      StepperPos = s; //change the 1 to steps to take when encoder turned
46
    rotating = false;
47
  }
48
}
49
50
void loop() {
51
52
53
  encoderPinANow = digitalRead(encoderPinA);
54
55
  if ((encoderPinALast == HIGH) && (encoderPinANow == LOW)) {
56
    if (digitalRead(encoderPinB) == HIGH) {
57
      encoderPos++;
58
    } else {
59
      encoderPos--;
60
    }
61
    Serial.println(encoderPos);
62
  }
63
64
  encoderPinALast = encoderPinANow;
65
66
67
68
}

Serielle ausgabe:
1
10:42:57.568 -> 16
2
10:42:59.888 -> 17
3
10:43:01.181 -> 18
4
10:43:02.208 -> 19
5
10:43:02.274 -> 18
6
10:43:03.235 -> 19
7
10:43:03.932 -> 18
8
10:43:04.262 -> 19
9
10:43:05.159 -> 20
10
10:43:05.192 -> 21
11
10:43:06.716 -> 22

Hier besteht das problem bereits! Es wird erst ab der zweiten 
Rastposition gezählt, es liegt also definitiv an meinem code.

J. S. schrieb:
> TL;DR, aber die Stepper Klasse ist sehr minimalistisch und
> step(StepperPos) ist blockierend, was sich auf das Einlesen des Encoders
> und die Reaktion auf die Tasten auswirkt bei großen Sprüngen in der
> Position.

Du kennst nicht zufällig eine bereits existierende Lib bei der ich 
dieses problem nicht habe?

Ich hab allerdings auch kein problem damit zu versuchen die klasse 
selbst zu ändern, wenn ich genug durchblick habe. ;-) (Also eher erst 
später.)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Kilo S. schrieb:
> wäre allerdings sehr schön wenn jede Rastposition vernünftig erkannt wird.
Die kurze Version der nächsten 50 Posts ist: frage den Impulsgeber auf 
geeignete Art und Weise ab, dann geht das.

Tadellos geht das mit einem Timerinterrupt und Auswertung der A- und 
B-Pegel in diesem Interrupt. Dann braucht man auch kein Gefrickel mit 
Kondensatoren am Geber.

Fazit insgesamt: du solltest die Aufgaben trennen und sie so aufteilen, 
dass sie sich gegenseitig nicht nachteilig beeinflussen.

Zuallererst machst du die Displayaktualisierung in der Hauptschleife und 
das Einlesen der EA und das Berechnen des Drehgebers in einem 
Timerinterrupt.

Ein ganz wichtiger Tipp zum Display: lass das LCD_clear() weg. Das 
dauert eine Ewigkeit. Und zudem flackert das LED dadurch wie blöd (wenn 
das Programm mal zügig durchläuft).

Ich mach das entweder so: bei der Initialisierung wird 1x der statische 
Text an die entsprechende Position geschrieben und danach nur noch die 
Teile des Displays aktualisiert, die sich ändern.

Oder ich schreibe alles blitzschnell in einen Displaybuffer und schreibe 
von links oben nach rechts unten pro ms ein Zeichen vom Puffer aufs 
Display. Ein 4x20 Display wird so ca. 12x pro Sekunde neu beschrieben. 
Da flackert nichts und es beliben auch keine "ESD-Leichen" zurück, wenn 
mal 1 Übertragung zum Display gestört wurde.

Zum Testen der Durchlaufzeit der Hautpschleife kannst du ja mal am 
Anfang eines Schleifendurchlaufs einen EA-Pin setzen und am Ende wieder 
zurücksetzen und diesen Pin auf einem Oszi/LA ansehen. Du wirst staunen, 
wie langsam das Programm ist...

J. S. schrieb:
> step(StepperPos) ist blockierend
Üble Sache.

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Hier besteht das problem bereits!

Schmeisse die Interrupt-Handler auch raus. Deren Ausgaben verwendet dein 
reduziertes Programm nicht mehr. So kannst du dich leichter auf den 
wirklich relevanten Code konzentrieren.

> Du kennst nicht zufällig eine bereits existierende
> Lib bei der ich dieses problem nicht habe?

Nein, ich bin kein Arduino Spezialist.

von Kilo S. (kilo_s)


Lesenswert?

Lothar M. schrieb:
> Die kurze Version der nächsten 50 Posts ist: frage den Impulsgeber auf
> geeignete Art und Weise ab, dann geht das.

Genau das möchte ich mir doch hier erarbeiten!?
Wenn ich bereits wüsste wie, dann müsste ich ja nicht Fragen, die Tipps 
bisher sind aber schon ein guter ansatz das ich gleich etwas zum thema 
entprellen finden konnte!

Lothar M. schrieb:
> Ein ganz wichtiger Tipp zum Display: lass das LCD_clear() weg. Das
> dauert eine Ewigkeit. Und zudem flackert das LED dadurch wie blöd (wenn
> das Programm mal zügig durchläuft).

Nun, wie ich bereits im anderen Beitrag angemerkt habe, ich bin mir 
durch das viele lesen und testen im klaren darüber das es "Schnarchlahm" 
sein kann weil ich noch keinen plan habe wie ich das mit dem Display am 
besten mache.
Meine allererste noch ganz ganz wenig bearbeitete version war so 
grottenschlecht das das Display nicht nur Flackerte oder Artefakte 
zeigte.

Das geht sogar beides zusammen. ;-)


Hier mal der neue Code mit hoffentlich "Richtigem" entprellen.
1
//ML Steuerung (Versuch 1)
2
#include <Wire.h>
3
4
int encoderPinA = 2;
5
int encoderPinB = 3;
6
int encoderPos = 0;
7
int encoderPinALast = LOW;
8
int encoderPinANow = LOW;
9
int StepperPos = 0;
10
int LastStepperPos = 0;
11
int s = 1;
12
13
boolean A_set = false;
14
boolean B_set = false;
15
16
17
18
void setup() {
19
  Serial.begin(9600);
20
  pinMode (encoderPinA, INPUT_PULLUP);
21
  pinMode (encoderPinB, INPUT_PULLUP);
22
  attachInterrupt(0, doEncoderA, CHANGE); //pin 2
23
  attachInterrupt(1, doEncoderB, CHANGE); //pin 3
24
}
25
26
void doEncoderA() {
27
  static unsigned long last_interrupt_time = 0;
28
  unsigned long interrupt_time = millis();
29
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
30
  if (interrupt_time - last_interrupt_time > 200) {
31
    // Test transition
32
    if (digitalRead(encoderPinA) != A_set ) { // debounce once more
33
      A_set = !A_set;
34
      // adjust counter - if A leads B
35
      if ( A_set && !B_set )
36
        StepperPos = -s;  //change the 1 to steps to take when encoder turned
37
    }
38
    last_interrupt_time = interrupt_time;
39
  }
40
}
41
42
void doEncoderB() {
43
  static unsigned long last_interrupt_time = 0;
44
  unsigned long interrupt_time = millis();
45
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
46
  if (interrupt_time - last_interrupt_time > 200) {
47
    if ( digitalRead(encoderPinB) != B_set ) {
48
      B_set = !B_set;
49
      //  adjust counter + 1 if B leads A
50
      if ( B_set && !A_set )
51
        StepperPos = s; //change the 1 to steps to take when encoder turned
52
    }
53
    last_interrupt_time = interrupt_time;
54
  }
55
}
56
57
void loop() {
58
59
60
  encoderPinANow = digitalRead(encoderPinA);
61
62
  if ((encoderPinALast == HIGH) && (encoderPinANow == LOW)) {
63
    if (digitalRead(encoderPinB) == HIGH) {
64
      encoderPos++;
65
    } else {
66
      encoderPos--;
67
    }
68
    Serial.println(encoderPos);
69
  }
70
71
  encoderPinALast = encoderPinANow;
72
73
}

Ausgabe:
1
10:59:56.006 -> 1
2
10:59:58.028 -> 2
3
11:00:00.315 -> 3
4
11:00:03.199 -> 4
5
11:00:11.991 -> 5
6
11:00:13.915 -> 6
7
11:00:15.539 -> 7
8
11:00:16.565 -> 8
9
11:00:16.930 -> 7
10
11:00:19.052 -> 8
11
11:00:20.179 -> 9
12
11:00:21.107 -> 10
13
11:00:21.305 -> 9
14
11:00:21.835 -> 10
15
11:00:21.901 -> 11
16
11:00:22.001 -> 12
17
11:00:22.100 -> 13
18
11:00:22.828 -> 14
19
11:00:22.896 -> 15
20
11:00:22.962 -> 16
21
11:00:22.962 -> 17
22
11:00:22.994 -> 18
23
11:00:23.094 -> 19
24
11:00:23.161 -> 20
25
11:00:23.858 -> 21
26
11:00:23.925 -> 22
27
11:00:23.991 -> 23
28
11:00:24.057 -> 24
29
11:00:24.123 -> 25
30
11:00:24.222 -> 26
31
11:00:24.289 -> 27
32
11:00:24.322 -> 26
33
11:00:25.020 -> 27
34
11:00:25.086 -> 28
35
11:00:25.120 -> 29
36
11:00:25.218 -> 30
37
11:00:25.218 -> 31
38
11:00:25.218 -> 32
39
11:00:25.252 -> 33
40
11:00:25.285 -> 34
41
11:00:25.352 -> 35
42
11:00:26.148 -> 36
43
11:00:26.248 -> 37
44
11:00:26.347 -> 38
45
11:00:26.381 -> 39
46
11:00:26.481 -> 40
47
11:00:27.310 -> 41
48
11:00:27.377 -> 42
49
11:00:27.410 -> 43
50
11:00:27.443 -> 44
51
11:00:27.477 -> 45
52
11:00:27.544 -> 46
53
11:00:27.577 -> 47
54
11:00:27.643 -> 48
55
11:00:29.101 -> 49
56
11:00:29.366 -> 50
57
11:00:29.400 -> 51
58
11:00:29.466 -> 52
59
11:00:29.466 -> 53
60
11:00:29.500 -> 54
61
11:00:29.533 -> 55
62
11:00:29.567 -> 56
63
11:00:29.567 -> 57
64
11:00:29.670 -> 58
65
11:00:30.932 -> 57
66
11:00:30.965 -> 56
67
11:00:30.999 -> 55
68
11:00:31.032 -> 54
69
11:00:31.065 -> 53
70
11:00:31.133 -> 52
71
11:00:31.199 -> 51
72
11:00:31.232 -> 52
73
11:00:31.266 -> 51
74
11:00:31.299 -> 50
75
11:00:31.928 -> 49
76
11:00:31.961 -> 48
77
11:00:31.994 -> 47
78
11:00:32.028 -> 46
79
11:00:32.092 -> 45
80
11:00:32.092 -> 44
81
11:00:32.126 -> 43
82
11:00:32.126 -> 42
83
11:00:32.192 -> 41
84
11:00:32.690 -> 40
85
11:00:32.756 -> 39
86
11:00:32.790 -> 38
87
11:00:32.823 -> 37
88
11:00:32.957 -> 36
89
11:00:33.488 -> 35
90
11:00:33.520 -> 34
91
11:00:33.553 -> 33
92
11:00:33.588 -> 32
93
11:00:33.622 -> 31
94
11:00:33.687 -> 30
95
11:00:34.117 -> 29
96
11:00:34.150 -> 28
97
11:00:34.185 -> 27
98
11:00:34.185 -> 26
99
11:00:34.185 -> 25
100
11:00:34.217 -> 24
101
11:00:34.251 -> 23
102
11:00:34.284 -> 22
103
11:00:34.681 -> 21
104
11:00:34.715 -> 20
105
11:00:34.748 -> 19
106
11:00:34.781 -> 18
107
11:00:34.813 -> 17
108
11:00:34.847 -> 16
109
11:00:34.913 -> 15
110
11:00:35.279 -> 14
111
11:00:35.346 -> 13
112
11:00:35.378 -> 12
113
11:00:35.412 -> 11
114
11:00:35.412 -> 10
115
11:00:35.445 -> 9
116
11:00:35.445 -> 8
117
11:00:35.477 -> 7
118
11:00:35.512 -> 6
119
11:00:35.942 -> 5
120
11:00:35.977 -> 4
121
11:00:36.008 -> 3
122
11:00:36.041 -> 2
123
11:00:36.075 -> 1
124
11:00:36.107 -> 0
125
11:00:36.173 -> -1
126
11:00:36.738 -> -2
127
11:00:36.805 -> -3
128
11:00:36.838 -> -4
129
11:00:36.905 -> -5
130
11:00:36.971 -> -6
131
11:00:37.004 -> -7
132
11:00:37.070 -> -8
133
11:00:37.169 -> -9
134
11:00:37.269 -> -10
135
11:00:41.413 -> -9
136
11:00:41.479 -> -8
137
11:00:41.545 -> -7
138
11:00:41.578 -> -6
139
11:00:41.678 -> -5
140
11:00:41.744 -> -4
141
11:00:41.811 -> -3
142
11:00:41.844 -> -2
143
11:00:41.910 -> -1
144
11:00:41.942 -> 0
145
11:00:41.976 -> 1
146
11:00:42.042 -> 2
147
11:00:42.077 -> 3
148
11:00:43.369 -> 4
149
11:00:43.534 -> 3
150
11:00:45.091 -> 4
151
11:00:45.754 -> 5
152
11:00:46.416 -> 6
153
11:00:47.011 -> 7

Auch hier das problem weiterhin.
jetzt muss ich diesem Hinweis mal nachgehen:

Stefan ⛄ F. schrieb:
> Die Interrupt Handler doEncoderA() und doEncoderB() fragen den Encoder
> mit Entprellung ab,  aber das Hauptprogramm (loop) fragt die Eingänge A
> und B erneut direkt (ohne Entprellung) ab.

ALso nicht nur lernen den Interrupt vernünftig zu entprellen sondern 
auch in der loop.

von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> ALso nicht nur lernen den Interrupt vernünftig zu entprellen sondern
> auch in der loop.

Die doppelte Abfrage des Encoders scheint mir allerdings äußerst 
fragwürdig.

Lothar hat einen guten Ansatz beschrieben. Dazu muss das gesamte 
Programm in seinen Grundzügen umgebaut werden. Aber ich denke der 
Aufwand wird sich lohnen.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Lothar hat einen guten Ansatz beschrieben. Dazu muss das gesamte
> Programm in seinen Grundzügen umgebaut werden. Aber ich denke der
> Aufwand wird sich lohnen.

Ja, nach ein wenig testen denke ich es ist sogar nur Hilfreich wenn ich 
das ganze nochmal neu angehe.

Ich versuche im kopf schon eine halbwegs vernünftige struktur zu finden.

Stefan ⛄ F. schrieb:
> Die doppelte Abfrage des Encoders scheint mir allerdings äußerst
> fragwürdig.

Ja, bin ich ganz bei dir.
Ich hab nur noch keinen plan wie genau ich das lösen soll.
Im moment nutze ich ja die Pinabfrage noch zum bestimmen ob der Zahler 
arbeiten soll.

Meine erste idee wäre im Interrupt eine weitere variable zu setzen und 
diese abzufragen, erscheint mir aber nicht ganz so elegant wie die 
rückgabe des interrupt direkt auszuwerten. Allerdings muss ich mir da 
erst was überlegen.

von J. S. (jojos)


Lesenswert?

für die Encoder gibt es doch DIE Routine von P. Dannegger die auch 
selbst mit den billigen ALPS Encodern klarkommt, findet man hier über 
die SuFu.
Den Stepper würde ich durch AccelStepper ersetzen. Da muss die 
Arbeitsroutine auch in einer Loop aufgerufen werden und sollte nicht 
zulange durch das Display verzögert werden. Wenn das ruckelt kann man 
den AccelStepper.run evtl im Timer Interrupt aufrufen. Beispiele ansehen 
wie in dem anderen Thread schon gesagt.

von Kilo S. (kilo_s)


Lesenswert?

J. S. schrieb:
> für die Encoder gibt es doch DIE Routine von P. Dannegger die auch
> selbst mit den billigen ALPS Encodern klarkommt, findet man hier über
> die SuFu.

Fürs erste würde ich das mit dem encoder gerne per hand machen, hat den 
ganz einfachen grund das eben dieses "Libs Zusammenklicken" nur halbwegs 
läuft und ich so auch besser und schneller in die sprache einsteige.

Nun hab ich mir für einen Test mal diesen code angesehen:
1
#define ClockPin 2 // Must be pin 2 
2
#define DataPin 3 // Must be pin 3
3
#define readA bitRead(PIND,2)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
4
#define readB bitRead(PIND,3)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
5
volatile long count = 0;
6
7
void setup() {
8
  Serial.begin(9600); //9600
9
  pinMode(ClockPin, INPUT);
10
  pinMode(DataPin, INPUT);
11
  /* Full 4 Step Count*/
12
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {(readA == readB)  ? count++ : count--;}, CHANGE);
13
  attachInterrupt(digitalPinToInterrupt(DataPin ), [] {(!readA == readB)  ? count++ : count--;}, CHANGE);
14
  
15
}
16
17
void loop() {
18
  long Counter;
19
  static long lastCtr;
20
  noInterrupts ();
21
  Counter = count;
22
  interrupts ();
23
  if (lastCtr != Counter) {
24
    Serial.println(Counter);
25
  }
26
  lastCtr = Counter;
27
}
Der ist auf alle fälle besser.
Er zahlt einzelne schritte bei relativ hoher geschwindigkeit ganz 
ordentlich, macht aber ab und zu (fehlende entprellung) vor/rück sprünge 
und überspringt auch mehrer dabei.
Er zählt auch einzelne schritte bei langsamer drehung, dabei überspringt 
er aber ab und zu schritte (auch mehrere) und macht häufiger vor/rück 
sprünge.

Allerdings ist der teils recht leicht zu kapieren und Zahlt annährend 
wie gewünscht.
Elegant und anscheinend schnell gelöst durch das integrieren des counter 
direkt in die definition des interrupt über eine verschachtelte if 
abfrage!

Hier mal die ausgabe "Langsam":
1
13:05:37.526 -> -1
2
13:05:38.521 -> 0
3
13:05:39.218 -> -1
4
13:05:39.881 -> 0
5
13:05:40.545 -> -1
6
13:05:41.141 -> 0
7
13:05:41.175 -> 1
8
13:05:41.175 -> 2
9
13:05:41.175 -> 3
10
13:05:41.175 -> 4
11
13:05:42.269 -> 3
12
13:05:44.356 -> 4
13
13:05:44.389 -> 5
14
13:05:44.389 -> 8
15
13:05:44.389 -> 9
16
13:05:44.986 -> 8

Schnell:
1
13:06:13.977 -> 7
2
13:06:13.977 -> 8
3
13:06:14.010 -> 9
4
13:06:14.010 -> 8
5
13:06:14.043 -> 9
6
13:06:14.076 -> 10
7
13:06:14.076 -> 12
8
13:06:14.076 -> 13
9
13:06:14.076 -> 14
10
13:06:14.076 -> 15
11
13:06:14.076 -> 16
12
13:06:14.076 -> 15
13
13:06:14.109 -> 16
14
13:06:14.109 -> 18
15
13:06:14.142 -> 20

Jetzt fehlt mir noch eine möglichkeit neben der if das entprellen im 
interrupt unterzubringen.

Bzw. ich vermute das es in den interrupt gehört um die loop nicht 
unnötig langsamer zu machen?

von J. S. (jojos)


Lesenswert?

Encoder per ISR führt hier regelmässig zum Krieg...
keine Lib, ein paar Zeilen C-Code, den man natürlich auch in eine C++ 
Klasse packen kann.
https://www.mikrocontroller.net/articles/Drehgeber
und da die Variante mit 'wackeligen Rastpunkten' wenn es einer der 
billigen Encoder ist. Gute optische Encoder funktionieren aber durchaus 
mit den Interrupts.

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Kilo S. schrieb:
> Bzw. ich vermute das es in den interrupt gehört um die loop nicht
> unnötig langsamer zu machen?
Es ist im Grunde schurzegal wie langsam die Mainloop ist. Wenn sie "zu 
langsam" ist, dann musst du die Ursache dafür halt suchen und und 
beheben(**).

Aber in einen Interrupt gehört so wenig wie möglich, am besten nur das, 
was nötig ist, um die Information, die du im Interrupt gewinnen kannst, 
hinterher in der Mainloop abarbeiten zu können.

So wie wenn du grade Milch kochst und es klingelt der Postbote, 
unterbricht diese Arbeit (Unterbrechung = Interrupt) und bringt einen 
Brief von Tante Erna, die darum bittet, dass du sie mal besuchst.

Sinnigerweise rennst du dann nicht gleich los und fährst zur Tante, 
sondern du nimmst den Brief entgegen und legst ihn auf den Tisch, und 
gehst dann schnellstmöglich zurück zur Milch, um dein Porridge fertig zu 
kochen. Und nach dem Essen (der Brei soll ja nicht kalt werden) kannst 
du dir mal ansehen, was die Tante geschrieben hat.


(**) Meist sind das dann so Dinger, wo unnötigerweise auf irgendwas 
gewartet wird. Da ist es dann besser, zu schauen, ob die Hardware schon 
fertig ist und wenn nicht mit der noormalen Tätigkeit weiter zu machen. 
So wie du ja auch nicht 2 Stunden vor die Waschmaschine hockst, sondern 
bestenfalls zwischendurch mal nachschaust, ob die schon fertig ist.

: Bearbeitet durch Moderator
von Kilo S. (kilo_s)


Lesenswert?

Lothar M. schrieb:
> Es ist im Grunde schurzegal wie langsam die Mainloop ist.

Ach, wenn das ganze einfach nur wie gewünscht einen schritt pro 
rastpunkt in sagen wir einer sekunde abarbeitet ohne fehler oder sprünge 
und dazwischen noch meine Relais schalten kann, bin ich völlig 
zufrieden.

J. S. schrieb:
> Gute optische Encoder funktionieren aber durchaus
> mit den Interrupts.

Irgendwo hab ich bestimmt noch eine defekte maus wo sogar ein Optischer 
drin sein könnte.

Der encoder steht zur debatte, ich hab keine schmerzen den 
auszutauschen.

Update von mobil aus:
Wenn ich zurück bin probiere ich das entprellen in der loop aus.

Das sollte ja mit millis wie bei meinem Versuch davor funktionieren.

: Bearbeitet durch User
von Kilo S. (kilo_s)


Lesenswert?

OK! Mooooooooment.

ich muss eben mal prüfen ob eventuell ein Hardware defekt (Encoder) 
vorliegt!
Gleicher Code wie Vorher, Arduino eingestöpselt und der zählt nur noch 
hoch aber runter geht nicht mehr?..

von Wolfgang (Gast)


Lesenswert?

Kilo S. schrieb:
> Jetzt fehlt mir noch eine möglichkeit neben der if das entprellen im
> interrupt unterzubringen.

Was hast du immer mit deinem Entprellen?
Der Encoder gibt Gray-Code aus und der ist von Natur aus resistent gegen 
Prellen. Die Kondensatoren sind dort genauso überflüssig. Frage das Ding 
einfach mit einem Timer ab, der ausreichend schnell taktet, damit kein 
Zustand übersehen wird.

von Kilo S. (kilo_s)


Lesenswert?

Wolfgang schrieb:
> Was hast du immer mit deinem Entprellen?
> Der Encoder gibt Gray-Code aus und der ist von Natur aus resistent gegen
> Prellen. Die Kondensatoren sind dort genauso überflüssig. Frage das Ding
> einfach mit einem Timer ab, der ausreichend schnell taktet, damit kein
> Zustand übersehen wird.

Hab ich ja bereits probiert. Funktionierte genauso schlecht wie meine 
eigene Lösung. Die Lib die angeblich jeden encoder packt weil sie 
Greycode und Statemachine verwendet...

Ist mir eigentlich auch egal!

Scheint als habe das Forum meinen letzten Post verschluckt.

Aber kurz dazu: Laptop Zugemacht, mit meiner Frau kurz die Tante 
besuchen...
Arduino wieder angesteckt und der scheiß zählt plötzlich nur noch hoch 
und nicht mehr runter.

Mein erstes Programm hingegen zählt hoch und runter. Die Hardware ist 
also OK!

Und jetzt erkläre mir mal ohne Änderungen am Code wieso der vorher 
funktionierende Code plötzlich NICHT MEHR funktioniert.

Und ja ich habe mehrere Adruinos (2) und bei beiden verhält es sich 
gleich!

Ich kam nicht Mal zu dem Versuch etwas zu entprellen weil nichts mehr 
richtig gelesen wird. Es wurde NICHTS anderes gemacht als ein mal USB 
Abstecken und wieder anstecken!...

von Kilo S. (kilo_s)


Lesenswert?

Probe aufs Exempel:
Hab gerade extra eine alte Maus zerlegt und dessen Encoder für das 
Mausrad (kein optischer) anstelle meines verwendet.

Gleiches verhalten!

Was kann sich da plötzlich verändert haben?

von Kilo S. (kilo_s)


Lesenswert?

Nochmals gegengetestet!
Der encoder aus einer Logitech G19, da er aus meinem Ersatzteilfundus 
stammt und ich garantieren kann das er funktioniert ist es jetzt 
offensichtlich der China Nano?

Je nach dem, manchmal nach dem neu anstecken am PC Klappt's, es werden 
beide Drehrichtungen erkannt, aber hauptsächlich nicht. Dann zählt er 
nur in die am Anfang verwendete Richtung...

von Norbert (Gast)


Lesenswert?

Kilo S. schrieb:
> void setup() {
>   Serial.begin(9600); //9600
>   pinMode(ClockPin, INPUT);
>   pinMode(DataPin, INPUT);

Also wenn das der letzte, aktuelle sourcecode ist,
da fehlt mir so ein ganz kleines bisschen Pull-Up.

von Kilo S. (kilo_s)


Lesenswert?

Norbert schrieb:
> Also wenn das der letzte, aktuelle sourcecode ist,
> da fehlt mir so ein ganz kleines bisschen Pull-Up.

Stimmt, die fehlen.
Aber dann hätte es doch von Anfang an eigentlich nicht funktionieren 
dürfen?!

Nun, ich hab gerade getestet..._PULLUP hinzugefügt.
Bringt auch keine Besserung.

von Uwe K. (ukhl)


Lesenswert?

Versuche es doch mal damit:

Beitrag "Re: Drehgeber/Encoder 1-, 2- oder 4-schrittig"

Sehr einfach und garatiert prellfrei.

von Kilo S. (kilo_s)


Lesenswert?

Uwe K. schrieb:
> Versuche es doch mal damit:

Nein, weil:

Kilo S. schrieb:
> Fürs erste würde ich das mit dem encoder gerne per hand machen, hat den
> ganz einfachen grund das eben dieses "Libs Zusammenklicken" nur halbwegs
> läuft und ich so auch besser und schneller in die sprache einsteige.

Außerdem will ich wissen wieso das vorher funktionierende Programm 
plötzlich nicht mehr will!

von Stefan F. (Gast)


Lesenswert?

Benutzt du ein Steckbrett und/oder Dupont Kabel?
Wenn ja, ersetze alle Verbindungen durch gelötete Kupferlitzen.

Hast du einen Logic Analyzer?
Wenn nicht, besorge dir einen. Der billigste ist besser als keiner.

von Uwe K. (ukhl)


Lesenswert?

Kilo S. schrieb:
> Uwe K. schrieb:
>> Versuche es doch mal damit:
>
> Nein, weil:
>
> Kilo S. schrieb:
>> Fürs erste würde ich das mit dem encoder gerne per hand machen, hat den
>> ganz einfachen grund das eben dieses "Libs Zusammenklicken" nur halbwegs
>> läuft und ich so auch besser und schneller in die sprache einsteige.
>
> Außerdem will ich wissen wieso das vorher funktionierende Programm
> plötzlich nicht mehr will!

Ich empfehle dir, auf den Link zu klicken und mal in die Library 
reinzuschauen. Versuche diese zu verstehen. Es sind nur ein paar 
Coding-Zeilen ohne Wudu. Es ist nicht die PeDa-Routine. Danach hast Du 
die Möglichkeit es von Hand mit deinem eigenen Coding umzusetzen. Es ist 
garantiert prellfrei und kann auch per Interrupt aufgerufen werden. Gebe 
der Idee wenigstens eine Chance.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Benutzt du ein Steckbrett und/oder Dupont Kabel?

Nein, ich hab das gewackelt damit satt . Es ist bereits alles gelötet. 
Da ich aktuell auch keine Verbindung zum Display oder dem ULN2003 
brauche hab ich diese auch Gerade nicht abgeschlossen. Es ist nur der 
Encoder verbunden.

Stefan ⛄ F. schrieb:
> Hast du einen Logic Analyzer?
> Wenn nicht, besorge dir einen. Der billigste ist besser als keiner.

Nein, wird aber bald angeschafft.

Allerdings Frage ich mich wie mir in diesem Fall ein Logic Analyzer 
helfen soll, es würde ja nichts verändert.

Außerdem ist ja der Witz das es beim anstecken ab und an auch mal wieder 
funktioniert, und dann beim weiteren testen einfach plötzlich nicht mehr 
beide Richtungen erkennen kann.

Interessant auch das mein erstes Programm ja genauso wie vorher auch 
zählt.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Kilo S. schrieb:
> Außerdem will ich wissen wieso das vorher funktionierende Programm
> plötzlich nicht mehr will!
Warum bist du dir so sicher, dass der Fehler in der Software ist?

Kilo S. schrieb:
> PULLUP hinzugefügt.
> Bringt auch keine Besserung
Irgendwie ist diese Vorgehensweise arg planlos.

So ähnlich wie Topfschlagen beim Kindergeburtstag. Nur mit einer kleinen 
Änderung: auch die, die Tipps geben, haben eine Augenbinde auf...

Kilo S. schrieb:
> es würde ja nichts verändert.
Wenn es die selbe Software ist und die selbe Schaltung und auch sonst 
nirgends was geändert wurde, dann muss das ein Wunder sein. Glaubst du 
an Wunder? Wenn nicht, dann mach dich strukturiert auf die Fehlersuche: 
Schreib ein Programm, das die Portpins einliest und auf anderen Portpins 
wieder ausgibt. Miss die Spannungen. Passt das alles zusammen und zu den 
Datenblattangaben?

: Bearbeitet durch Moderator
von J. S. (jojos)


Lesenswert?

beim rumprobieren passiert es schnell das man etwas im Code verbiegt und 
danach übersieht.
Ich empfehle da wärmstens sich mit git zu beschäfftigen und einfach 
häufig commits zu machen. Damit sind Änderungen zu vorherigen Versionen 
sofort sichtbar.
Sehr gut ist die git Anbindung in VisualStudio Code. Auch wenn wann 
weiter mit der simplen Arduino IDE arbeiten möchte geht das parallel. Im 
Code Verzeichnis 'git init' ausführen bzw. in VSC F1, dann git initialze 
Eintrag ausführen. Einmal einen commit mit allen Dateien machen und von 
da sieht man die Änderungen mit einem Click.

von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Allerdings Frage ich mich wie mir in diesem Fall ein Logic Analyzer
> helfen soll, es würde ja nichts verändert.

Du kannst damit überprüfen, ob die Signale vom Encoder richtig bzw. wie 
erwartet aussehen. Es macht nicht allzu viel Sinn, stundenlang übe rden 
Quelltext zu grübeln, wenn die Signale gar nicht dem entsprechen, wofür 
der Code geschrieben wurde.

> Außerdem ist ja der Witz das es beim anstecken ab und an auch mal wieder
> funktioniert, und dann beim weiteren testen einfach plötzlich nicht mehr
> beide Richtungen erkennen kann.

Klingt nach Wackelkontakt. Auch den würdest du mit dem Logic Analyzer 
schnell erkennen.

Da ich den Vorschlag mit Git sehr gut finde, hänge ich da mal meine 
Einstiegs-Anleitung dran: http://stefanfrings.de/git/index.html

von Kilo S. (kilo_s)


Lesenswert?

Uwe K. schrieb:
> Ich empfehle dir, auf den Link zu klicken und mal in die Library
> reinzuschauen. Versuche diese zu verstehen.

Ich soll jetzt anfangen zwei prachen zu lernen um dann am ende mit einer 
Sprache zu programmieren?
Vor allem soll ich als Anfänger jetzt hingehen und den C Code für C++ 
anpassen?

Also C++ geht sogar Recht gut zu verstehen, wenn man bereits PHP kann, 
Syntax ist ziemlich ähnlich bisher, nur kleine Unterschiede, C und vor 
allem sowas wie diese "Lib" die zwar auch nur eine Funktion enthält 
(zumindest interpretiere ich das so) bringt mir höchstens das ich gerade 
wie der Ochse vorm Berg stehe weil ich nicht weiß wie ich diese 
auswerten soll wenn Return immer 0 ist!

So... Reicht die Erklärung dafür dir zu zeigen das dein Vorschlag es 
NICHT von 0 an selbst in der Sprache die man lernen möchte (ich muss das 
nicht machen, ich möchte es!) in die falsche Richtung geht?

Ich möchte Arduino/C++ lernen, und mich nicht erst in C einarbeiten um 
danach zu sehen (Google spuckt vieles aus wenn man nach "Convert C Code 
zo C++ Class" sucht) das man dazu erst beide Sprachen können muss.

Mein Lieblingskommentar zu diesem thema: I would suggest using the same 
strategy you'd use to eat an elephant: take it one bite at a time. :-)

Wenn man den Aufwand, C in funktionierenden C++ Code umzuschreiben mit 
dem Essen eines Elefanten Vergleicht, fange ich das erst gar nicht an!

Vor allem macht es keinen Sinn, ich könnte das aktuell ja gar nicht 
übersetzen weil ich so viel C++ noch gar nicht kann!

von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Ich soll jetzt anfangen zwei Sprachen zu lernen?
> Ich möchte Arduino/C++ lernen, und mich nicht erst in C einarbeiten

Wenn du C++ kannst, dann hast du 99% von C ganz nebenbei mit gelernt. 
Und Arduino ist sowieso eine wilde Mischung aus beidem.

> Wenn man den Aufwand, C in funktionierenden C++ Code umzuschreiben mit
> dem Essen eines Elefanten Vergleicht, fange ich das erst gar nicht an!

Ich habe meine LCD Bibliothek zuerst in Plain C (kein Arduino) 
geschrieben, und dann nach Arduino/C++ portiert. Die Portierung dauerte 
samt Dokumentation und Test nicht einmal einen Tag. Also übertreibe mal 
nicht.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Du kannst damit überprüfen, ob die Signale vom Encoder richtig bzw. wie
> erwartet aussehen. Es macht nicht allzu viel Sinn, stundenlang übe rden
> Quelltext zu grübeln, wenn die Signale gar nicht dem entsprechen, wofür
> der Code geschrieben wurde.

Wackelkontakt kann ich ausschließen, da mein erster Versuch (erster 
Beitrag) mit allen drei encodern zählt kann ich Defekte Hardware auch 
ausschließen.

J. S. schrieb:
> beim rumprobieren passiert es schnell das man etwas im Code verbiegt und
> danach übersieht.

Nochmal, falls es überlesen würde: ich hab gestern das USB Kabel 
abgezogen, den Laptop zugeklappt, war kurz weg, kam wieder, klappte den 
Laptop auf, steckte das USB Kabel ein und ab dann ging schon der ganze 
Blödsinn los.

Es gab KEINE Änderungen an Hard oder Software, nicht mal ein Leerzeichen 
zusätzlich im Code, auch wenn ich das Funktionierende Beispiel ( Encoder 
Testcode) hier aus dem Beitrag in einen leeren Sketch kopiere und 
hochlade (läuft alles fehlerfrei durch) ergibt sich das gleiche 
verhalten.

Auch speichere ich Funktionierende Sketches zur Sicherheit immer doppelt 
ab bevor ich anfange Änderungen zu machen. Git brauche ich dafür nicht.

Stefan ⛄ F. schrieb:
> Da ich den Vorschlag mit Git sehr gut finde, hänge ich da mal meine
> Einstiegs-Anleitung dran: http://stefanfrings.de/git/index.html

Nett von dir, ich richte mir aber sicher kein lokales git ein, dessen 
Nutzung ich dann auch noch lernen muss.
Als ich vor 19! Jahren mit PHP anfing hab ich auch kein Git gehabt, 
Phase5 und XAMPP haben gereicht. In dem Fall kannst du Phase5 mit der 
Arduino IDE ersetzen und XAMPP mit dem Compiler.
Einziger Unterschied, ich hatte die Sprachreferenz als Offlineversion 
auf dem Rechner und es war leichter nachzuschlagen als aktuell bei 
Arduino.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Wenn du C++ kannst

Genau, Wenn. Kann ich aber (noch) nicht.

von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Es gab KEINE Änderungen an Hard oder Software

Also doch Wunder. Da können wir dir nicht helfen, das ist eher der 
Fachbereich der Katholischen Kirche.

> Nett von dir, ich richte mir aber sicher kein lokales git ein

Brauchst du auch nicht. Man kann Git ohne Server und ohne zentrales 
Repository nutzen. Schade dass du nicht einmal die ersten Absätze der 
Anleitung gelesen hast.

> Als ich vor 19! Jahren mit PHP anfing hab ich auch kein Git gehabt,

Ich auch nicht, aber heute entwickelt man aus gutem Grund mit diesem 
oder ähnlichen Tools. Wenn du auf dem Stand von vor 19 Jahren stecken 
bleiben willst, dann musst du konsequenterweise auch Arduino ablehnen.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Also doch Wunder

Entweder das oder der China Nano (kam ja bereits öfter vor) ist am Ende 
doch Defekt, was allerdings durch die Tatsache daß mein erster Versuch 
funktioniert unwahrscheinlich ist.

Stefan ⛄ F. schrieb:
> Brauchst du auch nicht. Man kann Git ohne Server und ohne zentrales
> Repository nutzen. Schade dass du nicht einmal die ersten Absätze der
> Anleitung gelesen hast.

Ob das nun als Serverdeamon, Programm oder sogar nur als Script läuft, 
völlig irrelevant. Ich benötige es nicht um die Änderungen im Programm 
nachzuvollziehen da ich mir immer Kopien des original als .Ino.old*n* 
(n=int) speichere und danach erst Änderungen mache.

Und wenn so ein Projekt größer und unübersichtlich wird folgen auch 
Kommentare, wie früher am Anfang von PHP. Aber bei 28 Zeilen Code wie im 
Testprogramm, das einfach lesbar ist und das ich auch Recht gut 
verstehen kann... Unnötig.

P.S
Ich lass gleich mal das Oszilloskop Warmlaufen, das pingewackel des 
Encoder dürfte sich auch damit erkennen lassen.

: Bearbeitet durch User
von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich auch nicht, aber heute entwickelt man aus gutem Grund mit diesem
> oder ähnlichen Tools. Wenn du auf dem Stand von vor 19 Jahren stecken
> bleiben willst, dann musst du konsequenterweise auch Arduino ablehnen.

Das hat doch damit nichts zu tun.
Git mag für manche ein mittel der Wahl sein, ich sehe nur für mich als 
einzelnen keinen Vorteil es zu nutzen.

Du darfst allerdings gerne ein wirklich gutes Beispiel nennen wo man das 
als einzelner programmierer unbedingt benötigt um seinen eigenen von 
niemand sonst veränderten Code nachzuvollziehen.

von J. S. (jojos)


Lesenswert?

Kilo S. schrieb:
> Ich soll jetzt anfangen zwei prachen zu lernen um dann am ende mit einer
> Sprache zu programmieren?

FYI:
du benutzt schon einen Mischmasch aus C/C++:
1
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);
2
LiquidCrystal_I2C lcd(0x27, 20, 4);
3
4
  myStepper.setSpeed(10);
5
  lcd.init();
6
  lcd.backlight();
7
  lcd.clear();

das gibt es in C nicht.

Kilo S. schrieb:
> Außerdem will ich wissen wieso das vorher funktionierende Programm
> plötzlich nicht mehr will!

Code der Scheiße ist kann auch einfach zufällig funktionieren wenn die 
Randbedingungen gerade passen.

von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Ob das nun als Serverdeamon, Programm oder sogar nur als Script läuft,
> völlig irrelevant.

Es muss gar nichts laufen! Du erstellst Schnappschüsse wann du willst. 
Und nur dann läuft das Programm kurzzeitig. Vergleiche es mit Winzip. 
Jeder Schnappschuss ist ein komprimiertes Archiv, das in ".git" 
Unterverzeichnis abgelegt wird. Nur das Dateiformat ist halt ein 
anderes.

> Ich benötige es nicht um die Änderungen im Programm
> nachzuvollziehen da ich mir immer Kopien des original
> als .Ino.old*n* (n=int) speichere und danach erst Änderungen mache.

Kannst du auch machen. Ja, dann brauchst du kein Git. Aber ein Tool, 
dass Unterschiede vergleicht wäre dann hilfreich. Falls du noch keins 
hast, für Windows empfehle ich WinMerge.

Einen Vorteil von Git kann ich dir für deinen Fall nennen: Wenn du eine 
Zeile Code vor dir hast wo du denkst "die war mal anders" aber du kannst 
dich nicht mehr an das Datum erinnern, dann kann dir die IDE dank Git 
Integration automatisch heraus suchen, wann du sie geändert hast und 
was du geändert hast.

Vermutlich brauchst du das nur selten, so dass eine manuelle Suche 
ebenfalls akzeptabel ist. Spannender wird das Feature, wenn mehrere 
Personen am Projekt arbeiten.

von Kilo S. (kilo_s)


Lesenswert?

J. S. schrieb:
> FYI:
> du benutzt schon einen Mischmasch aus C/C++:

Allerdings einen Mischmasch der leichter zu verstehen ist als reines C.

J. S. schrieb:
> Code der Scheiße ist kann auch einfach zufällig funktionieren wenn die
> Randbedingungen gerade passen.

Naja, ein Blanker Arduino, drei Stücke Litze und ein encoder sind jetzt 
nicht sonderlich viel an dem man die Randbedingungen verändern kann.

Übrigens ist das Ergebnis mit oder ohne Kondensatoren das gleiche, die 
hab ich also auch wieder entfernt. Allerdings erst nach dem der Fehler 
aufgetreten ist und dann auch erst sehr spät.

Aber: Wo ist der Code bis auf das er eben "Springt" beim zählen scheiße?

Solch verschachtelte Konstrukte wie die Funktion des Counter in den 
interrupt zu legen sprechen eigentlich dafür das man die Sprache ganz 
gut drin hat.
Ich hab solche verschachtelten If abfragen in PHP auch erst genutzt als 
ich schon flüssig ohne viel nachdenken programmiern könnte.
Theoretisch sind die nämlich eigentlich "Schlechter Stil", da nur von 
Leuten lesbar und nachvollziehbar die solche Konstrukte selbst Einsetzen 
bzw. diese Schreibweise einer If auch verstehen.

von Stefan F. (Gast)


Lesenswert?

Das erste Programm enthält so etwas ähnliches wie Entprellung alleine 
schon deswegen, weil nach jedem Dreh-Schritt eine LCD Ausgabe gemacht 
wird. Die verkürzten Programme fragen den Encoder schneller ab und 
reagieren daher auch empfindlicher auf Kontaktprellen.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Aber ein Tool, dass Unterschiede vergleicht wäre dann hilfreich.

Linuxer, diff. ;-)

Stefan ⛄ F. schrieb:
> Vermutlich brauchst du das nur selten, so dass eine manuelle Suche
> ebenfalls akzeptabel ist. Spannender wird das Feature, wenn mehrere
> Personen am Projekt arbeiten.

Ja, am ende ist mir tatsächlich das Datum der Änderung egal. Das ist 
auch das schöne, da ich immer eine Kopie vom funktionierenden Code als 
Backup habe, kann ich auch wenn ich mich ganz verrennen sollte (gab's 
auch bei PHP oft) einfach nochmal anfangen.

Bei PHP hatte ich immer folgende Struktur:
Main:
    -backup
    -working
    -progress
    -running_online

Backup ist selbsterklärend.

Working ist das Verzeichnis für die funktionierenden Dateien gewesen.

Progress war sozusagen die 1:1 Kopie von working, allerdings mein 
"Hauptverzeichnis" zum Arbeiten am Code.

running_online lief dann am ende auf dem echten Server. Das war immer 
"latest Working", und Working und Backup hatten bei mir immer den 
Anspruch "Stable" zu sein. Auf Progress hab ich sogar im IRC extra Leute 
geärgert und freiwillig und vollen Bewusstseins das es echt in die Hose 
gehen kann dafür gesorgt das die meinen "Exposed Host" (ja, ich habe 
einen XAMPP/Apache online gelassen! Später allerdings auch nur noch 
unter Linux und als ich die Administration auch wirklich unter Kontrolle 
hatte.) ordentlich Maltertieren.
Gratis Pentest. ;-)

Geile Zeit! Könnte man heute nicht mehr machen.


Und so in der Art wollte ich das eben mit Arduino auch machen. Nur eben 
Backup, Working, Progress und aus Backup (=Working Stable) wird dann 
"Running". Ich rationalisiere eben einen Ordner weg.

Der Mensch ist eben Gewohnheitstier.

von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Linuxer, diff.

Ach so. Für Linux empfehle ich Meld.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das erste Programm enthält so etwas ähnliches wie Entprellung alleine
> schon deswegen, weil nach jedem Dreh-Schritt eine LCD Ausgabe gemacht
> wird. Die verkürzten Programme fragen den Encoder schneller ab und
> reagieren daher auch empfindlicher auf Kontaktprellen.

Ja, interessant ist das es so scheint als sei das Schreiben (trotz 
lahmen lcd.clear () ) des Display noch nicht genügend entprellung um 
Sprünge zu Vermeiden.

Das verkürzte Programm ergänze ich gleich noch mal. Allerdings bin ich 
nicht sicher ob das so greift, millis und interrupt mögen sich nicht.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Kilo S. schrieb:
>> Linuxer, diff.
>
> Ach so. Für Linux empfehle ich Meld.

;-) Auch.

Kilo S. schrieb:
> Das verkürzte Programm ergänze ich gleich noch mal. Allerdings bin ich
> nicht sicher ob das so greift, millis und interrupt mögen sich nicht.

Hier mal eine (Quick&Dirty) testversion.
Allerding genauso funktionsunfahig in beide richtungen zu zählen.
gefühlt scheint es aber zu funktionieren, weniger sprünge egal ob langam 
oder schnell am encoder drehen.
1
#define ClockPin 2 // Must be pin 2 
2
#define DataPin 3 // Must be pin 3
3
#define readA bitRead(PIND,2)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
4
#define readB bitRead(PIND,3)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
5
volatile long count = 0;
6
7
void setup() {
8
  Serial.begin(9600); //9600
9
  pinMode(ClockPin, INPUT);
10
  pinMode(DataPin, INPUT);
11
  /* Full 4 Step Count*/
12
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {(readA == readB)  ? count++ : count--;}, CHANGE);
13
  attachInterrupt(digitalPinToInterrupt(DataPin ), [] {(!readA == readB)  ? count++ : count--;}, CHANGE);
14
}
15
16
void loop() {
17
  long Counter;
18
  static long lastCtr;
19
  noInterrupts ();
20
  Counter = count;
21
  interrupts ();
22
  static unsigned long last_interrupt_time = 0;
23
  unsigned long interrupt_time = millis();
24
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
25
  if (interrupt_time - last_interrupt_time > 2000) {
26
  
27
  }else {
28
    if (lastCtr != Counter) {
29
    Serial.println(Counter);
30
    lastCtr = Counter;
31
  }
32
  
33
  }
34
  last_interrupt_time = interrupt_time;
35
}

Was sagt ihr, Richtig angewendet?

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Mir leuchtet überhaupt nicht ein, warum du für den Data Pin einen 
Interrupt-Handler einrichtest. Du fragst doch schon in der ISR für den 
Takt den Data-Pin ab, und entscheidest dementsprechend, ob du hoch oder 
runter zählst.

Warum duplizierst du den selben Code nochmal mit dem Data-Pin? Damit 
reagierst du auf jeden Dreh-Schritt doppelt.

Eine Entprellung ist da nur schwer rein zu bekommen, das hast du ja 
selbst schon gemerkt. Folge doch besser dem Vorschlag von Lothar, die 
Pins in einem Timer-Interrupt abzufragen.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Mir leuchtet überhaupt nicht ein, warum du für den Data Pin einen
> Interrupt-Handler einrichtest. Du fragst doch schon in der ISR für den
> Takt den Data-Pin ab, und entscheidest dementsprechend, ob du hoch oder
> runter zählst.

Krieg ich keine logik für in den kopf...
Wie will ich denn die drehrichtung erkennen wenn ich nicht weiß ob data 
nun vor oder nach Takt high oder low war. Ein pin zum zählen, einer für 
die Richtungserkennung.

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Kilo S. schrieb:
> Wie will ich denn die drehrichtung erkennen wenn ich nicht weiß ob data
> nun vor oder nach Takt high oder low war.

Data und Clock ändern sich nie gleichzeitig. Zum Zeitpunkt des Taktes 
hat die Data Leitung daher den gleichen Pegel, wie kurz davor und auch 
kurz danach.

Aber nach wie vor möchte ich davon abraten, den Encoder auf diese Weise 
abzufragen, denn es funktioniert so nur mit extern entprelltem 
Taktsignal.

Siehe https://www.mikrocontroller.net/articles/Drehgeber

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Data und Clock ändern sich nie gleichzeitig. Zum Zeitpunkt des Taktes
> hat die Data Leitung daher den gleichen Pegel, wie kurz davor und auch
> kurz danach.

Das beantwortet meine frage immer noch nicht, ich sehe keinen logischen 
ansatz daraus eine verwertbare information zu gewinnen mit nur einem 
pin.

Stefan ⛄ F. schrieb:
> Folge doch besser dem Vorschlag von Lothar, die
> Pins in einem Timer-Interrupt abzufragen.

Ist wieder so ein Lib scheißdreck!
https://www.arduino.cc/reference/en/libraries/timerinterrupt/

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Das beantwortet meine frage immer noch nicht, ich sehe keinen logischen
> ansatz daraus eine verwertbare information zu gewinnen mit nur einem
> pin.

Von einem Pin war keine Rede, sondern von einem Interrupt. Löse bei 
jeder Flanke an Clock einen Interrupt aus. In der Interruptroutine 
fragst du wie gehabt ab, ob der Pegel von Data gleich dem Pegel von 
Clock ist. Wenn ja, wurde links herum gedreht. Wenn nicht, wurde rechts 
herum gedreht.

Aber:
Ich kann dir diese Methode nach wie vor nicht empfehlen, weil das so nur 
mit extern entprelltem Takt zuverlässig funktioniert. Eine Entprellung 
per Software lässt sich besser in eine periodische Abfrage per Timer 
Interrupt unterbringen.

> Ist wieder so ein Lib scheißdreck!

Niemand hindert dich daran, von den wenigen Zeilen Code zu lernen und es 
selbst zu programmieren. Du kannst es ja erst mal mit Clock-Interrupt 
versuchen und später auf die komplexere Variante mit Entprellung zurück 
kommen, wenn du mit dem Kontaktprellen konfrontiert wirst. 
Hobby-Schaltungen müssen nicht von Anfang an perfekt sein. Learning by 
doing, und mit kleinen Brötchen anfangen ist schon OK, wenn man die Zeit 
dazu hat und kein Business oder Leben davon abhängt.

von J. S. (jojos)


Lesenswert?

im mittlerweile mehrfach verlinkten Drehgeber Artikel ist simpler 
C-Code, keine Lib.
Läauft auf dem Nano, hat den Nachteil der HW Abhängigkeit. Mit den 
Arduino Lib Funktionen sollte es etwas portabler sein. Von daher ist 
Nutzung von Timer und attachInterrupt nix Schlimmes.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Niemand hindert dich daran, von den wenigen Zeilen Code zu lernen und es
> selbst zu programmieren.

Alles gut, ich hab ja auch nur erwähnt das es eben wieder eine ist.

Stefan ⛄ F. schrieb:
> Du kannst es ja erst mal mit Clock-Interrupt
> versuchen und später auf die komplexere Variante mit Entprellung zurück
> kommen, wenn du mit dem Kontaktprellen konfrontiert wirst.
> Hobby-Schaltungen müssen nicht von Anfang an perfekt sein. Learning by
> doing, und mit kleinen Brötchen anfangen ist schon OK, wenn man die Zeit
> dazu hat und kein Business oder Leben davon abhängt.

Ja, ich lese mich gerade ein.

Wenn ich das richtig verstehe meint ihr also ich soll mit dem Timer 
zyklisch die encoderabfrage laufen lassen und dessen rückgabewert 
auswerten.
Gut, ich hab mir jetzt ein beispiel rausgesucht und versuche es 
entsprechend anzupassen. Ich trottel such mir natürlich auch was aus wo 
ich direkt am anfang erst mal Register und den ganzen kram lernen muss, 
was einem bei Arduino ja eigentlich durch die vielen Libs abgenommen 
wird. Ist jedenfalls eher selten anzutreffen habe ich den eindruck. ;-)

Naja, überlebenswichtig ist es nicht. Allerdings ein lang gehegter 
"Wunsch" mir irgendwann mal eine steuerung für eine Magnetic Loop zu 
bauen.
Aktuell ist es auch das einzig Praktikable am Funkhobby die steuerung zu 
programmieren, außer mit kopfhörern am SDR lauschen.
Alles was Krach macht oder "Stinkt" (Löten) ist gerade nicht drin.
Heute ist meine Kleine Tochter genau 7 Tage alt, und jede sekunde ruhe 
die sie schläft darf Papa und Mamma auch Ausruhen. ;-)

von Kilo S. (kilo_s)


Lesenswert?

J. S. schrieb:
> im mittlerweile mehrfach verlinkten Drehgeber Artikel ist simpler
> C-Code, keine Lib.
> Läauft auf dem Nano, hat den Nachteil der HW Abhängigkeit.

So weit ich die durchblickt habe ist es eine funktion deren rückgabewert 
(jetzt hab ich's doch geblickt!) entweder 0, 1 oder -1 ist.
Auswertung wäre also;
0= Nichts ist passiert also mach auch nichts, 1= count++, -1 count--. So 
weit so Logisch.

Ok, jetzt wo das (in C) durchblickt habe das es in der switch case doch 
rückgabewerte gibt stellt sich mir die frage nach dem aufruf.
Die funktion selbst möchte (interpretation!) CLCK_PIN, DATA_PIN, reNum. 
(Array Index?) übergeben haben.

Alles Ganzzahlen von 0-255, auch alles so weit so logisch, die pins am 
encoder haben ja nur (eigentlich) nur 0 und 1, das prellen ist ja nur 
der schnelle wechsel zwischen diesen zuständen.

Also, die funktion im Timer interrupt aufrufen, den rückgabewert 
speichern und in der loop auswerten....

Ich hatte echt nicht gedacht das so was alltägliches (hat ja fast jeder 
ein gerät zuhause das solch einen encoder nutzt) am ende doch so viel 
"Softwareaufwand" beim lernen bedeutet.

P.S
Ja das mit dem Löten war leicht übertrieben, natürlich kann ich noch 
Löten.
Allerdings zeitlich Begrenzt auf dem Balkon. ;-)

P.P.S
Eine "Libary" (rotaryEncode.h) enthält ja abgesehen von den nötigen 
definitionen anderer "Libary’s" hauptsächlich funktionen. auch wenn .h 
eigentlich "Header" sind...hmmm. Ja man mag sich über die definition 
streiten.
Für mich definiere ich allerdings eine .h Datei die nur eine Funktion 
enthält als "Lib/Libary".

: Bearbeitet durch User
von Kilo S. (kilo_s)


Lesenswert?

Lothar M. schrieb:
> Wenn es die selbe Software ist und die selbe Schaltung und auch sonst
> nirgends was geändert wurde, dann muss das ein Wunder sein. Glaubst du
> an Wunder? Wenn nicht, dann mach dich strukturiert auf die Fehlersuche:
> Schreib ein Programm, das die Portpins einliest und auf anderen Portpins
> wieder ausgibt. Miss die Spannungen. Passt das alles zusammen und zu den
> Datenblattangaben?


Darauf möchte ich zusätzlich, nach reiflichem überlegen auch noch 
antworten.

Es ist ja bekannt, das die "Chinaklone" nicht umbedingt immer top sind.
Also durchaus auch "Funktionierender Ausschuss" aus Chipfabriken (Was 
eben als "Unverkäuflich" ausgemustert wird) aufgekauft, auf "Nano 
Kompatible V3" Boards verkauft wird. Manchmal hilft es den Bootloader 
neu zu brennen. Mal sind die Chips einfach Ausschuss, oder im besten 
Fall Teil Funktional.

Durch meine eigene Motivation konnte ich ja ausschließen das es so ist.
Wie oft erlebt man hier das jemand sich echt den A**** aufgerissen hat 
die Hardware auf eigene Faust zu prüfen weil er sich zwar sicher ist das 
sie funktioniert, allerdings trotzdem in Betracht zieht das genau das 
Gegenteil der Fall sein könnte?

Allerdings wir auch von "Wundersamen" verhalten bei manchen Boards 
berichtet, eher älteren Datums, allerdings innerhalb der letzten 5-7 
Jahre.

Ich weiß nicht mal wann ich mir die 2 für 5€ angeschafft habe, es sind 
auch keine originale, daher ist eine Fehlfunktion (auch Beider Nano 
Boards) therotetisch möglich gewesen.

Das meine versuche bisher ergeben haben das ich dies "ausschließen" 
kann, es allerdings auch keiner von euch mit "Ähnlichem Encoder und Nano 
Board"  probiert hat den code auf "Neuer Legacy Hardware" zu testen.... 
Wer weiß, wer weiß!

Wunder..... Naja, nicht bei Elektronik.  ;-)

Wäre echt cool wenn das jemand testen Könnte.

von Uwe K. (ukhl)


Lesenswert?

Kilo S. schrieb:

> So weit ich die durchblickt habe ist es eine funktion deren rückgabewert
> (jetzt hab ich's doch geblickt!) entweder 0, 1 oder -1 ist.
> Auswertung wäre also;
> 0= Nichts ist passiert also mach auch nichts, 1= count++, -1 count--. So
> weit so Logisch.

> Die funktion selbst möchte (interpretation!) CLCK_PIN, DATA_PIN, reNum.
> (Array Index?) übergeben haben.

CLCK und DATA sind sehr unglückliche Bezeichnungen, da diese streng 
genommen nicht stimmen. CLCK ist A und DATA ist B. Das "reNum" ist die 
Nummer des Rotary Encoders, da die Funktion bis zu 8 Rotary Encoder 
abfragen kann. Trage immer O ein, wenn es nur einer ist.

> Also, die Funktion im Timer Interrupt aufrufen, den rückgabewert
> speichern und in der loop auswerten....

Den Rückgabewert solltest Du auf deinen Zähler Addieren. Deine Loop 
könnte zu langsam sein.
1
encDelta += rotaryEncode(PHASE_A, PHASE_B, 0);

Wenn Du die Drehrichtung benötigt, dann speichere es in einen 
Delta-Wert. und addiere diesen in der Loop. Beim Addieren, die 
Interrupts mit CLI() ausschalten und danach mit SEI() wieder 
einschalten.

Sonst direkt in deinen Zähler.

Diese Funktion darfst Du auch in Interrupt auswerten (beide Pins 
brauchen ein Interrupt), da sie prellfrei ist. Muss man aber nicht.

> Ich hatte echt nicht gedacht das so was alltägliches (hat ja fast jeder
> ein gerät zuhause das solch einen encoder nutzt) am ende doch so viel
> "Softwareaufwand" beim lernen bedeutet.

Ja, das wird leicht unterschätzt besonders durch das Prellen und die 
Rastung.

> P.P.S
> Eine "Libary" (rotaryEncode.h) enthält ja abgesehen von den nötigen
> definitionen anderer "Libary’s" hauptsächlich funktionen. auch wenn .h
> eigentlich "Header" sind...hmmm. Ja man mag sich über die definition
> streiten.
> Für mich definiere ich allerdings eine .h Datei die nur eine Funktion
> enthält als "Lib/Libary".

Formell ist es wohl nicht perfekt. Letztendlich habe ich nur eine 
Funktion eingebaut. Durch RE_DETENT kann man den Rastpunkt einstellen. 
Meist ist es 1. Ein Rastung durchläuft alle 4 Zustände. Bei 2 werden 
zwei Zustande pro Rastung durchlaufen. Bei 4 ist keine Rastung vorhanden 
(sehr selten bei Handgebern).

Es ist kein Problem, wenn Du die Funktion einfach in dein Programm 
kopierst. Dann bist Du ohne Library. RE_DETENT solltest Du erstmal auf 1 
stellen.

von Kilo S. (kilo_s)


Lesenswert?

Heute kam ich endlich dazu weiter zu machen.
1
#include <Wire.h>
2
3
//encoder
4
int encPinA = 2;
5
int encPinB = 3;
6
int encPos = 0;
7
int encPinALast = 0;
8
int encPinANow = 0;
9
int s =1;
10
11
void setup() {
12
    
13
    cli();
14
    TCCR1A = 0;
15
    TCCR1B = 0;
16
    TCCR1B |= (1 << WGM12);
17
    TCCR1B |= (1 << CS11);
18
    TCNT1 = 0;
19
    OCR1A = 39999;
20
    TIMSK1 |= (1 << OCIE1A);
21
    sei();
22
    Serial.begin(9600);
23
    
24
}
25
26
ISR(TIMER1_COMPA_vect) {
27
    encPinANow = digitalRead(encPinA);
28
    if ((encPinALast == HIGH) && (encPinANow == LOW)) {
29
        if (digitalRead(encPinB) == HIGH) {
30
            encPos++;
31
        } else {
32
            encPos--;
33
       }
34
    }
35
    encPinALast = encPinANow;
36
37
}
38
void loop() {
39
 
40
  Serial.println(encPos);
41
  delay(1000);
42
  
43
}

So weit so gut, bis auf die tatsache das immer noch nur in eine richtung 
gezählt wird wie es scheint. ich teste weiter.

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Kilo S. schrieb:
> So weit so gut, bis auf die tatsache das immer noch nur in eine richtung
> gezählt wird wie es scheint. ich teste weiter.
Probiers doch mal so, dass du nicht am Drehgeber drehst, sondern einfach 
mit 2 Pullups und 2 Tastern definierte Pegel an A und B vorgibst. Denn 
dann kannst du dauerhaft für einen stabilen high- oder low-Pegel an B 
sorgen und am A beherzt herumtakten...

von J. S. (jojos)


Lesenswert?

vielleicht sollte man mal nicht annehmen das man einen Wechsel pro 
Rastung bekommt.
und nochmal der Link zum Artikel: 
https://www.mikrocontroller.net/articles/Drehgeber#Signalauswertung

von Kilo S. (kilo_s)


Lesenswert?

Ha, Kabelbruch!

Funktioniert! Ohne springen und fehler.
Das timing stimmt auch, 1 step/s packt das ganze.
Jetzt das ganze noch mit dem stepper zusammenbringen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Kilo S. schrieb:
> Ha, Kabelbruch!
Also doch eine andere Schaltung als ursprünglich.

Kilo S. schrieb:
> Allerdings Frage ich mich wie mir in diesem Fall ein Logic Analyzer
> helfen soll, es würde ja nichts verändert.
Du hättest sofort bei der 1. Messung am µC-Pin gesehen, dass sich da 
nichts tut. Die Fehlerursache "Kabelbruch" hättest du dann 30s später 
ganz von allein gefunden.

: Bearbeitet durch Moderator
von Kilo S. (kilo_s)


Lesenswert?

Lothar M. schrieb:
> Also doch eine andere Schaltung als ursprünglich.

Nein! es ist genau das gleich wie vorher, drei kabel an denen ein 
drehencoder hängt verbunden mit einem arduino. Das eins der kabel beim 
hin und her räumen mal kaputt geht ist halt mal so...

Wie du auf so eine idee kommst erschließt sich mir nicht!

Lothar M. schrieb:
> Du hättest sofort bei der 1. Messung am µC-Pin gesehen, dass sich da
> nichts tut. Die Fehlerursache "Kabelbruch" hättest du dann 30s später
> ganz von allein gefunden.

Schön, wie du siehst gings auch ohne ganz gut.

von Kilo S. (kilo_s)


Lesenswert?

Wo hab ich meinen Logikfehler?
1
#include <Wire.h>
2
//#include <LiquidCrystal_I2C.h>
3
#include <Stepper.h>
4
5
//encoder
6
int encPinA = 2;
7
int encPinB = 3;
8
int encPos = 0;
9
int encPosLast = 0;
10
int encPinALast = 0;
11
int encPinANow = 0;
12
int s = 1;
13
14
//Stepper
15
const int stepsPerRevolution = 48;
16
int StepperPos = 0;
17
int LastStepperPos = 0;
18
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);
19
20
21
void setup() {
22
  //Encoder Pins
23
    pinMode (encPinA, INPUT_PULLUP);
24
    pinMode (encPinB, INPUT_PULLUP);
25
26
  //Stepper
27
    myStepper.setSpeed(20);
28
    
29
  //interrupt 50Hz
30
    cli();
31
    TCCR1A = 0;
32
    TCCR1B = 0;
33
    TCCR1B |= (1 << WGM12);
34
    TCCR1B |= (1 << CS11);
35
    TCNT1 = 0;
36
    OCR1A = 39999;
37
    TIMSK1 |= (1 << OCIE1A);
38
    sei();
39
    Serial.begin(9600);       
40
}
41
42
ISR(TIMER1_COMPA_vect) {
43
    encPinANow = digitalRead(encPinA);
44
    if ((encPinALast == HIGH) && (encPinANow == LOW)) {
45
        if (digitalRead(encPinB) == HIGH) {
46
            encPos++;
47
            StepperPos =-s;
48
        } else {
49
            encPos--;
50
            StepperPos = s;
51
       }
52
    }
53
    encPinALast = encPinANow;    
54
}
55
56
void loop() {
57
58
  if (LastStepperPos != StepperPos) {
59
    Serial.println("LastStepperPos");
60
    Serial.println(LastStepperPos);
61
    LastStepperPos = StepperPos;
62
    Serial.println("StepperPos");
63
    Serial.println(StepperPos);
64
    myStepper.step(StepperPos);
65
    StepperPos = 0;
66
  }
67
 
68
}

An und für sich macht er einige schritte normal, danach springt er 
abrupt zurück und macht wieder einige schritte normal.

Die ausgabe sieht für mich auch wie erwartet aus:
1
16:17:08.182 -> StepperPos
2
16:17:08.182 -> 0
3
16:17:08.779 -> LastStepperPos
4
16:17:08.779 -> 0
5
16:17:08.779 -> StepperPos
6
16:17:08.779 -> 1
7
16:17:08.812 -> LastStepperPos
8
16:17:08.812 -> 1
9
16:17:08.812 -> StepperPos
10
16:17:08.845 -> 0
11
16:17:09.044 -> LastStepperPos
12
16:17:09.044 -> 0
13
16:17:09.044 -> StepperPos
14
16:17:09.044 -> 1
15
16:17:09.077 -> LastStepperPos
16
16:17:09.077 -> 1
17
16:17:09.077 -> StepperPos
18
16:17:09.077 -> 0
19
16:17:09.276 -> LastStepperPos
20
16:17:09.276 -> 0
21
16:17:09.276 -> StepperPos
22
16:17:09.276 -> 1
23
16:17:09.309 -> LastStepperPos
24
16:17:09.309 -> 1
25
16:17:09.309 -> StepperPos
26
16:17:09.309 -> 0
27
16:17:10.668 -> LastStepperPos
28
16:17:10.668 -> 0
29
16:17:10.668 -> StepperPos
30
16:17:10.668 -> 1
31
16:17:10.701 -> LastStepperPos
32
16:17:10.701 -> 1
33
16:17:10.701 -> StepperPos
34
16:17:10.701 -> 0
35
16:17:10.999 -> LastStepperPos
36
16:17:10.999 -> 0
37
16:17:10.999 -> StepperPos
38
16:17:10.999 -> 1
39
16:17:11.032 -> LastStepperPos
40
16:17:11.032 -> 1
41
16:17:11.032 -> StepperPos
42
16:17:11.066 -> 0
43
16:17:11.895 -> LastStepperPos
44
16:17:11.895 -> 0
45
16:17:11.895 -> StepperPos
46
16:17:11.895 -> 1
47
16:17:11.928 -> LastStepperPos
48
16:17:11.928 -> 1
49
16:17:11.928 -> StepperPos
50
16:17:11.928 -> 0
51
16:17:12.127 -> LastStepperPos
52
16:17:12.127 -> 0
53
16:17:12.127 -> StepperPos
54
16:17:12.127 -> 1
55
16:17:12.160 -> LastStepperPos
56
16:17:12.160 -> 1
57
16:17:12.160 -> StepperPos
58
16:17:12.160 -> 0
59
16:17:12.459 -> LastStepperPos
60
16:17:12.459 -> 0
61
16:17:12.459 -> StepperPos
62
16:17:12.459 -> 1
63
16:17:12.492 -> LastStepperPos
64
16:17:12.492 -> 1

irgendwo hat sich ein "Logikteufel" eingeschlichen, ich seh ihn nur 
nicht!

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Wo hab ich meinen Logikfehler?

Du hast keine Entprellung

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Du hast keine Entprellung

Ich seh aber keine sprünge die das verhalten erklären würden und somit 
auf ein prellen schliesen lassen.

Auch der Zähler selbst funktioniert ohne sprünge...

von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Ich seh aber keine sprünge die das verhalten erklären würden und somit
> auf ein prellen schliesen lassen.

Zwei aufeinander folgende (prellende) Take an Pin A während Pin B nicht 
prellt bewirkt, dass dein Zähler einmal herauf und dann wieder runter 
zählt.

Beim Prellen können auch viel mehr Impulse entstehen.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Zwei aufeinander folgende (prellende) Take an Pin A während Pin B nicht
> prellt bewirkt, dass dein Zähler einmal herauf und dann wieder runter
> zählt.

Wo siehst du da ein prellen?
Das sind zwei variablen, nicht eine. Der wechsel gleichzeitig ist 
gewollt und auch richtig so.

Der timer lauft 50 mal pro sekunde durch und fragt ab, setzt den zahler 
je nach richtung eins hoch oder runter und setzt StepperPos entweder auf 
-1 oder 1. Es gibt kein "Prellen", StepperPos ist immer nur ein mal da 
und der wert passt zur drehrichtung.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Kilo S. schrieb:
> Wo siehst du da ein prellen?

Kontakte prellen halt, das liegt in deren Natur.

Ich denke wir haben dieses Thema oft genug angesprochen. Wenn du noch 
irgendwelche Fragen dazu hast, lies deine Threads nochmal durch. Es 
wurde bereits alles erklärt und Lösungen vorgeschlagen.

von Uwe K. (ukhl)


Lesenswert?

Versuche mal die Interrupts bei der Abfrage zu sperren.
1
  cli();
2
  if (LastStepperPos != StepperPos) {
3
    Serial.println("LastStepperPos");
4
    Serial.println(LastStepperPos);
5
    LastStepperPos = StepperPos;
6
    Serial.println("StepperPos");
7
    Serial.println(StepperPos);
8
    myStepper.step(StepperPos);
9
    StepperPos = 0;
10
  }
11
  sei();

Nur um zu sehen, ob der Übeltäter da zu finden ist.

von Stefan F. (Gast)


Lesenswert?

Uwe K. schrieb:
> Versuche mal die Interrupts bei der Abfrage zu sperren.

Guter Hinweis. Während die erste Hälfte der int Variable (also 8 von 16 
Bit) gelesen wird könnten sich nämlich die anderen (via Interrupt) 
ändern und dann passen die 2 Bytes nicht mehr zusammen. Da er die 
Variable immer wieder auf 0 zurück setzt fällt das noch nicht auf. Das 
wird erst akut, wenn man Werte >255 hat.

Allerdings würde ich keine seriellen Ausgaben machen wollen, während die 
Interrupts gesperrt ist. Dabei hängt sich der Mikrocontroller gerne mal 
auf. Besser so:
1
  volatile int StepperPos = 0;
2
  ...
3
4
  cli();
5
  int currentPos=StepperPos;
6
  sei();
7
8
  if (LastStepperPos != currentPos) {
9
    Serial.println("LastStepperPos");
10
    Serial.println(LastStepperPos);
11
    LastStepperPos = currentPos;
12
    Serial.println("StepperPos");
13
    Serial.println(currentPos);
14
    myStepper.step(currentPos);
15
16
    cli();
17
    currentPos = 0;
18
    sei();
19
  }

Außerdem sollten die Variablen, die sowohl in der ISR als auch außerhalb 
verwendet werden, volatile sein. Sonst es es reines Glück, ob sie 
überhaupt funktionieren.

von Kilo S. (kilo_s)


Lesenswert?

Stefan ⛄ F. schrieb:
> Kontakte prellen halt, das liegt in deren Natur.

In dem Fall war es allerdings nicht das prellen.
Der Nano den ich die ganze zeit benutze, der hat eine macke, mit dem 
zweiten funktionierts!

Da lötet man schon alles extra zusammen, trotzdem spinnt die Hardware!

Stefan ⛄ F. schrieb:
> Außerdem sollten die Variablen, die sowohl in der ISR als auch außerhalb
> verwendet werden, volatile sein. Sonst es es reines Glück, ob sie
> überhaupt funktionieren.

Wird übernommen.


Nach dem der Spaß nun funktioniert geht's an die gestaltung des Display.

Beitrag #7139457 wurde von einem Moderator gelöscht.
Beitrag #7139467 wurde von einem Moderator gelöscht.
Beitrag #7139682 wurde von einem Moderator gelöscht.
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.