/** Модуль функций АЦП
 * adc.c
 *
 *
 *  Created on: 22.07.2010
 *      Author: ARV
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr_helper.h>
#include <util/atomic.h>
#include "config.h"
#include "adc.h"
#include <util/delay.h>

/*
 * метод измерения напряжения питания без лишнего пина

1. задаем в качестве опорного AVCC, для чего устанавливаем бит REFS0 в регистре ADMUX
2. измерять будем напряжение BANDGAP, для чего в ADMUX выбираем 14-й канал, т.е. устанавливаем биты MUX3, MUX2 и MUX1
3. напряжение BANDGAP для меги8 равно 1,3 вольта, оно стабильно. при питающем напряжении 5 вольт (не забываем - оно же и есть опорное АЦП) мы намеряем примерно 1,3 / 5 * 1023 = 265
4. если питающее напряжение вдруг упадет до 3 вольт, то замер BANDGAP даст уже 1,3 / 3 * 1023 = 443
Таким образом, при питании от источника мы будем получать результат не более 270 (с запасом), а при питании от батарейки - обязательно более 270. этого вполне достаточно для того, чтобы определить, надо ли, например, усыпить МК или нет.

откалибровать можно как написано здесь:
http://www.rlocman.ru/shem/schematics.html?di=73220

 */
/// массив текущих значений АЦП
//#define ADC_SAMPLE_COUNT 16							// кол-во отсчетов АЦП по которым усередняем значение с АЦП (для одного канала)
//static volatile int16_t adc_data[ADC_SAMPLE_COUNT];	// массив с показаниям от ацп
//static uint8_t chanel = 0;							// канал ацп на котором проводим измерения
//static volatile int8_t adc_status=0;
/* статус работы ацп:
													0  не изменяем ничего (не запущено измерение);
													-1 ведется измерение;
													-2 измерение окончено (еще осталось усереднить)
													1  измерение окончено, можно забирать данные из adc_result_data;
*/

//static int16_t adc_result_data=0;					// результирующее значение с ацп

/** Обработчик прерываний от АЦП
 * Сохраняет замеры в массиве каналов.
 */
/*ISR(ADC_vect){
	static uint8_t sample = 0;
	if (sample>ADC_SAMPLE_COUNT){
		sample=0;											// закончили измерения, т.к. набрали нужное кол-во измерений в массив adc_data
		adc_result_data=adc_result_data/ADC_SAMPLE_COUNT;	// получаем измеренное начение через среднее арифметическое
		//adc_status=-2;										// закончили измеренеия, есть готовность к обработке полученных данных
		adc_status=1;										// измерение готовы
	}else{
		adc_result_data+=ADC;								// сохраняем следущее измерение
		//adc_data[sample] = ADC;							// сохраняем следущее измерение и переходим к следующему
		sample++;
		ADCSRA |= _BV(ADSC);								// запускаем АЦП на измерение
	}
	adc_result_data=ADC;
	adc_status=1;
}*/

/**
 * Фукция возвращает статус в котором находится ацп, см. описание переменной adc_status
 * @return	теекущий статус ацп
 */
/*int8_t get_adc_status(){
	return adc_status;
}*/

/**
 * Измеряет напряжение батареи и возвращает 1 если напряжение ниже допустимого
 * @param нет
 * @return 0- батарею не нужно заряжать, 1- батарея разрядилась (нужно заряжать)
 */
uint8_t get_battery_low(void){
	static int16_t adc_result_data=0;											// результирующее значение с ацп

	ADMUX = ADMUX_BATTERY_METER;												// источник опорного напряжения - AVCC with external capacitor at AREF pin, VBG
	ADCSRA |= _BV(ADSC);														// запускаем АЦП на измерение
	while(ADCSRA & _BV(ADSC));													// ждем окончания измеренияя с ацп
	adc_result_data=(((ADC_BANDGAP_VALUE+config.adc_bandgap_adj)*1024)/ADC);	// переводим значение от ацп к вольтам*10
	if (adc_result_data<=BATTERY_LOW_NOTIFICATION){
		return 1;																// батарейку пора заряжать
	}else{
		return 0;																// батарейка не требует заряда
	}
}
/**
 * Измеряет напряжение батареи и возвращает кк значение
 * @param нет
 * @return напряжение батарейки *10
 */
uint16_t get_battery_voltage(void){

	//ADMUX |= ADMUX_BATTERY_METER;												// источник опорного напряжения - AVCC with external capacitor at AREF pin, VBG
	ADCSRA |= _BV(ADSC);														// запускаем АЦП на измерение
	while(ADCSRA & _BV(ADSC));													// ждем окончания измеренияя с ацп
	return ((((ADC_BANDGAP_VALUE+config.adc_bandgap_adj)*1024)/ADC)+ADC_RESULT_ADJUSTMENT);	// переводим значение от ацп к вольтам*10

}
/**
 * Измеряет напряжение батареи и возвращает  значение регистра ADC
 * @param нет
 * @return напряжение батарейки *10
 */
uint16_t get_battery_adc(void){

	//ADMUX |= ADMUX_BATTERY_METER;												// источник опорного напряжения - AVCC with external capacitor at AREF pin, VBG
	ADCSRA |= _BV(ADSC);														// запускаем АЦП на измерение
	while(ADCSRA & _BV(ADSC));													// ждем окончания измеренияя с ацп
	return ADC;	// возвращаем ADC

}
/**
 * Запускает измерение АЦП
 * @param channel	номер канала
 * @return 1-если запустил измерение; 2-если измерение уже работает и перезапустилось
 */
/*uint8_t start_adc(uint8_t channel){
	//ADMUX|=channel;								// выбираем канал на котором производим измерение
	ADCSRA |= _BV(ADSC);						// запускаем АЦП на измерение
	adc_result_data=0;							// зануляем последнее измеренное значение
	if (adc_status==0) {
		adc_status=-1;							// начали измерение
		return 1;								// запустили измерение в ацп
	}else{
		adc_status=-1;							// начали измерение
		return 2;								// перезапустили измерение в ацп
	}
	return 1;								// запустили измерение в ацп
}*/

/**
 * Возвращаем обработанный результат измерений
 * @return обработаное значени с ацп
 */
/*int16_t get_adc_result(void){
	static long tmp = 0;
	if (adc_status!=1){
	for(uint8_t i=0; i<ADC_SAMPLE_COUNT; i++){
	      tmp += adc_data[i];
	   }
	   adc_result_data= tmp / ADC_SAMPLE_COUNT;
	   tmp=0;
	   adc_status=1;
	}
	   return adc_result_data;
}*/

/** Значение АЦП
 *
 * @param chanel номер запрашиваемого канала
 * @return значение АЦП заданного канала
 */
/*int16_t get_temp(uint8_t chanel){
	// чтение из массива осуществляется при запрещенных прерываниях АЦП - атомарное чтение
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
		return adc_data[chanel];
	}
}*/

/** Температура канала
 * Осуществляет пересчет значений в соответствии с коэффициентами калибровки.
 * @param chanel номер запрашиваемого канала
 * @return температура в канале
 */
/*int16_t get_temperature(uint8_t chanel){
	return (get_temp(chanel) - config.offset[chanel]) * config.gain[chanel];
}*/

// автоматическая инициализация модуля
void adc_init(void){
	/// Вызывать из основного модуля эту функцию не нужно.
	//ADCSRA = _BV(ADEN) | _BV(ADIE) | ADC_DIV_128;			// разрешаем АЦП, прерывание от ацп, скорость работы - низкая
	ADCSRA = _BV(ADEN) | ADC_DIV_128;						// разрешаем АЦП, скорость работы - низкая
	ADMUX=0;
	ADMUX = ADMUX_BATTERY_METER;							// источник опорного напряжения - AVCC with external capacitor at AREF pin, VBG
	_delay_ms(10);
	//ADCSRA |= _BV(ADSC);									// запускаем АЦП на измерение
}

void adc_powerdown(){
	// вызываем при переходе в powerdown
	// The user is 	advised to write zero to ADEN before enter-ing such sleep modes to avoid excessive power consumption
	ADCSRA=0;
}
