Encoder gesucht

Hallo,

gibt es eine gute und nicht alzu kostspielige alternative zu den ALPS Drehencodern? Ich habe von diesen Encodern einige sehr günstig bei ebay geschossen. Problem aber, diese Prellen sehr stark auch bei software Entprellung mittels Greycode (war ein Sketch von Jurs). Das Problem ist, dass die Übergänge sehr hart sind, soll heißen, wenn ich in den nächsten Punkt nicht einraste, fällt dieser leicht zurück und gibt ein neues Signal aus.

Was ich suche, ist ein leichtgängiger Encoder mit Taster. Solche sind meist bei AV-Tuner o.ä. zu finden. Vom handling her fühlen sich diese an, als ob nur ein Poti angetrieben wird, jedoch ist hier auch ein Überschlag ohne Probleme möglich, somit kein Poti.

Poti soll für eine Menüführung sein.

Hallo,

möglicher Weise sind diese optischen Encoder das was Du suchst: http://de.farnell.com/bourns/em14a0d-c24-l032n/encoder-dreh-optik-14mm/dp/1470246

Aber der Preis ist schon ganz schön heftig.

Gruß,
Ralf

Hallo Ralf,

wie du schon sagtest, der Preis ist recht heftig und steht leider nicht im Verhältnis zum nutzen. Ich habe gerade noch einmal im Keller geschaut, ob ich noch einen alten defekten Verstärker hatte. Habe einen gefunden, aber leider hat dieser noch Potis drin.

sschultewolter:
gibt es eine gute und nicht alzu kostspielige alternative zu den ALPS Drehencodern? Ich habe von diesen Encodern einige sehr günstig bei ebay geschossen. Problem aber, diese Prellen sehr stark auch bei software Entprellung mittels Greycode (war ein Sketch von Jurs). Das Problem ist, dass die Übergänge sehr hart sind, soll heißen, wenn ich in den nächsten Punkt nicht einraste, fällt dieser leicht zurück und gibt ein neues Signal aus.

Drehgeber mit einem Arduino-Sketch auszuwerten ist normalerweise überhaupt kein Thema.
Und bis zu einer gewissen maximalen (ziemlich hohen) Zählrate spielt es überhaupt keine Rolle, ob es sich um billige stark prellende oder teure und wenig prellende Drehgeber handelt. Für alle normalen Anwendungsfälle tun es die billigen stark prellenden bei entsprechender sachdienlicher Auswertelogik genau so gut wie teure.

Allerdings darf man es nicht so machen wie es im Arduino-Playground beschrieben wird:
http://playground.arduino.cc/Main/RotaryEncoders

Auf der Seite steht zur Drehgeberauswertung nur ziemlich schlecht funktionierender Quatsch.
Praktisch unbrauchbarer Quatsch, der um so schlechter funktioniert, je stärker die Drehgeber prellen.

Wenn Du mit einem von mir hier geposteten Sketch ein Problem mit Drehgebern hast, gibt mal nähere Hinweise! Zu Drehgebern habe ich hier im Forum bisher sehr wenig gepostet, eigentlich kann ich mich nur an einen Spezialfall erinnern, bei dem es um eine Spindel ging, die immer abwechselnd und vorhersehbar eine Drehphase und eine Stillstandsphase hatte, und da ging es darum, Drehphasen und Stillstandsphasen zu unterscheiden, in Drehphasen die Impulse zu zählen und in Drehpausen die Zahl der gezählten Impulse anzuzeigen. Das hatte ich direkt in der loop gemacht. Diese Auswertung durch Polling in der loop-Funktion ist allerdings für Programme, bei denen die loop-Funktion auch mal länger als 1 bis 2 ms laufen kann, auch nicht der Weisheit letzter Schluß. Das Polling von Drehgebern eignet sich nur für Programme mit sehr schnelllaufender loop.

Für allgemeine Programme mit teils langsam laufender loop-Funktion muss die Auswertung von Drehgebern über regelmäßige Timer-Interrupts erfolgen. Ich kann mich nicht erinnern, dazu schon mal einen Beispielcode gepostet zu haben.

Hallo Jurs,

ich hatte hier schon einmal einen Thread eröffnet, wo du mir mit dem Sketch geholfen hattest. (Ist nicht der einzige Thread den ich gerade durchgeblättert habe)
Das Problem war aber immer, dass diese nicht so genau liefen wie erwünscht.

Meine Hardware für dieses Projekt ist ein Pro Mini, ALPS Drehencoder (falls ich keinen besseren finden sollte) sowie ein 1.8" Serial Display. Bei der loop kann es sehr gut vorkommen, dass diese nicht immer innerhalb von 1-2ms durchlaufen ist. Muss ich aber im Test mal messen.

Mit dem Taster des Encoders möchte ich 6 Säulen durchschalten (Werte für R, G, B, etc.) und mit dem Encoder selber die Werte hoch und runter regeln. Sobald der Taster erneut gedrückt wird, schalte ich eine Säule weiter und schicke, wenn Daten sich verändert, an den Arduino mit den WS2812B.

sschultewolter:
ich hatte hier schon einmal einen Thread eröffnet, wo du mir mit dem Sketch geholfen hattest. (Ist nicht der einzige Thread den ich gerade durchgeblättert habe)
Das Problem war aber immer, dass diese nicht so genau liefen wie erwünscht.

Welcher Thread war das?

sschultewolter:
Meine Hardware für dieses Projekt ist ein Pro Mini, ALPS Drehencoder (falls ich keinen besseren finden sollte) sowie ein 1.8" Serial Display. Bei der loop kann es sehr gut vorkommen, dass diese nicht immer innerhalb von 1-2ms durchlaufen ist. Muss ich aber im Test mal messen.

Um mit "Polling des Drehgebers in der Loop" bei starkem Prellen gut klarzukommen, sollte die Loop niemals länger als 2ms für einen Durchlauf benötigen, wenn keine Impulse verlorengehen sollten.

Ansonsten: Auswertung in Timer-Interrupts.

sschultewolter:
Mit dem Taster des Encoders möchte ich 6 Säulen durchschalten (Werte für R, G, B, etc.) und mit dem Encoder selber die Werte hoch und runter regeln. Sobald der Taster erneut gedrückt wird, schalte ich eine Säule weiter und schicke, wenn Daten sich verändert, an den Arduino mit den WS2812B.

Das hört sich doch eigentlich so an, als wenn Du einen Timer-Interrupt für die Drehgeber-Auswertung noch frei haben könntest?

Sind die WS2812B am selben Arduino angeschlossen wie der Drehgeber? Während die WS2812B LED-Controller angesteuert werden, sollen dann aber wohl hoffentlich keine Drehgeber-Impulse gezählt werden? Die Ansteuerung der WS2812B blockieren nämlich alle Abläufe auf dem Controller: Loop UND sogar Interrupts. Das meinst Du hoffentlich nicht, dass die Drehgeber nicht ausgewertet werden, während die Ansteuerung der LEDs erfolgt?

Ansonsten ist das harmlos. Mal angenommen, Du hast einen Drehgeber, der 20 Impulse pro Umdrehung abgibt, macht 80 Codewechsel pro Umdrehung, und Du drehst ihn mit einer Drehzahl von höchstens 2 Umdrehungen pro Sekunde. Dann wären das maximal 160 Codewechsel pro Sekunde (gültige, ohne Prellen), also
1000 ms / 160 = 6,25 ms pro Codewechsel.

Das Prellen zwischen den Codewechseln dauert praktisch nie länger als 1 ms, so dass abzüglich Prellzeit nach einem Codewechsel der richtige Code für mehr als 6,25ms-1ms= 5,25ms ansteht. Mit 500 Timer-Interrupts (alle 2ms) oder 1000 Timer-Interrupts pro Sekunde (je 1 pro ms) lassen sich selbst so stark prellende Drehgeber extrem sauber auswerten.

Wenn die Auswertelogik stimmt.

Bei der Ansteuerung von WS2812B LED-Controllern mußt Du allerdings berücksichtigen, dass der Controller komplett blockiert wird, während er die LEDs ansteuert. Der Controller kann in dieser Zeit nicht senden, nichts empfangen, nichts zählen und nichts anderes verarbeiten als die WS2812B-Ansteuerung und die Interrupts sind blockiert, bis die letzte LED angesteuert wurde.

jurs:
Welcher Thread war das?

Suche ich gleich raus.

jurs:
Um mit "Polling des Drehgebers in der Loop" bei starkem Prellen gut klarzukommen, sollte die Loop niemals länger als 2ms für einen Durchlauf benötigen, wenn keine Impulse verlorengehen sollten.
Ansonsten: Auswertung in Timer-Interrupts.

Das ist mir soweit klar, ich muss erst einmal sehen, wie stark das Display die Sache beeinflusst.

jurs:
Das hört sich doch eigentlich so an, als wenn Du einen Timer-Interrupt für die Drehgeber-Auswertung noch frei haben könntest?

Sind die WS2812B am selben Arduino angeschlossen wie der Drehgeber? Während die WS2812B LED-Controller angesteuert werden, sollen dann aber wohl hoffentlich keine Drehgeber-Impulse gezählt werden? Die Ansteuerung der WS2812B blockieren nämlich alle Abläufe auf dem Controller: Loop UND sogar Interrupts. Das meinst Du hoffentlich nicht, dass die Drehgeber nicht ausgewertet werden, während die Ansteuerung der LEDs erfolgt?

Ich nutzte 2 Arduinos für den Ablauf.
Auf dem einen ist ein Display angeschlossen, sowie der Drehencoder. Evtl. kommt ein DS3231 noch dazu, der im Standby die Uhrzeit auf dem Display ausgibt.

Ich wusste bereits mit dem Einsatz von Display + WS2812B, dass ich alles auf 2 Arduinos splitte. Denn die WS2812B machen bedingt Probleme mit dem Display. (Statische Daten werden bedingt überschrieben ohne das es gewollt war.)

jurs:
Ansonsten ist das harmlos. Mal angenommen, Du hast einen Drehgeber, der 20 Impulse pro Umdrehung abgibt, macht 80 Codewechsel pro Umdrehung, und Du drehst ihn mit einer Drehzahl von höchstens 2 Umdrehungen pro Sekunde. Dann wären das maximal 160 Codewechsel pro Sekunde (gültige, ohne Prellen), also
1000 ms / 160 = 6,25 ms pro Codewechsel.

Das Prellen zwischen den Codewechseln dauert praktisch nie länger als 1 ms, so dass abzüglich Prellzeit nach einem Codewechsel der richtige Code für mehr als 6,25ms-1ms= 5,25ms ansteht. Mit 500 Timer-Interrupts (alle 2ms) oder 1000 Timer-Interrupts pro Sekunde (je 1 pro ms) lassen sich selbst so stark prellende Drehgeber extrem sauber auswerten.

Wenn die Auswertelogik stimmt.

Ich bin mir noch nicht sicher, wie schnell ich die Werte inkremtiere. Max. wären aber 255 Werte, die nicht innerhalb von 1 Sekunde gedreht werden müssen.

jurs:
Bei der Ansteuerung von WS2812B LED-Controllern mußt Du allerdings berücksichtigen, dass der Controller komplett blockiert wird, während er die LEDs ansteuert. Der Controller kann in dieser Zeit nicht senden, nichts empfangen, nichts zählen und nichts anderes verarbeiten als die WS2812B-Ansteuerung und die Interrupts sind blockiert, bis die letzte LED angesteuert wurde.

Ich habe soweit schon die Tests erfolgreich abgeschlossen, bei dem ich an den Controller immer wieder mit seriellen Daten gefüttert hab,. Geht wunderbar. Desweiteren beschreibe ich die Leds nur, wenn es auch wirklich Änderung gab.

Ich hab gerade ein bisschen was durchgetestet. Bislang war die Encoder Libary nicht das schlechteste, aber defentiv noch nicht das, was ich davon gerne erwarte. Der Sketch unten ist weder vollständig, noch zwingend ordentlich oder korrekt. Fehler sind bekannt, vorallem das falsch gezählt wird beim Encoder auslesen.

@Jurs, sicher kann man auch unter bestimmten Umständen jeden Encoder verwenden, mir gefällt der Alps Encoder dennoch nicht. Die Drehungen gehen verhältnismäßig sehr schwer. Die Rastung ist sehr stark. Des weitern muss ein starker Druck erfolgen, um den Taster zu betätigen.

Alternativ würde ich auch einen Encoder nehmen, der keinen Taster hat. Dann würde ich auch die MenüItems mit einem Button + Encoder durchschalten.

Taster schaltet durch (Modus, Rot, Grün, Blau, Helligkeit, Geschwindigkeit, Weite) und mit dem Encoder soll dann der Wert +1 oder -1 den Wert umschalten.

Preislich sollte der Encoder wenn möglich unter 10€ inkl. Versand bleiben.

#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Bounce2.h>
#include <Encoder.h>

/* ITEAD 1.8" Display 160x240 */
#include <SPI.h>
#define TFT_CS 10
#define TFT_SCL 13
#define TFT_SDA 11
#define TFT_RS 9
#define TFT_RST 8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_RS, TFT_SDA, TFT_SCL, TFT_RST);

/* ALPS Drehencoder + Button */
#define ENCODER_A 2
#define ENCODER_B 3
#define ENCODER_C 4
Encoder  encoder(2, 3);
//Bounce encoderA = Bounce();
//Bounce encoderB = Bounce();
Bounce encoderC = Bounce();

/* Button */
#define BUTTON 5
Bounce button = Bounce();

void pillar(int x, int y, int val, unsigned int color)
{
	x += 4; y += 4;
	int mapVal = map(val, 0, 255, 0, 85);
	tft.fillRect(x, y, 12, 85 - mapVal, 0x0000);
	tft.fillRect(x, y + 85 - mapVal, 12, mapVal, color);

	tft.setTextColor(0xFFFF, 0x0000);
	tft.setCursor(x - 2, y + 90);
	if (val < 10) tft.print("  ");
	else if (val < 100) tft.print(" ");
	tft.print(val);
}

int main(void)
{
	init();
	/* ITEAD 1.8" Display 160x240 */
	tft.initR(INITR_BLACKTAB);
	tft.setRotation(3);
	tft.fillScreen(0x0000);
	for (int i = 0; i < 6; i++) tft.drawRect(i * 25 + 7, 20, 20, 93, 0xFFFF);
	tft.drawRect(7, 20, 20, 93, 0x00FF);

	/* ALPS Drehencoder + Button */
	pinMode(ENCODER_A, INPUT_PULLUP);
	pinMode(ENCODER_B, INPUT_PULLUP);
	pinMode(ENCODER_C, INPUT_PULLUP);
	// encoderA.attach(ENCODER_A);
	// encoderA.interval(1);
	// encoderB.attach(ENCODER_B);
	// encoderB.interval(5);
	encoderC.attach(ENCODER_C);
	encoderC.interval(50);


	/* Button */
	pinMode(BUTTON, INPUT_PULLUP);
	button.attach(BUTTON);
	button.interval(20);

	while (1)
	{
		static int data[6];
		static unsigned colorData[6] = { ST7735_RED, ST7735_GREEN, ST7735_BLUE, ST7735_YELLOW, ST7735_CYAN, ST7735_MAGENTA };
		enum data
		{
			red, green, blue, brightness, speed, width
		};

		/* ALPS Button */
		static int value1;
		static int lastValue1;
		if (encoderC.update() && !encoderC.read()) value1++;
		if (value1 < 0 || value1 > 5) value1 = 0;
		if (value1 != lastValue1)
		{
			lastValue1 = value1;
			for (int i = 0; i < 6; i++) tft.drawRect(i * 25 + 7, 20, 20, 93, 0xFFFF);
			tft.drawRect(value1 * 25 + 7, 20, 20, 93, 0x00FF);
		}

		/* Button */
		static int value2;
		static int lastValue2;
		if (button.update() && !button.read()) value2++;
		if (value2 < 0 || value2 > 20) value2 = 0;
		if (value2 != lastValue2)
		{
			lastValue2 = value2;
			tft.setCursor(0, 0);
			tft.setTextColor(0x0000, 0xFFFF);
			if (value2 < 10) tft.print(0);
			tft.print(value2);
		}

		/* ALPS Drehencoder */
		int newPos = encoder.read();
		static int oldPos;
		if (newPos != oldPos)
		{
			oldPos = newPos;
			data[value1] += newPos;
			if (data[value1] > 255) data[value1] = 255;
			pillar(value1 * 25 + 7, 20, data[value1], colorData[value1]);
		}
	}
	return 0;
}

Hallo,

nochmal was anderes: Kennst Du diese Chips: http://de.farnell.com/ams/as5030-atsu/encoder-magnet-dreh-8bit-16tssop/dp/1630797

Vielleicht wäre das was für Dich? Schau am besten mal ins Datenblatt.

Falls es nur um die Auswertung des Encoders (und nicht um einen anderen Encodertyp als den ALPS) geht, schau mal hier auf den zweiten Sketch: elektronik-bastelkeller.de

Vielleicht hilft auch einfach ein größerer Knopf am Encoder? Ich betreibe einen der genannten Encoder mit einem Knopf mit 28mm Durchmesser. Dieser größere Knopf bringt ein erheblich besseres "Drehgefühl" als wenn man nur an der nackten Achse des Encoders dreht.

Gruß,
Ralf

sschultewolter:
vorallem das falsch gezählt wird beim Encoder auslesen.

Das falsche Zählen ist systembedingt durch die Art der Auswertung in Deinem Sketch bzw. der verwendeten Drehgeber-Library. Mal eine kleine Übersicht der möglichen Auswertelogiken für Drehgeber mit einer Bewertung aus meiner Sicht:

Auswertelogik für Drehgeber basiert auf:

  • Hardware-Interrupts
  • PinChange-Interrupts
    ==> unbrauchbar für alle mechanischen, prellenden Drehgeber.
    ==> Eignung für hochwertige opto-elektrische Drehgeber mit eigener Auswertelogik, die ein prellendes Signal vermeidet

Auswertelogik für Drehgeber basiert auf:

  • Polling
    ==> brauchbar abhängig vom Anwendungsfall für jede Art Drehgeber, einschließlich mechanische, prellende.

Auswertelogik für Drehgeber basiert auf:

  • Timer-Interrupts
    ==> brauchbar in fast allen Anwendungsfällen für jede Art Drehgeber, einschließlich mechanische, prellende.

Alles, was im Playground an Drehgeberauswertung vorgestellt wird, ist nur für optoelektronische Drehgeber der 1000-Euro-Klasse mit eigener Signalelektronik brauchbar, die nicht-prellende Signale erzeugen. Solche Drehgeber haben eigene Elektronik eingebaut und benötigen eine eigene Stromversorgung. Intern basieren diese Geber auf berührungslosen optischen Prinzipien. Anwendungsfälle sind z.B. Drehspindeln von Maschinen, die genau in der Anzahl Umdrehungen und Winkelstellung der Spindel überwachst werden müssen. Für die einfachen mechanischen Drehgeber mit mechanischem Schleifkontakt sind sämtliche im Playground vorgestellten Auswertelogiken unbrauchbar (obowhl die Autoren des Playground-Beitrags das wohl anders zu sehen scheinen).

Was muss ich machen, damit TCCR2, OCR2, OCIE2 richtig geladen werden. Bekomme die Meldung, dass die nicht deklariert sind.
Habe mich an Drehgeber – Mikrocontroller.net (wackliger Rastpunkt) gehalten. Jedoch ist, soweit ich weiß, der Timer0 am Arduino für die millis() bereits belegt, habe den Timer2 gewählt und entsprechend angepasst.

//#include <Adafruit_GFX.h>
//#include <Adafruit_ST7735.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define XTAL		16E6				// 16 MHz
#define PHASE_A		(PIND & 1<<2)		// PD2
#define PHASE_B		(PIND & 1<<3)		// PD3

#include <SPI.h>
#define TFT_CS		10					// PB2
#define TFT_SCL		13					// PB5
#define TFT_SDA		11					// PB3
#define TFT_RS		A1					// PC1
#define TFT_RST		A0					// PC0
//Adafruit_ST7735 tft = Adafruit_ST7735(
//	TFT_CS, TFT_RS, TFT_SDA, TFT_SCL, TFT_RST);

volatile int8_t enc_delta;				// Drehgeberbewegung zwischen zwei Auslesungen im Hauptprogramm

const int8_t table[16] PROGMEM = {		// Dekodertabelle	
	0, 0, -1, 0, 0, 0, 0, 1,			// fuer wackeligen Rastpunkt
	1, 0, 0, 0, 0, -1, 0, 0 			// halbe Aufloesung
};

/*const int8_t table[16] PROGMEM = {		// Dekodertabelle
	0, 1, -1, 0, -1, 0, 0, 1,			// fuer normale Drehgeber
	1, 0, 0, -1, 0, -1, 1, 0 			// volle Aufloesung
};*/

ISR(TIMER2_COMP_vect)
{
	static int8_t last = 0;
	last = (last << 2) & 0x0F;
	if (PHASE_A) last |= 2;
	if (PHASE_B) last |= 1;
	enc_delta += pgm_read_byte(&table[last]);
}

void encode_init(void)
{
	TCCR2 = (1 << WGM20) | (1 << CS21) | (1 << CS20);
	OCR2 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);
	TIMSK2 |= 1 << OCIE2;
}

int8_t encode_read(void)
{
	int8_t val;
	cli();
	val = enc_delta;
	enc_delta = 0;
	sei();
	return val;
}

int main(void)
{
	int32_t val = 0;
	encode_init();
	sei();

	while (1)
	{
		val += encode_read();
	}
	return 0;
}

sschultewolter:
Was muss ich machen, damit TCCR2, OCR2, OCIE2 richtig geladen werden. Bekomme die Meldung, dass die nicht deklariert sind.

Welches Board mit welchem Controller?

Auf dem Atmega328 gibt es beispielsweise TCCR2A und TCCR2B als Register.

Wenn Du hardwarenah direkt mit Registern programmieren willst, mußt Du auch genau die Register ansprechen, die die verwendete Hardware tatsächlich hat.

Das muss an deiner AVR C Geschichte liegen.

In Standard Arduino muss man da nichts extra inkludieren.

Controller ist ein ATmega328P, hatte auch irgendwo geschrieben, dass ich den Pro Mini dafür einsetze. 168P gibts glaub garnicht mehr zu kaufen für den Pro Mini.

Gibts irgendwo eine Auflistung, wo ich sehen kann, welche Timer frei sind? 2A und B gingen gerade nicht. Das Display zeigt dann nichts mehr an.

Timer1A scheint frei zu sein.

EDIT:
Ich sehe jetzt deinen Fehler. TCCR2, OCR2, OCIE2 gibt es natürlich nicht. Es gibt die zwei Control Register TCCR2A/B. Dann hat der Timer zwei Kanäle mit getrennten Compare Registern OCR2A und OCR2B. Und im TIMSK2 Register gibt es natürlich 2 Interrupt Enable Bits OCIE2A und OCIE2B.
Du musst dann jeweils einen Kanal verwenden, als z.B. immer A

Die ISR musst dann auch anpassen, z.B:
ISR(TIMER2_COMPA_vect)
oder
ISR(TIMER1_COMPA_vect)

A und B sind aber nicht getrennte Timer! Das sind nur Ausgänge und Register des gleichen Timers. Der Atmega328 hat drei Timer. Auf 0 läuft millis() und delay(). 1 und 2 sind frei wenn nicht anderweitig durch Libs belegt.

Beim Timer/Counter Control Register hat A/B dagegen wieder ein andere Bedeutung. Da das nichts mit den Kanälen zu tun, sondern bezeichnet einfach verschiedene Register für die Einstellungen. A enthält die Compare Match Mode Bits und zwei der Waveform Mode Bits. B hat die Force Output Compare Bits (braucht man eigentlich nicht), das dritte WGM Bit und die Clock Select Bits. Timer1 hast sogar ein C Register, da es da noch mehr Optionen gibt.

Bei Timer1 musst du aufpassen, da der etwas anders ist als Timer2! Timer2 läuft in dem Code mit Precaler 32. Wenn du die gleichen Bits für Timer1 verwendest (d.h. CS11 und CS10) hast du Precaler 64. Das heißt der Timer läuft mit der halben Geschwindigkeit. Das kannst du aber kompensieren indem du die Formel für das OCR Register anpasst.
Siehe Datenblatt Seiten 137 und 162. Wobei OCR1A/B genaugenommen 16 Bit hat, da das ein 16 Bit Timer ist. Timer2 hat nur 8 Bit.

Für Timer1 sollte es glaube ich so aussehen:

void encode_init(void)
{
	TCCR1A = 1 << WGM10;
        TCCR1B = (1 << CS11) | (1 << CS10);
	OCR1A = XTAL / 128.0 * 1e-3 - 0.5;
	TIMSK1 |= 1 << OCIE1A;
}

ISR(TIMER1_COMPA_vect)
{
}

Den Wert des OCR Registers kann man aber auch per Hand ausrechnen. Der CTC Modus würde es hier aber auch tun. So läuft das in einem der PWM Modi.

Funktioniert erstaunlich gut :wink: Lasse jede Sekunde den Wert auf dem Display aktualisieren. Werde nun entsprechen mal die Steuerung wieder aussenherumbasteln und sehen, wie es sich dann verhält. Kann mir aber nicht mehr vorstellen, dass sich das nun verschlimmern könnte. Ich schließe lediglich noch den Button an, und nutzte anschließen I2C/Serial für die Datenübertragung, mehr macht das ganze dann nicht mehr.

Danke

#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define XTAL		16E6				// 16 MHz
#define PHASE_A		(PIND & 1<<2)		// PD2 (+) --- SW -- PD2 --- 10k --- (-) 
#define PHASE_B		(PIND & 1<<3)		// PD3 (+) --- SW -- PD2 --- 10k --- (-)

#include <SPI.h>
#define TFT_CS		10					// PB2
#define TFT_SCL		13					// PB5
#define TFT_SDA		11					// PB3
#define TFT_RS		A1					// PC1
#define TFT_RST		A0					// PC0
Adafruit_ST7735 tft = Adafruit_ST7735(
	TFT_CS, TFT_RS, TFT_SDA, TFT_SCL, TFT_RST);

volatile int8_t enc_delta;				// Drehgeberbewegung zwischen zwei Auslesungen im Hauptprogramm

const int8_t table[16] PROGMEM = {		// Dekodertabelle
	0, 0, -1, 0, 0, 0, 0, 1,			// halbe Aufloesung
	1, 0, 0, 0, 0, -1, 0, 0
};

ISR(TIMER1_COMPA_vect)
{
	static int8_t last = 0;
	last = (last << 2) & 0x0F;
	if (PHASE_A) last |= 2;
	if (PHASE_B) last |= 1;
	enc_delta += pgm_read_byte(&table[last]);
}

void encode_init(void)
{
	TCCR1A = 1 << WGM10;
	TCCR1B = (1 << CS11) | (1 << CS10);
	OCR1A = XTAL / 128.0 * 1e-3 - 0.5;
	TIMSK1 |= 1 << OCIE1A;
}

int8_t encode_read(void)
{
	int8_t val;
	cli();
	val = enc_delta;
	enc_delta = 0;
	sei();
	return val;
}

int main(void)
{
	init();
	uint8_t val = 127;
	encode_init();
	sei();
	tft.initR(INITR_BLACKTAB);
	tft.setRotation(3);
	tft.fillScreen(ST7735_BLACK);
	tft.setTextColor(ST7735_WHITE, ST7735_BLACK);

	while (1)
	{
		val += encode_read();

		static unsigned long lastMillis;
		if (millis() - lastMillis > 1000) {
			lastMillis = millis();
			tft.setCursor(0, 0);
			if (val < 10) tft.print("00");
			else if (val < 100) tft.print("0");
			tft.print(val);
		}
	}
	return 0;
}