/** @file   encoder.c
 *  @date   2014-12-04
 *  @author Frank Klee
 *  @brief  Drehencoder Library
 *
 *          Basisroutinen zum Abfragen eines Drehencoders mittels Polling.
 *          Quelle: https://www.mikrocontroller.net/articles/Drehgeber
 *
 *  @brief  Formatierungen und Umbenennungen der Funktionen
 *  @author Tom, DL7BJ
 *  @date   2023-03-23
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include "encoder.h"

int8_t DrehgeberPosition;           ///< akt. Drehencoderposition
int8_t DrehgeberMax = 127;          ///< maximaler Wert des Drehgebers
int8_t DrehgeberMin = -127;         ///< minimaler Wert des Drehgebers

volatile int16_t iButtonPressedCounter = 0;         ///< Zähler für kurzen Tastendruck
volatile int16_t iButtonDebounceCycles = 0;         ///< Zähler für Entprellung
volatile int16_t iButtonPressedLongCycles = 0;      ///< Zähler für langen Tastendruck

volatile int8_t enc_delta;                          ///< Delta des Encoders bei Betätigung
static int8_t last;                                 ///< letzter Wert des Encoders

typedef enum EButtonState
{
    ButtonState_Unpressed,      ///< Taster nicht gedrückt
    ButtonState_Pressed,        ///< Taster gedrückt
    ButtonState_Hold,           ///< Taster gehalten
    ButtonState_Released        ///< Taster geöffnet
}tEButtonState;

volatile tEButtonState buttonState = ButtonState_Unpressed;
volatile tEButtonPressedState buttonPressed = ButtonPressed_Unpressed;

/** @fn     void EncoderInit(void)
 *  @brief  Initialisierung des Drehencoders und der Startwerte
 *  @param  none
 *  @retval none
 */
void EncoderInit(void)
{
    int8_t new;
    new = 0;
    if(PHASE_A)
        new = 3;
    if(PHASE_B)
        new ^= 1;
    last = new;
    enc_delta = 0;
    iButtonDebounceCycles = BUTTON_DEBOUNCETIME_MS;
    iButtonPressedLongCycles = BUTTON_PRESSEDLONG_MS;
}
/** @fn     void EncoderPolling(void)
 *  @brief  EncoderPolling
 *
 *          Abfrage des Drehencoders und des Tasters
 *          Wird vom Timer 0 aufgerufen
 *
 *  @param  none
 *  @retval none
*/
void EncoderPolling(void)
{
    int8_t new, diff;
    new = 0;
    if(PHASE_A) new = 3;
    if(PHASE_B) new ^= 1;
    diff = last - new;
    if(diff & 1) {
        last = new;
        enc_delta += (diff & 2) - 1;
    }
    switch(buttonState)
    {
        case ButtonState_Unpressed:
            if(BUTTONPRESSED) buttonState = ButtonState_Pressed;
        break;
        case ButtonState_Pressed:
            buttonState = ButtonState_Hold;
        break;
        case ButtonState_Hold:
            iButtonPressedCounter++;
            if(iButtonPressedCounter >= iButtonDebounceCycles && (! BUTTONPRESSED))
            {
                buttonState = ButtonState_Released;
                if(buttonPressed != ButtonPressed_Long) 
                    buttonPressed = ButtonPressed_Short;
            }
            if(iButtonPressedCounter >= iButtonPressedLongCycles)
            {
                buttonState = ButtonState_Released;
                buttonPressed = ButtonPressed_Long;
            }
        break;
        case ButtonState_Released:
            iButtonPressedCounter = 0;
            if(!BUTTONPRESSED)
                buttonState = ButtonState_Unpressed;
        break;
    }
}
#if defined (SingleStep)
int8_t EncodeRead(void)
{
    int8_t val;
    cli();
    val = enc_delta;
    enc_delta = 0;
    sei();
    return val;
}
#elif defined (TwoStep)
int8_t EncodeRead(void)
{
    int8_t val;
    cli();
    val = enc_delta;
    enc_delta = val & 1;
    sei();
    return val >> 1;
}
#elif defined (FourStep)
int8_t EncodeRead(void)
{
    int8_t val;
    cli();
    val = enc_delta;
    enc_delta = val & 3;
    sei();
    return val >> 2;
}
#endif
/** @fn     int8_t EncoderRead(char Ueberlauf)
 *  @brief  Liest die akt. Position des Drehencoders
 *
 *  @param  Ueberlauf größer +127, -127
 *  @retval Aktuelle Position des Drehencoders
 */
int8_t EncoderRead(char Ueberlauf)
{
    DrehgeberPosition += EncodeRead();
    if(DrehgeberPosition > DrehgeberMax)
    {
        if(Ueberlauf)
            DrehgeberPosition = DrehgeberPosition - DrehgeberMax + DrehgeberMin - 1;
        else
            DrehgeberPosition = DrehgeberMax;
    }
    if(DrehgeberPosition < DrehgeberMin)
    {
        if(Ueberlauf)
            DrehgeberPosition = DrehgeberPosition + DrehgeberMax - DrehgeberMin + 1;
        else
            DrehgeberPosition = DrehgeberMin;
    }
    return(DrehgeberPosition);
}
/** @fn     void EncoderWrite(int8_t EncoderPos)
 *  @brief  Setzt die aktuelle Position des Drehencoders
 *
 *  @param  EncoderPos - die Position des Drehencoders
 *  @retval none
 */
void EncoderWrite(int8_t EncoderPos)
{
    DrehgeberPosition = EncoderPos;
}
/** @fn     void EncoderMinMax(int8_t EncoderMin, int8_t EncoderMax)
 *  @brief  Setzt den minimalen und maximalen Bereich des Drehencoders
 *  
 *  @param  EncoderMin minimaler Wert des Drehencoders
 *  @param  EncoderMax maximaler Wert des Drehencoders
 *  @retval none
 */  
void EncoderMinMax(int8_t EncoderMin, int8_t EncoderMax)
{
    DrehgeberMin = EncoderMin;
    DrehgeberMax = EncoderMax;
    if(DrehgeberPosition > DrehgeberMax) DrehgeberPosition = DrehgeberMax;
    if(DrehgeberPosition < DrehgeberMin) DrehgeberPosition = DrehgeberMin;
}
/** @fn     tEButtonPressedState EncoderGetButtonState(void)
 *  @brief  Liefert den Status des Tasters vom Drehencoder
 *  
 *  @param  none
 *  @retval tEButtonPressedState 
 */  
tEButtonPressedState EncoderGetButtonState(void)
{
    tEButtonPressedState retVal = buttonPressed;
    buttonPressed = ButtonPressed_Unpressed;
    return retVal;
}