Forum: Mikrocontroller und Digitale Elektronik [PIC] Problem mit Interrupt


von Andreas R. (blackpuma)


Angehängte Dateien:

Lesenswert?

Guten Morgen!

Ich habe hier ein Problem auf einem PIC mit dem Interrupt on Change am 
PortB. Ein Drehimpulsgeber ist wie am Bild angeschlossen.

Wenn ich am Drehimpulsgeber drehe funktioniert mein Programm. Wenn ich 
aber auf den Drehimpulsgeber drücke dann Schaltet er zwischen 10 und 100 
Hz um. Nur das er zusätzlich noch die frequenz (frequ) erhöht. Ich finde 
aber nicht heraus warum.
1
#pragma interruptlow high_isr
2
void high_isr( void )
3
{
4
  char hdt[] = "100";
5
  char zen[] = " 10";
6
7
  // Am Drehimpulsgeber wurde gedreht.
8
  if( INTCONbits.RBIF && PORTBbits.RB5 )    // Interrupt on Change ist aufgetreten
9
  {
10
    if( PORTBbits.RB4 == PORTBbits.RB3 )
11
    {
12
      frequ += add_frequ;
13
    }
14
    else
15
    {
16
      frequ -= add_frequ;
17
    }
18
    set_new_frequ();
19
  }
20
  // Der Drehimpulsgeber wurde gedrückt
21
  else if( INTCONbits.RBIF && !PORTBbits.RB5 )    // Interrupt on Change ist aufgetreten
22
  {
23
    if( stat == 0x00 )
24
      stat = 0x01;
25
    else
26
      stat = 0x00;
27
    
28
    if( add_frequ == 10 )
29
    {
30
      add_frequ = 100;
31
      SetDDRamAddr( 0x4C );
32
      while( BusyXLCD() );
33
      putsXLCD( hdt );
34
      while( BusyXLCD() );    
35
    }
36
    else if( add_frequ == 100 )
37
    {
38
      add_frequ = 10;  
39
      SetDDRamAddr( 0x4C );
40
      while( BusyXLCD() );
41
      putsXLCD( zen );
42
      while( BusyXLCD() );    
43
    }
44
45
    while( !PORTBbits.RB5 ); // Warten bis der Impulsgeber wieder los gelassen wird.
46
  }
47
48
  INTCONbits.RBIF = 0;  // Interruptflag löschen
49
  return;
50
}

Ich hoffe ihr seht da was. Wenn ich mehr vom Code braucht sagt es mir.

BG
Andreas

von Peter D. (pdiener) Benutzerseite


Lesenswert?

1
while( !PORTBbits.RB5 ); // Warten bis der Impulsgeber wieder los gelassen wird.
Das hat zwar mit dem eigentlichen Problem nichts zu tun, aber es ist 
generell keine so gute Idee, im Interrupt auf Benutzereingaben zu 
warten...
1
 // Am Drehimpulsgeber wurde gedreht.
2
  if( INTCONbits.RBIF && PORTBbits.RB5 )    // Interrupt on Change ist aufgetreten
Das verstehe ich auch nicht ganz. RB5 ist doch der Taster. Was hat das 
mit dem Drehen zu tun?


Grüße,

Peter

von morph1 (Gast)


Lesenswert?

er lässt es nur zu, dass die frequenz verändert wird wenn der taster 
nicht gedrückt ist :)

aber für die leserlichkeit:

#define TASTER PORTBbits.RB5

dann musst du nur mehr TASTER schreiben, geht klarerweise auch für 
anderes :)

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Nein, ich denke es soll so sein:
1
 // Am Drehimpulsgeber wurde gedreht.
2
  if( INTCONbits.RBIF && PORTBbits.RB4 )    // Interrupt on Change ist aufgetreten

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Der Fehler passiert beim Loslassen vom Taster. Das erzeugt auch einen 
Pinchange-Interrupt.
if( INTCONbits.RBIF && PORTBbits.RB5 )
ist dann einmal erfüllt und
if( PORTBbits.RB4 == PORTBbits.RB3 )
ist auch erfüllt, die Frequenz wird also um einen Schritt erhöht beim 
Loslassen des Tasters.

von Andreas R. (blackpuma)


Lesenswert?

Peter Diener schrieb:
> Der Fehler passiert beim Loslassen vom Taster. Das erzeugt auch einen
> Pinchange-Interrupt.
> if( INTCONbits.RBIF && PORTBbits.RB5 )
> ist dann einmal erfüllt und
> if( PORTBbits.RB4 == PORTBbits.RB3 )
> ist auch erfüllt, die Frequenz wird also um einen Schritt erhöht beim
> Loslassen des Tasters.

Das Interruptflag wird aber erst am Ende der Interruptroutine wieder 
gelöscht. Kann dann überhaupt noch ein Interrupt ausgelöst werden?

Ich habe jetzt mal versucht beim Einstieg in die Routine den Interrupt 
on Change auszuschalten und erst am ende der Routine wieder 
einzuschalten. Problem bleibt aber bestehen. :-(

von Peter D. (pdiener) Benutzerseite


Lesenswert?

>Kann dann überhaupt noch ein Interrupt ausgelöst werden?

Ja,  normalerweise wird ein PinchangeInterrupt bei beiden Flanken 
ausgelöst.

Es ist schon klar, dass er nicht nochmal kommt durch Drücken während der 
Interrupt noch läuft. Aber durch Loslassen triggert er nochmal.

von Andreas R. (blackpuma)


Lesenswert?

Peter Diener schrieb:
>>Kann dann überhaupt noch ein Interrupt ausgelöst werden?
>
> Ja,  normalerweise wird ein PinchangeInterrupt bei beiden Flanken
> ausgelöst.
>
> Es ist schon klar, dass er nicht nochmal kommt durch Drücken während der
> Interrupt noch läuft. Aber durch Loslassen triggert er nochmal.

Aber wenn ich beim Einstieg in die Routine den Interrupt on Change 
ausschalte und ich dann die Taste los lasse kann er nicht nochmal einen 
Auslösen oder? Ist ja deaktiviert! Und bei Sprut habe ich noch gelesen 
das GIE beim einstieg in die Interruptroutine auf 0 gesetzt wird. Also 
sind die Interrupts während eines Interrupts überhaupt aus.

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Ich würde es mal so versuchen:
1
// Am Drehimpulsgeber wurde gedreht.
2
if( INTCONbits.RBIF && ((!PORTBbits.RB4) || (!PORTBbits.RB3)) )    // Interrupt on Change ist aufgetreten

von Andreas R. (blackpuma)


Lesenswert?

Kein Erfolg.

von Stefan Kunz (Gast)


Lesenswert?

Der Interrupt wird nochmal ausgelößt, da die Interrupt um einiges 
schneller ausgeführt wird als der Mensch ihn wieder loslassen kann.

von Stefan Kunz (Gast)


Lesenswert?

wenn du wirklich darauf warten willst, dass die Taste losgelassen wird 
in der Interruptroutine dann solltest du

 while( !PORTBbits.RB5 ); // Warten bis der Impulsgeber wieder los 
gelassen wird.

in

 while( PORTBbits.RB5 ); // Warten bis der Impulsgeber wieder los 
gelassen wird.

ändern, da dein Taster den Pin sicher gegen GND zieht und wenn er 
losgelassen wurde gegen VCC.

Davon ab ist dies aber eine DEFENETIV schlechte lösung. Verfolge lieber 
die Anzahl der Interrupts und ihre Gründe, so das du den Interrupt 
unterscheiden kannst.

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Nein,
while( !PORTBbits.RB5 );
ist schon richtig um auf das Loslassen zu warten.

Der Punkt ist ein ganz anderer. Der Taster prellt.
Und das eventuell so schnell, dass er einen Interrupt auslöst und in 
diesem dann an verschiedenen Stellen verschiedene Werte liefert.

Das Konzept ist wie gesagt eh nicht gut mit dem Warten im Interrupt.

Ich würde einen Timerinterrupt aufsetzen, dort regelmäßig alle Kontakte 
prüfen und entprellen und dann damit weiterarbeiten.

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Hier gibt es ein paar gute Anregungen:
http://www.mikrocontroller.net/articles/Drehgeber

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.