/*********************************************************************
Модуль программной поддержки контактного квадратурного энкодера
типа РЕС16 и аналогичных (без использования прерываний)

Copyright 2008 © ARV Research.

Внимание! Вы можете использовать данный модуль по своему усмотрению
в любых проектах. При любой публикации ваших проектов, использующих
этот модуль, следует указать ссылку на сайт http://arv.radioliga.com

Требуется компилятор WinAVR
**********************************************************************/
#include <avr/io.h>
#include <util/delay.h> 
#include "encoder.h"

signed char enc_rotate(unsigned char *speed){
/* возвращает направление и скорость вращения энкодера
 параметр speed - указатель на переменную, в которую возвращается 
 условная скорость вращения (число от 0 до 100)

 возвращает:
     -1 - вращение влево (против часовой стрелки)
      0 - нет вращения
      1 - вращение вправо (по часовой стрелке)

 ВНИМАНИЕ! функция выполняется достаточно длительное время: 
 максимально (при остановленном энкодере) ENC_MAX_SPEED миллисекунд, 
 при вращении возвращает управление раньше
*/      
    char stage = 0;

    for(*speed = ENC_MAX_SPEED; (*speed) > 0; (*speed)--){
        switch(stage){
        case 0:
                if(EN_A_DN || ENC_B_DN) stage++;
                break;
        case 1:
                if(ENC_A_UP || ENC_B_UP) break;
                _delay_us(100);
                if(ENC_A_DN && ENC_B_DN) stage++;
                break;
        case 2:
                if(ENC_A_UP) return 1;
                else if(ENC_B_UP) return -1;
        }
        _delay_ms(1);
    }
    return 0;
}

signed char enc_delta(void){
/* возвращает приращение энкодера: 
    1 или -1 при медленном вращении,
    0 - при остановленном
    ENC_BIG_STEP или -ENC_BIG_STEP при быстром вращении

 ВНИМАНИЕ!!! Функция выполняется долго!!!
 при быстром вращении возвращает управление только ПОСЛЕ ОСТАНОВКИ энкодера!
 остановкой считается, если в течение 2*ENC_MAX_SPEED миллисекунд не было импульсов
 с энкодера.
*/
    signed char d;
    unsigned char w;

    d = enc_rotate(&w);
    if(rotate_fast(w)){
        if((d == enc_rotate(&w)) && rotate_fast(w)){
            d *= ~ENC_BIG_STEP;
            while(enc_rotate(&w)) _delay_ms(1);
        }
    }
    return d;
}

void enc_init(void){
// инициализирует порты, к которым подключен энкодер
    DDR(ENC_A_PORT)  &= ~(1 << ENC_A_PIN);
    DDR(ENC_B_PORT)  &= ~(1 << ENC_B_PIN);
    PORT(ENC_A_PORT) |= 1 << ENC_A_PIN;
    PORT(ENC_B_PORT) |= 1 << ENC_B_PIN;
}
