Forum: Mikrocontroller und Digitale Elektronik RGB-Steuerung umschalten zwischen Automatik und Manuell


von Michael N. (garril)


Lesenswert?

Hallo,
meine Steuerung mit der ich per 3 Potis (ADC) eine RBG-LED ansteuern 
konnte hat soweit funktioniert.
Nun wollte ich noch einen Schalter mit einbauen, mit dem ich zwischen 
manuellem Betrieb (eben per Potis) und automatischem Betrieb 
(automatischer Farbverlauf) wechseln kann.

Aktuell geht im manuellen Betrieb garnixmehr. Im Automatik-Modus blinkt 
die LED immer mal kurz blau (wiederholt)

Hier der Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define F_CPU 16000000L                  // Systemtakt in Hz
5
#define an(port,b) (port) |= (1<<(b))
6
#define aus(port,b) (port) &= ~(1<<(b))
7
8
  // aktueller Counterstand
9
  volatile int pwm_counter=0; // Wird durch den internen Timer hochgezählt
10
  volatile int poti_counter=0; // 1...3: Dieses Poti wird ausgelesen
11
12
  // Helligkeit von 1 bis 50
13
  volatile int pb1_soll = 0; //zw. 0 und 50: BLAU
14
  volatile int pb2_soll = 0; //zw. 0 und 50: GRÜN
15
  volatile int pb3_soll = 0; //zw. 0 und 50: ROT
16
  volatile int schalter = 1; //zw. 0 und 1: Schalter 0: manuell/1: automatisch
17
  volatile int auto_phase = 0; // Phasen für automatischen Betrieb
18
  /************************Phasen für automatischen Betrieb**********************
19
  *0:Rot an
20
  *1:Blau heller
21
  *2:Rot dünkler
22
  *3:Grün heller
23
  *4:Blau dünkler
24
  *5:Rot heller
25
  *6:Grün dünkler
26
  */
27
28
int main(void) {
29
30
  // Für ADC Werter
31
  uint16_t result;
32
33
  // Ausgänge setzen
34
  DDRB|=(1<<PB1);
35
  DDRB|=(1<<PB2);
36
  DDRB|=(1<<PB3);
37
38
  //ADC zum Poti auslesen
39
  ADMUX = (0<<REFS1) | (1<<REFS0);  // ADC Ref auf Avcc, PC0 gewählt, normale Formatierung
40
  ADCSRA= (1<<ADEN) | (1<<ADSC) | (0<<ADFR) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
41
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
42
  result = ADCW; //Dummy auslesen
43
  ADCSRA|= (1<<ADSC); //Aktuellen tatsächlichen Wert holen, wird später für erstes Poti verwendet
44
45
  // Timer 0 konfigurieren
46
  TCCR0 = (1<<CS00); // Prescaler 0!!!
47
48
  // Overflow Interrupt erlauben
49
  TIMSK |= (1<<TOIE0);
50
 
51
  // Global Interrupts aktivieren
52
  sei();
53
54
  while (1) {
55
    //Nur wenn Schalter auf manuell steht
56
    if (schalter==0) {
57
    //Poti an PC0 ->steuert-> PB1
58
    if (poti_counter==0) {
59
      while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
60
      result = ADCW; //PC0 auslesen
61
      pb1_soll=result/20;
62
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX2) | (0<<MUX1) | (1<<MUX0);  // ADC Ref auf Avcc, PC1 gewählt, normale Formatierung
63
      ADCSRA|= (1<<ADSC); //Aktuellen tatsächlichen Wert holen
64
      poti_counter++;
65
    }
66
    if (poti_counter==1) {
67
      while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
68
      result = ADCW; //PC1 auslesen
69
      pb2_soll=result/20;
70
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX2) | (1<<MUX1) | (0<<MUX0);  // ADC Ref auf Avcc, PC2 gewählt, normale Formatierung
71
      ADCSRA|= (1<<ADSC); //Aktuellen tatsächlichen Wert holen
72
      poti_counter++;
73
    }
74
    if (poti_counter==2) {
75
      while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
76
      result = ADCW; //PC2 auslesen
77
      pb3_soll=result/20;
78
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX2) | (1<<MUX1) | (1<<MUX0);  // ADC Ref auf Avcc, PC3 gewählt, normale Formatierung
79
      ADCSRA|= (1<<ADSC); //Aktuellen tatsächlichen Wert holen
80
      poti_counter++;
81
    }
82
    } //Ende manuell
83
    
84
    if ((poti_counter==3) || (schalter==1)) {
85
      while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
86
      result = ADCW; //PC3 auslesen
87
      result=result/20;
88
      if (result<=25) {
89
      //Schalter auf 0
90
        schalter=0;
91
        TCCR0 = (0<<CS01); // Prescaler 0!!!
92
      }
93
      else {
94
      //Schalter auf 1
95
        schalter=1;
96
        TCCR0 = (1<<CS01); // Prescaler 64!!!
97
      }
98
      if (schalter==0) { //Manueller Betrieb
99
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0);  // ADC Ref auf Avcc, PC0 gewählt, normale Formatierung
100
      ADCSRA|= (1<<ADSC); //Aktuellen tatsächlichen Wert holen
101
      poti_counter=0;
102
      }
103
      else { //Automatischer Betrieb
104
        ADCSRA|= (1<<ADSC); //Aktuellen tatsächlichen Wert holen
105
      }
106
    }
107
  }
108
}
109
110
#ifndef TIMER0_OVF_vect
111
// Für ältere WinAVR Versionen z.B. WinAVR-20071221 
112
#define TIMER0_OVF_vect TIMER0_OVF0_vect
113
#endif
114
// Interrupt
115
ISR (TIMER0_OVF_vect) {
116
117
  if (schalter==1) { //Automatischer Betrieb
118
    // Phasen
119
      // 0: Rot an; Rest aus
120
      if (pwm_counter==0) {
121
        pb1_soll=0;
122
        pb2_soll=0;
123
        pb3_soll=50;
124
      }
125
      // 1: Blau heller
126
      if ((pwm_counter>=1)&(pwm_counter<=43)) {
127
        pb1_soll=pwm_counter;
128
      }
129
      // 2: Rot dünkler
130
      if ((pwm_counter>=44)&(pwm_counter<=86)) {
131
        //pb3_soll=pb3_soll-(pwm_counter-43);
132
        pb3_soll=0;
133
      }
134
      // 3: Grün heller
135
      if ((pwm_counter>=87)&(pwm_counter<=128)) {
136
        pb2_soll=pwm_counter-86;
137
      }
138
      // 4: Blau dünkler
139
      if ((pwm_counter>=129)&(pwm_counter<=171)) {
140
        //pb1_soll=pb1_soll-(pwm_counter-128);
141
        pb1_soll=0;
142
      }
143
      // 5: Rot heller
144
      if ((pwm_counter>=172)&(pwm_counter<=214)) {
145
        pb3_soll=pwm_counter-171;
146
      }
147
      // 6: Grün dünkler
148
      if ((pwm_counter>=215)&(pwm_counter<=255)) {
149
        //pb2_soll=pb2_soll-(pwm_counter-214);
150
        pb2_soll=0;
151
      }
152
  }
153
154
  if (pb1_soll==pwm_counter) {
155
    aus(PORTB,PB1);
156
  }
157
  if (pb2_soll==pwm_counter) {
158
    aus(PORTB,PB2);
159
  }
160
  if (pb3_soll==pwm_counter) {
161
    aus(PORTB,PB3);
162
  }
163
  if (pwm_counter>=50) {
164
    pwm_counter=0;
165
    if (pb1_soll>0) {
166
      an(PORTB,PB1);
167
    }
168
    if (pb2_soll>0) {
169
      an(PORTB,PB2);
170
    }
171
    if (pb3_soll>0) {
172
      an(PORTB,PB3);
173
    }
174
  }
175
  else {
176
    pwm_counter++;
177
  }
178
  
179
180
}

Finde meinen Denkfehler irgendwie nicht...
(Bevor ich die Teile mit schalter eingebaut habe, hat alles wunderbar 
funktioniert mit der manuellen Steuerung)

edit: Schalter ist sicherlich auch richtig dran. Hab nämlich mal statt 
dem Farbverlauf einfach pb1_soll bis pb3_soll alles auf 10 gesetzt, wenn 
der Schalter auf 1 ist. Das hat funktioniert.

von Helfer (Gast)


Lesenswert?

Wie ist der Gedankengang hinter diesem Codestück?
1
      if (schalter==0) { //Manueller Betrieb
2
  ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0);  // ADC Ref auf Avcc, PC0 gewählt, normale Formatierung
3
  ADCSRA|= (1<<ADSC); //Aktuellen tatsächlichen Wert holen
4
  poti_counter=0;
5
      }

von Helfer (Gast)


Lesenswert?

In Verbindung mit diesem diesem Codestück:
1
      if (result<=25) {
2
      //Schalter auf 0
3
        schalter=0;
4
        TCCR0 = (0<<CS01); // Prescaler 0!!!
5
      }

von Michael N. (garril)


Lesenswert?

Ich lass im Schleifendurchlauf schon die Register für den ADC für den 
nächsten Schleifendurchlauf setzen, falls du das meinst?!

Wenn am ADC weniger als die Hälfte der Spannung anliegen, dann ist 
schalter 0. Ansonsten ist er 1.

Habs mitlerweile schon gelöst. Hab das ganze noch etwas umgeschrieben.

Video der Steuerung unter: 
http://www.myvideo.de/watch/8195239/AVR_RGB_Steuerung

von Karl H. (kbuchegg)


Lesenswert?

Michael N. schrieb:
> Ich lass im Schleifendurchlauf schon die Register für den ADC für den
> nächsten Schleifendurchlauf setzen, falls du das meinst?!


Ist soweit ok, aber trotzdem noch nicht ganz das was du eigentlich 
wolltest.

Du wolltest, dass der ADC sampelt, währnd dein Programm was anderes 
macht.

Dann überleg dir doch jetzt mal, wie sich das mit der Steuerung durch 
deine poti_counter Variable ausgeht

Ich kürze mal den Code ein wenig auf den relevanten Teil, dann sieht man 
es besser
1
    if (poti_counter==0) {
2
      ...
3
      poti_counter++;
4
    }
5
6
    if (poti_counter==1) {
7
      ....
8
      poti_counter++;
9
    }
10
11
    if (poti_counter==2) {
12
      ....
13
      poti_counter++;
14
    }

die ursprüngliche Absicht ist voll in die Hose gegangen. Wenn 
poti_counter gleich 0 ist, dann wird der ADC ausgelesen und poti_counter 
erhöht. Er ist jetzt also 1. Gleich danach kommt die Abfrage, ob 
poti_counter gleich 1 ist. Jaowhl, ist er! Wurde ja gerade im 
vorhergehenden if auf 1 erhöht. Als Folge wird gewartet, bis der ADC 
fertig ist und wieder: poti_counter um 1 erhöhen, aud 2. Weiter gehts 
zum nächsten if, und wieder trifft die Bedingung zu.

Deine ganze Abfragerei lässt sich daher auch so zusammenfassen
1
   if (poti_counter == 0 ) {
2
3
      sample Kanal 0
4
      sample Kanal 1
5
      sample Kanal 2
6
7
      poti_counter = 3;
8
   }

nur dass deine Version dieses Verhalten besser versteckt und eigentlich 
nicht das ist, was du ursprünglich wolltest :-)

von Michael N. (garril)


Lesenswert?

Wow. Du hast recht!

Habs ja mittlerweile zum laufen gebracht, aber das ist immer noch so...

Ist natürlich ein schmarrn, dass so zu schreiben.

Gibts sowas wie ein break(2) oder sowas, womit ich das dann machen 
könnte?
Ansonsten müsste ich halt noch ne Variable einsetzen, die nach dem 
ersten wahren if-Teil verändert wird.

von Karl H. (kbuchegg)


Lesenswert?

Michael N. schrieb:
> Wow. Du hast recht!
>
> Habs ja mittlerweile zum laufen gebracht, aber das ist immer noch so...
>
> Ist natürlich ein schmarrn, dass so zu schreiben.
>
> Gibts sowas wie ein break(2) oder sowas, womit ich das dann machen
> könnte?
> Ansonsten müsste ich halt noch ne Variable einsetzen, die nach dem
> ersten wahren if-Teil verändert wird.

else if

ist schon erfunden
1
    if (poti_counter==0) {
2
      ...
3
      poti_counter++;
4
    }
5
6
    else if (poti_counter==1) {
7
      ....
8
      poti_counter++;
9
    }
10
11
    else if (poti_counter==2) {
12
      ....
13
      poti_counter++;
14
    }


Alternativ kann man auch die Reihenfolge der einzelnen Fälle umdrehen
1
    if (poti_counter==2) {
2
      ...
3
      poti_counter++;
4
    }
5
6
    if (poti_counter==1) {
7
      ....
8
      poti_counter++;
9
    }
10
11
    if (poti_counter==0) {
12
      ....
13
      poti_counter++;
14
    }

dann kann sich ebenfalls ein erhöhter Wert nicht mehr auf die 
nachfolgenden if auswirken, denn die vergleichen sukzessive mit 
kleineren Werten.

Aber else if ist besser. Denn wenn poti_counter gleich 1 war, dann hast 
du gar kein Interesse mehr daran, ob er 2 war oder nicht. Der else if 
berücksichtigt das.

In beiden Fällen kann man aber dann auch das erhöhen aus all den Fällen 
selbst herausziehen und ist so ebenfalls das Problem los:
1
    poti_counter++;
2
3
    if (poti_counter==0) {
4
      ...
5
    }
6
7
    else if (poti_counter==1) {
8
      ....
9
    }
10
11
    else if (poti_counter==2) {
12
      ....
13
    }



(Und den ADC Kram in Funktionen verpacken. Das bringt Übersicht ins 
Programm!)

von Karl H. (kbuchegg)


Lesenswert?

In deiner ISR genau dasselbe
1
      if (pwm_counter==0) {
2
        pb1_soll=0;
3
        pb2_soll=0;
4
        pb3_soll=50;
5
      }
6
      if ((pwm_counter>=1)&(pwm_counter<=43)) {
7
        pb1_soll=pwm_counter;
8
      }
9
      // 2: Rot dünkler
10
      if ((pwm_counter>=44)&(pwm_counter<=86)) {
11
        //pb3_soll=pb3_soll-(pwm_counter-43);
12
        pb3_soll=0;
13
14
....

Wenn pwm_counter im Bereich 1 bis 43 war, dann KANN er nicht mehr im 
Bereich 44 bis 86 sein. Das ist unmöglich. Eine Zahl kann nicht 
gleichzeitig 22 (also im ersten Abschnitt) und 57 (also im zweiten 
Abschnitt) sein. Die Fälle schliessen sich gegenseitig aus. Daher else 
if
1
      if (pwm_counter==0) {
2
        pb1_soll=0;
3
        pb2_soll=0;
4
        pb3_soll=50;
5
      }
6
7
      else if ((pwm_counter>=1)&(pwm_counter<=43)) {
8
        pb1_soll=pwm_counter;
9
      }
10
      // 2: Rot dünkler
11
      else if ((pwm_counter>=44)&(pwm_counter<=86)) {
12
        //pb3_soll=pb3_soll-(pwm_counter-43);
13
        pb3_soll=0;

Weiters: Die Untergrenzen musst du nicht mehr testen. Damit du in den 
dritten Fall kommst (Rot dünkler) muss pwm_counter schon mal größer als 
43 gewesen sein. Wenn der Wert kleiner wäre, dann wäre der zweite if 
schon zum Zug gekommen. Wenn die Bedingung im else if daher bearbeitet 
wird, steht daher schon fest, dass die erste Teilbedingung pwm_counter > 
43 schon mal wahr sein muss. Also kann man sie die auch sparen:
1
      if (pwm_counter==0) {
2
        pb1_soll=0;
3
        pb2_soll=0;
4
        pb3_soll=50;
5
      }
6
7
      else if (pwm_counter<=43) {
8
        pb1_soll=pwm_counter;
9
      }
10
11
      // 2: Rot dünkler
12
      else if (pwm_counter<=86) {
13
        //pb3_soll=pb3_soll-(pwm_counter-43);
14
        pb3_soll=0;
15
16
      ....

All das sind unnötige Instruktionen, die du dem µC für nichts und wieder 
nichts aufhalst.

Dann solltest du dir angewöhnen, <= bzw. >= zu meiden. Die Logik wird 
dadurch auch nicht einfacher, du wirst aber sehen, dass du speziell bei 
for-Schleifen praktisch immer < und nicht <= hast. D.h. du gewöhnst dich 
daran, die zu lesen. Dann noch ein wenig optischen Zucker in Form von 
Leerzeichen (seit du 6 Jahre alt bist, hat dein Gehirn gelernt, dass 
zwischen Wörtern ein Leerraum ist, der dem Gehirn dabei hilft einzelne 
Wörter schnell zu identifizieren. Nutze das doch um deinem Code etwas 
mehr die Qualität von geschriebenem Text zu geben. Du wirst sehen, dass 
du Fehler viel leichter siehst, weil dein Gehirn nicht erst lange die 
einzelnen 'Wörter' identifiziern muss. Nur weil es in C erlaubt ist, 
alles Knirsch an Knirsch zu schreiben, muss man das noch lange nicht 
tun. OderfindestdudiesenTextetwabesserlesbar als diese Fortsetzung? 
Lesbar ist er, keine Frage. Aber wie lange hast du gebraucht um die 
Wörter zu finden?)
1
      if ( pwm_counter == 0 ) {
2
        pb1_soll =  0;
3
        pb2_soll =  0;
4
        pb3_soll = 50;
5
      }
6
7
      else if ( pwm_counter < 44 ) {
8
        pb1_soll = pwm_counter;
9
      }
10
11
      // 2: Rot dünkler
12
      else if ( pwm_counter < 86 ) {
13
        //pb3_soll = pb3_soll - (pwm_counter-43);
14
        pb3_soll = 0;
15
16
      ....


und wenn du jetzt noch die Variable nicht pb1_soll sondern zb Rot_soll 
bzw. Grn_soll und Blau_soll nennst, dann bist du gleich wieder einen 
Schritt weiter. Ein Variablenname soll dir sagen, welche Bedeutung der 
Zahlenwert da drinnen hat. In der Variable ist der Wert gespeichert, der 
der Helligkeit der roten Led entspricht! Das kann, darf und soll sich 
auch im Variablennamen niederchlagen. Welche Anweisung verstehst du rein 
nur beim Lesen besser? Die hier
1
      dValue = value1 + value1 * wert / 100.0;
oder die hier
1
      NettoBetrag = BruttoBetrag + BruttoBetrag * Mehrwertsteuer / 100.0;
Worum geht es in der Berechnung? In der ersten Version kann man nur 
raten. In der zweiten Version erzählt mir die Anweisung selber was sie 
macht. Nämlich auf einen Bruttobetrag die Mehrwertsteuer draufschlagen. 
Sie erzählt mir auch, dass der Mehrwertsteuersatz als Prozentangabe 
anzugeben ist. All das hab ich in der ersten Version nicht erkennen 
können.

Das Argument "Ja aber da muss ich doch soviel tippen", das zählt nicht. 
Erstens ist ein selbstdokumentierendes Programm immer besser, zweitens 
spart das schon mal den ansonst notwendigen Kommentar ein und drittens 
liest man eine Programmzeile im laufe der Entwicklung viele male, man 
tippt sie aber nur einmal (und verbessert sie vielleicht noch). D.h. ich 
richte mir den Code so her, dass ich ihn gut lesen kann und nicht so, 
dass ich ihn schnell tippen kann.

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.