/*

convert two simple buttons into a rotary encoder signal (2 bit gray code)

short button press advances single steps
long button press goes into auto repeat mode

ATtiny13 @ 1.2 MHz

*/

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

#define F_CPU 1200000UL

#include <util/delay.h>

#define KEY_PIN         PINB
#define KEY0            3
#define KEY1            4
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1)
#define REPEAT_MASK     (1<<KEY0 | 1<<KEY1)       // repeat: key1, key0
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20                        // every 200ms
 

int main(void) {

    // gray encoder outputs on PB0/1
    DDRB  = (1<<PB1) | (1<<PB0);
    // button inputs on PB3/4, internal pull up 
    PORTB = (1<<KEY0) | (1<<KEY1);

    // timer 0 init
    // mode 2, CTC, prescaler 64

    TCCR0A = (1<<WGM01);
    TCCR0B = (1<<CS01) | (1<<CS00);
    TIMSK0 |= (1<<OCIE0A);
    OCR0A = 188;

    sei();

    while (1) {
        // sleep and save energy
        set_sleep_mode(SLEEP_MODE_IDLE);
        sleep_mode();
    }

}

// timer isr @ 100Hz

ISR (TIM0_COMPA_vect) {
    static uint8_t key_state;                       // debounced and inverted key state:                                               // bit = 1: key pressed
    static uint8_t key_press;                       // key press detect 
    static uint8_t key_rpt;                         // key long press and repeat
    static uint8_t gray = 0xCC;                     // gray code table

    static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
    uint8_t i, key_mix;

    // debounce buttons

    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;
    }

    key_mix = key_press | key_rpt;
    // clear key presses
    key_press &= ~ALL_KEYS;
    key_rpt   &= ~ALL_KEYS;

    // gray encoder FSM

    if (key_mix & (1<<KEY0)) {
        // left step, rotate left
        if (gray & 0x80) {
            gray = (gray << 1) | 0x01;
        } else {
            gray <<= 1; 
        }
    } 

    if (key_mix & (1<<KEY1)) {
        // right step, rotate right
        if (gray & 0x01) {
            gray = (gray >> 1) | 0x80;
        } else {
            gray >>= 1; 
        }
    }

    // update outputs

    PORTB = ( PORTB & ~0x03 ) | (gray & 0x03);
}
