/*****************************************************
Project : EncoderDivider
Version : 1.0
Date    : 2008.08.27.
Author  : Laszlo Snider
Company : E-Studio
Comments: 

Chip type           : ATtiny2313
Clock frequency     : 20,000000 MHz
Memory model        : Tiny
External RAM size   : 0
Data Stack size     : 32

A program inkrementális enkóderek impulzus frekvenciáját 
osztja 1, 2, 4, és 8-ad arányban. Az osztási arány a D port
2-es és 3-as bitjein állítható kívülről.
A kimenet egy dekódolt step/dir impulzus, illetve szint.

Hibaszűrés:
    - legális állapot átmenetek
    - illegális átmenetre marad helyben
    - A,B csatornának legalább 4 óracikluson stabilnak kell lenni
    - (további javaslat?) 
*****************************************************/

#include <tiny2313.h>

#define MODE        (PIND >> 2) & 0x03  // Ostási arány beállítása
#define ENC         (PINB & 0x03)       // Az enkóder A és B csatornája
#define ESTEP       PORTD.4             // Kimenő step impulzus
#define EDIR        PORTD.5             // Kimenő dir jel

#define STEPTIME    5                   // A step impulzus szélessége
#define STEPBIT     1                   // step helye az állapottáblában
#define DIRBIT      2                   // dir helye az állapottáblában

void init(void);

register char flash *table @2;

//DIV1
flash char statearay_1[] = {
   0x00, 0x07, 0x19, 0x00, 0x01, 0x04, 0x04, 0x0f,
   0x13, 0x08, 0x08, 0x0d, 0x0c, 0x05, 0x0b, 0x0c,
   0x10, 0x17, 0x09, 0x10, 0x11, 0x14, 0x14, 0x1f,
   0x03, 0x18, 0x18, 0x1d, 0x1c, 0x15, 0x1b, 0x1c,
};

//DIV2
flash char statearay_2[] = {
   0x00, 0x07, 0x18, 0x00, 0x01, 0x04, 0x04, 0x0c,
   0x10, 0x08, 0x08, 0x0d, 0x0c, 0x04, 0x0b, 0x0c,
   0x10, 0x17, 0x08, 0x10, 0x11, 0x14, 0x14, 0x1c,
   0x00, 0x18, 0x18, 0x1d, 0x1c, 0x14, 0x1b, 0x1c,
};

//DIV4
flash char statearay_4[] = {
   0x00, 0x07, 0x18, 0x00, 0x01, 0x04, 0x04, 0x0c,
   0x10, 0x08, 0x08, 0x0c, 0x0c, 0x04, 0x08, 0x0c,
   0x10, 0x17, 0x08, 0x10, 0x11, 0x14, 0x14, 0x1c,
   0x00, 0x18, 0x18, 0x1c, 0x1c, 0x14, 0x18, 0x1c,
};

//DIV8
flash char statearay_8[] = {
   0x00, 0x07, 0x18, 0x00, 0x01, 0x04, 0x04, 0x0c,
   0x10, 0x08, 0x08, 0x0c, 0x0c, 0x04, 0x08, 0x0c,
   0x10, 0x14, 0x08, 0x10, 0x10, 0x14, 0x14, 0x1c,
   0x00, 0x18, 0x18, 0x1c, 0x1c, 0x14, 0x18, 0x1c,
};

register char enc   @4;     // Beolvasott A,B csatorna
register char enc1  @5;     //      prell szűréshez
register char prev  @6;     // A,B csatorna előző állapota
register char idx   @7;     // Állapottábla indexe
register char val   @8;     // Állapotváltozó értéke
register char stepcnt @9;   // Számláló a step szélességhez

#asm
.equ    PINB        = 0x16
.equ    PORTD       = 0x12
.equ    ESTEP       = 4
.equ    EDIR        = 5
.equ    STEPBIT     = 0
.equ    DIRBIT      = 1
.equ    STEPTIME    = 4
#endasm

void setup(void)
{
    // Osztásnal megfelelő állapottábla kiválasztása
    switch(MODE)
    {
    case 0:
        table = statearay_1;
        break; 
    case 1:
        table = statearay_2;
        break; 
    case 2:
        table = statearay_4;
        break; 
    case 3:
        table = statearay_8;
        break; 
    }
}


// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
    // Ha változik a dipswitch
    setup();
}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
    // Ha változik a dipswitch
    setup();
}

void main(void)
{
    init();
    setup();
    
    enc     = ENC;
    enc1    = enc;
    prev    = enc;
    idx     = 0;
    val     = 0;
    stepcnt = 0;

    #asm
        sei
    loop:
        tst     _stepcnt
        breq    nostep
        dec     _stepcnt
        brne    nostep
        cbi     PORTD,ESTEP
    nostep:
        in      _enc,PINB       ;enc = ENC
        in      _enc1,PINB
        cp      _enc,_enc1
        brne    loop            ;Ha nem stabil az AB bemenet 
        ldi     r16,0x03
        and     _enc,r16
        cp      _enc,_prev      ;if (enc == prev)
        breq    loop
        mov     _prev,_enc      ;prev = enc
        mov     r16,_idx
        add     r16,_enc
        clr     r17
        movw    r30,r2
        add     r30,r16
        adc     r31,r17
        lpm     _val,Z          ;val = table[idx + enc]
        mov     _idx,_val
        ldi     r16,0xfc
        and     _idx,r16        ;idx = val & 0xfc;
        sbrs    _val,STEPBIT    ;if (val & STEPBIT)
        rjmp    loop
        sbrs    _val,DIRBIT     ;if (val & DIRBIT)
        cbi     PORTD,EDIR               
        sbrc    _val,DIRBIT     ;if (val & DIRBIT)               
        sbi     PORTD,EDIR               
        sbi     PORTD,ESTEP
        ldi     r16,STEPTIME
        mov     _stepcnt,r16
        rjmp    loop               
    #endasm             
}

void init(void)
{
    // Crystal Oscillator division factor: 1
    #pragma optsize-
    CLKPR=0x80;
    CLKPR=0x00;

    // Input/Output Ports initialization
    // Port A initialization
    // Func2=In Func1=In Func0=In 
    // State2=T State1=T State0=T 
    PORTA=0x00;
    DDRA=0x00;

    // Port B initialization
    // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
    // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
    PORTB=0x00;
    DDRB=0x00;

    // Port D initialization
    // Func6=In Func5=Out Func4=Out Func3=In Func2=In Func1=In Func0=In 
    // State6=T State5=0 State4=0 State3=P State2=P State1=T State0=T 
    PORTD=0x0C;
    DDRD=0x30;

    // Timer/Counter 0 initialization
    // Clock source: System Clock
    // Clock value: Timer 0 Stopped
    // Mode: Normal top=FFh
    // OC0A output: Disconnected
    // OC0B output: Disconnected
    TCCR0A=0x00;
    TCCR0B=0x00;
    TCNT0=0x00;
    OCR0A=0x00;
    OCR0B=0x00;

    // Timer/Counter 1 initialization
    // Clock source: System Clock
    // Clock value: Timer 1 Stopped
    // Mode: Normal top=FFFFh
    // OC1A output: Discon.
    // OC1B output: Discon.
    // Noise Canceler: Off
    // Input Capture on Falling Edge
    // Timer 1 Overflow Interrupt: Off
    // Input Capture Interrupt: Off
    // Compare A Match Interrupt: Off
    // Compare B Match Interrupt: Off
    TCCR1A=0x00;
    TCCR1B=0x00;
    TCNT1H=0x00;
    TCNT1L=0x00;
    ICR1H=0x00;
    ICR1L=0x00;
    OCR1AH=0x00;
    OCR1AL=0x00;
    OCR1BH=0x00;
    OCR1BL=0x00;

    // External Interrupt(s) initialization
    // INT0: On
    // INT0 Mode: Any change
    // INT1: On
    // INT1 Mode: Any change
    // Interrupt on any change on pins PCINT0-7: Off
    GIMSK=0xC0;
    MCUCR=0x05;
    EIFR=0xC0;

    // Timer(s)/Counter(s) Interrupt(s) initialization
    TIMSK=0x00;

    // Universal Serial Interface initialization
    // Mode: Disabled
    // Clock source: Register & Counter=no clk.
    // USI Counter Overflow Interrupt: Off
    USICR=0x00;

    // Analog Comparator initialization
    // Analog Comparator: Off
    // Analog Comparator Input Capture by Timer/Counter 1: Off
    ACSR=0x80;
}
