/*

Beispielprogramm zur Tastenentprellung und -dekodierung.
Beim æP 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). Wird die 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

*/

#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
static 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 temp, aenderung;
static TASTENWERT temp_tasten, letzte_aenderung;
static char rep_flag=0, entprell_ctr=0, rep_ctr=0;

  temp = PINA;			// PortA als aktiv-low Eingang
  temp = ~temp;			// alle Bits invertieren
  if(rep_ctr) rep_ctr--;        // repeat-counter dekrementieren
  entprell_ctr++;               // Entprell-counter inkrementieren
  if(temp != temp_tasten) {     // Aenderung der Bits ?
    temp_tasten=temp;		// 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
  }
				// Auswertung der Tasten
  temp = aenderung;
  n=0;
  do {				// bit suchen
    if(temp & 1) {		// bit gefunden
      if(temp == 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; // repeat nur mit gueltigem Code
	else rep_flag = 0;	// kein repeat, wenn keine gueltige taste
      }
      return;			// taste erkannt, dekodierung fertig
    }
    n++;
    temp >>= 1;
  } while(n < MAX_BITS);
  return;			// keine taste gefunden
}


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 zun„chst 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;
    }
  }
}