(narren-)fehler in code - hilfe?

Gast #1890396
Lesenswert?

hallo zusammen, ich suche den fehler in diesem code, den ich (möglichst 
einfach) zum lernen mit Interrupts umzugehen geschrieben habe.
Es sollte passieren:
Blinken der led in ihrer 1. frequenz bis taster gedrückt wird, dann in 
der 2., bis er erneut gedrückt wird (wieder die erste) usw.

es wird immer der erste teil der schleife ausgeführt, dh die led blinkt 
immer in der selben frequenz.
wo ist der fehler?
1
/*=================================================================================== 
2

3
#include <stdbool.h>
4
#include <stdlib.h>
5
#include <stdint.h>
6

7
#include <avr/io.h>
8
#include <avr/interrupt.h>  
9

10
#define F_CPU 9.6E6        
11
#include <util/delay.h>    
12

13

14
bool onoff1;
15
bool onoff2;
16

17

18
ISR(PCINT0_vect)
19
{
20
  if (onoff1 == 1)
21
  {
22
    onoff2 = 1;
23
    onoff1 = 0;
24
  }
25
  else  
26
  {
27
    onoff2 = 0;
28
    onoff1 = 1;
29
  }
30

31
}
32

33

34
int main(void) 
35
{        
36

37
    DDRB  = 0b00000010;   
38
    PORTB = 0b00000000;
39

40
    GIMSK   |=  (1<<PCIE);         
41
   PCMSK   |=  (1<<PCINT0);     
42
   sei();        
43
  
44
  onoff1 = 1;
45
  onoff2 = 0;       
46
                          
47
   while (1)
48
  {
49
    if (onoff1)
50
    {
51
      for (int i=0; i<10; i++)
52
       {
53
        PORTB = 0b00000000;
54
        _delay_ms(50);
55
        PORTB = 0b00000010;
56
        _delay_ms(50);
57
      }
58
    }
59
    else if (onoff2)
60
    {  
61
      for (int i=0; i<10; i++)  
62
      {
63
        PORTB = 0b00000000;
64
        _delay_ms(200);
65
        PORTB = 0b00000010;
66
        _delay_ms(200);
67
      }
68
    }
69
    else 
70
    PORTB = 0b00000000;
71

72
  }
73
                                              
74
  return 0;                        
75
}
Gast #1890752
Lesenswert?

irgendwie bekomme ich es nicht hin, eine globale bool variable über die 
isr funktion zu verändern (siehe code) - auch wenn sie jetzt volatile 
deklariert ist. was ich auch mache im code, die variable bleibt die 
selbe.
kann mir da irgendjemand einen ansatz geben?
Persönliche Seite #1890934
Lesenswert?

Wie löst du den PCINT0 aus?

Wenn mit einem Taster, dann denke daran, dass Taster prellen, d.h. du 
bekommst nicht voraussagbar viele PCINT0s pro Tastendruck.

In diesem Fall würde ich mir zunächst einen prellfreien Digitaltaster 
aufbauen, um die PCINT Funktionalität zu erforschen.

In der Praxis macht man i.d.R. keine Tastenauswertung mit einem externen 
Interrupt, sondern nutzt ein Pollingverfahren per Software oder Timer 
(s. Artikel entprellung). Die Ausnahme: Wecken des µC aus einem 
Sleepmodus.
1
// Target: Attiny13 @ 9.6 MHz
2
// Debugstand: Simulation
3
#include <stdbool.h>
4
#include <stdlib.h>
5
#include <stdint.h>
6

7
#include <avr/io.h>
8
#include <avr/interrupt.h>  
9

10
#define F_CPU 9.6E6        
11
#include <util/delay.h>    
12

13
volatile bool onoff1 = 1; // <===
14
volatile bool onoff2 = 0; // <===
15

16
ISR(PCINT0_vect)
17
{
18
  if (onoff1)
19
  {
20
    onoff1 = 0;
21
    onoff2 = 1;
22
  }
23
  else  
24
  {
25
    onoff1 = 1;
26
    onoff2 = 0;
27
  }
28
}
29

30
void blinken(uint8_t num)
31
{ 
32
  uint8_t i, j;
33
  for (i=0; i<20; i++)
34
  {
35
    for (j=0; j<num; j+=50)
36
      _delay_ms(50);
37
    PORTB ^= (1<<PB1);
38
  }
39
}
40

41
int main(void) 
42
{        
43
  DDRB = (1<<PB1);   // PB1 Ausgabe
44

45
  GIMSK |=  (1<<PCIE);         
46
  PCMSK |=  (1<<PCINT0);     
47
  sei();        
48
  
49
  while (1)
50
  {
51
    if (onoff1)
52
      blinken(50);
53
    else if (onoff2)
54
      blinken(200);
55
    else // !onoff1 && !onoff2 sollte nie erreicht werden!
56
      PORTB &= ~(1<<PB1); // PB1 Ausgabe LOW
57
  }
58
}
Gast #1890959
Lesenswert?

hallo stefan
ja, den PCINT0 löse ich per taster aus, den ich hardwareentprellt habe 
mit einem 33kohm widerstand und 0,33µF.
wie gut das funktioniert habe ich allerdings noch nicht mit dem oszi 
angeschaut.

Um genau den sleepmodus geht es mir - ich möchte den µC über den selben 
taster ein bzw ausschalten (dh in bzw aus dem sleepmode holen) - und 
wollte das über eine boolvariable machen, die ich bei jeder 
tasteraktivierung ändere - und damit in der main steuern, ob der 
controller sleepen soll oder seine funktion ausführen.

wie würde man das denn (sonst) umsetzen?

grüße
Persönliche Seite #1890967
Lesenswert?

Mit dem PCINT habe ich das noch nicht gemacht.

Mit dem INT0 Interrupt ist ein Beispiel im Wiki
http://www.mikrocontroller.net/articles/Sleep_Mode#Aufwachen_per_Tastendruck

und ich habe auf dem Pollin Board mit INT0 bei meinem Suppentimer 
experimentiert
http://www.mikrocontroller.net/articles/Pollin_Funk-AVR-Evaluationsboard#Suppentimer

Das Prellen spielt dort keine Rolle, weil nur das einfache Wecken per 
Interrupt implementiert wird. Dann ist es egal, ob die Wecktaste prellt. 
Wach ist wach. Das Schlafenlegen kann dann mit einer regulären 
Tasteneingabe oder mit einem Timeout (Suppentimer) gemacht werden.

Allerdings ist das Wecken per Interrupt auch empfindlich gegen 
Spikes/Störungen auf der Leitung am Interruptpin. Man kann das 
berücksichtigen indem man nach dem Wecken zusätzlich den Tastenzustand 
pollt und wenn die Mindestgedrücktdauer nicht erfüllt ist direkt wieder 
pennen geht.
Gast #1890970
Lesenswert?

ich wurde eben darauf aufmerksam gemacht, dass ich ja beim drücken des 
tasters zwei pinchanges ausführe und damit meine boolvariablen danach 
wieder genauso aussehen wie vorher.. logisch.
wie kann ich das verhindern? ich erinnere mich dunkel daran, dass man 
irgendwo die flanke für den interrupt auswählen kann(?) oder anders?
ich bin auf dem gebiet noch seeeehr grün hinter den ohren...
Gast #1890976
Lesenswert?

Warum immer so viel Code ?
1
bool pressed=0,down=0,slow=0;
2
 
3
void main(void)
4
{
5
  DDRB = (1<<PB1);   // PB1 Ausgabe
6
  while(1)
7
  {
8
    pressed=(PINB&1)!=0; // angenommen Taster schaltet auf HI, sonst ==0
9
    if(pressed) slow^=!down;
10
    down=pressed;
11
    _delay_ms(slow?200:50);
12
    PORTB^=(1<<PB1);
13
  }
14
}
Persönliche Seite #1890988
Lesenswert?

> Du kannst den PCINT0 Pin abfragen, ob z.B. ein HIGH Pegel anliegt, d.h.
> ein Wechsel von LOW nach HIGH der Auslöser war. Und nur dann die Aktion
> machen.

Nachteil bzw. Beschränkung der Einfachmethode: Der Tastendruck muss 
länger dauern als die Aufrufzeit der ISR, damit der Pegel innerhalb der 
ISR noch ansteht und gemessen werden kann. Die Methode ist daher nix bei 
sehr schnellen Signalen z.B. von Sensoren für eine Drehzahlmessung
Persönliche Seite #1890992
Lesenswert?

War grad im Datenblatt des Attiny13 unterwegs. Dort sieht es so aus, 
dass man beim PCINT nicht einstellen kann, auf welche Flanke reagiert 
wird.

Das ist aber beim INT0 Interrupt möglich.

Wenn der Sleepmodus dazu kommt gilt weiter, dass nur bestimmte externe 
Interrupts den µC aufwecken können und auch nur bestimmte Ereignisse wie 
z.B. LOW Pegel am externen Interruptpin. Näheres im Datenblatt.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren