/*  BSD-License:

Copyright (c) 2010 by Matthias Bunte, Germany

All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
  * Neither the names of the authors the name nicai-systems nor
    the names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

/*! @file    feelers.c
 *  @brief   Key debouncing at the "feelers"
 *  @author  Matthias Bunte (m_bunte@arcor.de)
 *  @date    2010-03-07
 */

/*
Tastenerkennung

Anforderungen:

- Tastenentprellung in ca. 50 ms
- Nutzt beelib
- Geringe Systemlast

Konzept:

- Nutze SystemTimer-Funktionen
- Entprellintervall fest 7 ms, 7 mal = 49 ms oder 8 mal = 56 ms
- Tastenstatus kann "Aenderung", "Alt" oder "Geaendert" sein
- Lese Port
- Pruefe, ob Tastenstatus "Aenderung" ist
- Nein -> es ist nichts weiter zu tun
- Ja -> Aenderung aufnehmen
- Wenn sich etwas geaendert hat, soll ein Event ausgeloest werden
- Registrierbare CallBack-Funktionen wären eine sehr flexible
  Lösung für diese Events

Lösung:

- Invertiere den Eingang, da es sich um negative Logik handelt
- Prüfe, ob der Zustand sich geändert hat (PortVal != alter PortVal)
- Prüfe, ob gerade keine Entprellung im Gang ist

- Falls beide Prüfungen erfolgreich waren, Entprellung aktivieren. Dazu
Vorbereitungen treffen und Funktion mit SystemTimer in 7 ms aktivieren

- Die Vorbereitungen sind:
  o Betroffene(n) Taster merken (Entprellungstimer im Array hoch oder runterzaehlen)
//  o Ausgangszustand ebenfalls merken!!!
//  o Den neuen Zustand speichern
  o Entprellung als Aktiv markieren
  o Entprellfunktion mit SystemTimer in 7 ms aktivieren

- Die Entprellfunktion
  o Entprellungstimer im Array hoch oder runterzaehlen
  o Prüft, ob alle Einträge im Array einem stabilen Zustand entsprechen (0 oder 7 bzw. 8)
  o Falls dies der Fall ist, 
    1. zugehörigen Funktionspointer der Eventfunktionen aller geänderten Einträge aufrufen
    (alternativ: Callback-Funktion zur Auswertung aufrufen)
    2. den neuen Portzustand speichern
    3. Entprellung als abgeschlossen markieren
  o Anderenfalls: diese Funktion in 7 ms wieder ausfuehren

*/

#include <stdlib.h>
#include <stdint.h>
#include "iodefs.h"
#include "../../abc/scheduler.h"     // time table scheduler
#include "feelers.h"

// 
#define FEELERS_KEY_COUNT           (4)
#define FEELERS_FIRST_KEY_BIT       (4)
#define FEELERS_KEY_TO_BIT(KEY_VAL) (KEY_VAL + FEELERS_FIRST_KEY_BIT)

// Grenzen fuer den Entprellzaehler:
// Eine Taste gilt als gedrueckt, wenn der Wert 4 erreicht ist und
// als geloest, wenn der Wert 0 erreicht ist
#define FEELERS_GEDRUECKT_ENTPRELLT     (4)
#define FEELERS_GELOEST_ENTPRELLT       (0)

// Wartezeit in ms nach Initialisierung des Feelers-Moduls
#define FEELERS_ENTPRELLEN_START        (25)             // 100 ms
// Periodendauer in ms zwischen zwei Entprell-Abtastungen
#define FEELERS_ENTPRELL_PERIODE        (4)              // 12 ms 

#ifdef __cplusplus
extern "C" {
#endif

// Initialisierungszustand
static uint8_t  Feelers_InitState = 0;

// Aktueller Zustand der Tasten
// Wegen negativer Logik mit der invertierten Maske vorladen,
//   bei positiver Logik waere hier eine "0" vorzusehen
static uint8_t  Feelers_KeysState   = (uint8_t)(~IO_FEELERS_MASK);

// Tasten, deren Zustand sich zuletzt geaendert hat
static uint8_t  Feelers_KeysChanged = 0;

// Funktion, die bei Tastenaenderung aufgerufen werden soll
static pFeelers_KeysChanged_Fkt_t pFeelers_KeysChanged_Fkt = NULL;


/*!
 * Initialisierung der Feelers-Taster.
 * Die Pullups werden durch Aufruf dieser Funktion aktiviert.
 */
void Feelers_Init(pFeelers_KeysChanged_Fkt_t pFeelers_NewKeysChanged_Fkt)
{
   // Konfiguration der Portpins fuer die Feelers-Taster als Inputs
   deactivate_output_group(IO_FEELERS);
   // Pull-Ups an diesen Portpins durch Schreiben von 1 in die Ports aktivieren
   set_output_group(IO_FEELERS);
    
   pFeelers_KeysChanged_Fkt = pFeelers_NewKeysChanged_Fkt;
      
   // Die Entprellfunktion in das Timersystem einstellen
   Insert_TT_Entry( FEELERS_ENTPRELLEN_START, Feelers_Entprellung, 0);
   
   // Initialisierung ist nun abgeschlossen
   Feelers_InitState = 1;
}

// Initialisierungszustand auslesen
uint8_t Feelers_Initialized(void)
{
  return Feelers_InitState;
}


/*!
 * Feelers-Entprellung
 *
 * 
 */
void Feelers_Entprellung( unsigned char dummy)
{
   static uint8_t  EntprellungAktiv = 0;
//  static uint8_t  Last_PortVal     = 0xff;
   static int8_t   EntprellWerte[FEELERS_KEY_COUNT] = { 0 };
   uint8_t PortVal;
//  uint8_t Tasten_PortPin;
   uint8_t TastenNr;

   // In FEELERS_ENTPRELL_PERIODE soll diese Funktion wieder ablaufen
   Insert_TT_Entry( FEELERS_ENTPRELL_PERIODE, Feelers_Entprellung, 0);   // li time Table scheduler
   // Lese Wert am Port ein, invertierte Logik
   PortVal = ~get_input_group( IO_FEELERS );
   // Pruefe, ob sich der HW-Tastenzustand geaendert hat
   // und ob gerade keine Entprellung aktiv ist
   if ( (PortVal != Feelers_KeysState) || CHECK(EntprellungAktiv) )
   {
      // Annahme: Die Entprellung wird in diesem Durchlauf fertig
      CLR(EntprellungAktiv);
      // Fuer alle aktiven Taster durchlaufen
      for (TastenNr = 0; TastenNr < FEELERS_KEY_COUNT; TastenNr++)
      {
         // Unterscheide, ob die Taste gedrueckt oder losgelassen wurde
         // Bei gedrueckter Taste pruefe, ob der Entprellwert noch unterhalb des
         // "Gedrueckt"-Werts liegt. Inkrementiere den Entprellwert in diesem Fall.
         // Anderenfalls pruefe, ob der gespeicherte Tastenzustand "Geloest" war.
         // Setze dann die Bits fuer 
         if ( GET_BIT(PortVal, FEELERS_KEY_TO_BIT(TastenNr)) )
         {
            // Taste gedrueckt
            // Pruefe, ob der Entprellwert noch unterhalb des "Gedrueckt"-Werts liegt.
            // In diesem Fall ist die Entprellung doch noch aktiv, inkrementiere den
            // Entprellwert.
            if (EntprellWerte[TastenNr] < FEELERS_GEDRUECKT_ENTPRELLT)
            {
               SET(EntprellungAktiv);
               EntprellWerte[TastenNr]++;
            }
            // Entprellung ist zuletzt Aktiv gewesen, dieser Entprellwert ist
            // am Anschlag. Wenn er sich gegenueber dem gespeicherten Wert geaendert
            // hat, ist jetzt der Zeitpunkt, den neuen Wert zu uebernehmen und
            // diesen Wert als "geaendert" zu markieren.
            else if ( !GET_BIT(Feelers_KeysState, FEELERS_KEY_TO_BIT(TastenNr)) )
            {
               // Bit in Variable fuer letzten Tastenzustand setzen
               // Bit in Variable fuer Aenderung des Tastenzustands setzen
               SET_BIT( Feelers_KeysState, FEELERS_KEY_TO_BIT( TastenNr ));
               SET_BIT( Feelers_KeysChanged, FEELERS_KEY_TO_BIT( TastenNr ));
            }
         }
         else
         {
            // Taste losgelassen
            // Pruefe, ob der Entprellwert noch oberhalb des "Geloest"-Werts liegt.
            // In diesem Fall ist die Entprellung doch noch aktiv, dekrementiere den
            // Entprellwert.
            if (EntprellWerte[TastenNr] > FEELERS_GELOEST_ENTPRELLT)
            {
               SET(EntprellungAktiv);
               EntprellWerte[TastenNr]--;
            }
            // Entprellung ist zuletzt Aktiv gewesen, dieser Entprellwert ist
            // am Anschlag. Wenn er sich gegenueber dem gespeicherten Wert geaendert
            // hat, ist jetzt der Zeitpunkt, den neuen Wert zu uebernehmen und
            // diesen Wert als "geaendert" zu markieren.
            else if ( GET_BIT(Feelers_KeysState, FEELERS_KEY_TO_BIT(TastenNr)) )
            {
             // Bit in Variable fuer letzten Tastenzustand loeschen
             // Bit in Variable fuer Aenderung des Tastenzustands setzen
             CLR_BIT(Feelers_KeysState, FEELERS_KEY_TO_BIT(TastenNr));
             SET_BIT(Feelers_KeysChanged, FEELERS_KEY_TO_BIT(TastenNr));
            }
         }
      }
      // Hat sich ein Wert geaendert? Wenn ja und eine Tastenauswertefunktion
      // angegeben wurde, diese Aufrufen.
      if (Feelers_KeysChanged)
      {        
         if ( pFeelers_KeysChanged_Fkt )
         {
            pFeelers_KeysChanged_Fkt( &Feelers_KeysChanged, Feelers_KeysState );
         }
      }
   }
  // Ende :-)
  return;
}


/*!
 * Liest den aktuellen Tastenzustand
 */
uint8_t Feelers_Read_KeysState(void)
{
  return Feelers_KeysState;
}


/*!
 * Liest, welche Tasten geaendert sind.
 *
 * Die Bits dieser Variable werden von dem Feelers-Paket nur gesetzt, das
 * zuruecksetzen muss in der Applikation durch die "Write"-Funktion erfolgen.
 */
uint8_t Feelers_Read_KeysChanged(void)
{
   return Feelers_KeysChanged;
}


/*!
 * Schreibt die aktualisierten Tastenaenderungen zurueck.
 *
 * Hier duerfen die mit der "Read_KeysChanged"-Funktion ausgelesenen Bits nur
 * zurueckgesetzt werden, wenn die Auswertung der Tastenaenderung abgeschlossen
 * ist.
 */
void Feelers_Write_KeysChanged(uint8_t KeysChanged)
{
   Feelers_KeysChanged = KeysChanged;
}


#ifdef __cplusplus
} // extern "C"
#endif



