-
Ja ok gern.
Aber nicht lachen, der Code funktioniert bei beliebigen Geschwindigkeiten.
Kurz zur Vorgeschichte: Dieser Code ist Teil eines Steuergerätes, das eine Unmenge an LEDs als Zimmerbeleuchtung steuern kann. Und zwar frei dimmbar von 0% auf 100%. Zudem gibt es zwei LED-Stromkreise, einen blauen und einen weißen. (Für romantisches und helles Licht) - Edit: Ich sehe gerade, den weißen Codeteil habe ich gestrichen, da ich für jemanden anderes nur die romantische Variante gebaut habe.
Wenn man die Encoder drückt, dimmt er das Licht an oder aus. Und nur, wenn das Licht an ist, kann man mittels drehen die Helligkeit steuern. Dreht man über 100% oder 0% hinaus, so ertönt ein Geräusch.
Achtung, unvollständig - Das Programm ist im kompletten sehr groß.
Code:
#define PIN_ENCODER PINB
// Blauer Encoder linker Kanal, rechter Kanal, Masse
#define PIN_ENCODER_B_L 1
#define PIN_ENCODER_B_GND 2
#define PIN_ENCODER_B_R 3
// Blauer Encoder Button
#define PIN_ENCODER_B_BTN 0
#define PIN_ENCODER_B_BTN_GND 4
// Ausgang für den blauen Stromkreis
#define PIN_PWM_B 3
// Sind beide Kanäle auf '1'?
#define ENC_B_BOTH (_BV(PIN_ENCODER_B_L) | _BV(PIN_ENCODER_B_R))
// Ist der linke Kanäle auf '1'?
#define ENC_B_LEFT _BV(PIN_ENCODER_B_R)
// Ist der rechte Kanäle auf '1'?
#define ENC_B_RIGHT _BV(PIN_ENCODER_B_L)
// Hilfsmakro
#define FILTER_ENC_B (PIN_ENCODER & ENC_B_BOTH)
#define PWM_CHANGE_VALUE 25
// Aktueller Programmzustand
#define MODE_OFF 0
#define MODE_FADE_IN 1
#define MODE_ON 3
#define MODE_FADE_OUT 4
SIGNAL (SIG_PCINT)
{ // BTN blue pressed
if (PIN_ENCODER & PIN_ENCODER_B_BTN) // Nur bei LOW!
return;
if (blue_pwm_mode == MODE_FADE_IN || blue_pwm_mode == MODE_FADE_OUT)
return;
if (blue_pwm_mode == MODE_OFF)
{
blue_pwm_mode = MODE_FADE_IN;
pwm_fadewert_blue = 0;
}else{
blue_pwm_mode = MODE_FADE_OUT;
pwm_fadewert_blue = pwm_wert_blue;
}
pin_changed = 1;
}
int main(void)
{
uint8_t oldB;
uint8_t newB;
uint8_t peep_no;
unsigned int* rot;
rot = &OCR1A;
pins_init();
timer1_init();
sei();
_delay_ms(250);
peep_ack();
peep_ack();
oldB = FILTER_ENC_B;
while (1)
{
if (pin_changed)
{
pin_changed = 0;
peep_ack();
}
// MODE_ON heißt, die Lampen sind an
if (blue_pwm_mode == MODE_ON)
if (FILTER_ENC_B != oldB)
{
newB = FILTER_ENC_B;
peep_no = 0;
if ((newB == ENC_B_BOTH) && (oldB == ENC_B_LEFT))
{
if (pwm_wert_blue >= PWM_CHANGE_VALUE+1)
pwm_wert_blue -= PWM_CHANGE_VALUE;
else
peep_no = 1;
}else if ((newB == ENC_B_BOTH) && (oldB == ENC_B_RIGHT)){
if (pwm_wert_blue < 255-PWM_CHANGE_VALUE+1)
pwm_wert_blue += PWM_CHANGE_VALUE;
else
peep_no = 1;
}else if ((newB == 0) && (oldB == ENC_B_RIGHT)){
if (pwm_wert_blue >= PWM_CHANGE_VALUE+1)
pwm_wert_blue -= PWM_CHANGE_VALUE;
else
peep_no = 1;
}else if ((newB == 0) && (oldB == ENC_B_LEFT)){
if (pwm_wert_blue < 255-PWM_CHANGE_VALUE+1)
pwm_wert_blue += PWM_CHANGE_VALUE;
else
peep_no = 1;
}
if (peep_no == 1)
peep_ack();
oldB = FILTER_ENC_B;
}
}
}
Falls es jemanden interessiert, hier ist der Part für die Software-PWM
Code:
SIGNAL (SIG_OUTPUT_COMPARE0A)
{
static uint8_t pwm_counter;
pwm_counter++;
if (blue_pwm_mode == MODE_ON && pwm_wert_blue >= pwm_counter)
{
PORT_PWM |= _BV(PIN_PWM_B);
}else
if (blue_pwm_mode != MODE_OFF && blue_pwm_mode != MODE_ON && pwm_fadewert_blue >= pwm_counter)
{
PORT_PWM |= _BV(PIN_PWM_B);
}else
PORT_PWM &= ~_BV(PIN_PWM_B);
if (pwm_counter == 255)
{
if (blue_pwm_mode == MODE_FADE_IN)
{
pwm_fadewert_blue ++;
if (pwm_fadewert_blue >= pwm_wert_blue)
blue_pwm_mode = MODE_ON;
} else if (blue_pwm_mode == MODE_FADE_OUT)
{
pwm_fadewert_blue --;
if (pwm_fadewert_blue <= 1)
blue_pwm_mode = MODE_OFF;
}
}
}
-
Hallo thewulf00,
ich bin jetzt zwar nicht ganz durch deinen Code durchgestiegen, aber ich glaube im Pirnzip ist der große Kern es das gleiche wie der Code von Nic.
Kann es sein, das dein Code nur jede zweite Stellung des Encoders erkennt? (Ich kann es gerade mal wieder nicht testen)
Viele Grüße
Andreas
-
Mein Code erkennt jede Stellung, deshalb ist er ja so "kompliziert".
Der Code ist nicht so schwer.
Im untersten Teil werden 4 Fälle abgebildet. Es fallen bei einem Drehimpuls immer zwei Kanaländerungen an. Nur die Reihenfolge dieser beiden Änderungen gibt Aufschluss über die Richtung. Deshalb frage ich auch nur einen der beiden Zustände ab. Aber ich vergleiche natürlich mit dem alten Zustand vor der Drehung, so dass ich auf jeden Fall quasi durch vergleichen alle acht Fälle abbilde.
-
Ok, dann ist es wirklich das gleiche wie der Code von Nic. Ich bin aber heute wieder nicht zum probieren gekommen.
Übrigens mein erster Code macht ja im Endeffekt auch was ähnliches, nur noch vieeeeel komplizierter wie deiner. ;-)
-
Warum wirden die INT0 und INT1 nicht verwendet ? Dafur ist doch eine ISR gedacht, um solche schnelle signalen zu verarbeiten. In meinen robby hang der drehgeber an INT0. In der ISR von INT0 wird dan der zweite kanal geprueft. Zaehlt 100% richtig. Der frequenz ist bei mir max 2000 Hz. Der INT0 wird activiert bei jede positive flanke.
-
Hallo RP6conrad,
ich stimme dir voll zu, normalerweise sind dafür die INT-Eingänge perfekt.
Aber da ich mit diesen Drehimpulsgebern schon so oft auf die Schnauze gefallen bin, wollte ich dieses Thema endlich mal seperat behandeln und eine funktionierende Lösung erarbeiten, bei der ich auch sehe woran es liegen kann, wenn die Auswertung nicht geht. Und zwar ohne das ganze "außenrum", dass vielleicht wieder störenden Einfluß haben könnte.
Ich finde das ist mir hier mit Hilfe der Leute im Forum perfekt gelungen. Der beste Lerneffekt ist doch, wenn man Fehler macht, diese ausbessert und auch noch weiß, warum der Fehler so aufgetreten ist. Genauso ist es hier passiert.
Viele Grüße
Andreas
-
Ich habe bewusst auf Interrupts verzichtet, weil dann die Auswertung schwieriger ist. Du musst ja zwei Kanäle betrachten. Und allein der Zeitpunkt des Eintreffens entscheidet über die Wirkung. Sowas ist für mich eine typische "Hauptprogramm-Aufgabe" mit Schleifen.
-
Aber das ist doch gerade der Vorteil von IRQ's. Die werden genau dann ausgeführt, wenn das Ereignis auftritt. In diesem Fall, wenn sich der Eingang ändert. Man verzichtet dann einfach auf die Schleife im Hauptprogramm und prüft in der IRQ-Routine am Start gleich den Status der beiden Spuren und kann damit seine Auswertung machen.
Bei mics erstem Code könnte man dann quasi auf folgendes Verzichten:
Code:
while (1)
{
do // warten bis Bitwechsel erkannt wird
{
SpurA_akt = SpurA;
SpurB_akt = SpurB;
if (count > 0)
{
count--;
if (count == 0)
{
Rechts0;
Links0;
}
}
}
while ((SpurA_akt == SpurA_old) && (SpurB_akt == SpurB_old));
und statt dessen in die IRQ-Routine nur folgendes schreiben:
Code:
if (SpurA_akt == SpurA_old) // Bitwechsel war bei Spur B
{
if (SpurA_akt)
{
if (SpurB_akt)
Links1;
else
Rechts1;
}
else
{
if (SpurB_akt)
Rechts1;
else
Links1;
}
}
else // Bitwechsel war bei Spur A
{
if (SpurB_akt)
{
if (SpurA_akt)
Rechts1;
else
Links1;
}
else
{
if (SpurA_akt)
Links1;
else
Rechts1;
}
}
SpurA_old = SpurA_akt;
SpurB_old = SpurB_akt;
-
Versuchs mal umzusetzen, dann reden wir weiter ;p
-
Praktischerweise habe ich bei meinem Tiny ja die beiden INT Eingänge belegt. Ich werde es also mal versuchen.