/*
Diplomova prace : Palubni pocitac

car_meas.c

Atmega128A 10 MHz XTAL

Autor: Ondrej Lutera

2011/2012

*/

#include "car_meas.h"

/* makra pro zjednoduseni zapisu portu */

#define _PORT(C) PORT ## C
#define PORT(C) _PORT(C)
#define _PIN(C) PIN ## C
#define PIN(C) _PIN(C)
#define _DDR(C) DDR ## C
#define DDR(C)  _DDR(C)

/* promenne pro vycteni citacu a praci s prerusenimi */
volatile uint32_t injTicks=0;
volatile uint32_t odoTicks = 0;
volatile uint32_t rpmTicks = 0;

volatile uint16_t  injTicksOvf=0;
volatile uint16_t  odoTicksOvf=0;
volatile uint16_t  rpmTicksOvf=0;
volatile uint16_t newMeas=0;

/* kalibracni data, spolecna tj jedna struktura v eeprom a jedna v ram*/
clb_t calibData;
clb_t ee_calibData EEMEM;
/* lokalni data , do techto dat se zaznamenavaji hodnoty v pripade ze uzivatel neni jeste nalogovan a motor bezi
   potrebujeme cas, vstrik, rpm a odo.
   pokud se ovsem automobil rozjede, zacneme s ukladanim dat do uzivatele "Duch" - a zneprijemnime Duchovi jidzu pipanim
*/
inj_t tmpInj_imp=0;
tm_t  tmpTime=0;
odo_t tmpImp=0;
rpm_t tmpRpm=0;

/* promenne potrebne pro snimani stavu nadrze */
volatile uint16_t TankV[TANKV_MAX];      /* merime nadrz 10bitove ovsem dva spodni bity zaokrouhlujeme */
volatile uint8_t TankSampleMax=0;
volatile uint8_t TankVindex =0;
volatile uint8_t running = 0;

/* zaznam napajeciho napeti */
uint16_t V12=0;

#ifdef MEAS_DEBUG
uint16_t debugInjTicks=0;
uint16_t debugRpmTicks=0;
uint8_t  debugOdoTicks=0;

inline void setDebugInjTicks(uint16_t ticks)
{
    debugInjTicks = ticks;
}
inline void setDebugRpmTicks(uint16_t ticks)
{
    debugRpmTicks = ticks;
}
inline void setDebugOdoTicks(uint8_t ticks)
{
    debugOdoTicks = ticks;
}

#endif

/* ------------------- preruseni od citacu a externich zdroju ---------------------------- */

TANK_INT_ISR()
{
    // static uint8_t isrCnt=0;

    uint16_t meas;
    uint8_t cnt = 0;
    /* tato sekce provadi mereni */
    StartAD(TANK_CH);

    uint16_t bck = TankV[TankVindex];
    TankV[TankVindex]=0;

    ReadAD(TANK_CH);

    do
    {
        StartAD(TANK_CH);
        meas=ReadAD(TANK_CH);

        if(meas > TankV[TankVindex]) TankV[TankVindex]=meas;    /* pokud najdeme maximum tak zaznamename, casova konstanta RC clanku dela zakladni filtraci */

        cnt++;

    }
    while(bit_is_clear(PIN(TANK_INT_PORT),TANK_INT_PIN) && cnt < 5);       /* dokud je komparator sepnuty tak mer maximum - merime kratke pulzy*/

    if(TankV[TankVindex])TankVindex++;
    else TankV[TankVindex]=bck;

    if(TankVindex==TANKV_MAX)
    {
        TankVindex=0;
        TankSampleMax=1;

    }



    /* blokujeme dalsi interrupt abychom nemerili prilis casto*/
    TANK_INT_DISABLE;
    //    isrCnt=0;



    TANK_FLAG_REG|=1<<TANK_INT_FLAG;

}

/* dojde-li k preteceni citace impulzu vstrikovace tj v pripade ze nastavime vice jak 50kHz a nebo nestihneme vyzvednuti v 1s */
INJ_TIM_ISR()
{

    injTicksOvf++;

}

/* v pripade ze dojde k preteceni citace otacek citac je 16bit */
RPM_TIM_ISR()
{

    rpmTicksOvf++;

}
/* v pripade preteceni citace impulzu od prevodovky - odo, citac je 8bit */
ODO_TIM_ISR()
{

    odoTicksOvf++;

}
/*  -------------- funkce ---------------- */

void initGenFreq(void)
{

    DDR(GEN_PORT)|=_BV(GEN_PIN);

    GEN_TCCR=0;

    GEN_TCNT=0;

    GEN_OCR = GEN_FVZ; /* 200kHz */

    GEN_TCCR = GEN_TCCR_INIT;

}

void InitCMeas(void)
{
    if(eeprom_read_byte(&ee_calibData.init)!=EE_INIT_B)
    {

        memset(&calibData,0,sizeof(clb_t));

#ifdef CAR_DEBUG
        calibData.Meters = 200;
        calibData.InjFlow = 1000;
        calibData.NumV = 4;
        calibData.Rpm = 50;
        calibData.tankInt = 1;
#endif

        calibData.init = EE_INIT_B;

        eeprom_update_block(&calibData,&ee_calibData,sizeof(clb_t));

    }

    eeprom_read_block(&calibData,&ee_calibData,sizeof(clb_t));

    initGenFreq();

    DDR(ODO_PORT)&=~_BV(ODO_PIN);
    ODO_TCCR = ODO_TCCR_INIT;
    ODO_TCNT = 0;
    ODO_TIMSK|=ODO_TIMSK_INIT;

    DDR(RPM_PORT)&=~_BV(RPM_PIN);
    RPM_TCCRA = 0;
    RPM_TCCRB = RPM_TCCR_INIT;
    RPM_TCNT = 0;
    RPM_TIMSK|=RPM_TIMSK_INIT;

    DDR(INJ_PORT1) &=~_BV(INJ_PIN1);
    DDR(INJ_PORT2) &=~_BV(INJ_PIN2);

    INJ_TCCRA = 0;
    INJ_TCCRB = INJ_TCCR_INIT;
    INJ_TCNT = 0;
    INJ_TIMSK|=INJ_TIMSK_INIT;



    DDR(TANK_INT_PORT)&=~_BV(TANK_INT_PIN);
    TANK_EICR |= TANK_EICR_INIT;
    TANK_INT_ENABLE;

    memset(&TankV,0,TANKV_MAX);

    sei();

}

void TankMeasPoll(void)
{
    static uint8_t TankMeasTime=0;      /* odmery probihaji pouze ve stanovenem case danem TANKM_TIME*/

    if(CompTimer(TankMeasTime,TANKM_TIME))
    {
        TankMeasTime = RetTimer();

        if(calibData.tankInt)
        {
            /* pokud je preruseni zakazane, zakazalo samo sebe :) tak ho povolime pro dalsich TANK_INT_MAX_POLL odmeru */
            TANK_INT_ENABLE;

        }
        else
        {

            TANK_INT_DISABLE;

            StartAD(TANK_CH);           /* prvni mereni */
            ReadAD(TANK_CH);

            StartAD(TANK_CH);

            TankV[TankVindex++]=ReadAD(TANK_CH);

            if(TankVindex==TANKV_MAX)
            {
                TankVindex=0;
                TankSampleMax=1;

            }

        }

    }

}

void V12MeasPoll(void)
{
    static uint8_t V12MeasTime=0;

    if(CompTimer(V12MeasTime,V12_TIME))
    {

        V12MeasTime = RetTimer();

        StartAD(V12_CH)   ;
        ReadAD(V12_CH);

        StartAD(V12_CH);
        V12 =ReadAD(V12_CH);

    }

}

inline uint8_t isNewMeas(void)
{
    cli();
    uint8_t m = newMeas;
    newMeas = 0;
    sei();

    return m;

}

inline void ClearTankMeas(void)
{

    TankVindex=0;
    TankSampleMax=0;

}

inline uint8_t RetTankMeasMax(void)
{
    return TankSampleMax;
}

/* kvuli dobre odezve na zobrazeni rpm je tato fce volana 8* za 1s T=0.125s*/
inline void RpmPoll(void)
{
    uint16_t rImp;
    uint16_t rOvf;

#ifndef MEAS_DEBUG
    rImp = RPM_TCNT;
    RPM_TCNT-= rImp;
    rOvf=rpmTicksOvf;
    rpmTicksOvf=0;

    rpmTicks = (uint32_t)rImp+((uint32_t)rOvf*65536UL);
#else
    rpmTicks = debugRpmTicks;
#endif

    tmpRpm += rpmTicks;
    newMeas = 1;
}
/* tato funkce se inline vklada do INT preruseni RTC hodin */
inline void CMeasPoll(void)
{
    uint16_t iImp;
    uint16_t iOvf;
    uint16_t oImp;
    uint16_t oOvf;

#ifndef MEAS_DEBUG
    iImp = INJ_TCNT;
    INJ_TCNT-= iImp;

    oImp = ODO_TCNT;
    ODO_TCNT-= oImp;

    iOvf=injTicksOvf;
    injTicksOvf=0;

    oOvf = odoTicksOvf;
    odoTicksOvf=0;

    injTicks = ((uint32_t)iImp)+(((uint32_t)iOvf)*(uint32_t)65536UL);
    odoTicks = (uint32_t)oImp+((uint32_t)oOvf*256);

    injTicks/=4;    /* vzorkujeme 200 kHz abychom chytli dobre vsechny hrany 6000ot/min -> 100ot/s tj az 200 hran za 1s..nyni podvzorkujeme na 50kHz coz pro vypocty staci */

#else

    injTicks = debugInjTicks;
    odoTicks = debugOdoTicks;

#endif

    newMeas = 1;

    /* osetreni maxima - pri vypnutem motoru, nektere vstrikovace spinaji vstupni logiku do log 0 a nechtelo to bez tohoto uspavat */
    if(  (odoTicks > 2)  ||  (injTicks<TICKS_MAX && injTicks!=0)  )
    {

        tmpInj_imp += injTicks;

        tmpImp += odoTicks;

        tmpTime++;

        running=1;  /* flag ktery nam urcuje zda automobil je v provozu ci ne */

    }
    else
    {
        running=0;
        injTicks=0;
        odoTicks=0;
        rpmTicks=0;

    }

}
mv_t RetTmV(uint16_t ad)
{

    uint32_t sum=ad;
    sum = ((uint32_t)REF_VOLT*(uint32_t)sum*10)/1024;

    return ((sum*(uint32_t)(TANK_R1+TANK_R2))/TANK_R2/10);

}

mv_t RetTankmV(void)
{


//    uint32_t sum = 0;
    uint16_t val=TankV[0];
    uint8_t index,i,j;
    uint16_t sort[TANKV_MAX];

    if(TankSampleMax==1)index = TANKV_MAX;
    else index = TankVindex;

    memcpy(sort,(uint16_t*)TankV,2*index);

    for(i=0; i<index; i++)
    {

        for(j=i+1; j<index; j++)
        {

            if(sort[i]>sort[j])
            {

                val=sort[i];
                sort[i]=sort[j];
                sort[j]=val;

            }

        }

    }



    return RetTmV(sort[index/2]);

}

mv_t GetV12mV(void)
{

    uint32_t mv;

    mv = ((uint32_t)V12 * REF_VOLT)/1024;

    return ((mv*(uint32_t)(V12_R1+V12_R2))/V12_R2);

}
/* pridano 8.10.2011 - implementace po castech linearni aproximace nadrze */
tank_t GetTankL(void)
{

    int32_t l2=0;
    int32_t l1=0;
    int32_t mv2=0;
    int32_t mv1=0;
    int32_t mv = RetTankmV();
    uint8_t i=0;

    while(i<CALCT_CNT)
    {

        if(calibData.TankCalib[i].init == EE_INIT_B)
        {

            if(calibData.TankCalib[i].mvolt>mv)
            {

                break;
            };

        }

        i++;

    }
    /* pokud je namerena hodnota proste vetsi nez vsechny ulozene konstanty, pak pouzij nejvyssi ulozene konstanty tj posledni v poli*/
    if(i==CALCT_CNT)
    {
        i--;
        while(i>0)
        {
            /* najde nejvyssi horni*/
            if(calibData.TankCalib[i].init == EE_INIT_B) break;
            i--;
        }

        l2 = calibData.TankCalib[i].litr;
        mv2 = calibData.TankCalib[i].mvolt;
        i--;
        while(i>0)
        {
            /* najde druhou nejvyssi horni*/
            if(calibData.TankCalib[i].init == EE_INIT_B) break;
            i--;
        }
        /* pokud je ovsem i==0 a zaroven neni inicializovane pole pak je problem a nelze pocitat protoze nejsou ulozene konstanty*/
        if(i==0&&calibData.TankCalib[i].init!=EE_INIT_B) return 0;

    }
    else
    {

        l2 = calibData.TankCalib[i].litr;
        mv2 = calibData.TankCalib[i].mvolt;
        if(i>0)
        {
            i--;
            while(i>0)
            {

                if(calibData.TankCalib[i].init == EE_INIT_B) break;
                i--;

            }

            if(i==0&&calibData.TankCalib[i].init!=EE_INIT_B) return 0;

            l1=calibData.TankCalib[i].litr;
            mv1=calibData.TankCalib[i].mvolt;

        }
        else
        {

            /* tzn ze nulta hodnota je jiz vetsi nez ta namerena takze potrebujem dalsi vyssi hodnotu */
            l1=l2;
            mv1=mv2;
            i++;
            while(i<CALCT_CNT)
            {
                if(calibData.TankCalib[i].init == EE_INIT_B) break;
                i++;
            }

            if(i>=CALCT_CNT) return 0;

            l2 = calibData.TankCalib[i].litr;
            mv2 = calibData.TankCalib[i].mvolt;

        }

    }

    int32_t k = ((l2 - l1)*1000);

    k /=(mv2-mv1);

    int32_t q = (l1*1000)-(mv1*k);

    k = ( (k*mv)+q )/100;

    if(k<0) k = 0;

    else if(k%10 > 4) k+=10;

    k/=10;

    return  (k>calibData.TankL?calibData.TankL:k);


}

fuel_t GetFuel(inj_t *inj)
{

    if(*inj ==0 || calibData.InjFlow ==0 || calibData.NumV ==0) return 0;

    /* CalibInjFlow format 3500 = 350,0 ccm/min
        NumV 4 = 4 valce
        inj 50 000 = 1s

    */

    inj_t calc1 = (( (inj_t)(*inj/6000)*(inj_t)calibData.NumV*(inj_t)calibData.InjFlow)/(500000) );

    return calc1;
}

m_t RetMeters(odo_t * imp)
{

    if(calibData.Meters ==0 || *imp ==0 ) return 0;

    odo_t m=0;

    m=(((*imp) * (odo_t)100)  / (odo_t)calibData.Meters);

    return m;
}

fuel_t RetFuelCons(odo_t * imp, inj_t *inj)
{

    if( *imp<4   || (*inj ==0) || calibData.InjFlow ==0 || calibData.NumV ==0) return 0;

    /* CalibInjFlow format 3500 = 350,0 ccm/min
       NumV 4 = 4 valce
       inj 50 000 = 1s

    */


    inj_t calc = ((*inj * (inj_t)calibData.NumV * (inj_t)calibData.InjFlow)/(inj_t)RetMeters(imp))/(inj_t)30000;
    return calc;

}

fuel_t RetFuelConsLh(void)
{

    if( injTicks == 0 || calibData.NumV ==0 || calibData.InjFlow ==0) return 0;

    /* Counter / 50 000 -> (50khz) mam sekundy. *valce* InjFlow v (ccm/min)/10 / 60 / 1000 ( na dm2=litry) * 10 ( presnost na jedno misto) *3600 -> xx,x L za hodinu */

    return ( (((((uint32_t)injTicks *3* (uint32_t)calibData.NumV)/250) * (uint32_t)calibData.InjFlow/10))/1000 );
}

rpmmin_t RetRpm(rpm_t * impRpm,tm_t *time)
{

    if(impRpm && *impRpm && time && *time && calibData.Rpm)
    {

        rpm_t calc = (*impRpm * (rpm_t)60 )/(rpm_t)calibData.Rpm/ *time;

        return calc;

    }
    else return 0;
}

inline uint32_t GetInjCnt(void)
{
    cli();
    uint32_t inj = injTicks;
    sei();

    return inj;

}

inline uint32_t GetImpCnt(void)
{

    cli();
    uint32_t imp = odoTicks;
    sei();

    return imp;

}

inline uint32_t GetRpmCnt(void)
{

    cli();
    uint32_t rpm = rpmTicks;
    sei();

    return rpm;
}

inline uint8_t IsRunning(void)
{

    return running;

}

inline ipm_t GetCalibMeters(void)
{

    return calibData.Meters;

}

inline ccmmin_t GetCalibInjFlow(void)
{

    return calibData.InjFlow;

}

inline void SetCalibMeters(ipm_t cm)
{

    calibData.Meters = cm;

}

inline void SetCalibInjFlow(ccmmin_t ij)
{

    calibData.InjFlow = ij;

}

inline uint8_t GetCalibValve(void)
{

    return calibData.NumV;

}
inline void SetCalibValve(uint8_t v)
{

    calibData.NumV = v;

}

inline tank_t GetCalibLx(uint8_t i)
{

    if(i>=CALCT_CNT) return 0;

    return calibData.TankCalib[i].litr;

}

inline mv_t GetCalibVx(uint8_t i)
{

    if(i>=CALCT_CNT) return 0;

    return calibData.TankCalib[i].mvolt;

}

inline uint8_t GetCalibIx(uint8_t i)
{

    if(i>=CALCT_CNT) return 0;

    return calibData.TankCalib[i].init;

}

inline void SetCalibLx(uint8_t i,tank_t litr)
{

    if(i>=CALCT_CNT) return;

    calibData.TankCalib[i].litr=litr;
    calibData.TankCalib[i].init = EE_INIT_B;

}

inline void SetCalibVx(uint8_t i,mv_t mv)
{

    if(i>=CALCT_CNT) return ;
    calibData.TankCalib[i].mvolt=mv;
    calibData.TankCalib[i].init = EE_INIT_B;

}

inline void SetCalibIx(uint8_t i,uint8_t init)
{

    if(i>=CALCT_CNT) return ;
    calibData.TankCalib[i].init = init;

}

inline tank_t GetCalibL(void)
{

    return calibData.TankL;

}
inline void SetCalibL(tank_t l)
{


    calibData.TankL = l;

}

inline uint8_t GetCalibTankInt(void)
{
    return calibData.tankInt;
}


speed_t RetSpeed(odo_t*  imp,tm_t* time)
{

    if(*imp==0 || *time==0) return 0;


    odo_t m = (((*imp) * (odo_t)1000)  / (odo_t)calibData.Meters);

    m =((m*(odo_t)36)/(odo_t)10)/(odo_t) *time;

    if(m%10 > 5) m+=10;

    return (m/10);
}

void GetCalibData(clb_t *data)
{

    if(data==NULL)return;

    memcpy(data,&calibData,sizeof(clb_t));

}


void SetCalibData(clb_t *data)
{


    if(data==NULL) return ;

    if(memcmp(data,&calibData,sizeof(clb_t))!=0)
    {

        memcpy(&calibData,data,sizeof(clb_t));

        eeprom_update_block(&calibData,&ee_calibData,sizeof(clb_t));

    }

}

inline inj_t getTmpInjImp(void)
{
    cli();
    inj_t inj = tmpInj_imp;
    tmpInj_imp = 0;
    sei();
    return inj;
}

inline tm_t getTmpTime(void)
{
    cli();
    tm_t tm = tmpTime;
    tmpTime = 0;
    sei();
    return tm;
}
inline odo_t getTmpImp(void)
{
    cli();
    odo_t imp = tmpImp;
    tmpImp = 0;
    sei();
    return imp;
}
inline rpm_t getTmpRpm(void)
{
    cli();
    rpm_t rpm = tmpRpm;
    tmpRpm = 0;
    sei();
    return rpm;
}

inline uint32_t * getImpTicksPtr(void)
{
    return (uint32_t*)&odoTicks;
}

inline uint16_t * getImpOvfPtr(void)
{
    return (uint16_t*)&odoTicksOvf;
}
inline uint32_t * getInjTicksPtr(void)
{
    return (uint32_t*)&injTicks;
}
inline uint16_t * getInjOvfPtr(void)
{
    return (uint16_t*)&injTicksOvf;
}
inline uint32_t * getRpmTicksPtr(void)
{
    return (uint32_t*)&rpmTicks;
}
inline uint16_t * getRpmOvfPtr(void)
{
    return (uint16_t*)&rpmTicksOvf;
}
