Forum: Mikrocontroller und Digitale Elektronik DIP Schalter abfrage funktioniert nicht


von Sascha712 (Gast)


Lesenswert?

Hallo,

bin grad dabei meinem ComingHome Modul paar weitere Funktionen zu geben.

Dabei hab ich auch einen DIP Schalter, der je nach zustand das licht 
entweder gar nicht dimmen soll 00 ,
auf und abdimmen aber licht bei 60% halten 01 ,
auf und abdimmen aber licht bei 80% halten 10 ,
auf und abdimmen aber licht bei 100% halten 11 ,

Nun: 00 10 11 funktionieren.
Wenn ich aber 01 mache, dann dimmt er auf, scheint sich dann aber iwie 
aufzuhängen und steckt in der zeit schleife fest und kommt so nie zum 
runter dimmen.

Das ganze ist noch nicht fertig, da ich noch nen Lichtsensor abfragen 
werde und INT0 und INT1 Interrupt routinen einbauen werde.

Bloß ich hänge hier grad bei der Sache und weis nicht worans liegen 
könnte. Im grunde sind ja nur paar variabeln die geändert werden. 
Selbst, wenn ich den Code von 80% da reinlege, funktioniert es nicht. 
Woran liegt es also?
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <avr/sleep.h>
5
6
volatile int sekunde = 0;          //wird vom Timer immer um 1 erhöht.
7
                            //1 = 0,01sekunden
8
int dutycycle = 0;                    //aktueller Dutycycle
9
int maxdutycycle = 153;                  //maximaler Dutycycle
10
int dutycycleoff = 1;                  //Auf- und abdimmen deaktivieren
11
int lichtsensor = 0;                  //ADC-wert des Lichtsensors
12
int lichtschwelle = 0;                  //unterhalb dieses wertes wird der Lichtwert als dunkel erkannt
13
int abdimmen = 0;                    //wird auf 1 gesetzt. wenn die Hauptzeit läuft
14
int anbleibzeit = 1;                  //Zeit in sekunden in der das Licht aktiv bleiben soll
15
int dimmzeit = 17;                    //Zeit pro PWM änderung
16
int stop = 0;                      //wenn 1, dann aus
17
18
int main(void)
19
{
20
  ACSR |= 1<<ACD;                    //Analog Comparator ausschalten um Strom zu sparen
21
  
22
  //Konfiguration ADC
23
  
24
  //PWM
25
  DDRB |= 1<<DDB3;                  //PWM Ausgang auf PINB3
26
  
27
  PORTD |= 1<<PIND5 | 1<<PIND6 | 1<<PIND7;      //Pull up
28
  PORTB |= 1<<PINB0;
29
  PORTC |= 1<<PINC2 | 1<<PINC3 | 1<<PINC4 | 1<<PINC5;
30
  
31
  
32
  sei();                        //Interrup zulassen
33
  while(1)
34
  {
35
    //Timer für Sekunde definieren
36
    TCCR1B |= 1<<WGM12 | 1<<CS11 | 1<<CS10;    //Taktgeber für genau eine Sekunde
37
    TIMSK |= 1<<OCIE1A;
38
    OCR1A = 15624;
39
    
40
          //DIP-Schalter abfrage
41
      
42
      //anbleibzeit = 5;
43
      if(( !(PIND & (1<<PIND5)) )&&( !(PIND & (1<<PIND6)) )&&( !(PIND & (1<<PIND7)) ))
44
      {
45
        anbleibzeit = 5;
46
      }
47
      //anbleibzeit = 10;
48
      if(( !(PIND & (1<<PIND5)) )&&( !(PIND & (1<<PIND6)) )&& (PIND & (1<<PIND7)) )
49
      {
50
        anbleibzeit = 10;
51
      }
52
      //anbleibzeit = 15;
53
      if(( !(PIND & (1<<PIND5)) )&&(PIND & (1<<PIND6)) &&( !(PIND & (1<<PIND7)) ))
54
      {
55
        anbleibzeit = 15;
56
      }
57
      //anbleibzeit = 20;
58
      if(( !(PIND & (1<<PIND5)) )&&(PIND & (1<<PIND6))&&(PIND & (1<<PIND7)) )
59
      {
60
        anbleibzeit = 20;
61
      }
62
      //anbleibzeit = 25;
63
      if((PIND & (1<<PIND5))&&( !(PIND & (1<<PIND6)) )&&( !(PIND & (1<<PIND7)) ))
64
      {
65
        anbleibzeit = 25;
66
      }
67
      //anbleibzeit = 30;
68
      if((PIND & (1<<PIND5))&&( !(PIND & (1<<PIND6)) )&& (PIND & (1<<PIND7)) )
69
      {
70
        anbleibzeit = 30;
71
      }
72
      //anbleibzeit = 40;
73
      if((PIND & (1<<PIND5))&&(PIND & (1<<PIND6)) &&( !(PIND & (1<<PIND7)) ))
74
      {
75
        anbleibzeit = 40;
76
      }
77
      //anbleibzeit = 50;
78
      if((PIND & (1<<PIND5))&&(PIND & (1<<PIND6))&&(PIND & (1<<PIND7)) )
79
      {
80
        anbleibzeit = 50;
81
      }
82
      
83
      //Lichtschwelle
84
85
86
      //Dutycycle Off / 60% / 80% / 100% 
87
     
88
      if(( !(PINB & (1<<PINB0)) )&&( !(PINC & (1<<PINC1)) ))
89
      {
90
        dutycycleoff = 1;
91
      }
92
      //60%
93
      if(( !(PINB & (1<<PINB0)))&&(PINC & (1<<PINC1)) )         //Hier irgendwo Fehler?
94
      {
95
        dutycycleoff = 0;
96
        dimmzeit = 12;
97
        maxdutycycle = 204;
98
      }
99
      //80%
100
      if((PINB & (1<<PINB0))&&( !(PINC & (1<<PINC1)) ))
101
      {
102
        dutycycleoff = 0;
103
        dimmzeit = 12;
104
        maxdutycycle = 204;
105
      }
106
      //100%
107
      if((PINB & (1<<PINB0))&&(PINC & (1<<PINC1)))
108
      {
109
        dutycycleoff = 0;
110
        dimmzeit = 10;
111
        maxdutycycle = 255;
112
      }
113
114
115
116
      //Lichtwert einlesen
117
      //einlesen vorbereiten
118
      //Atmega -> adc noise reduction
119
      //Wert in Lichtwert schreiben
120
      //ISR -> ADC
121
122
123
124
    if((stop == 0) && (dutycycleoff == 1))
125
    {
126
      if(abdimmen==0)
127
      {
128
        PORTB |= 1<<PINB3;
129
        abdimmen = 1;
130
        sekunde = 0;
131
      }
132
      
133
      if((abdimmen==1) && (sekunde > anbleibzeit))
134
      {
135
        PORTB &= ~1<<PINB3;
136
        stop = 1;
137
        //Strom sparen
138
        set_sleep_mode(SLEEP_MODE_IDLE);        //Schlaf-Modus=Idle
139
        sleep_mode();                  //Schlaf-Modus aktivieren
140
      }
141
    }
142
143
    if((stop == 0) && (dutycycleoff == 0))
144
    {
145
      TCCR2 |= 1<<WGM20 | 1<<WGM21 | 1<<COM21 | 1<<CS22;//8Bit Timer, Fast PWM, PINB3(OC2),nicht invertiert,
146
                                //Teiler von 64
147
      OCR2 = dutycycle;                  //OCR2 ist der Duty-cycle 0-255
148
      if((dutycycle < maxdutycycle) && (abdimmen==0))
149
      {
150
        dutycycle++;
151
        _delay_ms(dimmzeit);
152
      }
153
    
154
      if((dutycycle == maxdutycycle) && (abdimmen==0))
155
      {
156
        abdimmen = 1;
157
        sekunde = 0;
158
      }
159
      if((abdimmen==1) && (sekunde > anbleibzeit))
160
      {
161
        dutycycle--;
162
        _delay_ms(dimmzeit);
163
      }
164
      if((dutycycle == 0) && (abdimmen == 1))
165
      {
166
        stop = 1;
167
        TCCR2 &= ~1<<CS22;
168
        //Strom sparen
169
        set_sleep_mode(SLEEP_MODE_IDLE);        //Schlaf-Modus=Idle
170
        sleep_mode();                  //Schlaf-Modus aktivieren
171
      }
172
    }  
173
  }
174
}
175
176
//Impuls durch Blinken/ZV steigende Flanke
177
178
//Impuls durch Zündungsplus aus fallende Flanke
179
180
181
ISR(TIMER1_COMPA_vect)
182
{
183
  sekunde++;
184
}


MfG Sascha

von Karl H. (kbuchegg)


Lesenswert?

AUf die Schnelle.
Du hast keinen Pullup an Pin PC1

Ist das Absicht oder hast du einen externen?

von Karl H. (kbuchegg)


Lesenswert?

Bist du soweit ein paar Tips anzunehmen, mit denen du dein Programm 
einfacher machen kannst?

Da ist zb. die komplette If-Orgie mit der du die Pins PD5, PD6 und PD7 
auswertest.

Grundsätzlich ist da (fast) nichts falsches drann. Aber so
1
int anbleibZeitPreset[] = { 5, 10, 15, 20, 25, 30, 40, 50 };
2
3
....
4
5
6
      uint8_t zeitDip = PIND & (( 1 << PIND5 ) | ( 1 << PIND6 ) | ( 1 << PIND7));
7
      zeitDip = zeitDip >> PIND5;
8
      anbleibzeit = anbleibZeitPreset[zeitDip];
9
...

ist es dann doch einfacher ausgedrückt als diese komplette if-Orgie, die 
mit ihren "! oder nicht !" dann doch eher fehleranfällig zu lesen und zu 
schreiben sind.

Das benutzt natürlich, dass deine 3 Schalter an aufeinanderfolgenden 
Pins liegen. Der komplette Port wird geholt, die 3 Bits mit dem & 
freigestellt (alle anderen Bits auf 0), dann um entsprechend viele 
Stellen nach rechts geschoben, so dass sich aus den 3 Bits eine Zahl 
zwischen 0 und 7 ergibt und damit kann man dann aus dem Array den zu 
benutzenden Wert holen.

: Bearbeitet durch User
von Sascha712 (Gast)


Lesenswert?

PINC1 benutz ich nicht. Der schalter liegt auf PIN 28,27,26,25 beim 
Atmega 8
Belegt somit an PORTC PINC5 PINC4 PINC3 PINC2.

Der zweite DIP Schalter ist an den PINS 11,12,13 und 14 angeschlossen.

Beide Schalter schalten gegen GND. Ich benutze die internen Pull Ups.

Ja, man kanns bestimmt schöner machen. Aber es müsste doch trotzdem 
gehen.

Weil er erkennt ja die Stellungen für Off, 80% und 100%

Hab bereits die Dip schalter durchgemessen und auch schon nen anderen 
Atmega genommen, aber das Problem bleibt bestehen. Er schaltet nach der 
Anbleibzeit nicht mehr aus.

von Karl H. (kbuchegg)


Lesenswert?

Sascha712 schrieb:
> PINC1 benutz ich nicht.


Dann muss ich mich wohl verlesen haben
1
      if((PINB & (1<<PINB0))&&(PINC & (1<<PINC1)))
2
                               ****       *****

doch, tust du

Ein Grund mehr, mal darüber nachzudenken, wie du den Code einfacher 
gestalten kannst :-)

: Bearbeitet durch User
von ich (Gast)


Lesenswert?

Sascha712 schrieb:
> PINC1 benutz ich nicht.

Ich denke doch:
1
if(( !(PINB & (1<<PINB0)) )&&( !(PINC & (1<<PINC1)) ))
2
                                                ^-------hier
und in den drei folgenden Vergleichen genauso.

Schau nochmal nach.

: Bearbeitet durch User
von Sascha712 (Gast)


Lesenswert?

Oh, da hab ich mich da wohl vertan...PINC1 ist aber offen auf der 
Platine.

Man bin ich blöd -.-

werds direkt mal testen

Danke schonmal, werde gleich berichten

von Sascha712 (Gast)


Lesenswert?

Jap, das wars.....könnte mich selber in den Hintern treten für sowas 
....

Vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

Sascha712 schrieb:

> Ja, man kanns bestimmt schöner machen. Aber es müsste doch trotzdem
> gehen.

Das interessante ist, das Code welcher übermässig komplex und 'nicht 
schön' ist, auch oft Code ist, in dem die tollsten Fehler schlummern.

Daher ist die Aussage "erst muss es funktionieren, dann mach ich es 
schön" nur die halbe Miete. Denn wenn man Code von vorne herein gleich 
(in vernünftigem Masse) einfach bzw. schön schreibt, spart man sich 
viele Fehler, die man dann auch nicht suchen muss.


Deine Dimm-Routine. Noch durchblicke ich die noch nicht in allen 
Einzelheiten aber aus dem Bauch raus müsste das funktionieren. D.h. wenn 
da nicht noch irgendwo ein kleiner fieser Fehler steckt.

Und nimm der Grundaufsatz für den Timer am Anfang der while Schleife aus 
dieser raus. Solche Dinge werden einmalig gemacht und danach lässt man 
die Bits in Ruhe.

von Sascha712 (Gast)


Lesenswert?

Ich hab per Dipschalter auch die möglichkeit das auf und abdimmen 
wegzulassen. Also quasi nur an und aus.
Darum sind die Bits für den Timer dort.

Im grunde ist es ganz simpel. Immer nach ablauf von delay-Dimmzeit wird 
Dutycycle entweder um eins erhöht oder um eins verringert, dadurch auch 
das "Faden"

Gibts irgendwo paar informationen, wie man "schön" schreibt?


gruß

von Karl H. (kbuchegg)


Lesenswert?

Sascha712 schrieb:
> Ich hab per Dipschalter auch die möglichkeit das auf und abdimmen
> wegzulassen. Also quasi nur an und aus.
> Darum sind die Bits für den Timer dort.

Schon.
Aber was bringt dir ein
1
  while(1)
2
  {
3
    //Timer für Sekunde definieren
4
    TCCR1B |= 1<<WGM12 | 1<<CS11 | 1<<CS10;    //Taktgeber für genau eine Sekunde
5
    TIMSK |= 1<<OCIE1A;
6
    OCR1A = 15624;
am ANfang der Schleife?
Die Bits verändern sich nie.

Du schaltest einzig und alleine den Vorteiler zu oder weg. Aber der Rest 
bleibt immer gleich.

> Gibts irgendwo paar informationen, wie man "schön" schreibt?

Zum Beispiel das angesprochene.

Zum Beispiel, indem man sowas
>       if((PINB & (1<<PINB0))&&( !(PINC & (1<<PINC1)) ))

in einem realen Programm mit Makrohilfe ein wenig aufpeppt.
1
  if( IS_CLOSED( DIMM_DIP_1 ) && IS_OPEN( DIMM_DIP_0 ) )
ist schon wesentlich einfacher zu lesen.
Noch einfacher ist es aber, wenn man die beiden Bits als das auffasst, 
was sie sind. Beide zusammen bilden eine Zahl zwischen 0 und 3 und davon 
abhängig werden dann Parameter ausgewählt. Daher ist es auch verünftig 
genau das zu tun: aus den beiden Bits eine Zahl zusammenzubauen und dann 
von dieser Zahl ausgehend den Brückenschlag zu den Parametern zu 
schaffen, wie immer der dann auch aussieht, zb mit einem Array.

was ist leichter zu verstehen. Deine Version, oder die hier
1
struct dimmPreset_
2
{
3
  uint8_t  dutyCycleOff;
4
  uint16_t dimmZeit;
5
  uint16_t maxDutyCycle;
6
};
7
8
struct dimmPreset_[] =
9
{
10
                                // PINB0  PINC1
11
  {  1,   0,   0 },             //    0     0          Off
12
  {  0,  12, 204 },             //    0     1          60%
13
  {  0,  12, 204 },             //    1     0          80%
14
  {  0,  10, 255 }              //    1     1         100%
15
}; 
16
17
....
18
19
   DimmLevel = 0;
20
   if( PINB & ( 1 << PINB0 ) )
21
     DimmLevel += 2;
22
   if( PINC & ( 1 << PINC1 ) )
23
     DimmLevel += 1;
24
25
   dutycycleoff = dimmPreset[DimmLevel].dutyCycleOff;
26
   dimmzeit     = dimmPreset[DimmLevel].dimmZeit;
27
   maxdutycycle = dimmPreset[DimmLevel].maxDutyCycle;
28
....

welche ist kürzer? Welche ist leichter an andere Zahlenwerte anzupassen? 
Welche ist leichter an andere Pinnummern anzupassen?

Anstatt PINB, PINC, PINB0, PINC1 wäre es auch vernünftig mittels #define 
ein paar sprechende Namen zu definieren, so dass man die tatsächliche 
Belegung nicht quer durch das ganze Programm verstreut hat, sondern am 
Anfang ganz oben.

denn beispielsweise
1
#define LED_PORT  PORTD
2
#define ERROR_LED PD5
3
4
5
...
6
7
8
    LED_PORT |= ( 1 << ERROR_LED );

erzählt mir im Programmtext als Leser schon wesentlich mehr als es ein
1
    PORTD |= ( 1 << PD5 );
jemals könnte. Und wenn ich da noch einen Kommentar dazu mache
1
    PORTD |= ( 1 << PD5 );    // Error LED einschalten
dann binn ich schon fast in der besseren Variante, hab aber den Vorteil, 
dass die nicht falsch sein kann ...
1
    PORTD |= ( 1 << PD6 );    // Error LED einschalten
weil an PD6 in Wirklichkeit gar nicht die Error LED sitzt, sondern die 
Busy LED :-)
1
#define LED_PORT  PORTD
2
#define ERROR_LED PD5
3
#define BUSY_LED  PD6
4
5
...
6
7
8
    LED_PORT |= ( 1 << ERROR_LED );
9
    LED_PORT &= ~( 1 << BUSY_LED );

Noch ein bischen Makro Magie darüber gestreut
1
#define LED_PORT  PORTD
2
#define ERROR_LED PD5
3
4
#define TURN_ON(b)   LED_PORT |= ( 1 << b )
5
#define TURN_OFF(b)  LED_PORT &= ~( 1 << b )
6
7
...
8
9
10
    TURN_ON( ERROR_LED );
11
    TURN_OFF( BUSY_LED );

und dann liest sich der Programmtext schon fast wie englische Prosa, 
obwohl nach all den Ersetzungen da auch nichts anderes steht als
1
    PORTD |= ( 1 << PD5 );
2
    PORTD &= ~( 1 << PD6 );

Aber die Information, die mir ein
1
    TURN_ON( ERROR_LED );
2
    TURN_OFF( BUSY_LED );
ganz automatisch beim Lesen vermittelt, ist eine ungleich höhere und 
höherwertige. Hier geht es nicht mehr um Ports und Bits. Hier geht es 
darum, was das Programm an dieser Stelle aus einer logischen Sicht der 
Dinge macht.

: Bearbeitet durch User
von Pete K. (pete77)


Lesenswert?

@Karl Heinz: Das könnte man glatt in einen Wiki-Artikel übernehmen 
(vielleicht kleiner Exkurs zum AVR-Tutorial?)

von Sascha712 (Gast)


Lesenswert?

Ja, ich find das ist gut erklärt. Dazu sollte es wirklich nen Wiki 
geben!

Werd ich so machen.

Nach dem Compilieren nimmt die schönere Version aber genausoviel Platz 
weg, wie die "meine"?

Danke übrigens für die guten Beispiel! :)

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.