Forum: Mikrocontroller und Digitale Elektronik (narren-)fehler in code - hilfe?


von narr (Gast)


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
}

von Peter (Gast)


Lesenswert?

klassicher Fehler, doku nicht gelesen.

Suche mal im Tutorial nach volatil.

mfg
Peter

von narr (Gast)


Lesenswert?

danke, jetzt habe ich es gelesen - die deklaration der beiden 
boolvariablen als "volatile bool" ändert aber leider aucht nichts.. (?)

von narr (Gast)


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?

von Stefan B. (stefan) Benutzerseite


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
}

von narr (Gast)


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

von Stefan B. (stefan) Benutzerseite


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.

von narr (Gast)


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...

von MaWin (Gast)


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
}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Am einfachsten?

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.
1
ISR(PCINT0_vect)
2
{
3
  if ( (PINB & (1<<PB0)) ) // PVINT0 = PB0 beim Attiny13
4
  {
5
    onoff1 = ~onoff1;
6
    onoff2 = ~onoff2;
7
  }
8
}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

MaWin schrieb:

> Warum immer so viel Code ?

Weil der TO mit PCINT0 experimentieren will?

von Stefan B. (stefan) Benutzerseite


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

von Stefan B. (stefan) Benutzerseite


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.

von narr (Gast)


Lesenswert?

danke soweit, ich mache mich mal dran und schaue was passiert ;)

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.