Forum: Mikrocontroller und Digitale Elektronik Wieder einmal Rotary Encoder ALPS STEC12


von Andreas G. (andreas_g498)


Lesenswert?

Hallo,

ich versuche schon eine ganze Weile für folgende Aufgabenstellung eine 
Lösung zu finden, ohne dabei an die Kapaztätsgrenzen des ATTiny2313 zu 
kommen.

An diesen µC sollen oder sind 15 LEDs angeschlossen, die in einem Kreis 
angeordnet sind. Von diesen LEDs sollen immer 3, jeweils zur gleichen 
Zeit an sein. Nun sollen die LEDs, ähnlich einem Leuchtturm eine 
Rotationsbewegung vollziehen. Soweit kein Problem, und wohl der 
leichteste Teil der Aufgabe.

Die Startbedingungen sind Drehrichtung rechts, Anzeit bis zur 
Weiterschaltung auf die nächste LED Gruppe 1000ms.

Bisher habe ich den Gedankenansatz verfolgt, die Pindefinition in C über 
ein Array zu machen, damit ich über eine For() Schleife von 0 bis 4 
zählen eine vollständige Drehbewegung darstellen kann.
1
...
2
int Richtung = 1;
3
int Time_Step = 50;
4
int Time_Start = 1000;
5
int Time_Min = 200;
6
int Time_Max = 2500;
7
int is_Time;
8
int old_Pair;
9
10
int PINs[] = {'Pinliste'};
11
int Encoder_PIN_A = PIN_PA0;
12
int Encoder_PIN_B = PIN_PA1;
13
14
int main (void) {
15
  for (int i = 0; i < 15; i++) {
16
    pinMode(PINs[i], OUTPUT);
17
  }
18
  pinMode(Encoder_PIN_A, INPUT_PULLUP);
19
  pinMOde(Encoder_PIN_B. INPUT_PULLUP);
20
  is_time = Time_Start;
21
  while (true) {
22
    if (Richtung > 0) {
23
      for (int j = 0; j < 5; j++) {
24
        LED_ON(PINs[j], PINs[j + 5], PINs[j + 10]);
25
        _delay_ms(is_Time);
26
        LED_OFF(PINs[j], PINs[j + 5], PINs[j + 10]);
27
        old_Pair = j;
28
    }
29
    if (Richtung < 0) {
30
      for (int j = 4; >= 0; j--) {
31
        LED_ON(PINs[j], PINs[j + 5], PINs[j + 10]);
32
        _delay_ms(is_time);
33
        LED_OFF(PINs[j], PINs[j + 5], PINs[j + 10]);
34
        old_Pair = j;
35
    } else {
36
      LED_ON(PINs[old_Pair], PINs[old_Pair + 5], PINs[old_Pair + 10]);
37
    }
38
  }
39
}
40
...

Das mal als Auszug zum bisherigen Programm.
Nun würde ich gerne das Programm um folgende Funktion, die durch einen 
ALPS STEC12 umgesetzt wird, ergänzen wollen:
Grundsätzlich gleich noch folgende Angabe dazu es handelt sich um eine 
gelegendliche Handeineingabe, der Encoder muss somit nicht zwingend via 
Polling ständig abgefragt werden.
Ausgehend von der Startbedingung Rechtsdrehung, und einer 
Verzögerungszeit = is_Time soll num bei einer Encoderdrehung nach rechts 
die is_Time um jeweils Time_Step bis Time_Min verringert werden. Alles 
was darüber hinaus geht soll ignoriert werden. Dreht man nun den 
Encoder-Knopf nach links, erhöht sich die is_Time bis zum Werte von 
Time_Max. Und nun kommt der Überschlag. Dreht man noch eine Raste 
weiter, soll die Drehbewegung gestoppt werden. Und noch eine Raste 
weiter nach links, soll eine Richtungsänderung stattfinden. Dabei ist 
is_Time immer noch Time_Max. Bei einem weiterdrehen am Encoder-Knopf 
nach links soll nun pro Rate is_Time um jeweils Time_Step verringert 
werden.
Das bekomme ich auch noch mit Polling hin.
Hier fehlt jedoch noch ein Taster. Dazu müsste ich das Reset-PIN 
wegfusen.
Bei einem Tastendruck, soll die dargestellte Drehbewegung angehalten, 
und bei einem erneuten fortgesetzt wrden.

Jetzt die Große Frae zu dem ganzen Projekt, wie setzt man das am 
elegantesten um ? Nutzt man Interripts zur Encode- und Taster Abfrage  ? 
Bekommt man damit den Speicherverbrauch besser in den Griff, oder muss 
man hier nun womöglich auf Assembler zurückgreifen ?

Im Voraus schon mal Danke für alle Antworten, und beste Grüße

Andreas

von Olaf (Gast)


Lesenswert?

> gelegendliche Handeineingabe, der Encoder muss somit nicht zwingend via
> Polling ständig abgefragt werden.

Doch das muss. Wenn du anderer Meinung bist hast du die Theorie dahinter 
noch nicht verstanden.

> Jetzt die Große Frae zu dem ganzen Projekt, wie setzt man das am
> elegantesten um ?

Indem du einen TimerIRQ aufsetzt der die Abfrage fuer dich macht.
Ueberleg dir wie schnell du maximal drehen willst, wieviele Impulse das 
pro Sekunde gibt, multipliziere das noch mit 4 und du weisst wie schnell 
dein Timer laufen muss.

Olaf

von Hugo H. (hugo_hu)


Lesenswert?


von Peter D. (peda)


Lesenswert?

Andreas G. schrieb:
> Bekommt man damit den Speicherverbrauch besser in den Griff, oder muss
> man hier nun womöglich auf Assembler zurückgreifen ?

Welcher Speicher denn?
Ich sehe da nichts kritisches.

Man kann nicht erkennen, wie groß "PINs[]" ist. Besser daher das 
compilierbare Programm als Anhang.
"PINs[]" könnte man in den Flash legen, da konstant.

"int" verdoppelt schonmal den RAM-Verbrauch. Wenn 0..255 reicht, nimmt 
man besser "uint8_t".

von Wolfgang (Gast)


Lesenswert?

Andreas G. schrieb:
> An diesen µC sollen oder sind 15 LEDs angeschlossen, die in einem Kreis
> angeordnet sind. Von diesen LEDs sollen immer 3, jeweils zur gleichen
> Zeit an sein. Nun sollen die LEDs, ähnlich einem Leuchtturm eine
> Rotationsbewegung vollziehen.

> Bisher habe ich den Gedankenansatz verfolgt, die Pindefinition in C über
> ein Array zu machen, damit ich über eine For() Schleife von 0 bis 4
> zählen eine vollständige Drehbewegung darstellen kann.

Bei 15 LEDs hätte ich eine for()-Schleife von 0 bis 14 erwartet. Bei 
einem Leuchtturm läuft der Lichtkegel gleichmäßig durch, egal wie breit 
der Sektor ist. Der spring nicht um Sektorbreiten.

von Peter D. (peda)


Lesenswert?

Wolfgang schrieb:
> Bei 15 LEDs hätte ich eine for()-Schleife von 0 bis 14 erwartet.

Man könnte auch ein uint16_t nehmen und rotieren.
Wenn man dann die LEDs gleich richtig an PB und PD anschließt, wird das 
Programm besonders kurz und SRAM-sparend.
1
uint16_t pattern:
2
3
void rotate(void)
4
{
5
  pattern <<= 1;
6
  PORTB = pattern;
7
  PORTD = pattern >> 8;
8
}

von Andreas G. (andreas_g498)


Lesenswert?

Wolfgang schrieb:
> Bei 15 LEDs hätte ich eine for()-Schleife von 0 bis 14 erwartet. Bei
> einem Leuchtturm läuft der Lichtkegel gleichmäßig durch, egal wie breit
> der Sektor ist. Der spring nicht um Sektorbreiten.

Ist es nicht vom Programmlauf egal ob ich
1
 for (int i = 0 ; i <= 14 ; i++) {}
oder
1
 for (int i = 0 ; i < 15 ; i++) {}

zumindest bei mir ist das Ergebnis immer das gleich. Es wird von 0 bis 
14 gezählt.

Hat das eine hintergründige Relevanz, wenn ich damit ein Arrray 
anspreche ?

von batman (Gast)


Lesenswert?

Peter D. schrieb:
> Wolfgang schrieb:
>> Bei 15 LEDs hätte ich eine for()-Schleife von 0 bis 14 erwartet.
>
> Man könnte auch ein uint16_t nehmen und rotieren.
> Wenn man dann die LEDs gleich richtig an PB und PD anschließt, wird das
> Programm besonders kurz und SRAM-sparend.uint16_t pattern:
> void rotate(void)
> {
>   pattern <<= 1;
>   PORTB = pattern;
>   PORTD = pattern >> 8;
> }

Da rotiert aber nix. Die Shift-Operation in C wird NICHT zirkulär 
vollzogen.

von Falk B. (falk)


Lesenswert?

Peter D. schrieb:
> Man könnte auch ein uint16_t nehmen und rotieren.
> Wenn man dann die LEDs gleich richtig an PB und PD anschließt, wird das
> Programm besonders kurz und SRAM-sparend.

Wenn von den 15 LEDs max. 3 gleichzeitig leuchten soll, würde ich 
Charliplexing nutzen.

https://www.mikrocontroller.net/articles/LED-Matrix#Charlieplexing

von Andreas G. (andreas_g498)


Lesenswert?

Falk B. schrieb:
> Wenn von den 15 LEDs max. 3 gleichzeitig leuchten soll, würde ich
> Charliplexing nutzen.
>
> https://www.mikrocontroller.net/articles/LED-Matrix#Charlieplexing

Danke für den Hinweis. Da es das Thema Integration eines Rotary Encoders 
und eines Interrupt-abgefragten Tasters nicht wirklich trifft, hier noch 
der *allgemeine Hinweis*:

Die Enscheidung für den Tiny2313 SOIC erfolgte in erste Linie aus 
Platzgründen.
Auf einer Platine die nicht größer als 22x43 mm sein darf, habe ich 
keinen Platz mehr für zusätzliche Schaltkreise, Leiterbahnen und 
Treibertransistoren.

von noiasca (Gast)


Lesenswert?


von Peter D. (peda)


Lesenswert?

batman schrieb:
> Da rotiert aber nix. Die Shift-Operation in C wird NICHT zirkulär
> vollzogen.

Dann eben:
1
if (pattern == PATTERN_END)
2
  pattern = PATTERN_START;
3
else
4
  pattern <<= 1;

von Wolfgang (Gast)


Lesenswert?

Peter D. schrieb:
> Dann eben:
> ...

Das wird nix. Wenn immer 3 LEDs leuchten sollen, braucht man einen 
Übertrag vom letzten Bit aufs erste.
1
-------------xxx
2
x-------------xx
3
xx-------------x
4
xxx------------- PATTERN_START

von Andreas G. (andreas_g498)


Lesenswert?

Hallo,

danke noch einmal das sich hier so viele Leute auf diesen Beitrag 
gemeldet haben.
Um der Verwirrung, die hier wohl mit der Nutzng der PLATTERN aufgetaucht 
ist mal ein Ende zu bereiten:
- 15 LEDs in der Summe.
- immer 3 LEDs leuchten Zeitgleich
- ein Aus für alle gibt es nicht
- Anhand der Darstellung von Wolfgang:
1
Schritt 0
2
Reihe 1 | *----
3
Reihe 2 | *----
4
Reihe 3 | *----
5
Schritt 1
6
Reihe 1 | -*---
7
Reihe 2 | -*---
8
Reihe 3 | -*---
9
Schritt 2
10
Reihe 1 | --*--
11
Reihe 2 | --*--
12
Reihe 3 | --*--
13
Schritt 3
14
Reihe 1 | ---*-
15
Reihe 2 | ---*-
16
Reihe 3 | ---*-
17
Schritt 4
18
Reihe 1 | ----*
19
Reihe 2 | ----*
20
Reihe 3 | ----*

Das heißt, der eigentlich Ablauf beschränkt sich auf 5 Schritte, und die 
Aufteilung kann über einfaches addieren +5 und +10 stattfimden, falls es 
bei der Grunddefinition der Port-Pins in einem Array bleibt.

von Andreas G. (andreas_g498)


Lesenswert?

noiasca schrieb:>
> 
[url]https://forum.arduino.cc/t/verstandnisfrage-zu-rot-geber-alps-stec12/958252[/url]

Danke für den Hinweis.
Der Versuch diesen Entwurf zu einem Programm für einen ATTiny2313 daraus 
zu machen, scheitet am Resourcenbedarf, sowie die benötigte 
Speichergröße Programmflash wie auch bei der Variablenbelegung RAM.

von batman (Gast)


Lesenswert?

Also ein Leuchtturm, der sich in 5 Schritten einmal rum dreht? 72° pro 
"Schritt".

von Wolfgang (Gast)


Lesenswert?

Andreas G. schrieb:
> Das heißt, der eigentlich Ablauf beschränkt sich auf 5 Schritte ...

Versteh' ich nicht, wieso dann eine Schleife mit 15 Durchläufen?

72°-Sprünge macht kein klassischer Leuchtturm - der Kegel läuft dort 
schön gleichmäßig um.

von Falk B. (falk)


Lesenswert?

Andreas G. schrieb:
> Danke für den Hinweis.
> Der Versuch diesen Entwurf zu einem Programm für einen ATTiny2313 daraus
> zu machen, scheitet am Resourcenbedarf, sowie die benötigte
> Speichergröße Programmflash wie auch bei der Variablenbelegung RAM.

Dann machst du mehrere Fehler. Wenn gleich der Tiny2313 nur 2kB Flash 
hat, bekommt man deine Funktion dort rein, wenn man weiß was man tut.

Mit welcher Umgebung programmierst du? pinMode deutet auf Arduino hin. 
Das ist zwar etwas bequemer, frißt aber sinnlose Resourcen. Man sollte 
mit einfachem C und avr gcc arbeiten, das ist bei so einem kleinen 
COntroller problemlos.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

So könnte es gehen. 526 Bytes (25.7%). Das ist noch REICHLICH Platz.

von batman (Gast)


Lesenswert?

Man kann die übliche Encoderauswertung von der üblichen Echtzeittakt-ISR 
pollen oder auch von einer modifizierten Busy-Warteroutine in der 
Hauptschleife wie
1
void my_delay_ms(ms)
2
{
3
  for(t=0;t<ms;t++)
4
  {
5
    encoder_auswertung();
6
    _delay_ms(1);
7
  }
8
}
könnte es gehen.

von Andreas G. (andreas_g498)


Lesenswert?

Hallo,

Für alle die jetzt ein Problem mit der 15, den 3 Paaren, und einem 
Vollkreis von 360 Grad haben.
- 15 LEDs verteilt auf 360 Grad = ca. 24 Grad Abstand zwischen den LEDS 
( ca. nur deshalb, weil es nicht 100 % genau auf der Leiterplatte geht, 
aber schlussendlich nicht wirklich funktions relevant )
- immer 3 LEDs bilden ein Paar -> somit sendet der virtuell als 
Gedankenstütze gedachte "Leuchtturm" immer 3 Strahlen gleichzeitig aus, 
wobei der Versatz zwischen den eingeschalteten LEDs immer 4 LEDs beträt, 
die zeitgleich nicht leuchten.

Die For() Schleife mit 15 Durchläufen nutzen ich in meinem bisherigen 
Programm, welches schon zT eim Eingangspost veröffentlicht wurde nur um 
die Initiierung der Port-Pins durchzuführen.

@Falk

Wow.
bis auf wohl einen Übertragungsfehler in Zeile 114
1
int8_t new, diff, tmp;
wo ich das "new", sowie in den Folgezeilen durch ein "neu" ersetzt habe, 
ist das Programm wirklich ein winzling.

Nun auf in den Elektronikshop des Vertrauens, und eine Lochrasterplatte 
für den ersten Versuchsaufbau besorgt.

von Falk B. (falk)


Lesenswert?

Andreas G. schrieb:

> - immer 3 LEDs bilden ein Paar -> somit sendet der virtuell als

Drei sind nie ein Paar, bestenfalls eine Gruppe. Ein Paar sind immer 2.

> bis auf wohl einen Übertragungsfehler in Zeile 114int8_t new, diff, tmp;
> wo ich das "new", sowie in den Folgezeilen durch ein "neu" ersetzt habe,
> ist das Programm wirklich ein winzling.

Muss man nicht. In C darf man new als Variablenname verwenden, das ist 
kein reserviertes Wort. In C++ ist das anders.

von Falk B. (falk)


Lesenswert?

Da ist noch ein kleiner Fehler drin, war gestern wohl zu spät.
1
leds[i] = pgm_read_byte(&led_codes[j]);

Richtig ist
1
leds[i] = pgm_read_word(&led_codes[j]);

von Andreas G. (andreas_g498)


Lesenswert?

Hallo, und Danke

besonders Falk B. hat gezeigt, dass mit einer umfassenden Kenntnis der 
Programmiermöglichkeiten, man doch mehr machen kann, als was hier von 
einigen als Meinung vertreten wird.

Der Vorschlag und das Beispiel von ihm hat gezeigt, dass für einige und 
noch benötigte Erweiterungen noch genögend Flsh-Speicher vorhanden ist.

Nach den ersten Versuchen mit der vorgestellten Schaltung und kleinen 
Änderungen an dem dazugehörigen Programm, haben sich meine Befürchtungen 
bewahrheitet, dass mit Low-Power LEDs diese Schaltung zweifelsfrei 
funktioniert, und sich auf dem mir zur Verfügung stehenden Platzgebot 
für eine Leiterplatte umsetzen lässt.  Danke nochmals an Falk.

Jetzt muss ich mir erneute Gedanken machen, wie ich das nun auseinander 
frickle. Der ursprüngliche Plan, die schon entworfene Schaltung später 
dort MOSFET gesteuerte Power LED mit bis zu 150 mA pro LED nutzen zu 
wollen, geht mit Charliplexing auf der mir zur Verfügung stehenden 
Maximalgröße der Platine nicht. Für dem AVR 2313 hatte ich enen MCP1804 
Festspannungsregler 150mA SOT-23 mit Minimalbeschaltung ( zwei 1µF SMD 
Kerko ) vorgesehen.

Andreas

von Falk B. (falk)


Lesenswert?

Andreas G. schrieb:
> Jetzt muss ich mir erneute Gedanken machen, wie ich das nun auseinander
> frickle. Der ursprüngliche Plan, die schon entworfene Schaltung später
> dort MOSFET gesteuerte Power LED mit bis zu 150 mA pro LED nutzen zu
> wollen, geht mit Charliplexing auf der mir zur Verfügung stehenden
> Maximalgröße der Platine nicht.

150mA LEDs? Soll das ein Scheinwerfer werden? Nimm ultrahelle LEDs, die 
blenden auch bei 2mA. Du kannst auch die Widerstände kleiner machen oder 
gar ganz weglassen, die Ausgangswiderstände des AVR von ca. 30 Ohm 
begrenzen dann den Strom. Man kann auch Platz sparen, indem man einen 
etwas kleineren AVR nimmt, z.B. ATtiny24 in SOIC14, nochmal deutlich 
kleiner als der 2313 und trotzdem noch human lötbar. Wenn's eng wird, 
nimm den im QFN20 Gehäuse mit 4x4mm ;-)

von Andreas G. (andreas_g498)


Lesenswert?

Hallo Falk B.,

Ja, es geht um Beleuchtungszwecke. Und entschuldige bitte meine 
verspätete Rückmeldung.
Die kleinerewn Super-hellen LEDs sind für den Prototypen Aufbau durchaus 
ganz brauchbar.
Nur haben erste Versuche in den Einsatzbedingungen Nacht gezeigt, das 
der Abstrahlwinkel dieser kleinen LEDs nicht ausreichen, um diese sowohl 
in der horizontalen Ebene wie auch von oben aus größeren Entfernungen 
erkennen zu können.
Von oben mit einer Drohne betrachtet aus etwa 15 Meter Flughöhe ist das 
nur noch ein einziger "Farbbrei" ;) der keine klare Zuordnung der 
Richtungsvektoren mehr zulässt, wenn diese in der Ebene breit 
ausstrahlen. Nach oben gerichtete LEDs erkennt man vom Boden aus auch 
nur noch auf etwa 30 Meter. Das es den 2313 auch in SMD SOIC-20 gibt war 
schon in der Planung angedacht.
Dahingehend haben schon erste Tests gezeigt, dass Power LEDs mit einem 
größeren Absteahlwinkel von ca. 60 Grad, angeordnet und angestellt auf 
der Aussenfläche eines Kegelstumpfes mit 45 Grad Neigung auch aus 
größeren Höhen, und in der Ebene aus über 100 Meter sicher erkannt 
werden können.
Jetzt auf mehrere LEDS in unterschiedlichen Abstrahlwinkeln ( Horizontal 
bis Vertikal) zu setzen, bringt mich mit Charlieplexing auch nicht 
weiter.

Die LED Anordnung ist selber nicht das Problem. Die erfolgt angesetzt 
via Kabelverbindung mit der Steuereinheit. Auch das eigentliche händisch 
bediente Steuerelement, der Rotary Encoder muss auf der 
Controllerplatine keinen Platz finden. Auch dieser soll via Kabel mit 
der Controllerplatine verbunden werden.

Konstruktiv um alles auf der zur Vefügung stehenden Fläche 
unterzubringen, befindet sich auf der Oberseite nur der 2313 und ein 
MCP1804 mit den beiden Kerkos, und den notwendigen Durchkontaktierungen. 
Auf der Unterseite ein MCP1726 für die LEDs mit Aussenbeschaltung, den 
15 NMOS (250mA) in SOT323 mit Vorwiderständen und den 
Durchkontaktierungen zu den PINs den 2313. Die LEDs werden nur über 
platzsparende Lötflächen ohne Durchkontaktierung ausgeführt. Also 
Oberseite nur der ATTiny mit Stromversorung und den Lötflächen für den 
Rot-Encoder, und der Spannungszuführung, und auf der Rückseite nur das 
Leistungsteil zur Ansteuerung der LEDs. Wobei hier noch 15 der ATTiny 
Pins jeweile eine einzige LED Treiberstufe ansteuern.

Gruß

von W.S. (Gast)


Lesenswert?

Andreas G. schrieb:
> Nun würde ich gerne das Programm um folgende Funktion, die durch einen
> ALPS STEC12 umgesetzt wird, ergänzen wollen

Halte erst mal die verschiedenen Dinge auseinander.
Du hast:
- eine Art Systemuhr, die dir die verschiedenen Leucht-Zeiten usw. macht
- einen Algorithmus, der in Abhängigkeit von den Zeitsignalen deiner 
Systemuhr deinen "Leuchtturm" weiterschaltet

Und jetzt willst du noch dazu haben:
- einen Treiber, der aus den Signalen eines eventuell angeschlossenen 
Drehgebers Kommandos (nach rechts, nach links) macht
- ein Kommandoprogramm, das die gegebenen Kommandos auswertet und damit 
die E/A-Zeit-Vorgaben deiner Systemuhr oder die Eckdaten deines 
"Leuchtturmes" ändert.

Schreib dir erstmal deinen Treiber, damit du zu den Kommandos "rechts" 
oder "Links" kommst. Wenn das funktioniert, dann kannst du dich an das 
Kommandoprogramm machen. Und denke daran, daß man einen Drehgeber sowohl 
entprellen sollte, als auch sich entscheiden muß, ob man ihn nun pollen 
will oder mit Interrupts arbeiten will. Ich selber mache das eigentlich 
immer per Interrupt, aber hier tummeln sich recht viele Leute, die 
sich da lieber auf's Pollen verlegen, und denen der Aufwand an 
Rechenzeit für sowas einfach schnurz ist.

W.S.

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.