/*
 * atkbbb.c
 *
 * Created: 9/21/2018 4:18:52 PM
 * Author : Jake
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>
#include "hardware.h"
#include "fastmath.h"
#include "pin.h"
#include "adc.h"

// PC1 is D2 / PWM
// PC7 is EN
// PC2 is IN1
// PC3 is IN2

void clock_init(void){
	OSC.XOSCCTRL = OSC_XOSCSEL_XTAL_256CLK_gc | OSC_FRQRANGE_12TO16_gc; // select external source
	OSC.CTRL = OSC_XOSCEN_bm; // enable external source
	while(!(OSC.STATUS & OSC_XOSCRDY_bm)); // wait for external
	OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | OSC_PLLFAC0_bm | OSC_PLLFAC1_bm; // select external osc for pll, do pll = source * 3
	//OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | OSC_PLLFAC1_bm; // pll = source * 2 for 32MHz std clock
	OSC.CTRL |= OSC_PLLEN_bm; // enable PLL
	while (!(OSC.STATUS & OSC_PLLRDY_bm)); // wait for PLL to be ready
	CCP = CCP_IOREG_gc; // enable protected register change
	CLK.CTRL = CLK_SCLKSEL_PLL_gc; // switch to PLL for main clock
}

void uarts_init(void){
	rb_init(&up0rbrx);
	rb_init(&up0rbtx);
	pin_init(&up0rxled, &PORTF, PIN0_bm, 0, 1);
	pin_init(&up0txled, &PORTF, PIN1_bm, 1, 1);
	uart_init(&up0, &USARTF0, &PORTF, PIN2_bm, PIN3_bm, &up0rbrx, &up0rbtx, &up0rxled, &up0txled);
	
	uart_start(&up0, SYSTEM_BAUDA, SYSTEM_BAUDB);
	
	ups[0] = &up0;
}

void atkps_init(void){
	atkport_init(&atkp0, 0, &up0);
}

// -------------------------------- PWM GEN CODE

void set_pwm_ccb(uint16_t val){
	// clamp
	if(val > 4095){
		val = 4095;
	}
	
	uint8_t vall = (uint8_t) val;
	uint8_t valh = (uint8_t) (val >> 8);
	
	TCC0.CCBBUFL = vall;
	TCC0.CCBBUFH = valh;
}

// this is ~16khz 
void hbridge_pwm_init(void){
	PORTC.DIRSET = PIN1_bm;
	
	TCC0.CTRLA = TC_CLKSEL_DIV1_gc;
	TCC0.CTRLB = TC_WGMODE_DSBOTH_gc | (1 << 5);
	
	uint16_t per = 4096;
	uint8_t perl = (uint8_t) per;
	uint8_t perh = (uint8_t) (per >> 8);
	
	TCC0.PERBUFL = perl;
	TCC0.PERBUFH = perh;

	set_pwm_ccb(1);
}

// -------------------------------- CONTROL TICKER 

// this is ~ 4khz
void control_ticker_init(void){
	TCD1.CTRLA = TC_CLKSEL_DIV8_gc; 
	TCD1.CTRLB = TC_WGMODE_NORMAL_gc;
	
	uint16_t per = 4096;
	uint8_t perl = (uint8_t) per;
	uint8_t perh = (uint8_t) (per >> 8);
	
	TCD1.PERL = perl;
	TCD1.PERH = perh;
	
	TCD1.INTCTRLA = TC_OVFINTLVL_MED_gc;
}

// -------------------------------- ADC CODE

void init_adc(void){
	/*
	inits adc for single-ended operation, unsigned, on PB0
	*/
	ADCB.CALL = SP_ReadCalibrationByte(PROD_SIGNATURES_START + ADCBCAL0_offset);
	ADCB.CALH = SP_ReadCalibrationByte(PROD_SIGNATURES_START + ADCBCAL1_offset);
	
	ADCB.CTRLB = ADC_RESOLUTION_12BIT_gc;// | ADC_CONMODE_bm; // conmode is for signed ADC 
	
	ADCB.REFCTRL = ADC_REFSEL_INTVCC_gc;
	
	ADCB.PRESCALER = ADC_PRESCALER_DIV16_gc;
	
	ADCB.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCB.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
	
	ADCB.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_MED_gc;
		
	// start it up 
	ADCB.CTRLA |= ADC_ENABLE_bm;
}

void kick_adc(void){
	ADCB.CH0.CTRL |= ADC_CH_START_bm;
}

uint16_t lookup[128] = {
	2047, 2147, 2247, 2347, 2446, 2544, 2641, 2736, 2830, 2922, 3011, 3099, 3184, 3266, 3345, 3421, 3494, 3563, 3629, 3691, 3749, 3802, 3852, 3897, 3938, 3974, 4005, 4032, 4054, 4071, 4084, 4091, 4094, 4091, 4084, 4071, 4054, 4032, 4005, 3974, 3938, 3897, 3852, 3802, 3749, 3691, 3629, 3563, 3494, 3421, 3345, 3266, 3184, 3099, 3011, 2922, 2830, 2736, 2641, 2544, 2446, 2347, 2247, 2147, 2047, 1947, 1847, 1747, 1648, 1550, 1453, 1358, 1264, 1172, 1083, 995, 910, 828, 749, 673, 600, 531, 465, 403, 345, 292, 242, 197, 156, 120, 89, 62, 40, 23, 10, 3, 0, 3, 10, 23, 40, 62, 89, 120, 156, 197, 242, 292, 345, 403, 465, 531, 600, 673, 749, 828, 910, 995, 1083, 1172, 1264, 1358, 1453, 1550, 1648, 1747, 1847, 1947
};

void init_dac(void){
	// DAC0 on PB2
	// DAC1 on PB3 
	DACB.CTRLB = (DACB.CTRLB & ~DAC_CHSEL_gm) | DAC_CHSEL_DUAL_gc;
	DACB.CTRLC = (DACB.CTRLC & ~(DAC_REFSEL_gm | DAC_LEFTADJ_bm)) | DAC_REFSEL_AVCC_gc;
	DACB.CTRLA = (DACB.CTRLA & ~DAC_CH0EN_bm) | DAC_CH1EN_bm | DAC_ENABLE_bm;
}

void init_encoder(void){
	pin_init(&spiEncCsPin, &PORTD, PIN4_bm, 4, 1);
	spi_init(&spiEnc, &USARTD1, &PORTD, PIN6_bm, PIN7_bm, PIN5_bm, &spiEncCsPin);
	spi_start(&spiEnc, 1);
	ams5047_init(&ams5047, &spiEnc);
}

void init_control_vars(){
	// counter for poscontrol
	ctcntr = 0;
	
	// current control variables 
	cc_r = 0; // current control reading 
	cc_l = 0; // current control last 
	cc_t = 750; // current control target 
	cc_err = 0; // current control error 
	cc_edt = 0; // current control error derivative 
	cc_ei = 0; // current control error integral 
	
	// current control parameters
	cc_kpn = 18;
	cc_kpdp = 2;
	
	cc_kin = 20;
	cc_kidp = 5;
	
	// control encoder variables 
	pc_r = 0; // position control reading
	pc_l = 0; // position control last reading
	pc_t = 8192; // target ! // 8192 mid
	pc_err = 0; // position control error 
	pc_edt = 0; // position control error derivative
	pc_ei = 0; // position control error integral 
	
	pc_kpn = 24;
	pc_kpdp = 3;
	
	pc_kin = 4;
	pc_kidp = 8;
	
	pwm_output = 0;
}

// control hooks

void set_pc_t(uint32_t pcktval){
	if(pcktval > 16384){
		pcktval = 16384;
	}
	pc_t = pcktval;
}

void set_cc_t(uint16_t val){
	if(val > 4096){
		val = 4096;
	}
	cc_t = val;
}

// comment this code out
// write hwinterface for setting setpoint
// write wraparound / shortest-walk-to-pt
// swing some weight
// write update tuning variables 
// draw new hardware, print 

void control(){
	ctcntr ++;
	pin_set(&stldbg);
	if(ctcntr & 0b00000100){
		// position loop: 1/4th control loop 
		ctcntr = 0;
		
		// get that encoder value
		pc_l = pc_r;
		ams5047_read(&ams5047, &pc_r);
		//DACB.CH1DATA = pc_r >> 2;
		
		// get that error
		pc_err = (int32_t)pc_t - (int32_t)pc_r;
		DACB.CH1DATA = abs(pc_err) >> 2;
		// k term 
		pc_kterm = (pc_err * pc_kpn) >> pc_kpdp;
		
		// roll on to that integral, then limit 
		pc_iterm += (pc_err * pc_kin) >> pc_kidp;
		if(pc_iterm > 4096){
			pc_iterm = 4096;
		} else if (pc_iterm < -4096){
			pc_iterm = -4096;
		}
		
		pc_term = pc_kterm + pc_iterm;
		
		// find direction based on current error 
		if(pc_term < 0){
			pin_set(&stlerr);
			pin_set(&pin_inv);
		} else {
			pin_clear(&stlerr);
			pin_clear(&pin_inv);
		}
		
		// set targets in abs() ? 
		pc_term = abs(pc_term);
		set_cc_t(pc_term);
		DACB.CH1DATA = cc_t;
	} 
	// do pzwork if ~ 4th cc tick 
	
	cc_l = cc_r;
	cc_r = ADCB.CH0.RES;
	
	// error
	cc_err = (int32_t)cc_t - (int32_t)cc_r;
	
	// integral, and limits 
	// rethink this ? 
	cc_ei += cc_err;
	if(cc_ei > 2048){
		cc_ei = 2048;
	} else if (cc_ei < -2048){
		cc_ei = -2048;
	}
	
	// check for zero-pwm and then assert 
	pwm_output = ((cc_err * cc_kpn) >> cc_kpdp) + ((cc_ei * cc_kin) >> cc_kidp);
	if(pwm_output < 0){
		pwm_output = 0;
		//pin_clear(&stlerr);
	}
	//DACB.CH1DATA = pwm_output;
	set_pwm_ccb(pwm_output);
	pin_clear(&stldbg);
}

int main(void)
{
	// start clock
	clock_init();
	
	// start networking hardware
	uarts_init();
	atkps_init();
	
	// allow interrupts (and set handlers below)
	sei();
	PMIC.CTRL |= PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
	
	// lights
	pin_init(&stlclk, &PORTE, PIN2_bm, 2, 1);
	pin_init(&stlerr, &PORTE, PIN3_bm, 3, 1);
	pin_set(&stlerr);
	
	// debug tick
	pin_init(&stldbg, &PORTB, PIN1_bm, 1, 1);
	pin_clear(&stldbg);
	
	// hbridge begin
	pin_init(&pin_en, &PORTC, PIN7_bm, 7, 1);
	pin_clear(&pin_en);
	pin_init(&pin_inv, &PORTC, PIN6_bm, 6, 1);
	pin_init(&pin_in1, &PORTC, PIN2_bm, 2, 1);
	pin_init(&pin_in2, &PORTC, PIN3_bm, 3, 1);
	
	// set direction '0'
	pin_clear(&pin_inv);
	pin_set(&pin_in2);
	pin_clear(&pin_in1); // in2 = 4.3v ? 
	
	hbridge_pwm_init();
	
	pin_set(&pin_en);
	
	// adc begin
	init_adc();
	
	// dac begin
	init_dac();
	
	// spi begin
	init_encoder();
	
	// begin control ticker
	init_control_vars();
	control_ticker_init();
	
	uint16_t tck = 0;
	uint16_t angle = 0;
    while (1) 
    {
		atkport_scan(&atkp0, 2);
		tck ++;
		if(!fastModulo(tck, 4096)){
			pin_toggle(&stlclk);
		}
    }
}


ISR(USARTF0_RXC_vect){
	uart_rxhandler(&up0);
}

ISR(USARTF0_DRE_vect){
	uart_txhandler(&up0);
}

ISR(TCD1_OVF_vect){
	//pin_toggle(&stlerr);
	kick_adc();
}

ISR(ADCB_CH0_vect){
	control();
}