/*-
 *	Copyright (c) 2014-2015	Alexander Morozov. www.nixd.org
 *	All rights reserved.
 *
 * LICENSE: This source file is subject to the New BSD license that is
 * available through the world-wide-web at the following URI:
 * http://www.opensource.org/licenses/bsd-license.php or in the root folder
 * of the project in file LICENSE.TXT.
 */


#ifndef ADC_H_
#define ADC_H_

//Reference: doc2549.pdf ATmega2560 p290-292
#define ADC_REVISION 1

#define ADC_FLAG_LEFT_ADJ 1
#define ADC_FLAG_DISABLE_DIGITAL 2

#define ADC_CONVERSION_RUNNING (ADCSRA & (1<<ADIF))

#if ADC_CHANNELS > ADC_CHANNELS_BANK
	#undef MUX5
	#define MUX5 5
#endif

typedef int8_t adc_id;                  //adc channel id

enum chan_id
{
	ADC_CHAN_0		=	(uint8_t) 0,
	ADC_CHAN_1		=	(uint8_t) 1,
	ADC_CHAN_2		=	(uint8_t) 2,
	ADC_CHAN_3		=	(uint8_t) 3,
	ADC_CHAN_4		=	(uint8_t) 4,
	ADC_CHAN_5		=	(uint8_t) 5,
	ADC_CHAN_6		=	(uint8_t) 6,
	ADC_CHAN_7		=	(uint8_t) 7, // <-- BANK 0 ends
#if ADC_CHANNELS > ADC_CHANNELS_BANK
	ADC_CHAN_8		=	(uint8_t) 8,
	ADC_CHAN_9		=	(uint8_t) 9,
	ADC_CHAN_10		=	(uint8_t) 10,
	ADC_CHAN_11		=	(uint8_t) 11,
	ADC_CHAN_12		=	(uint8_t) 12,
	ADC_CHAN_13		=	(uint8_t) 13,
	ADC_CHAN_14		=	(uint8_t) 14,
	ADC_CHAN_15		=	(uint8_t) 15
#endif
};

/*
 * enum adc_ref
 * ADC references
 */
#define FOREACH_ADC_REF(DV)\
    DV(ADC_REF_INT, (uint8_t) 0)\
    DV(ADC_REF_EXT, (uint8_t) (1 << REFS0))\
    DV(ADC_REF_1VE, (uint8_t) (1 << REFS1))\
    DV(ADC_REF_256VE, (uint8_t) (1 << REFS1) | (1 << REFS0))

enum adc_ref
{
	FOREACH_ADC_REF(GENERATE_ENUM_VAL)
};

/*
 * enum adc_presc
 * ADC clock prescaller
 */
#define FOREACH_ADC_PRESC(DV) \
    DV(ADC_PRESC_2X, (uint8_t) (1 << ADPS0))\
    DV(ADC_PRESC_4X, (uint8_t) (1 << ADPS1))\
    DV(ADC_PRESC_8X, (uint8_t) (1 << ADPS1) | (1 << ADPS0))\
    DV(ADC_PRESC_16X, (uint8_t) (1 << ADPS2))\
    DV(ADC_PRESC_32X, (uint8_t) (1 << ADPS2) | (1 << ADPS0))\
    DV(ADC_PRESC_64X, (uint8_t) (1 << ADPS2) | (1 << ADPS1))\
    DV(ADC_PRESC_128X, (uint8_t) (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0))

enum adc_presc
{
	FOREACH_ADC_PRESC(GENERATE_ENUM_VAL)
};

/*
 * enum adc_event_source
 * ADC event source (see datasheet)
 */
#define FOREACH_ADC_EVENT_SOURCE(DV) \
	DV(ADC_MODE_FREE_RUN, (uint8_t) 0b00000000)	\
	DV(ADC_MODE_ANALOG_COMP, (uint8_t) (1 << ADTS0)) \
	DV(ADC_MODE_EXT_INT_0, (uint8_t) (1 << ADTS1)) \
	DV(ADC_MODE_COUNTER0_CMA, (uint8_t) (1 << ADTS1) | (1 << ADTS0)) \
	DV(ADC_MODE_COUNTER0_OV, (uint8_t) (1 << ADTS2)) \
	DV(ADC_MODE_COUNTER1_CMB, (uint8_t) (1 << ADTS2) | (1 << ADTS0)) \
	DV(ADC_MODE_COUNTER1_OV, (uint8_t) (1 << ADTS2) | (1 << ADTS1))	\
	DV(ADC_MODE_COUNTER1_CE, (uint8_t) (1 << ADTS2) | (1 << ADTS1) | (1 << ADTS0)) \


enum adc_event_source
{
	FOREACH_ADC_EVENT_SOURCE(GENERATE_ENUM_VAL)
};

/*
 * enum adc_channel_selector
 * ADC channel selector
 */
enum adc_channel_selector
{
	ADC_SEL_CHAN_0					= (uint8_t) 0,
	ADC_SEL_CHAN_1					= (uint8_t) (1 << MUX0),
	ADC_SEL_CHAN_2					= (uint8_t) (1 << MUX1),
	ADC_SEL_CHAN_3					= (uint8_t) (1 << MUX1) | (1 << MUX0),
	ADC_SEL_CHAN_4					= (uint8_t) (1 << MUX2),
	ADC_SEL_CHAN_5					= (uint8_t) (1 << MUX2) | (1 << MUX0),
	ADC_SEL_CHAN_6					= (uint8_t) (1 << MUX2) | (1 << MUX1),
	ADC_SEL_CHAN_7					= (uint8_t) (1 << MUX2) | (1 << MUX1) | (1 << MUX0),
	ADC_SEL_GAIN10x_CHAN_0			= (uint8_t) (1 << MUX3),
	ADC_SEL_GAIN10x_CHAN_1_0		= (uint8_t) (1 << MUX3) | (1 << MUX0),
	ADC_SEL_GAIN200x_CHAN_0			= (uint8_t) (1 << MUX3) | (1 << MUX1),
	ADC_SEL_GAIN200x_CHAN_1_0		= (uint8_t) (1 << MUX3) | (1 << MUX1) | (1 << MUX0),
	ADC_SEL_GAIN10x_CHAN_2			= (uint8_t) (1 << MUX3) | (1 << MUX2),
	ADC_SEL_GAIN10x_CHAN_3_2		= (uint8_t) (1 << MUX3) | (1 << MUX2) | (1 << MUX0),
	ADC_SEL_GAIN200x_CHAN_2			= (uint8_t) (1 << MUX3) | (1 << MUX2) | (1 << MUX1),
	ADC_SEL_GAIN200x_CHAN_3_2		= (uint8_t) (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0),
	ADC_SEL_DIF_CHAN_0_1			= (uint8_t) (1 << MUX4),
	ADC_SEL_DIF_CHAN_1				= (uint8_t) (1 << MUX4) | (1 << MUX0),
	ADC_SEL_DIF_CHAN_2_1			= (uint8_t) (1 << MUX4) | (1 << MUX1),
	ADC_SEL_DIF_CHAN_3_1			= (uint8_t) (1 << MUX4) | (1 << MUX1) | (1 << MUX0),
	ADC_SEL_DIF_CHAN_4_1			= (uint8_t) (1 << MUX4) | (1 << MUX2),
	ADC_SEL_DIF_CHAN_5_1			= (uint8_t) (1 << MUX4) | (1 << MUX2) | (1 << MUX0),
	ADC_SEL_DIF_CHAN_6_1			= (uint8_t) (1 << MUX4) | (1 << MUX2) | (1 << MUX1),
	ADC_SEL_DIF_CHAN_7_1			= (uint8_t) (1 << MUX4) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0),
	ADC_SEL_DIF_CHAN_0_2			= (uint8_t) (1 << MUX4) | (1 << MUX3),
	ADC_SEL_DIF_CHAN_1_2			= (uint8_t) (1 << MUX4) | (1 << MUX3) | (1 << MUX0),
	ADC_SEL_DIF_CHAN_2_2			= (uint8_t) (1 << MUX4) | (1 << MUX3) | (1 << MUX1),
	ADC_SEL_DIF_CHAN_3_2			= (uint8_t) (1 << MUX4) | (1 << MUX3) | (1 << MUX1) | (1 << MUX0),
	ADC_SEL_DIF_CHAN_4_2			= (uint8_t) (1 << MUX4) | (1 << MUX3) | (1 << MUX2),
	ADC_SEL_DIF_CHAN_5_2			= (uint8_t) (1 << MUX4) | (1 << MUX3) | (1 << MUX2) | (1 << MUX0),
#if ADC_CHANNELS > ADC_CHANNELS_BANK
	ADC_SEL_CHAN_8					= (uint8_t) (1 << MUX5),
	ADC_SEL_CHAN_9					= (uint8_t) (1 << MUX5) | (1 << MUX0),
	ADC_SEL_CHAN_10					= (uint8_t) (1 << MUX5) | (1 << MUX1),
	ADC_SEL_CHAN_11					= (uint8_t) (1 << MUX5) | (1 << MUX1) | (1 << MUX0),
	ADC_SEL_CHAN_12					= (uint8_t) (1 << MUX5) | (1 << MUX2),
	ADC_SEL_CHAN_13					= (uint8_t) (1 << MUX5) | (1 << MUX2) | (1 << MUX0),
	ADC_SEL_CHAN_14					= (uint8_t) (1 << MUX5) | (1 << MUX2) | (1 << MUX1),
	ADC_SEL_CHAN_15					= (uint8_t) (1 << MUX5) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0)
#endif
};

/*-----------------PROTOCOL------------------*/
/*
 * enum adc_cmds
 * ADC commands
 */
/*#define FOREACH_ADC_CMD(DV)\
	DV(CMD_ENABLE_CHANNEL, (uint8_t) 1)\
	DV(CMD_DISABLE_CHANNEL, (uint8_t) 2)\
	DV(CMD_RUN_READ_CHANNEL, (uint8_t) 3)\
	DV(CMD_RUN_READ_ALL, (uint8_t) 4)\
	DV(CMD_READ_RESULT, (uint8_t) 5)\
	DV(CMD_READ_REASULT_ALL, (uint8_t) 6)


enum adc_cmds
{
	FOREACH_ADC_CMD(GENERATE_ENUM_VAL)
};*/

/*
 * enum p_adc_ref
 */
#define FOREACH_P_ADC_REF(DV)\
	DV(P_ADC_REF_INT, (uint8_t) 0, ADC_REF_INT)\
	DV(P_ADC_REF_EXT, (uint8_t) 1, ADC_REF_EXT)\
	DV(P_ADC_REF_1VE, (uint8_t) 2, ADC_REF_1VE)\
	DV(P_ADC_REF_256VE, (uint8_t) 3, ADC_REF_256VE)

enum p_adc_ref
{
	FOREACH_P_ADC_REF(GENERATE_ENUM_VAL_EX)
};

/*
 * enum p_adc_presc
 */
#define FOREACH_P_ADC_PRESC(DV)\
	DV(P_ADC_PRESC_2X, (uint8_t) 0, ADC_PRESC_2X)\
	DV(P_ADC_PRESC_4X, (uint8_t) 1, ADC_PRESC_4X)\
	DV(P_ADC_PRESC_8X, (uint8_t) 2, ADC_PRESC_8X)\
	DV(P_ADC_PRESC_16X, (uint8_t) 3, ADC_PRESC_16X)\
	DV(P_ADC_PRESC_32X, (uint8_t) 4, ADC_PRESC_32X)\
	DV(P_ADC_PRESC_64X, (uint8_t) 5, ADC_PRESC_64X)\
	DV(P_ADC_PRESC_128X, (uint8_t) 6, ADC_PRESC_128X)

enum p_adc_presc
{
	FOREACH_P_ADC_PRESC(GENERATE_ENUM_VAL_EX)
};
/*-------------------------------------------*/

struct adc_channel
{
    struct dev_descr_node * ddn;    //owner
	adc_id aid;                     //instance id
	uint8_t flags;                  //flags
	uint8_t chans_ids;              //allocated channels
	uint8_t adcsra;                 //register adcsra
	uint8_t adcsrb;                 //register adcsrb
	uint8_t admux;                  //register admux
	uint16_t adc_reading;           //reading

	void (*READING_FINISHED)(const struct adc_channel * s_chanl);   //reading finished callback

	struct adc_channel * next;  //pointer to the next record
};

struct adc_setup
{
    enum adc_event_source event_source;
};

struct adc_config
{
    struct dev_descr_node * ddn;
    struct adc_channel * sel_chanl;
    uint8_t adc_async;
    uint8_t admux_temp;
    uint8_t adcsra_temp;
    uint8_t adcsrb_temp;
    uint8_t chans_list_len;
    enum adc_event_source event_source;
    struct adc_channel * chans_ptr_list;
};

VERIFY_ENUM_FUNCTION_DEC_P(adc_eventsource);


/*
 *   Standart module functions
 */
int8_t adc_hal_message(struct dev_descr_node * ddn,
                          enum dd_event e_event,
                          void * data0,
                          size_t * dsize0);

int8_t adc_kstream_init(struct dev_descr_node * ddn,
                           struct kstream * ks);

int8_t adc_stream_init(struct dev_descr_node * ddn,
                             FILE * file);


const struct adc_channel * get_adc(adc_id aid);
uint8_t get_adc_chans_amount();
int8_t get_adc_mem(struct adc_channel * adc, adc_id offset);
adc_id adc_enable_channel(struct dev_descr_node * ddn,
                          enum adc_channel_selector adc_ch_sel,
                          enum adc_ref ref_src,
                          enum adc_presc adc_clock,
                          uint8_t flags,
                          void (*READING_FINISHED_HND)(const struct adc_channel * ));
int8_t adc_disable_channel(adc_id adi);
void adc_control_digital_input(enum chan_id chan, uint8_t digital_input_status);
int8_t adc_run_reading_SYNC();
int8_t adc_run_reading_ASYNC();
int8_t adc_run_reading(adc_id adi, uint16_t * reading);
int8_t adr_get_reading(adc_id adi, uint16_t * reading);
int8_t adc_get_reading_offset(uint16_t * read, adc_id * chid, adc_id offset);
void adc_print_info();

#endif /* ADC_H_ */
