/*******************************************************
Project					: Li Switch
Chip type               : ATtiny402
Program type            : Application
AVR Core Clock frequency: 10,000000 MHz
File					: main.c
*******************************************************/

/*_____ I N C L U D E S ____________________________________________________*/

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

#include "main.h"
#include "usart.h"

/*______D E C L A R A T I O N ______________________________________________*/

uint32_t	adc_val;

uint16_t	Vdrop;
uint16_t	Vdrop_percent;

t_Vrefs		VrefType;
t_swStatus	swStatusActual   = SW_STATUS_OFF;
t_swStatus	swStatusRequired = SW_STATUS_OFF;

// Constants

uint16_t	vdrop_max_soft;
uint16_t	vdrop_max_hard;

uint16_t	adc_coefs[VREF_ENUM_COUNT];

uint8_t		Vdrop_percent_max[MAX_SOFT_CYCLES] = { 83, 66, 52, 42, 34, 27, 22, 19, 16, 13 };	// Worst case RC charging curve - Cload = 25 mF

// Counters

t_tasks currentTask = TASK_ADC_TRIGGER;

/*----- Interrupts ---------------------------------------------------------*/

ISR(ADC0_RESRDY_vect) 
{
	adc_val = ADC_RESULT;
	currentTask = TASK_SW_CONTROL;
}

ISR(TCA0_OVF_vect) 
{
	CLK_TIM_OVF_CLR();
	currentTask = TASK_ADC_TRIGGER;
}

/*----- Functions ----------------------------------------------------------*/

void initCoefs()
{
	uint16_t resistor_divider_ratio = 16;
	
	// Calculate ADC coefs
	adc_coefs[VREF_4V34] = (2170 * resistor_divider_ratio);		// (4340/8192) = 2170
	adc_coefs[VREF_0V55] = (275  * resistor_divider_ratio);		// (550/8192)  = 275
}

void vrefSel( t_Vrefs vref )
{
	switch( vref )
	{
		case VREF_4V34: VREF_4V34_INIT();	break;
		case VREF_0V55: VREF_0V55_INIT();	break;
		default:							break;
	}
	
	VrefType = vref;
}


void swControl( void )
{
	static uint8_t soft_cycle_counter;
	
	if ( swStatusRequired == swStatusActual )
	{
		SW_OFF( SW_SOFT_bm );
		return;
	}
		
	if( swStatusRequired == SW_STATUS_OFF )
	{
		SW_OFF( SW_HARD_bm );
		SW_OFF( SW_SOFT_bm );
		swStatusActual = SW_STATUS_OFF_SHORT;
		
		vrefSel( VREF_4V34 );
	}
	
	else if( swStatusRequired == SW_STATUS_ON )
	{
		uint8_t vdrop_index = ( soft_cycle_counter > 0 ) ? soft_cycle_counter-1 : 0;
		
		switch( swStatusActual )
		{
			case SW_STATUS_OFF: case SW_STATUS_OFF_SHORT:
				// Trigger Soft start routine
				SW_ON( SW_SOFT_bm );
				swStatusActual = SW_STATUS_SOFT;
				
				soft_cycle_counter++;
				break;
			
			case SW_STATUS_SOFT:	
				// Soft start takes too long or Vdrop falling too slow due to too large load capacity
				if( (soft_cycle_counter >= MAX_SOFT_CYCLES) || (Vdrop_percent > Vdrop_percent_max[vdrop_index]) )
				{
					SW_OFF( SW_HARD_bm );
					SW_OFF( SW_SOFT_bm );
					swStatusRequired = SW_STATUS_OFF_SHORT;
					swStatusActual = SW_STATUS_OFF_SHORT;
					
					soft_cycle_counter = 0;
				}
			
				// Soft start completed
				else if( (soft_cycle_counter >= MIN_SOFT_CYCLES) && (Vdrop < vdrop_max_soft) )
				{
					SW_ON( SW_HARD_bm );
					swStatusActual = SW_STATUS_ON;
					
					vrefSel( VREF_0V55 );
					soft_cycle_counter = 0;
				}
				
				// Soft start in progress
				else
				{
					soft_cycle_counter++;
				}
				break;
			
			default:
				break;
		}
	}
}


void measDrop( void ) 
{
	static uint16_t Vopen;
	
	Vdrop = ( adc_val * adc_coefs[VrefType] ) >> ADC_MUL_FORMAT;
	
	switch( swStatusActual )
	{
		case SW_STATUS_OFF: case SW_STATUS_OFF_SHORT:
			Vopen = Vdrop;
			break;
			
		case SW_STATUS_SOFT:	
			Vdrop_percent = ( (uint32_t)Vdrop*100 ) / Vopen;	
			break;
			
		default:
			break;
	}	
}


void readDrop( void ) 
{
	const uint8_t datalen = 5;
	
	tx_buffer[BUF_POS_ADDR] = device_id;
	tx_buffer[BUF_POS_DATALEN] = datalen+2;
	tx_buffer[BUF_POS_CMD]	= CMD_READ_SW_DROP;
	tx_buffer[BUF_POS_ARGL] = _BYTEX(Vdrop, 0);
	tx_buffer[BUF_POS_ARGH] = _BYTEX(Vdrop, 1);

	usart_txStart( datalen );	// Send five bytes + CRC
}

void readStat( void ) 
{
	const uint8_t datalen = 5;
	
	tx_buffer[BUF_POS_ADDR] = device_id;
	tx_buffer[BUF_POS_DATALEN] = datalen+2;
	tx_buffer[BUF_POS_CMD]	= CMD_READ_SW_STATUS;
	tx_buffer[BUF_POS_ARGL] = 0;
	tx_buffer[BUF_POS_ARGH] = swStatusActual;
	
	usart_txStart( datalen );	// Send five bytes + CRC
}

void process_uart_data( void ) 
{
	uint8_t cmd;
	
	rx_flags = USART_FLG_NO_DATA;
	cmd = rx_buffer[BUF_POS_CMD];
	
	switch( cmd ) 
	{
		case CMD_SET_SW_ON:			swStatusRequired = SW_STATUS_ON;	break;
		case CMD_SET_SW_OFF:		swStatusRequired = SW_STATUS_OFF;	break;
		case CMD_READ_SW_DROP:		readDrop();							break;
		case CMD_READ_SW_STATUS:	readStat();							break;
	}
}

/*----- Main ---------------------------------------------------------------*/

int main( void )
{
	// Startup
	CLK_INIT();
	SLEEP_INIT();
	PORTMUX_INIT();
	
	USART_INIT();
	USART_TIMOUT_TIM_INIT();
	USART_TIMOUT_RST();
	
	vrefSel( VREF_4V34 );
	initCoefs();
	
	ADC_INIT();
	
	PORTA_INIT();
	
	CLK_TIM_INIT();
	
	SW_ON( SW_HARD_bm );
	
	// Debug
	device_id =			eeprom_read_byte( EEPROM_ID );
	vdrop_max_soft =	eeprom_read_word( EEPROM_DROP_SOFT );
	
	WDT_INIT();
	
	sei();
	
	// Loop
	while ( 1 ) 
	{
		// Task Manager
		switch( currentTask ) 
		{
			case TASK_ADC_TRIGGER: 
				ADC_START( ADC_DROP_CH );
				break;
			
			case TASK_SW_CONTROL: 
				measDrop();
				//swControl();
				break;
				
			default: 
				break;
		}
		currentTask = TASK_NONE;
		
		if( rx_flags ) 
		{
			process_uart_data();
		}
		
		SLEEP();
		wdt_reset();
    }
}