Forum: Mikrocontroller und Digitale Elektronik Problem bei Komfortroutine fürs Entrellen von Peter Dannegger


von Christoph H. (webturtle)


Lesenswert?

Hallo Forum.

Da mir hier so gut geholfen wurde habe ich ja inzwischen hinbekommen 
diverse Dimmer und Funksteckdosen mit dem RFM12 zu schalten.
Nun wollte ich dafür eine anständige Schaltbox bauen.

Dazu kommt ein Mega48 zum Einsatz.
Das RFM12 hängt an PC0-PC3 und 8 Schalter hängen am PORTB.

Mit dem Debounce Makro von Peter funktioniert die Sache auch 
einwandfrei. Ich kann alle 8 Taster stabil abfragen und entsprechende 
Werte senden.

Nun würde ich gerne diese Komfortroutine von Peter Dannegger verwenden: 
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29

Einfach weil die Option bei langem Tastendruck was anderes zu machen für 
sehr toll nutzbar wäre.

Wenn ich den Code in mein Projekt integriere habe ich was merkwürdiges:
Sobald ich die Funktion get_key_short oder get_key_long in der Mainloop 
abfrage schein es einen Reset zu geben.

Hier mal mein Code der in der entwicklung ist und daher noch nicht ganz 
aufgeräumt.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <stdlib.h>
#include <stdio.h>
#include "global.h"
#include "rf12.h"
#include "../EigeneHeader/funkcodes.h" //Da steht drin was das RFM12 
senden soll

#define F_CPU 8000000UL
#include <util/delay.h>
#define BAUD 9600UL      // Baudrate
#define UART_FIFO_LENGTH 9U
// Berechnungen
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = 
kein Fehler.
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
  #error Systematischer Fehler der Baudrate grˆsser 1% und damit zu 
hoch!
#endif

// debounce macro das funktionier
#define debounce( port, pin )            \
({                  \
  static uint8_t flag = 0;  /* new variable on every macro usage */  \
  uint8_t i = 0;              \
                  \
  if( flag ){      /* check for key release: */    \
    for(;;){      /* loop ... */        \
      if( !(port & 1<<pin) ){  /* ... until key pressed or ... */  \
  i = 0;      /* 0 = bounce */      \
  break;                \
      }                  \
      _delay_us( 98 );    /* * 256 = 25ms war 98 */     \
      if( --i == 0 ){    /* ... until key >25ms released */  \
  flag = 0;    /* clear press flag */      \
  i = 0;      /* 0 = key release debounced */    \
  break;                \
      }                  \
    }                  \
  }else{      /* else check for key press: */    \
    for(;;){      /* loop ... */        \
      if( (port & 1<<pin) ){  /* ... until key released or ... */  \
  i = 0;      /* 0 = bounce */      \
  break;                \
      }                  \
      _delay_us( 98 );    /* * 256 = 25ms war 98*/      \
      if( --i == 0 ){    /* ... until key >25ms pressed */  \
  flag = 1;    /* set press flag */      \
  i = 1;      /* 1 = key press debounced */    \
  break;                \
      }                  \
    }                  \
  }                  \
  i;        /* return value of Macro */    \
})

//Komfortroutine
#define KEY_DDR         DDRB
#define KEY_PORT        PORTB
#define KEY_PIN         PINB
#define KEY1            0
#define KEY2            1
#define KEY3            2
#define KEY4            3
#define KEY5            4
#define KEY6            5
#define KEY7            6
#define KEY8            7

#define ALL_KEYS        (1<<KEY1 | 1<<KEY2| 1<<KEY3| 1<<KEY4| 1<<KEY5| 
1<<KEY6| 1<<KEY7| 1<<KEY8)

#define REPEAT_MASK     (1<<KEY1)       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20                        // every 200ms

volatile uint8_t key_state;                                // debounced 
and inverted key state:
                                                  // bit = 1: key 
pressed
volatile uint8_t key_press;                                // key press 
detect

volatile uint8_t key_rpt;                                  // key long 
press and repeat

void uart_init()
{
    UCSR0B |= (1<<TXEN0);                 // UART TX einschalten
    UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
    UBRR0H = UBRR_VAL >> 8;
    UBRR0L = UBRR_VAL & 0xFF;
    UCSR0B |= (1<<RXEN0)|(1<<RXCIE0);
}

int uart_putc(unsigned char c)
{
    while (!(UCSR0A & (1<<UDRE0)))  // warten bis Senden moeglich
    {
    }
    UDR0 = c;                      // sende Zeichen
    return 0;
}

void uart_puts (char *s)
{
    while (*s)
    {   // so lange *s != '\0' also ungleich dem "String-Endezeichen"
        uart_putc(*s);
        s++;
    }
}

ISR( TIMER0_OVF_vect )                            // every 10ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;

  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload 
for 10ms

  i = key_state ^ ~KEY_PIN;                       // key changed ?
  ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll 
over ?
  key_state ^= i;                                 // then toggle 
debounced state
  key_press |= key_state & i;                     // 0->1: key press 
detect

  if( (key_state & REPEAT_MASK) == 0 )            // check repeat 
function
     rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
  }
}

///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed. Each pressed key is reported
// only once
//
uint8_t get_key_press( uint8_t key_mask )
{
  cli();                                          // read and clear 
atomic !
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}

///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed long enough such that the
// key repeat functionality kicks in. After a small setup delay
// the key is reported beeing pressed in subsequent calls
// to this function. This simulates the user repeatedly
// pressing and releasing the key.
//
uint8_t get_key_rpt( uint8_t key_mask )
{
  cli();                                          // read and clear 
atomic !
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}

///////////////////////////////////////////////////////////////////
//
uint8_t get_key_short( uint8_t key_mask )
{
  cli();                                          // read key state and 
key press atomic !
  return get_key_press( ~key_state & key_mask );
}

///////////////////////////////////////////////////////////////////
//
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}

int main(void)
{
  char s[32];
  uart_init();
  uart_puts("Hallo. Ich teste das senden:\n\r");
  DDRD |= (1<<PD7);
  PORTD |= (1<<PD7);
  for (unsigned char i=0; i<10; i++)
    _delay_ms(10);      // wait until POR done
  rf12_ports();
  sprintf( s, "Vor Init: 0x%04X\n\r",rf12_trans(0x0000));
  uart_puts(s);
  rf12_init();          // ein paar Register setzen (z.B. CLK auf 10MHz)
//  sprintf( s, "Nach Init: 0x%04X\n\r",rf12_trans(0x0000));
//  uart_puts(s);
  rf12_setfreq(RF12FREQ(433.92));  // Sende/Empfangsfrequenz auf 
433,92MHz einstellen
//  sprintf( s, "Nach SetFreq: 0x%04X\n\r",rf12_trans(0x0000));
//  uart_puts(s);
  rf12_setbandwidth(4, 1, 4);    // 200kHz Bandbreite, -6dB Verst‰rkung, 
DRSSI threshold: -79dBm
//  sprintf( s, "Nach Bandwidth: 0x%04X\n\r",rf12_trans(0x0000));
//  uart_puts(s);
  rf12_setbaud(19200);      // 19200 baud
//  sprintf( s, "Nach Baud: 0x%04X\n\r",rf12_trans(0x0000));
//  uart_puts(s);
  rf12_setpower(0, 6);      // 1mW Ausgangangsleistung, 120kHz 
Frequenzshift
//  sprintf( s, "Nach Power: 0x%04X\n\r",rf12_trans(0x0000));
//  uart_puts(s);
  rf12_txdata(S_ROT_H);
  sprintf( s, "Nach TX: 0x%04X\n\r",rf12_trans(0x0000));
  uart_puts(s);

  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors

    TCCR0B = (1<<CS02)|(1<<CS00);      // divide by 1024
    TIMSK0 |= 1<<TOIE0;        // enable timer interrupt

  while (1)
  {
    if ((debounce (KEY_PIN, KEY2)))
    {
      uart_puts("Key2");
        rf12_txdata(S_TV_2);
        sprintf( s, "Nach Power: 0x%04X\n\r",rf12_trans(0x0000));
  uart_puts(s);

        _delay_ms(100);
    }
// dieses Statement macht Probleme
   if (get_key_long(1<<KEY1))
    {
      uart_puts("Key1");
      rf12_txdata(S_ROT_H);
       _delay_ms(100);
    }
// Ende Problemstatement
  }
}

Wenn das Statement welches Probleme bereitet nicht auskommentiert ist, 
sehe ich auf dem Terminal (der Uart dient nur zum debuggen) den Text 
"Hallo. Ich teste das senden durchlaufen". Bei Druck von Taste 2 
schreibt er "Key2Hallo. Ich teste das senden".
Bei Druck von Taste 1 passiert gar nichts.
Uch wenn ich weniger Tastern oder gar nur eine definiere, dieses 
Verhalten kommt, sobald ich get_key_long oder get_key_short aufrufe.
Wo kann ich ansetzen?

von ... (Gast)


Lesenswert?

Auch wenn es dir bei dem jetzigen Problem nicht hilft, hast du schonmal 
an eine einfache Hardwareentprellung gedacht? Ein RC Glied pro Taster 
ist doch schnell angelötet und kostet auch nicht die Welt.
Der Platzbedarf ist zumindest bei SMD-Bauteilen vernachlässigbar.

von Christoph H. (webturtle)


Lesenswert?

Ja das hab ich mir überlegt, und könnte es auch unterbringen.
Allerdings hätte ich dann ja immer noch keine Lösung für das auswerten 
eines langen Tastendrucks um da einen anderen Befehl auszuführen.

Und dann kann ich ja auch genauso gut beim debounce Macro bleiben, da 
dies ja prima funktioniert.

Und der Code wird ja von vielen eingesetzt daher muss es ja irgendwei 
auch bei mir gehen. Es ist halt für mich extrem schwer zu debuggen, da 
ich nicht weiss warum er wieder an den Anfang springt. Das hatte ich 
noch nie.

von Falko (Gast)


Lesenswert?

Hallo Christoph,
kann es sein das noch irgendein anderer Interrupt auftritt, für den du 
keine ISR geschrieben hast? So ein Problem hatte ich mal.....
Gruß
Falko

von Christoph H. (webturtle)


Lesenswert?

Hmmm.
Kann natürlich schon sein, aber wie könnte ich das herausfinden?
Ausserdem dachte ich, ein C Compiler würde einem da die Arbeit abnehmen.
Soll ich einfach mal in allen Interruptvektoren eine UART Ausgabe 
machen?

Wäre auf jeden Fall ein Ansatz.

von Christoph H. (webturtle)


Lesenswert?

Noch was für die Profis:
Da fällt mir ein, dass ich ja den Interrupt Sachen an den Mega48 
anpassen musste.
Könnte da jemand draufsehen ob ich es richtig gemacht habe?

Könnte es an den 8Mhz liegen?

Chris

von Christoph H. (webturtle)


Lesenswert?

Ähhh noch was:

Als du mich da jetzt drauf hingewiesen hast habe ich meinen Code nochmal 
durchforstet.

Mir viel auf, dass ich vor der Endlosschleife gar nicht mit sei() die 
Interrupts aktiviert habe.
Das heisst beim aufruf einer der beiden Funktionen war noch nie ein 
Interrupt aktiv.

Kann dies das verhalten auslösen?

Oh mann, jetzt wär ich gern zuhause um das zu testen



Chris

von Klaus W. (mfgkw)


Lesenswert?

Christoph Hoell schrieb:
> Hmmm.
> Kann natürlich schon sein, aber wie könnte ich das herausfinden?

In einem "Catch-all interrupt vector" ein freies Beinchen setzen
bzw. eine LD leuchten lassen?

http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

von Christoph H. (webturtle)


Lesenswert?

Danke.
Das werde ich auf jeden Fall testen. Und das oben geschriebene...

von Christoph H. (webturtle)


Lesenswert?

Ich denke ich habe es gefunden ohne es jetzt hier auf der Arbeit testen 
zu können.

Ausgehend davon, dass es ein nicht behandelter Interrupt sein könnte 
habe ich das Programm noch mal ausgiebig gelesen.

Vor allem die Sache mit meinem vergessenen sei().
Eigentlich heisst dies ja nur, dass ich die Interrupts global erst 
aktiviere wenn ich eine der beiden genannten Funktionen aufrufe. Das 
wäre dann auch auch der Zusammenhang warum diese Funktionen Fehler 
machen.

Ich bin mir fast sicher wenn ich sei() vor die Main Loop schreibe 
funktioniert es auch nicht wenn ich die Funktionen wieder kommentiere.

Das wäre dann zumindest logisch.

Also habe ich geschaut wo ich überhaupt noch einen weiteren Interrupt im 
Programm habe.
Und da kam ich auf die UART Schnittstelle die ich ja nur zu debuggen 
verwende.
Und da ich immer auf meine vorhanden Codezeilen zurückgreife habe 
natürlich in uart_init() den Empfang via Interruppt aktiviert. Und im 
ganzen Programm finde ich den UART Reveive Handler nicht.

Ich bin mir ziemlich sicher, dass wenn ich
UCSR0B |= (1<<RXEN0)|(1<<RXCIE0);
herauskommentiere das Programm laufen wird.
Freue mich auf den Test.

Chris

von Christoph H. (webturtle)


Lesenswert?

Es gab in mir einfach keine Ruhe.
Ich hab kurz zuhause den Rechner einschalten lassen und bin mit der 
Fernwartung drauf.

Es war in der Tat der UART Receive Interrupt.

Danke für die tolle Hilfe.
Ich hab das mit dem Catch All Interrupt mal in die Checkliste gepostet:
http://www.mikrocontroller.net/articles/AVR_Checkliste#Alle_Interruptvektoren_definiert.3F

Danke Chris

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.