/**
 * \file
 *
 * \brief Empty user application template
 *
 */

/**
 * \mainpage User Application template doxygen documentation
 *
 * \par Empty user application template
 *
 * Bare minimum empty user application template
 *
 * \par Content
 *
 * -# Include the ASF header files (through asf.h)
 * -# Minimal main function that starts with a call to board_init()
 * -# "Insert application code here" comment
 *
 */

/*
 * Include header files for all drivers that have been imported from
 * Atmel Software Framework (ASF).
 */
#include <asf.h>

/* Include sleepwalking_appnote header */
#include "sleepwalking_appnote.h"

/***********************************************************************/
/*                          Global var                                 */
/***********************************************************************/
/*	Initial State	*/
volatile char seq_state = INIT_ACTIVE_MODE;

/* ADC Instance declaration */
struct adc_dev_inst g_adc_inst;

extern struct ast_calendar calendar;

/***********************************************************************/
/*                          Functions Prototypes                       */
/***********************************************************************/
 void Button_Callback(void);
 void CATB_callback_sleepwalking(void);
 void ADCIFE_Callback_sleepwalking(void);
 void init_ADCIFE(void);
 void init_qtouch(void);
 void init_ast(void);
 void init_PEVC(void);
 void init_eic(void);
 void app_init(void);
 void prepare_to_sleepwalking(void);
 void enter_wait_mode(void);
 void prepare_to_active_mode(void);
 void app(void);
 
 
 /**
 * Interrupt handler for EIC interrupt
 */
void Button_Callback(void)
{
	/* enable the EIC at Peripheral Bridge Level Mandatory to enable the
	 * next interrupt */
	eic_enable(EIC);

	/* Clear EIC interupt */
	eic_line_clear_interrupt(EIC, GPIO_PUSH_BUTTON_EIC_LINE);

	/* Disable the EIC at Peripheral Bridge Level Mandatory to enable the
	 *next interrupt */
	eic_disable(EIC);

	/* Change the state of the sequential variable to be in Sleepwalking */
	seq_state = INIT_SLEEPWALKING;
	
	ioport_set_pin_level(PIN_PC10,LED0_INACTIVE_LEVEL);
}//end of Button_Callback
 
/**
 * CATB intouch callback.
 */
void CATB_callback_sleepwalking(void)
{
	/* Clear CATB IT */
	CATB->CATB_SCR = CATB_SCR_INTCH;
	while (CATB->CATB_ISR & CATB_ISR_INTCH) {
	}

	/* Change the state of the sequential variable to initialize the Active
	 * Mode*/
	seq_state = INIT_ACTIVE_MODE;
	ioport_set_pin_level(PIN_PC10,LED0_ACTIVE_LEVEL);
}//end of CATB_callback_sleepwalking


/**
 * ADCIFE callback when light sensor detection
 */
void ADCIFE_Callback_sleepwalking(void)
{
	/* clear All interrupt flags for the ADCIFE */
	adc_clear_status(	&g_adc_inst, 
						ADCIFE_SCR_TTO 		| 
						ADCIFE_SCR_SMTRG 	| 
						ADCIFE_SCR_WM 		| 											
						ADCIFE_SCR_LOVR 	|
						ADCIFE_SCR_SEOC 	);
	
	/* Change the state of the sequential variable to initialize the Active Mode*/
	seq_state = INIT_ACTIVE_MODE;
	
	/* Switch ON the LED0 to see when interrupt Hits */
	ioport_set_pin_level(PIN_PC10,LED0_ACTIVE_LEVEL);
}


/**
 * Analog to Digital Converter (ADCIFE) configuration
 */
void init_ADCIFE(void)
{
	/** ADC Configuration structure. **/
	struct adc_config adc_cfg;
	//0.625*VCC, 75k speed, DIV16, Generic clock sources
	/* System clock division factor is 16 */
	adc_cfg.prescal = ADC_PRESCAL_DIV16;
	/* The Generic  clock is used */
	//.clksel = ADC_CLKSEL_GCLK,
	adc_cfg.clksel = ADC_CLKSEL_GCLK;
	/* Max speed is 75K */
	adc_cfg.speed = ADC_SPEED_75K;
	/* ADC Reference voltage is 0.625*VCC */
	adc_cfg.refsel = ADC_REFSEL_2;
	/* Enables the Startup time */
	adc_cfg.start_up = CONFIG_ADC_STARTUP;  //enable the ADC start up value==> the ADC need 12 ADC_CLK at least before starting to convert
		
	/** ADC Sequencer Configuration structure. **/
	struct adc_seq_config adc_seq_cfg;
	/* Select Vref for shift cycle */
	adc_seq_cfg.zoomrange = ADC_ZOOMRANGE_0;
	/* Pad Ground */
	adc_seq_cfg.muxneg = ADC_MUXNEG_1;
	/* Input PB5 ==> Light Sensor */
	adc_seq_cfg.muxpos = ADC_MUXPOS_6;
	/* Enables the internal voltage sources */
	adc_seq_cfg.internal = ADC_INTERNAL_2;
	/* Disables the ADC gain error reduction */
	adc_seq_cfg.gcomp = ADC_GCOMP_DIS;
	/* Disables the HWLA mode */
	adc_seq_cfg.hwla = ADC_HWLA_DIS;
	/* 12-bits resolution */
	adc_seq_cfg.res = ADC_RES_12_BIT;
	/* Enables the single-ended mode */
	adc_seq_cfg.bipolar = ADC_BIPOLAR_SINGLEENDED;
	/* Use the internal Trig source which would correspond to the peripheral event system and so the AST */
	adc_seq_cfg.trgsel = ADC_TRIG_INTL_TRIG_SRC;
	/* Set gain to 0.5 */
	adc_seq_cfg.gain = ADC_GAIN_HALF;
	
	/** ADC Channel Configuration structure. **/	
	struct adc_ch_config adc_ch_cfg;
	
	adc_ch_cfg.seq_cfg = &adc_seq_cfg;
	
	/* Internal Timer Max Counter */
	adc_ch_cfg.internal_timer_max_count = 60;
	/* Window monitor mode is on */
	adc_ch_cfg.window_mode = 3;   //Window Mode 3: active when LT < result<HT , Channel 6 enabled
	adc_ch_cfg.low_threshold = 200;
	adc_ch_cfg.high_threshold = 300;

	/*	Enable The GPIO	for the Light Sensor*/
	ioport_set_pin_mode(GPIO_PB05A_ADCIFE_AD6, MUX_PB05A_ADCIFE_AD6);
	ioport_disable_pin(GPIO_PB05A_ADCIFE_AD6);
	
	/* Apply ADC Configurations */
	adc_init(&g_adc_inst, ADCIFE, &adc_cfg);
	
	/* Enable the ADC */
	adc_enable(&g_adc_inst);

	/*	ADC Sequencer Config	*/
	adc_ch_set_config(&g_adc_inst, &adc_ch_cfg);

	/*	Window mode interupt config	*/
	adc_set_callback(&g_adc_inst, ADC_WINDOW_MONITOR, ADCIFE_Callback_sleepwalking, ADCIFE_IRQn, 1);
}//end init ADCIFE


/**
 * Initialize the CATB and the Qtouch library for the example.
 */
void init_qtouch(void)
{
	
	/*Enable CATB clock at peripheral level	*/
	sysclk_enable_peripheral_clock(CATB);
	
	/*Perform a qtouch Soft reset	*/
	CATB->CATB_CR|=CATB_CR_SWRST;
	
	/*enable CATB clock at cell level in the control register(EN bit) */
	CATB->CATB_CR|=CATB_CR_EN;
	
	/*Enable Peripheral event in CATB module by setting the ETRIG bit */
	CATB->CATB_CR|=CATB_CR_ETRIG;
	
	/*	Configure the QTouch Library to enable the autonomous QTouch detection	*/
	touch_autonomous_sensor_enable();
	
	/* Adjust the Qtouch sensitivity */
	CATB->CATB_CR |= CATB_CR_ESAMPLES(13);
	
	/*Enable CATB IRQN interrupts at core level (NVIC)*/
	NVIC_ClearPendingIRQ	(CATB_IRQn);
	NVIC_SetPriority		(CATB_IRQn,0);
	NVIC_EnableIRQ			(CATB_IRQn);
	
	/*Enable the in touch (INTCH) IT at Peripheral Level*/
	CATB->CATB_IER|=CATB_IER_INTCH;
}//End init_qtouch

/**
 * Initialize the Asynchronous Timer (AST) in calendar mode using 
 * the 32Khz oscillator as clock source.
 */
void init_ast(void)
{
	osc_enable(OSC_ID_OSC32);
	/* wait while the OSC has not started */
	while(!osc_is_ready(OSC_ID_OSC32));
	
	/*Enable the the AST clock at peripheral bridge level and at cell level	*/
	ast_enable(AST);
	
	/*Configure the AST registers to
				•		Set the AST in counter mode
				•		Set the OSCI32KHz as the AST clock source
				•		Initialize the AST counter value to 0				*/
	struct ast_config ast_conf;
	ast_conf.mode = AST_CALENDAR_MODE; // we set the AST in CALendar mode
	ast_conf.osc_type = AST_OSC_32KHZ; //The OSCI 32KHz is the AST clock source
	ast_conf.psel = AST_PSEL_32KHZ_1HZ;//We want a 1s clock frequency
	ast_conf.calendar = calendar;// the calendar struct is used to set up the calendar register
	
	/* Apply AST configuration */
	ast_set_config(AST, &ast_conf);
	
	/* Initialize Calendar value*/
	ast_write_calendar_value(AST, calendar); 
	
	/*Set Periodic event 0 every 0.5 second INSEL bitfield of the Periodic	interval register */
	ast_write_periodic0_value(AST, AST_PSEL_32KHZ_1HZ-1);
	
	/*Enable the Event Interface for Peripheral Event system use in the AST Level*/
	ast_enable_event(AST, AST_EVENT_PER);
}//end init_ast

/**
 * Initialize the Peripheral Event Controller for the CATB and the ADCIFE.
 */
void init_PEVC(void)
{
	/*declare the peripheral CATB event channel configuration structure */
	struct events_ch_conf ch_config_catb;
	
	/*declare the peripheral ADCIFE event channel configuration structure */
	struct events_ch_conf ch_config_adcife;
		
	/*Enable the PEVC Peripheral Clock which is not 
	enabled by default after a RESET (contrary to the AST) */
	events_enable();
	
	/*Configure the PEVC channel to link the AST Periodic 
	event 0 to the  Peripheral Event system CATB trigger one autonomous 
	touch sensing */
	events_ch_get_config_defaults(&ch_config_catb);
	
	ch_config_catb.channel_id = PEVC_ID_USER_CATB;
	ch_config_catb.generator_id = PEVC_ID_GEN_AST_2;
	ch_config_catb.shaper_enable = true;

	/*	call the events_ch_configure() function, defined 
	in events.h to configure the PEVC channel new parameters*/
	events_ch_configure(&ch_config_catb);

	/*Enable the EVENT channel dedicated to the CATB*/
	events_ch_enable(PEVC_ID_USER_CATB); 
	
	/*Configure the PEVC channel to link the AST Periodic 
	event 0 to the  Peripheral Event system ADCIFE trigger one autonomous 
	touch sensing */
	events_ch_get_config_defaults(&ch_config_adcife);
	
	ch_config_adcife.channel_id = PEVC_ID_USER_ADCIFE_SOC;
	ch_config_adcife.generator_id = PEVC_ID_GEN_AST_2;
	ch_config_adcife.shaper_enable = true;

	/*	call the events_ch_configure() function, defined 
	in events.h to configure the PEVC channel new parameters*/
	events_ch_configure(&ch_config_adcife);

	/*Enable the EVENT channel dedicated to the ADCIFE*/
	events_ch_enable(PEVC_ID_USER_ADCIFE_SOC); 
}//end init_PEVC
	

/**
 * Initialize the External Interrupt Controller for the example.
 */
void init_eic(void)
{
		
	/*Configure EIC module to as described previously*/
	struct eic_line_config eic_line_conf;
	
	/*set the EIC mode Triggered*/
	eic_line_conf.eic_mode = EIC_MODE_EDGE_TRIGGERED;
	
	/*	set the EIC mode detection on falling edge*/
	eic_line_conf.eic_edge = EIC_EDGE_FALLING_EDGE;
	
	/*	EIC_LEVEL_LOW_LEVEL is defined in eic.h to set the EIC mode 
		detection on low level	*/
	eic_line_conf.eic_level = EIC_LEVEL_LOW_LEVEL;
	
	/*disable the EIC input filter */
	eic_line_conf.eic_filter = EIC_FILTER_DISABLED;
	
	/*	enable the EIC asynchronous mode detection */
	eic_line_conf.eic_async = EIC_ASYNCH_MODE;

	/*Enable the peripheral clock of the EIC module	*/
	eic_enable(EIC);
	
	/*Apply the interrupt configuration*/
	eic_line_set_config(EIC, GPIO_PUSH_BUTTON_EIC_LINE, &eic_line_conf);
	
	/*Enable the peripheral clock of the EIC module	*/
	eic_line_set_callback(EIC, GPIO_PUSH_BUTTON_EIC_LINE, Button_Callback, EIC_5_IRQn, 1);	
	
	/*Enable the corresponding line	*/
	eic_line_enable(EIC, GPIO_PUSH_BUTTON_EIC_LINE);
}//end of init EIC

/**
 * Application Initialization function Implementation
 */
void app_init(void)
{
	/* Initialize the SAM4L-EK board */
	board_init();
	
	/* Application’s clock setting configuration */
	sysclk_init();
	
	/* Analog to Digital Converter (ADCIFE) configuration */
	init_ADCIFE();
	
	/* Initialize the CATB module and the Qtouch Library */
	init_qtouch();

	/* Initialize the peripheral Event System Controller allowing
	 * Peripherals to request theirs clocks */
	init_PEVC();
	
	/* Initialize the EIC module */
	init_eic();
	
	/* Initialize the black box application */
	black_box_app_init();
	
	/* Initialize & configure the AST */
	init_ast();
}//end app_init


/**
 * Prepare the application to enter in Sleepwalking
 */
void prepare_to_sleepwalking(void)
{

	/* Prepare the Black Box application to reduce consumption*/
	black_box_prepare_to_sleepwalking();

	/* Change the clock source by Switching to the FAST RC clock running at 12MHz */
	switch_to_slower_clock();

	/* Enable CATB clock at peripheral level */
	sysclk_enable_peripheral_clock(CATB);
	
	/* Enable ADCIFE if used */
	adc_enable(&g_adc_inst);

	/* Reinitialize the AST & the PEVC */
	init_ast();
	init_PEVC();

	/* Disable EIC interrupt to avoid external interrupt from PB0 */
	eic_enable(EIC);
	eic_line_disable(EIC, GPIO_PUSH_BUTTON_EIC_LINE);
	eic_disable(EIC);

	/* Go to Wait mode */
	seq_state = SLEEPWALKING;
}//end prepare_to_sleepwalking

/**
 * Function used to enter in wait mode used in Sleepwalking state
 * with power scaling 1 and Fast wake up enabled
 */
void enter_wait_mode(void)
{
	/*Change Power scaling to be in very low power mode 	*/
	bpm_power_scaling_cpu(BPM, BPM_PS_1);

	/* Unlock the BPM PMCON register before modifying it */
	BPM_UNLOCK(PMCON);

	/*enable the fast wake up capability by setting the FASTWKUP bit in the PMCON register*/
	bpm_enable_fast_wakeup(BPM);
	
	/* Enter in wait mode */
	bpm_sleep(BPM, BPM_SM_WAIT);
}//end enter_wait_mode


/**
 * Prepare the application to enter in Active mode
 */
void prepare_to_active_mode(void)
{
	/* switch to Full speed mode (PLL+OSC0) to make available a powerful data processing capability */
	switch_to_full_speed_clock();

	/* Disable CATB clock at peripheral level to avoid qtouch interrupt*/
	sysclk_disable_peripheral_clock(CATB);
	
	/* Disable ADCIFE clock at peripheral level to avoid Light sensor interrupt if ADCIFE is used*/
	adc_disable(&g_adc_inst);

	/* Prepare the Black Box application to active mode */
	black_box_prepare_to_active();

	/* Init the External interrupt Controller */
	init_eic();
	
	/* Go in Run Mode */
	seq_state = ACTIVE_MODE;
}//end prepare_to_active_mode

/**
 * State Machine Function
 * Sleepwalking Application State machine
 */
void app(void)
{
	switch (seq_state) {
	case INIT_SLEEPWALKING:
		/* Prepare the application to enter in Sleepwalking */
		prepare_to_sleepwalking();
		break;

	case SLEEPWALKING:
		/* Sleepwalking : enter in wait mode... */
		enter_wait_mode();
		break;

	case INIT_ACTIVE_MODE:
		/* Prepare the application to enter in active mode */
		prepare_to_active_mode();
		break;

	case ACTIVE_MODE:
		while (seq_state == ACTIVE_MODE) {
			/* use PB0 to perform interrupt to perform the
				*break */
		}
		break;

	default:
		break;
	}
}//end App

/***********************************************************************/
/*                          MAIN Function                              */
/***********************************************************************/
void main(void)
{
	/* Initialize the System and the App */
	app_init();
	
	while (1) {
		app();
	}	
}