Forum: Mikrocontroller und Digitale Elektronik [Tiny 13] LED on/off schaltung | Interrupt | Anfängerproblem


von µCBeginner (Gast)


Lesenswert?

Hallo Leute

Ich habe vor kurzem ein Buch über µC Gekauft und ein bisschen 
herumprobiert.
Ich will nun ein Programm schreiben, mitdem ich per Knopfdruck eine LED 
an bzw aus machen kann.

Habe dazu den ATtiny 13 benutzt.
An Port PB0 ist eine LED angeschlossen,
an Port PB4 ein Taster der bei Tastendruck auf GND liegt.

hier der Code:
1
#define F_CPU      1200000
2
3
#include <avr/interrupt.h>
4
#include <avr/io.h>
5
#include <util/delay.h>
6
7
8
int on;    // led on oder off?
9
10
ISR (INT0_vect) {
11
    if(on) { DDRB = 0b000000; on = 0;  }//ausschalten
12
  if(!on) { DDRB = 0b000001; on = 1;  }//einschalten
13
} 
14
15
int main(void)
16
{
17
    DDRB = 0b000001; //PortB0 als output
18
19
  PORTB = 0b000001; //Einschalten, testen
20
  _delay_ms(1000);
21
  PORTB = 0b000000;
22
  on = 0;
23
24
25
    DDRB &= ~_BV(PB4);  //Einganb bei Port PB4
26
  PORTB |= _BV(PB4);  //Pull-Up Aktivieren
27
  PCMSK |= (1<<4); //Ext Interrupt an PinB4
28
  GIMSK  |= (1<<INT0);  // Ext. Interrupt an INT0
29
    MCUCR |=(0<<ISC01) | (0<<ISC00);           // Interrupt wenn INT0 = GND
30
    sei();    //Interrupts Erlauben
31
32
33
34
35
  while (1)
36
  {  
37
38
  }
39
  return 0;
40
}

Das Licht geht am Anfang an und wieder aus, aber auf Tastendruck 
passiert nichts.
Was mach ich falsch?
Danke schonmal :)

von Jannis C. (kabelwurm)


Lesenswert?

Hallo,
dafür einen Interupt zu benutzen ist nicht sinvoll.
Man schreibt sowas eher so:
1
#define F_CPU      1200000
2
3
#include <avr/interrupt.h>
4
#include <avr/io.h>
5
#include <util/delay.h>
6
7
uint8_t on 0;//variable definieren und Wert zuweisen
8
9
int (main) void 
10
{
11
 while (1);
12
 DDRB = 0b1;// PortB0 als Ausgang Rest eingänge
13
 PORTB = 0b100;//Pullup an PortB4
14
 //testen
15
 PORTB = (1<<PORTB0); // Nur PORTB0 verändern
16
 _delay_ms(1000);
17
 PORTB &= ~(1<<PORTB0);
18
19
 if (!(PORTB(1<<PORTB4)
20
   {
21
    if (on == 0) {
22
      PORTB = (1<<PORTB0);
23
      on = 1;
24
   }
25
    if (on == 1) {
26
       PORTB &= ~(1<<PORTB0);
27
       on = 0;
28
   }
29
  }
30
  return 0;
31
 }
Gruß Jannis

von Sam .. (sam1994)


Lesenswert?

µCBeginner schrieb:
> Das Licht geht am Anfang an und wieder aus, aber auf Tastendruck
> passiert nichts.
Reagiert der INT nicht nur auf fallende oder steigende Flanke?
> Was mach ich falsch?
Den Taster kann man nicht einfach einmal per INT Interrupt "abfragen". 
Stichwort Tastenentprellung.

von Karl H. (kbuchegg)


Lesenswert?

Jannis sagte schon: Ein Interrupt ist dafür nicht besonders gut 
geeignet. Allerdings hat Jannis das Programm an anderen Stellen 
verbockt.

Daher von vorne. Gehen wir dein Programm durch

ISR (INT0_vect) {
    if(on) { DDRB = 0b000000; on = 0;  }//ausschalten
  if(!on) { DDRB = 0b000001; on = 1;  }//einschalten
}

(Im folgenden ist x immer der Name eines Ports. Also zb bei Port B wäre 
x daher B, und dein PORTx wäre dann PORTB, DDRx wäre dann DDRB, PINx 
wäre PINB. Für den Port C wäre das dann PORTC, PINC und DDRC etc. etc)

DDRx
Das DDRx Register steuert, welcher Pin Eingang ist und welcher Pin 
Ausgang. Typischerweise stellt man das am Programmanfang ein (man weiß 
ja welcher Pin was sein soll) und lässt diese Einstellung danach in 
Ruhe!

Wenn du ausgeben willst, dann wird an das PORTx Register ausgegeben!
Wenn du einlesen willst, dann kriegst du diese Information vom PINx 
Register.

Aber das DDRx Register lässt du in Ruhe!

WEnn du einen AUsgangspin auf 1 setzen willst, dann lautet die Phrase

   PORTx |= ( 1 << Pinnummer );

Wenn du denselben Ausgangspin auf 0 setzen willst, dann lautet die 
Phrase

   PORTx &= ~( 1 << Pinnummer );

Um einen Portpin abzufragen, lautet die Phrase

   if( PINx & ( 1 << Pinnummer ) ) {
     ... der Eingangspin ist auf 1
   }
   else {
     ... der Eingangspin ist auf 0
   }

oder

   if( !( PINx & ( 1 << Pinnummer ) ) {
     ... der Eingangspin ist auf 0
   }
   else {
     ... der Eingangspin ist auf 1
   }

der grundsätzliche Programmaufbau sieht (fürs erste) IMMER so aus
1
#include ....
2
3
int main()
4
{
5
6
   Einstellen aller beteiligten Komponenten
7
   das bedeutet: Pins auf Eingang oder Ausgang
8
                 Pullupwiderstände ein/aus
9
                 Timern ihre Grundkonfiguration
10
                 UART einstellen
11
                 ...
12
13
   eventuell ein sei(), wenn mit Interrupts gearbeitet wird
14
15
   while( 1 ) {
16
17
      hier kommt die eigentliche Arbeit, die der µC machen soll.
18
      Wie zb Pins abfragen und andere Pins auf 1 oder 0 stellen
19
20
   }
21
}

Du wirst die nächsten 30 Programme mit diesem Grundgerüst durchkommen.
Tasten macht man nicht über Interrupts. Denn du hast da ein 
Grundproblem: Tasten prelln. Dieses Prellen ist mechanisch bedingt und 
du kannst es nicht so einfach umgehen. Prellen bedeutet: Du drückst 
einmal auf den Taster; dein µC registriert aber 2 bis 4 Schaltvorgänge, 
weil Taster nun mal keine idealen mechanischen Bauteile sind.

von Probier (Gast)


Lesenswert?

doch lieber mal BASCOM , das hast Du schneller eine Erfolgserlebnis.

von Sam .. (sam1994)


Lesenswert?

Statt

if(on) { PORTB = 0b000000; on = 0;  }//ausschalten
  if(!on) { PORTB = 0b000001; on = 1;  }//einschalten

Kann man auch

on = PORTB = 1 - on;

schreiben.
Oder ohne beeinflussen der bits 2-8:

on = PORTB = on ^ 0b00000001;

Im übrigen sollte man on als volatile deklarieren, damit es im SRAM 
gespeichert wird. Sonst könnte es beim Interrupt zufällig nicht im 
adressierten Register liegen. Schließlich weiß der Compiler nicht, dass 
die ISR überhaupt aufgerufen wird.

Probier schrieb:
> doch lieber mal BASCOM , das hast Du schneller eine Erfolgserlebnis.

Probier das lieber nicht. Zwar hast du anfangs schnellere 
Erfolgerlebnisse, wenn du aber C dann mal beherscht, erleichtert es dir 
die Arbeit enorm.

von Karl H. (kbuchegg)


Lesenswert?

Samuel K. schrieb:

> Im übrigen sollte man on als volatile deklarieren, damit es im SRAM
> gespeichert wird. Sonst könnte es beim Interrupt zufällig nicht im
> adressierten Register liegen. Schließlich weiß der Compiler nicht, dass
> die ISR überhaupt aufgerufen wird.

Das alles trifft in seinem Fall nicht zu. Der Compiler ist sowieso 
verpflichtet am Ende einer Funktion (auch eine ISR ist letzten Endes nur 
eine Funktion) alle Variablen zurückzuschreiben.
Bei volatile geht es um was anderes.

von µCBeginner (Gast)


Lesenswert?

Danke für eure Hilfe!

Hab jetzt mein Programm etwas umgeschreiben:
1
#define F_CPU      1200000
2
3
#include <avr/interrupt.h>
4
#include <avr/io.h>
5
#include <util/delay.h>
6
7
8
int on;    // led on oder off?
9
/*
10
ISR (INT0_vect) {
11
    if(on) { DDRB = 0b000000; on = 0;  }//ausschalten
12
  if(!on) { DDRB = 0b000001; on = 1;  }//einschalten
13
} 
14
*/
15
int main(void)
16
{
17
    DDRB = 0b000001; //PortB0 als output
18
19
  PORTB = 0b000001; //Einschalten, testen
20
  _delay_ms(1000);
21
  PORTB = 0b000000;
22
  on = 0;
23
24
    DDRB &= ~_BV(PB4);  //Einganb bei Port PB4
25
    PORTB |= _BV(PB4);  //Pull-Up Aktivieren
26
27
  while (1)
28
  {  
29
      if( !(PINB & ( 1 << PINB4 )) ) //eingang auf gnd
30
      {
31
          if(on) //led ist an
32
    {
33
                   PORTB = 0b000000; //ausschalten
34
       on = 0;
35
    }
36
    else if (!on) //led ist aus
37
    {
38
       PORTB = 0b000001; //einschalten
39
       on = 1;
40
                }
41
            }
42
  }
43
  return 0;
44
}

So kann ich die LED am anfang zwar einschalten, aber bei einem zweiten 
Tastendruck geht sie nicht mehr aus. Warum?
Außerdem flackert die LED hin und wieder. Von was kommt das?
Um Tasten abzufragen benutzt man also keine Interrupts. Für was benutzt 
man sie dann?

Gruß

von A. F. (frankalicious)


Lesenswert?


von Jannis C. (kabelwurm)


Lesenswert?

Hallo,
wenn ich mich diemal im Text nicht wieder so katastrophal verschreibe 
ist es wahrscheinlich richtig. Also dein Programm krankt noch an dieser 
Stelle:
1
      if( !(PINB & ( 1 << PINB4 )) ) //eingang auf gnd
2
      {
3
           if(on)//led ist an
4
    {
5
                   PORTB = 0b000000; //ausschalten
6
       on = 0;
7
    }
8
    else if (!on) //led ist aus
9
    {
10
       PORTB = 0b000001; //einschalten
11
       on = 1;
12
                }
Meines Wissen nachs kann man das mit dem if(on) nicht einfach so 
schreiben.
Das Codefragment also überarbeitet:
1
      if( !(PINB & ( 1 << PINB4 )) ) //eingang auf gnd
2
      {
3
          if(on== 1){ //led ist an
4
             PORTB = 0b000000; //ausschalten
5
             on = 0;
6
             _delay_ms(100); //Entprellen
7
          }
8
          else{            
9
            PORTB = 0b000001; //einschalten
10
            on = 1;
11
            _delay_ms(100);//Entprellen
12
          }
13
       }
Die zweite If-Abfrage kann man sich sparen da es nur zwei Zustände gibt.
Das Entprellen ist notwendig, damit ein Tastendruck nicht als zwei oder 
noch mehr gezählt werden.
Gruß Jannis

von Sam .. (sam1994)


Lesenswert?

µCBeginner schrieb:
>   while (1)
>   {
>       if( !(PINB & ( 1 << PINB4 )) ) //eingang auf gnd
>       {
>           if(on) //led ist an
>           {
>               PORTB = 0b000000; //ausschalten
>               on = 0;
>           }
>           else if (!on) //led ist aus
>           {
>               PORTB = 0b000001; //einschalten
>               on = 1;
>           }
>       }
>   }

Wenn du deinen Code ein bisschen formatierst kann ihn jeder wunderbar 
lesen.

Für dein Codestück ist (abgesehen von der fehlenden Entprellung), dieser 
Operator ideal: ^ (XOR)
01 ^ 11 = 10
Mit XOR kann man bits umdrehen und geanu das brauchst du.
1
if( !(PINB & ( 1 << PINB4 )) ) //eingang auf gnd
2
{
3
    on = on ^ 0b00000001;
4
    PORTB = on;
5
    //oder kurz: PORTB = on ^= 0x01;
6
    delay_ms(100); //Entprellen wie Vorposter beschrieben hat,
7
                   //es gibt aber bessere Methoden
8
}

Das on kannst dir bei dieser Variante schenken. Wenn du es drin lässt 
verbesserst es sich die Performance um einen Takt, was aber 
vernachlässigbar ist.

von µCBeginner (Gast)


Lesenswert?

Hab jetzt den Code verändert:
1
#define F_CPU      1200000
2
3
#include <avr/interrupt.h>
4
#include <avr/io.h>
5
#include <util/delay.h>
6
7
8
int on;    // led on oder off?
9
10
int main(void)
11
{
12
    DDRB = 0b000001; //PortB0 als output
13
14
    PORTB = 0b000001; //Einschalten, testen
15
    _delay_ms(1000);
16
    PORTB = 0b000000;
17
    on = 0;
18
19
    DDRB &= ~_BV(PB4);  //Einganb bei Port PB4
20
    PORTB |= _BV(PB4);  //Pull-Up Aktivieren
21
    
22
    while (1)
23
    {  
24
        if( !(PINB & ( 1 << PINB4 )) ) //eingang auf gnd
25
        {
26
        
27
           on = on ^ 0b00000001;
28
           PORTB = on;
29
          //oder kurz: PORTB = on ^= 0x01;
30
          _delay_ms(100);//Entprellen wie Vorposter beschrieben hat,
31
                   //es gibt aber bessere Methoden
32
        }
33
    }
34
    return 0;
35
}

Wenn ich jetzt jedoch den taster drücke Blinkt die LED. Warum?

von Jannis C. (kabelwurm)


Lesenswert?

Hallo,
hast du mal die Entprellzeit verändert?
Gruß Jannis

von Sam .. (sam1994)


Lesenswert?

µCBeginner schrieb:
> Wenn ich jetzt jedoch den taster drücke Blinkt die LED. Warum?
dafür brauchst du einen Variable die speichert ob der Taster schon 
gedrückt war oder neu gedrückt ist.
1
//vor while
2
uint8_t state = 0;
3
4
//in while
5
if( !(PINB & ( 1 << PINB4 )) ) //eingang auf gnd
6
{
7
     if(!state)
8
     {
9
           on = on ^ 0b00000001;
10
           PORTB = on;
11
          //oder kurz: PORTB = on ^= 0x01;
12
          _delay_ms(100);//Entprellen wie Vorposter beschrieben hat,
13
          //es gibt aber bessere Methoden
14
          state = 1;
15
     }
16
}
17
else
18
    state = 0;

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.