/*
 * File:   ADC.c
 * Author: Vojta
 *
 * Created on September 1, 2022, 4:32 PM
 */


#include "hlava.h"



void adc_init(void)
{
    //******adc setup*******************
    ADPCHbits.PCH = 0x3C; // ADC channel select: Temperature Indicator
    
    ADCON0bits.ON = 1; // adc ON
    ADCON0bits.CONT = 0; //GO is cleared upon completion of each conversion trigger
    ADCON0bits.CS = 0; // Clock supplied by FOSC, divided according to ADCLK register
    ADCON0bits.FM = 1; //ADRES and ADPREV data are right-justified !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
    ADCLK = 63; // Fosc/(2*(ADCLK+1)) = Fosc/128  , TAD = 8 us
    
    ADCON2bits.MD = 0; //basic mode
    
    ADREFbits.NREF = 0; // neg to GND
    ADREFbits.PREF = 0b11; // pos to FVR

    
    //*******FVR - Fixed Voltage Reference ***********
    FVRCONbits.FVREN = 1; //Fixed Voltage Reference is enabled
    FVRCONbits.ADFVR = 0x02; // ADC FVR Buffer Gain is 2x, (2.048V) (2x setting)
    
    //*******temperature indicator setup***********
    TSRNG = 1; //HIGH range
    TSEN = 1; // temp indicator ON
    
    __delay_ms(250); // doba potrebna pro switch mezi ADC channels
    venek_temp(); // zmeri teplotu cipu (teplota okoli)

    //**********vypnuti temp modulu a pridruzenych modulu*****
    ADPCHbits.PCH = 0x14 ; // ADC channel select: RC4 (baterie)
    ADREFbits.PREF = 0; // pos to VDD 
    FVRCON = 0; // temp indicator OFF, FVR OFF
    __delay_ms(200);  
    battery_read();
}

void venek_temp(void)
{
    ADCON0bits.GO = 1; //start adc conversion
    while(ADCON0bits.GO); //dokud neni hotovo
  
    adc_res =  (unsigned int) ( ADRESL | (ADRESH<<8) );
    temp0=  (char) ( 90 + ( ( ( (long)adc_res - TSHR2 ) * (long)FVRA2X * 1000 ) / ( ((long)-3684 * (long)4095) ) ) -12 ); // -12 kvuli nepresnosti mereni teploty okoli       
}

void battery_read(void)
{
    battery_check = 1; //flag pro nacteni z hodnoty baterie (aby to preslo do teto funkce dokud to nezmeri baterku)
    
    check_adc_channel(battery_adc); //zkontroluj jestli je adc preply na pin mereni baterie
    if(!ADC_ready) return; // dokud neni preply a neubehlo 250ms


    ADCON0bits.GO = 1; 
    while(ADCON0bits.GO);

    adc_res = (unsigned int) ( ADRESL | (ADRESH<<8) );
    V_bat = (unsigned int) ( ( (unsigned long)adc_res * 5*1000* 2 ) / 40960  ); // VDD = 5000 mV, ADR resolution = 4096-1, /10 pro spravne zobrazeni na displaji: Vbat * 10^-2 (nap? 7.42V bude 742)
    battery_check = 0; //zmereno, flag=0
}

void ohm_read(void)
{
    
    check_adc_channel(ohm_adc);
    if(!ADC_ready) return;
  
 
    if(poprve == 1 || zmena == 1) // jestli se ohm_read() provádí poprve ( nebo kdyz prepinam mezi zobrazenim na 7_segmentu nebo kdyz menim hodnotu TC)
    {
        fire = 0;
        if(operace1 == nic || poprve == 1) //bud se to dela poprve a nebo pri zmene spirálky kdy bude splnena zaroven podminka zmena=1 a zároven promenna "operace" je "nic" coz se prepne kdyz sekundu drzim selektovaci tlacitko pri tom kdy to detekovalo predtzim rozpojeni 
        {
            unsigned long R0avg = 0;
            for(i=0;i<10;i++) //average hodnota pro coil_res0
            {
               get_coil_res();
               R0avg += coil_res; 
            }
            coil_res0 = (unsigned int) ( R0avg / i ); //average hodnota R0
            i = 0;  
            zmena = 0;
            operace1 = novy_odpor;
        }  
    }
  
   
    if((seg_adc_count >= 1000 && !fire) ||  fire || poprve || zmena) //seg_adc_count >= 1000 pro mereni kazdou 1s kdyz se nic nedela, poprve == 1 jestli se ohm_read() provádí poprve, fire kdyz zhavim, zmena kdyz jsem vymenil spiralku
    {
        if(fire && new_meas_ready ) //zacatek mereni odporu pri zhaveni
        {
            CCP1CONbits.MODE = 0; //PWM off
                new_meas_ready = 0;
                T4TMR = 0;  
        }
        
        if( ( T4TMR >= 200 && CCP1CONbits.MODE == 0 ) || !fire) // Pri zhaveni kdy vypnes pwm 800us pockej nez zacnes merit
        { 
           
            get_coil_res(); //zmer odpor
         
            if(coil_res_disp == 999 && !fire)//zmena spiralky
            {
                zmena = 1; //flag pro nacteni novych hodnot
                mode1 = ohm; //prepnuti na zobrazovani odporu
            }

            if( fire && coil_res_disp >= 39 ) //aby to nezhavilo pokud je odpor mensi jak 0.38 ohm
            if( coil_res < coil_res1  || !TC_ON )// bud splnena podminka pro TC teplotu nebo pokud je TC OFF
            {
                set_vykon(); //nastav novy vykon podle odporu spirálky (pri zhaveni odpor spiralky odpor roste)
                CCP1CONbits.MODE = 0x0F; //PWM ON
                T4TMR = 0; //vynuluj timer
                meas_count = 0; 
                new_meas_ready = 1;                
            }

        }
        seg_adc_count = 0; 
        poprve = 0;
    }
     
}

void get_coil_res(void) //zhruba 400us 
{
    ADCON0bits.GO = 1; 
    while(ADCON0bits.GO);
    
    adc_res = (unsigned int) ( ADRESL | (ADRESH<<8) );

    Vout = (unsigned long) ( ((unsigned long)adc_res * 5 * 10000) / 4095 ); //*10^-4 V


    coil_res = (unsigned int)  ( ( (unsigned long ) Vout * 10000 ) / ( 93 * 101  )); // *10^-4 ohmu  Ispiralka = 9,3 mA, zesileni 101
    
    if( coil_res <= 52000 ) // 5,3 ohmu je max co to muze namerit
        coil_res_disp = (unsigned int)((coil_res + 50)/100); // +50 kvuli zaokrouhleni (napr 3952 + 50 ~~ 4000/100 = 40 = 0.40 na displayi)
    else
        coil_res_disp = 999;

    if( ( (int)coil_res_disp - (int)coil_res_disp1 ) < (-1) || ( (int)coil_res_disp - (int)coil_res_disp1 ) >= (1) ) //jestli je tam zmena nad 0.01 ohm tak to zobraz jinak nech tak
    coil_res_disp1 = coil_res_disp;    
    
}

unsigned int coil_res1_vypocet(unsigned int T1) //vypocet R1 z TCR max, //
{
    unsigned int R1;
    R1 = (unsigned int) ( (unsigned long)( (unsigned long)coil_res0 * ( ( 100000 + 92 * ( T1 - temp0 ) ) / 10 ) ) /10000 ); //TCR = 92 * 10^-5, T1 = nastavena teplota
    if(coil_res0 <= 10000)
        R1 = (unsigned int)( ((unsigned long)R1*97)/100 );
    return R1;
}

void check_adc_channel(channel channelx)
{
    if(channelx == ohm_adc)
    {
        if(ADPCHbits.PCH != 0x15 ) // jestli neni prepnuto na ADC kanal pro mereni odporu (RC5)
        {
            ADPCHbits.PCH = 0x15;
            adc_switch = 0;
        }       
    }
    
    if(channelx == battery_adc)
    {
        if(ADPCHbits.PCH != 0x14 ) // jestli neni prepnuto na ADC kanal pro mereni baterie (RC4)
        {
            ADPCHbits.PCH = 0x14;
            adc_switch = 0;
        }       
    }
    
    if(adc_switch < 1) // 1ms delay pri prepinani mezi ADC kanaly
    {
        adc_res = 0;
        ADC_ready = 0;
       
    } else{
        ADC_ready = 1;
        adc_switch = 2;
        
    } 
      
}