/*This file is prepared for Doxygen automatic documentation generation.*/
//! \file *********************************************************************
//!
//! \brief Contains high level functions for initializing the ADC,
//!  interrupt handling, and treatment of samples.\n
//!  The ADC is set to free running mode and uses an internal reference
//!  voltage.\n
//!
//! - Compiler:           IAR EWAVR and GNU GCC for AVR
//! - Supported devices:  ATmega32U4
//!
//! \author               Atmel Corporation: http://www.atmel.com \n
//!                       Support and FAQ: http://support.atmel.no/
//!
//! ***************************************************************************

/* Copyright (c) 2009 Atmel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an Atmel
 * AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "structs.h"
#include "main.h"

#ifdef __GNUC__
   #include "lib_mcu/adc/adc.h"
   #include "lib_mcu/adc/adc_drv.h"
#elif __ICCAVR__
   #include "adc.h"
   #include "adc_drv.h"    
#else
   #error Current COMPILER not supported
#endif

//******************************************************************************
// Variables
//******************************************************************************
// ADC status struct.
//! \brief Holds sampled data and ADC-status
volatile ADC_Status_t ADCS;

//******************************************************************************
// Functions
//******************************************************************************
/*! \brief Interrupt Service routine for ADC.
 *
 * This ISR stores the sampled values in the ADC status-struct, then
 * updates the ADC MUX to the next channel in the scanning-sequence.\n
 * Once the sequence is completed, ADCS.Flag is set and unless
 * ADCS.Halt has been set, the sequence starts over. Otherwise, the ADC 
 * is disabled.\n
 * If the mains voltage is below minimum, ADCS.Mains gets set to FALSE.
 * VREF = 2.56V
 * \note Table of scanning sequence:
 * <pre>
 * Seq | MUX5 | MUX4:0 |  pos I/P  |  neg I/P |  gain |   measure   | signed
 * ----+------+--------+-----------+----------+-------+-------------+-------
 *  01 |  0   | 01001  | ADC0/PF0  | diff/neg |   10x |   Vshunt-   |   no
 *     |      |        | ADC1/PF1  | diff/pos |   10x |   Vshunt+   |   no
 *     |      |        |           |          |       |             |
 *  02 |  0   | 00000  | ADC0/PF0  |   n/a    |   1x  |   Vbatt     |   no
 *     |      |        |           |          |       |             |
 *  03 |  1   | 00011  | ADC11/PB4 |   n/a    |   1x  |   NTC       |   no
 *     |      |        |           |          |       |             |
 *  04 |  1   | 00100  | ADC12/PB5 |   n/a    |   1x  |   RID       |   no
 *     |      |        |           |          |       |             |
 *  05 |  0   | 01001  | ADC0/PF0  | diff/neg |   10x |   Vshunt-   |   no
 *     |      |        | ADC1/PF1  | diff/pos |   10x |   Vshunt+   |   no         |          |       |             |  
 *     |      |        |           |          |       |             |
 * </pre>
 *
 * \todo IIN (#7 in sequence) is never used.
 *
 * \todo Signed is never set. Signed measurements of IBAT will halve the
 * measuring sensitivity, and is therefore not favourable. At the moment,
 * great currents (f.ex. if something happens with the battery) will be
 * interpreted as negative, which might cause unfavourable behaviour during
 * charging (depending on what PWM behaviour is defined), f.ex.
 * ConstantCurrent() will keep increasing the PWM output. This results in an
 * PWM controller error being flagged and the program going into
 * error-state and eventually reinitializing.
 */

#ifdef __GNUC__
 ISR(ADC_vect)
#else
#pragma vector = ADC_vect
__interrupt void ADC_ISR(void)
#endif
{
	static unsigned char avgIndex = 0;
	unsigned char i, Next;
	signed int  temp = 0;
	unsigned long temp_buffer;
	
	// Handle the conversion, depending on what channel it is from, then
	// switch to the next channel in the sequence.
	switch (ADCS.MUX){
	  
		// MUX = 0x09 => ADC0/ADC1 (PF0/PF1) = Vshunt
		case 0x01:
		case 0x05:		  
			//First differential with no accuracy
			//need second one
			if(ADCS.MUX == 0x01)
			{
				Next=0x05;
			}
			else
			{
			
				// If bipolar, from -512 to 0, to 511:
				// 0x200 ... 0x3ff, 0x000, 0x001 ... 0x1FF
				if (ADC > 511) {
					//Negative value (battery charge)
					temp_buffer = (I_SCALE_1 * (1024 -ADC)) / I_SCALE_2;	
					ADCS.IBAT = -(signed int)temp_buffer;

					//for demo current always positive
					ADCS.IBAT = 0;
				}
				else if (ADC > 0){
					//Positive value (battery discharge) 
					temp_buffer = (I_SCALE_1 * ADC) / I_SCALE_2;				  
  			        ADCS.IBAT = (signed int)temp_buffer;			  
				}
				else{	
					ADCS.IBAT = 0;
				}
			
				// Insert sample of battery current into the averaging-array
				// (overwriting the oldest sample), then recalculate and store the
				// average. This is the last conversion in the sequence, so
				// flag a complete ADC-cycle and restart sequence.
				ADCS.discIBAT[(avgIndex++ & 0x03)] = ADCS.IBAT;
				for (i = 0; i < 4 ; i++) {
					temp += ADCS.discIBAT[i];
				}
			
				ADCS.avgIBAT = (temp / 4);			
							
				Next=0x02;				
							
				//Mux 0x00 for next measurement (case 0x02)
				Clear_adc_mux();		
			}  
		break;
		
		// MUX = 0x00 => ADC0 (PF0) = VBAT
		case 0x02:
			temp_buffer = (VBAT_SCALE_1 * ADC )/VBAT_SCALE_2;

			ADCS.rawVBAT = (unsigned int)temp_buffer;
            ADCS.rawVBAT = ADCS.rawVBAT;
			
            //ADCS.VBAT = ADCS.rawVBAT;
           ADCS.VBAT = (ADCS.VBAT + ADCS.rawVBAT)>>1;


#if(TARGET_BOARD == MEGAU4EK)
			//Only to use the same SW that VARTA batteries
			ADCS.rawNTC = 671;
			ADCS.rawRID = 300;

			ADCS.Flag = TRUE;
			Next=0x01;	

			//Mux 09 for next measurement (case 0x01)
			Clear_adc_mux();
			Select_adc_channel(0x09); 

#else
			Next=0x03;


			//Mux Ox23 for next measurement (case 0x03)
			Clear_adc_mux();
			Select_adc_channel(0x23); 
#endif
				
		break;
		
		// MUX = 0x23 => ADC11 (PB4) = NTC
		case 0x03:
#ifdef TRUSTFIRE
			ADCS.rawNTC = 671;//ADC replaced for TrustFire battery (26°)
#else
			ADCS.rawNTC = ADC;
#endif

			Next=0x04;
			
			//Mux 0x24 for next measurement (case 0x04)
			Clear_adc_mux();
			Select_adc_channel(0x24);								
		break;
	
		// MUX = 0x24 => ADC12 (PB5) = RID
		case 0x04:
#ifdef TRUSTFIRE
			ADCS.rawRID = 300;//ADC replaced for TrustFire battery (550mA)					
#else
			ADCS.rawRID = ADC;
#endif
			ADCS.Flag = TRUE;
			Next=0x01;	

			//Mux 09 for next measurement (case 0x01)
			Clear_adc_mux();
			Select_adc_channel(0x09);					
		break;
		
		default:  // Should not happen. (Invalid MUX-channel)
			Next=0x01;  // Start at the beginning of sequence.
			
			//Mux 09 for next measurement (case 0x01)
			Clear_adc_mux();
			Select_adc_channel(0x09);		
		break;
	}
	
	// Update MUX to next channel in sequence, set a bipolar conversion if
	// this has been flagged.
	ADCS.MUX = Next;                    
	
	// Re-enable the ADC unless a halt has been flagged and a conversion
	// cycle has completed.
	if (!(ADCS.Flag))
	{
 		Start_conv();	     
	}

}


/*! \brief Waits for two full cycles of ADC-conversions to occur.
 *
 * This function clears the cycle complete-flag, then waits for it to be set
 * again. This is then repeated once before the function exits.
 * 
 */
void ADC_Wait(void)
{
	//adc enable
//	Enable_adc();
	ADCS.Flag = FALSE; 

	//start a conversion
	Start_conv();

	//wait a complete conversion
	do {
	} while (ADCS.Flag == FALSE);      
		 
	//adc enable
//	Enable_adc();
	ADCS.Flag = FALSE; 
	
	//start a second conversion
	Start_conv();
  
	//wait a complete conversion
	do {
	} while (ADCS.Flag == FALSE);     
	
	//stop ADC (to share CPU time)
//	Disable_adc();		
}


/*! \brief Initializes ADC and input pins.
 *
 * This function initializes the ADC to free running mode,.
 *
 *
 */
void ADC_Init(void)
{
	unsigned char i;

#ifdef __GNUC__
	Disable_interrupt();
#else
	__disable_interrupt();
#endif	

	ADCS.Halt = FALSE; // Enable consecutive runs of ADC.

	// Configure ADC pins (inputs and disabled pull-ups).
	//PF0 PF1
	DDRF &= ~((1<<DDF1)|(1<<DDF0));
	PORTF &= ~((1<<PORTF1)|(1<<PORTF0));

	//DIDR0: disable digital input buffer for PF1 and PF0
	//power consumption improvement
	DIDR0 |= ((1<<ADC1D)|(1<<ADC0D));

#if(TARGET_BOARD != MEGAU4EK)	
	//PB5 PB4
	DDRB &= ~((1<<DDB5)|(1<<DDB4));
	PORTB &= ~((1<<PORTB5)|(1<<PORTB4));

	//DIDR2: disable digital input buffer for PB5 and PB4
	//power consumption improvement
	DIDR2 |= ((1<<ADC12D)|(1<<ADC11D));			
#endif
		
	// Reset the ADC-cycle.
	ADCS.Flag = FALSE;	
	ADCS.MUX = 0x01; 
	
	//Vshunt measurement
	//Select 2.56V as reference + MUX = 0x09 
	//Mux 09
	Clear_adc_mux();
	Enable_internal_vref();
	Select_adc_channel(0x09); 							

    Enable_adc_high_speed_mode();

#if(TARGET_BOARD == MEGAU4EK)	 		
    //Set prescaler to 7 (\128) => 16MHz\128 = 125kHz (max 200kHz)
    Set_adc_prescaler(7);
#else
    //Set prescaler to 6 (\64) => 8MHz\64 = 125kHz (max 200kHz)
    Set_adc_prescaler(6);
#endif

	// Clear averaged battery current and the discrete readings.
	ADCS.avgIBAT = 0;
	
	for (i = 0; i < 4; i++) {
		ADCS.discIBAT[i] = 0;             
	}
	
	// Re-enable the ADC and ISR.
	Enable_adc_it();
	Enable_adc();

#ifdef __GNUC__
	Enable_interrupt();
#else
	__enable_interrupt();
#endif

	// Get a complete cycle of data before returning.
	ADC_Wait();		
}
