//
// AVR ADC common operations
// Homelab library
//
// Department of Mechatronics
// Tallinn University of Technology
// ITT Group
// © Copyrights 2014
//

#include <avr/io.h>
#include "bit.h"
#include "adc.h"

uint8_t adc_ref = 0;

#if defined (__AVR_ATxmega128A1U__)

int8_t offset;

//
// ADC initialization
// 1- ADC_REF_xxx (AVCC)
// 2- ADC_PRESCALE_xxx (256)
//
void adc_init(uint8_t reference, uint8_t prescale)
{
    if(reference == ADC_REF_AVCC) adc_ref = 2;
    else if(reference == ADC_REF_AREF) adc_ref = 3;

    /* Set up ADC A to have signed conversion mode and 12 bit resolution. */
    ADC_ConvMode_and_Resolution_Config(&ADCA, ADC_ConvMode_Signed, ADC_RESOLUTION_12BIT_gc);

    // The ADC has different voltage reference options, controlled by the REFSEL bits in the
    // REFCTRL register. Here the internal reference is selected
    ADC_Reference_Config(&ADCA, reference);

    // The clock into the ADC decide the maximum sample rate and the conversion time, and
    // this is controlled by the PRESCALER bits in the PRESCALER register. Here, the
    // Peripheral Clock is divided by 8 ( gives 250 KSPS with 2Mhz clock )
    ADC_Prescaler_Config(&ADCA, prescale);

    // The used Virtual Channel (CH0) must be set in the correct mode
    // In this task we will use single ended input, so this mode is selected

    /* Setup channel 0 to have single ended input. */
    ADC_Ch_InputMode_and_Gain_Config(	&ADCA.CH0,
                                        ADC_CH_INPUTMODE_SINGLEENDED_gc,
                                        ADC_CH_GAIN_1X_gc);

    // Setting up the which pins to convert.
    // Note that the negative pin is internally connected to ground
    ADC_Ch_InputMux_Config(&ADCA.CH0, ADC_CH_MUXPOS_PIN15_gc, ADC_CH_MUXNEG_PIN0_gc);

    /* Move stored calibration values to ADC B */
//		ADC_CalibrationValues_Load(&ADCA);

    // Before the ADC can be used it must be enabled
    ADC_Enable(&ADCA);

    // Wait until the ADC is ready
    ADC_Wait_32MHz(&ADCA);

    // Get offset value for ADC A.
    offset = ADC_Offset_Get_Signed(&ADCA, &(ADCA.CH0), true);
}

//
// ADC conversion waiting
//
inline void adc_wait_until_done()
{
    while (!ADC_Ch_Conversion_Complete(&ADCA.CH0));
}

//
// ADC channel value getting
//
void adc_set_channel(unsigned char channel)
{
    ADC_Ch_InputMux_Config(&ADCA.CH0, (channel<<3), ADC_CH_MUXNEG_PIN0_gc);
}

//
// ADC channel value getting
//
unsigned short adc_get_value(unsigned char channel)
{
    // Specify channel
    adc_set_channel(channel);

    // Take test sample to "warm up" converter
    // Usually the first sample is discarded
    adc_start_conversion();
    adc_wait_until_done();
    adc_get_result();

    // Real sampling, sum up specifed number of samples
    adc_start_conversion();
    adc_wait_until_done();

    // Return result
    int16_t adc_value = adc_get_result();

    if(adc_value >= 0)
        return adc_get_result();
    else
        return 0;
}

//
// ADC channel value sampling
//
unsigned short adc_get_average_value(unsigned char channel, unsigned char num_samples)
{
    uint32_t result = 0;
    uint8_t i;

    // Specify channel
    adc_set_channel(channel);

    // Take test sample to "warm up" converter
    // Usually the first sample is discarded
    adc_start_conversion();
    adc_wait_until_done();

    // Real sampling, sum up specifed number of samples
    for (i = 0; i < num_samples; i++)
    {
        uint16_t temp;
        adc_start_conversion();
        adc_wait_until_done();

        temp = adc_get_result();
        // Sum-up
        if((temp >= 0) && (temp < 2050))
            result += temp;
    }

    // Return averaged result

    if((result / num_samples) >= 0)
        return (result / num_samples);
    else
        return 0;
}
#endif