//
// AVR ADC common operations
// Homelab library
//
// Department of Mechatronics
// Tallinn University of Technology
// © Copyrights 2009
//

//
// Include adc.h only once
//
#ifndef _ADC_H_
#define _ADC_H_

//
// Include common library
//
#include "common.h"

//
// Include XMega ADC driver
//
#if defined (__AVR_ATxmega128a1u__)
#include "xmega/adc_driver.h"
#endif

//
// Include avrlibc files
//
#include <avr/io.h>

//
// Take use of bit manipulation operations
//
#include "bit.h"

//
// C++ compatibility
//
#ifdef __cplusplus
extern "C" {
#endif

#if defined (__AVR_ATmega2561__) || defined (__AVR_ATmega128__)
//
// Configuration types
//
typedef enum
{
    ADC_REF_AREF = 0X00,
    ADC_REF_AVCC = 0X01,
    ADC_REF_2V56 = 0X03
}
adc_reference;

typedef enum
{
    ADC_PRESCALE_2   = 0X01,
    ADC_PRESCALE_4   = 0X02,
    ADC_PRESCALE_8   = 0X03,
    ADC_PRESCALE_16  = 0X04,
    ADC_PRESCALE_32  = 0X05,
    ADC_PRESCALE_64  = 0X06,
    ADC_PRESCALE_128 = 0X07
}
adc_prescale;

//
// ADC channel value getting
//
inline unsigned char adc_get_channel(void)
{
    return ADMUX & 0x0F;
}

//
// ADC conversion starting
//
inline void adc_start_conversion(void)
{
    bit_set(ADCSRA, ADSC);
}

inline bool adc_is_ready(void)
{
    return bit_is_clear(ADCSRA, ADSC);
}

//
// ADC conversion result returning
//
inline unsigned short adc_get_result(void)
{
    return ADC;
}

//
// ADC interrupt configuration
//
inline void adc_interrupt_enable(bool enable)
{
    bit_set_to(ADCSRA, ADIE, enable);
}

#elif defined (__AVR_ATxmega128A1U__)

#include "xmega/adc_driver.h"
extern int8_t offset;
extern uint8_t adc_ref;

//
// Configuration types
//
typedef enum
{
    ADC_REF_AREF = ADC_REFSEL_AREFA_gc,
    ADC_REF_AREFB = ADC_REFSEL_AREFB_gc,
    ADC_REF_AVCC = ADC_REFSEL_INTVCC_gc,
    ADC_REF_VCCDIV2 = ADC_REFSEL_INTVCC2_gc,
    ADC_REF_1V0 = ADC_REFSEL_INT1V_gc
}
adc_reference;

typedef enum
{
    ADC_PRESCALE_4   = ADC_PRESCALER_DIV4_gc,
    ADC_PRESCALE_8   = ADC_PRESCALER_DIV8_gc,
    ADC_PRESCALE_16  = ADC_PRESCALER_DIV16_gc,
    ADC_PRESCALE_32  = ADC_PRESCALER_DIV32_gc,
    ADC_PRESCALE_64  = ADC_PRESCALER_DIV64_gc,
    ADC_PRESCALE_128 = ADC_PRESCALER_DIV128_gc,
    ADC_PRESCALE_256 = ADC_PRESCALER_DIV256_gc
}
adc_prescale;

//
// ADC conversion starting
//
inline void adc_start_conversion(void)
{
    ADC_Ch_Conversion_Start(&ADCA.CH0);
}

inline bool adc_is_ready(void)
{
    return ADC_Ch_Conversion_Complete(&ADCA.CH0);
}

//
// ADC conversion result returning
//
inline unsigned short adc_get_result(void)
{
    return  ADC_ResultCh_GetWord(&ADCA.CH0);
}
#else
#error "Wrong part"
#endif

//
// ADC initialization
// 1- ADC_REF_xxx (AVCC)
// 2- ADC_PRESCALE_xxx (128)
//
extern void adc_init(adc_reference reference, adc_prescale prescale);

//
// ADC channel value getting
//
extern void adc_set_channel(unsigned char channel);

//
// ADC channel value getting
//
extern unsigned short adc_get_value(unsigned char channel);

//
// ADC channel value sampling
//
extern unsigned short adc_get_average_value(unsigned char channel, unsigned char num_samples);

//
// C++ compatibility
//
#ifdef __cplusplus
}
#endif

#endif
