PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Ein Drehregler und ein Arduino Nano



hirnfrei
06.07.2018, 19:19
Mahlzeit!

Ich versuche gerade einen Drehregler, oder wie man die Dinger nun genau heisst, mit dem Arduino Nano korrekt auszulesen. Ich habe ihn an 5V angeschlossen, GND ist auch dort, wo es hin soll, die drei Pins sind mit den analogen Pins 2-4 angeschlossen. Die Pins sind im Sketch auf INPUT gestellt und wenn ich sie auslese, ohne etwas am Regler zu verändern, dann sind alle drei HIGH. Soweit so gut. Drücke ich den Regler, wird Pin 2 LOW, bis ich den Knopf wieder loslasse. Also wie es sein soll.

Drehe ich aber an dem Ding, dann passiert manchmal gar nichts, manchmal werden Pin 3 und 4 LOW, manchmal Pin 4, obwohl ich in die andere Richtung drehe usw. Was mache ich da falsch? Der Regler hat drei Pull-Down Widerstände auf der Rückseite. Daran sollte es also nicht liegen, denke ich mal. Das macht mich gerade echt irre! Das Ding soll doch beim Drehen bei jedem Klick 1 hoch- oder runterzählen. Ist denn das zu viel verlangt?

Siro
06.07.2018, 20:06
Dieser Drehregler ist vermutlich ein Incrementalgeber und der
erzeugt je nach Drehrichtung an 2 Pins zeitversetzte Impulse.

Am einfachsten kann man das mit einem Interrupt auswerten.
Ein Signal geht an den Interrupt Pin das andere Signal an irgend einen Portpin
Der Interrupt wird dann per Sofwatre so eingestellt, dass z.B. bei
jeder steigende Flange ein Interrupt ausgelöst wird.
In der Interrupt Funktion schaut man dann nach welchen Pegel
der andere Pin grade hat. ist er High wurde der Regler in die eine Richtung gedreht, ist er Low wurde der Regler in die andere Richtung gedreht.
Dann zählt man einfach die Impulse hoch bzw. runter.

Also im Prinzip sieht das so aus:

Interruppt Funktion: wird ausgelöst wenn der erste Pin vom Drehgeber
Seinen Pegel ändert.
Dann kommt die Abfage:
wenn Pegel am zweiten Pin = Low dann Richtung = -1
ansonsten Richtung = +1

Zählerwert = Zählerwert + Richtung

Im Prinzip war es das schon.
Siro

- - - Aktualisiert - - -

Im Prinzip kann man das auch ohne Interrupt machen: ist aber problematisch
warte bis der eine Pin von Low nach High wechselt,
dann lese schnell den Pegel des anderen Pins
ist der 2te Pin High wurde der Regler in die eine, ansonsten in die andere Richtung gedreht.
Beim schnellen Drehen kann man aber schnell auf 50 Signale pro Sekunde kommen, damit ist die Abfrage recht problemeatisch, weil man ja nie weis wann man dreht.
Deshalb der Interrupt, der kommt sofort mit wenn sich der Pegel ändert und verzweigt dann in die entsprechende Auswerte Funktion

hirnfrei
06.07.2018, 20:41
Ich habe das nun so versucht zu lösen:



void ui()
{
int aState = digitalRead(A);

if(State != aState)
{
if(digitalRead(B) != aState)
{
volt += 0.05;
}
else volt -= 0.05;
}

State = aState;
}


Das scheint die Variante zu sein, die du hinzugefügt hast. Ich habe selbst bislang noch nichts mit Interrupts beim Arduino gemacht, weiss also gerade nicht, wie ich es damit realisieren müsste :(. So funktioniert es auch, wenn der Sketch nichts anderes als den Regler abfragen und das dann an seriell weitergeben soll. In meinem eigentlichen Sketch funktioniert es nicht mehr. Runterzählen geht, rauf nicht. Aber auch beim runterzählen zählt er auch gerne wieder hoch.

HaWe
06.07.2018, 21:32
eine genaue Artikelbezeichnung und Händler-Link wäre ja schon hilfreich - wer weiß, ob es das ist, was wir vermuten?
Schließlich sind Kristallkugeln auch nicht immer 100% treffsicher, und Orakel in Delphi auch nicht ;)

WENN es ein Dreh-Encoder ist, such mal bei Google nach
Arduino Encoder Tutorial
z.B. https://www.boecker-systemelektronik.de/epages/63381271.sf/de_DE/?ObjectPath=/Shops/63381271/Categories/Tutorials/Roboter_programmieren_mit_Arduino_Teil_3

es gibt dazu 2 grundsätzliche Auslese-Methoden
- Pinchange-Interrupt (Leistungsfähigkeit nimmt ab bei zunehmender Zahl an Encoder-Pins)
- Timer-Interrupt (mache ich lieber bei >=2 die getrennt zu überwachen sind, im 500µs Intervall)

hirnfrei
06.07.2018, 21:46
Wenn ich eine Herstellerseite kennen würde, dann hätte ich die auch angegeben. Wie das aber manchmal bei Chinaware ist, da sind Bezeichnungen, Seriennummern und ähnliches nicht immer vorhanden. Auch weiss ich nicht, in wie weit die Artikelbeschreibung hilfreich wäre. Aber ich kann sie ja mal kopieren:

1 Stück Drehgeber-modul Brick Sensorentwicklung für arduino Dropshipping

Was aber tatsächlich helfen könnte wäre ein Bild von dem Teil. Ich kleb mal eins an.

33535

HaWe
06.07.2018, 21:52
na wer sagt's denn...
https://www.conrad.de/de/drehgeber-1-st-iduino-se055-1485328.html?WT.mc_id=google_pla&WT.srch=1&ef_id=Wz-HiQAABUyQeVza:20180706194825:s&gclid=EAIaIQobChMI0Nr-h5-L3AIVkBsYCh0INwpnEAYYAiABEgKnzPD_BwE&hk=SEM&insert_kz=VQ&s_kwcid=AL!222!3!254339639432!!!g!!

Trotzdem müsstest du eigtl auch einen Artikel-Link von der Händler-Seite haben, denke ich, irgendwo musst du ihn schließlich her haben....

Aber dann lies dir jetzt am besten mal die Tutorials durch! 8)

hirnfrei
06.07.2018, 22:03
Hilft dir Aliexpress? Es müsste ein KY-040 sein. Habe dazu auch schon einige Tuts durch und da wird es immer ähnlich beschrieben, wie ich es gemacht habe. Ich nehme mal an, in meinem Sketch stört das Display. ALso stören im Sinne von, die Ansteuerung des Displays frisst zu viel Zeit und deshalb klappt es nicht wirklich.

Siro
06.07.2018, 22:10
Wir dürfen nur auf EINE Flanke reagieren, sonst geht das schief.
Entweder nur auf die Aufsteigende oder nur die Fallende Flanke.
Da die Pins über Pullupwiderstände an Plus liegen, würde ich die fallende Flanke auswerten,
also wenn der Pin A von High nach Low geht.
Ist der Pin A also auf High bedeutet dies Ruhezustand.
Wir benötigen noch ein Flag um den Zustand festzuhalten bzw. um festzuhalten ob wir die Flanke
schon ausgewertet haben, damit auch wirklich nur ein Puls ausgewertet wird.

Doch nun zum Code, den ich nicht testen kann, aber so in etwa sollte das gehen:


int Flag;

void ui()
{ if (digitalRead(A)==1) // wenn die Leitung High ist, haben wir die Ruhestellung
{
Flag=0; // Ruhestellung
return; // nix weiter machen
}

// hier kommen wir nun nur an, wenn der Pin A Low wird:
if (Flag == 1) return; // wenn das Flag schon gesetzt war, machen wir aber keine Auswertung mehr

Flag = 1; // ansonsten setzen wir nun das Flag und führen nur einmal die Auswertung von Pin B durch

if (digitalRead(B) volt+=0.05;
else volt-=0.05;
}

// Info: Das Flag wird jetzt erst wieder gelöscht, wenn die Datenleitung A auf High (also in den Ruhezustand) geht.
// Wichtig: Die Variable Flag muss GLOBAL sein, also ausserhalb der Funktion definiert werden.

Das ist nur mal zum Testen, ich hoffe das hilft Dir weiter.

Nanu, das Datenblatt hab ich eben erst gesehen, funktioniert der etwa anders.....
jetzt bin ich mir da nicht mehr sicher......

HaWe
06.07.2018, 22:17
Hilft dir Aliexpress? Es müsste ein KY-040 sein. Habe dazu auch schon einige Tuts durch und da wird es immer ähnlich beschrieben, wie ich es gemacht habe. Ich nehme mal an, in meinem Sketch stört das Display. ALso stören im Sinne von, die Ansteuerung des Displays frisst zu viel Zeit und deshalb klappt es nicht wirklich.

ja, das funktioniert identisch. Wie gesagt, mach mal ein paar Tutorials, das ist besser als Code zu kopieren ohne es richtig verstanden zu haben.

hirnfrei
06.07.2018, 22:28
Leider tut es das nicht Siro :(.

Ich nehme aber mal an, es liegt an dem Rest von meinem Sketch. Diese Variante funktioniert zum Beispiel, auch wenn sie immer gleich 2 Schritte hochzählt:



int c = 0;
int State;

void setup()
{
Serial.begin(9600);

pinMode(3, INPUT);
pinMode(4, INPUT);
}

void loop()
{
int aState = digitalRead(3);

if(State != aState)
{
if(digitalRead(4) != aState)
{
c++;
}
else c--;
}

State = aState;

Serial.println(c);
}


Da zählt er problemlos hoch und runter. Benutze ich aber folgenden Sketch, wobei ich an dem Aufbau der Hardware nichts ändere, funktioniert es manchmal abwärts, fast gar nicht aufwärts und gelegentlich zählt er auch wieder hoch, wenn er runtergezählt hat. Vielleicht kannst du darin ja ein Problem erkennen.



#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

#define A 3
#define B 4
#define FIRE 2

float volt = 7.4;
float maximum = 8.4;
float aktuell = 8.4;

int Flag;
int toInterval = 5000;

long timeout = toInterval;
long time = 0;

void setup(void)
{
u8g2.begin();

pinMode(A, INPUT);
pinMode(B, INPUT);
pinMode(FIRE, INPUT);
}

void loop(void)
{
time = millis();
ui();

u8g2.clearBuffer();

// akkustand();

if(time < timeout)
{
u8g2.setFont(u8g2_font_crox3h_tf);
u8g2.setFontRefHeightExtendedText();
u8g2.setDrawColor(1);
u8g2.setFontPosTop();
u8g2.setFontDirection(0);

u8g2.drawStr(18, 0, "K2-Box V 0.3");
u8g2.drawLine(0, 18, 128, 18);
u8g2.drawLine(0, 50, 128, 50);

power();

// debug();
}

u8g2.sendBuffer();
}

void power()
{
String p = String(volt) + "V";

u8g2.setFont(u8g2_font_ncenR18_tr);

u8g2.drawStr(30, 25, p.c_str());
}

void akkustand()
{
float proz = aktuell * 100 / maximum;

int stand = 128 - round(128 * proz / 100);

u8g2.drawBox(stand, 50, 128, 64);
}

void debug()
{
u8g2.setFont(u8g2_font_t0_11_tf);

String ausgabe;

ausgabe = String(digitalRead(A)) + " " + String(digitalRead(FIRE)) + " " + String(digitalRead(B));

u8g2.drawStr(0, 52, ausgabe.c_str());

String check;

if(digitalRead(A) == LOW) check = "-";
if(digitalRead(B) == LOW) check = "+";

u8g2.drawStr(50, 52, check.c_str());
}

void ui()
{
if(digitalRead(A) == HIGH)
{
Flag = LOW;
return;
}

if(Flag == HIGH) return;

Flag= HIGH;

if(digitalRead(B)) volt += 0.05;
else volt -= 0.05;
}


Das Schlimme daran ist, das komplette Projekt steht und fällt mit diesem Regler...

Siro
06.07.2018, 22:59
Das große Problem ist, dass Dir vermutich Signale abhanden kommen, da in deinem Programm doch noch "reichlich" anderer Code ausgeführt wird.
Alleine die Floating Point Berechnungen auf dem kleinen 8 Bit Controller fressen schon viel Zeit.
dann sehe ich da Grafikfunktionen. Das schafft der Controller nicht mit dieser Programmiertechnik,
da kommt man eigentlich nicht drumherum mit Interrupts für den Impulsgeber zu arbeiten.
Du könntest Dir eventuell noch behelfen indem Du so oft wie möglich deine ui Funktion aufrufst,
quasi nach jeder anderen Softwarezeile, sieht kacke aus und das macht man eigentlich auch nicht, aber Versuchs mal.



void loop(void)
{
time = millis();
ui();

u8g2.clearBuffer();
ui();

// akkustand();

if(time < timeout)
{
u8g2.setFont(u8g2_font_crox3h_tf);
ui();
u8g2.setFontRefHeightExtendedText();
ui();
u8g2.setDrawColor(1);
ui();
u8g2.setFontPosTop();
ui();
u8g2.setFontDirection(0);
ui();
u8g2.drawStr(18, 0, "K2-Box V 0.3");
ui();
u8g2.drawLine(0, 18, 128, 18);
ui();
u8g2.drawLine(0, 50, 128, 50);
ui();
power();
ui();
// debug();
}

hirnfrei
06.07.2018, 23:35
Irgendwie habe ich mir das schon gedacht. Nun, dann sollte ich mir das mit den Interrupts mal genauer anschauen. Kann ja nicht schaden wenn man das versteht. Wenn nicht, mit den vielen ui() würde es mit Sicherheit funktionieren. Sieht aber tatsächlich sehr unschön aus. Wenn das alles nicht hilft, werde ich wohl abspecken müssen. Gibt noch andere, harmloserer Libs, um das Display anzusteuern. Sieht dann aber nicht mehr so schön aus, also die Anzeige. Aber besser als wenn es gar nicht funktioniert.

- - - Aktualisiert - - -

Jetzt bin ich etwas erschüttert! Nach 5 Minuten lesen musste ich feststellen, die Interrupts sind ja gar keine Herausforderung! Jetzt habe ich meinen Sketch auf Interrupt umgestellt und man glaubt es kaum, es funktioniert perfekt!

Vielen, vielen Dank für deine Hilfe!