/*
Beispielprogramm zur Tastenentprellung und -dekodierung.
Beim uP AT90S8515 werden die Eingaenge von PortA (int. pullup) gegen 0V
geschaltet (aktiv-low). Beim 1. Tastendruck wird der normale Code 'A' - 'H'
erzeugt (bit0 -> bit7). Ist in 'tast_tab[]' ein ' ' als Tastencode vorhanden,
wird die Aenderung nicht dekodiert. So lassen sich Schalter entprellen.
Wird eine Taste gedrueckt gehalten, wird der betreffende repeat Code erzeugt,
sofern dieser nicht mit ' ' deaktiviert ist.
In 'main()' wird auf einen Tastendruck gewartet und eine an PortC geschaltete
LED gegen 0V aktiviert. Weitere Tastendruecke (oder repeat) togglen die LED.

Es wird jeweils nur eine Taste ausgewertet; diese kann aber gedrueckt bleiben,
ohne andere Tasten zu blockieren: n-key-rollover. Schalter und Taster koennen
daher gemischt verwendet werden. Der aktuelle, entprellte Zustand von PortA
findet sich in 'entprellte_tasten'.

Compiler: icca90

Alle Angaben ohne Gewaehr.

Michael Nowak, www.mino-elektronik.de, Dezember 2002

*/

#include <io8515.h>
#include <ina90.h>

#define MAX_BITS        8               // hier nur ein Port-Byte
#define TASTENWERT      unsigned char   // auch short oder long

#define T0_NACHLADEWERT -39     // fuer ca. 100 Hz bei 4 MHz
#define ENTPRELL_ZEIT   5       // 5 Zyklen -> 50ms entprellen
#define FIRST_REPEAT    50      // 0,5 Sek. Pause bei gedrueckter Taste
#define NEXT_REPEAT     10      // danach Wiederholung alle 0,1 Sek.

static char taste;              // 0 oder Tastencode
TASTENWERT entprellte_tasten;   // zur externen Verwendung

flash char     tast_tab[] = "ABCDEFGH"; // 1. Tastencode
flash char rep_tast_tab[] = "a c e g "; // repeatcode teilweise zulassen

char lese_taste()
{
char temp;
  TIMSK &= 0xfd;                // T0_overflow-interrupt kurz verbieten
  temp = taste;                 // Tastencode lesen
  taste=0;                      // und loeschen
  TIMSK |= 0x02;                // T0-int wieder freigeben
  return(temp);
}

static void dekodiere_taste()
{
char n;
TASTENWERT tasten_schalter, aenderung;
static TASTENWERT temp_tasten, letzte_aenderung;
static char rep_flag=0, entprell_ctr=0, rep_ctr=0;

// in 'tasten_schalter' muss das Eingangsbitmuster stehen hier nur PortA

  tasten_schalter = PINA;                  // PortA als aktiv-low Eingang
  tasten_schalter = ~tasten_schalter;      // alle Bits invertieren

  if(rep_ctr) rep_ctr--;                   // repeat-counter dekrementieren
  entprell_ctr++;                          // Entprell-counter inkrementieren
  if(tasten_schalter != temp_tasten)
  {					   // Aenderung der Bits ?
    temp_tasten = tasten_schalter;         // ja, speichern
    entprell_ctr=0;                        // und neu entprellen
    return;                                // mehr nicht
  }

  if(entprell_ctr < ENTPRELL_ZEIT) return; // Entprellzeit abwarten
  entprell_ctr=0;       	           // und neu starten
                                	   // Tasten sind entprellt
                                	   // jetzt auf positive Aenderungen testen
  aenderung = (temp_tasten ^ entprellte_tasten) & temp_tasten;
  entprellte_tasten = temp_tasten;         // und letzten Zustand merken

  if(aenderung) 
  { 			                   // falls neue Tasten aktiviert
    letzte_aenderung = aenderung;          // Aenderung merken
    rep_ctr = FIRST_REPEAT;                // und 1.Repeat aktivieren
    rep_flag = 0;
  }
  else if(rep_ctr == 0 && rep_flag) 
  {					   // keine neue Taste aber repeat aktiv
    aenderung = entprellte_tasten & letzte_aenderung; // letzte Aenderung
    rep_ctr = NEXT_REPEAT;                 // wiederholen, mit kurzer repeat-Zeit
  }

  n=0; 					   // Auswertung der Tasten
  do 
  {                          		   // bit suchen
    if(aenderung & 1) 
    { 			        	   // bit gefunden
      if(aenderung == 1) 
      {     				   // und nur eine Taste
        if(!rep_flag) 
        {			           // kein repeat: normale Codes verwenden
          n = tast_tab[n];
          rep_flag = 1;    		   // aber beim naechsten Mal repeat !
        } else
          n = rep_tast_tab[n];  	   // repeat ist schon aktiv
        if(n != ' ') taste = n; 	   // nur gueltigen Code speichern
        else rep_flag = 0;     		   // kein repeat, wenn keine gueltige taste
      }
      return;                   	   // taste erkannt, dekodierung fertig
    }
    n++;
    aenderung >>= 1;
  } while(n < MAX_BITS);
}


void interrupt [TIMER0_OVF0_vect] timer0_int()
{
  TIMSK &= 0xfd;                // weitere T0_overflow-interrupt verbieten
  _SEI();                       // und Interrupts global zulassen
  TCNT0 = T0_NACHLADEWERT;
  dekodiere_taste();
  TIMSK |= 0x02;                // T0-int wieder freigeben
}


void main()
{
char temp;
  TCNT0 = T0_NACHLADEWERT;
  TCCR0 = 0x05;         // timer0 mit /1024 Vorteiler
  TIMSK |= 0x02;        // timer0 overflow int
  PORTA = 0xff;         // PortA pullups einschalten
  _SEI();

  DDRC  = 0xff;         // alle LEDs zunaechst eingeschaltet
  PORTC = 0;            // aktiv-low
  for(;;) 
  {
    while(!(temp = lese_taste()));      // auf Tastendruck warten
    switch(temp) 
    {
      case 'A':
      case 'a':
          DDRC ^= 1;    // nur LED invertieren
          break;
      case 'B':
      case 'b':
          DDRC ^= 2;
          break;
      case 'C':
      case 'c':
          DDRC ^= 4;
          break;
      case 'D':
      case 'd':
          DDRC ^= 8;
          break;
      case 'E':
      case 'e':
          DDRC ^= 16;
          break;
      case 'F':
      case 'f':
          DDRC ^= 32;
          break;
      case 'G':
      case 'g':
          DDRC ^= 64;
          break;
      case 'H':
      case 'h':
          DDRC ^= 128;
          break;
    }		// switch
  }		// for
}		// main