/*! \file a2d.c \brief Analog-to-Digital converter function library. */
//*****************************************************************************
//
// File Name    : 'a2d.c'
// Title                : Analog-to-digital converter functions
// Author               : Pascal Stang - Copyright (C) 2002
// Created              : 2002-04-08
// Revised              : 2002-09-30
// Version              : 1.1
// Target MCU   : Atmel AVR series
// Editor Tabs  : 4
//
// This code is distributed under the GNU Public License
//              which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include <avr/io.h>
#include <avr/interrupt.h>

#include "global.h"
#include "a2d.h"

// global variables

//! Software flag used to indicate when
/// the a2d conversion is complete.
volatile unsigned char a2dCompleteFlag;

// functions

// initialize a2d converter
void a2dInit(void)
{
        sbi(ADCSR, ADEN);                               // enable ADC (turn on ADC power)
        cbi(ADCSR, ADFR);                               // default to single sample convert mode
        a2dSetPrescaler(ADC_PRESCALE);  // set default prescaler
        a2dSetReference(ADC_REFERENCE); // set default reference
        cbi(ADMUX, ADLAR);                              // set to right-adjusted result

        sbi(ADCSR, ADIE);                               // enable ADC interrupts

        a2dCompleteFlag = FALSE;                // clear conversion complete flag
        sei();                                                  // turn on interrupts (if not already on)
}

// turn off a2d converter
void a2dOff(void)
{
        cbi(ADCSR, ADIE);                               // disable ADC interrupts
        cbi(ADCSR, ADEN);                               // disable ADC (turn off ADC power)
}

// configure A2D converter clock division (prescaling)
void a2dSetPrescaler(unsigned char prescale)
{
        outb(ADCSR, ((inb(ADCSR) & ~ADC_PRESCALE_MASK) | prescale));
}

// configure A2D converter voltage reference
void a2dSetReference(unsigned char ref)
{
        outb(ADMUX, ((inb(ADMUX) & ~ADC_REFERENCE_MASK) | (ref<<6)));
}

// sets the a2d input channel
void a2dSetChannel(unsigned char ch)
{
        outb(ADMUX, (inb(ADMUX) & ~ADC_MUX_MASK) | (ch & ADC_MUX_MASK));        // set channel
}

// start a conversion on the current a2d input channel
void a2dStartConvert(void)
{
        sbi(ADCSR, ADIF);       // clear hardware "conversion complete" flag 
        sbi(ADCSR, ADSC);       // start conversion
}

// return TRUE if conversion is complete
u08 a2dIsComplete(void)
{
        return bit_is_set(ADCSR, ADSC);
}

// Perform a 10-bit conversion
// starts conversion, waits until conversion is done, and returns result
unsigned short a2dConvert10bit(unsigned char ch)
{
        a2dCompleteFlag = FALSE;                                // clear conversion complete flag
        outb(ADMUX, (inb(ADMUX) & ~ADC_MUX_MASK) | (ch & ADC_MUX_MASK));        // set channel
        sbi(ADCSR, ADIF);                                               // clear hardware "conversion complete" flag 
        sbi(ADCSR, ADSC);                                               // start conversion
        //while(!a2dCompleteFlag);                              // wait until conversion complete
        //while( bit_is_clear(ADCSR, ADIF) );           // wait until conversion complete
        while( bit_is_set(ADCSR, ADSC) );               // wait until conversion complete

        // CAUTION: MUST READ ADCL BEFORE ADCH!!!
        return (inb(ADCL) | (inb(ADCH)<<8));    // read ADC (full 10 bits);
}

// Perform a 8-bit conversion.
// starts conversion, waits until conversion is done, and returns result
unsigned char a2dConvert8bit(unsigned char ch)
{
        // do 10-bit conversion and return highest 8 bits
        return a2dConvert10bit(ch)>>2;                  // return ADC MSB byte
}

//! Interrupt handler for ADC complete interrupt.
SIGNAL(SIG_ADC)
{
        // set the a2d conversion flag to indicate "complete"
        a2dCompleteFlag = TRUE;
}