/**********************************************************************
	Copyright (C) 2018  MisfitTech LLC,  All rights reserved.

 	MisfitTech uses a dual license model that allows the software to be used under
	a standard GPL open source license, or a commercial license.  The standard GPL
	license  requires that all software statically linked with MisfitTec Code is
	also distributed under the same GPL V2 license terms.  Details of both license
	options follow:

	- Open source licensing -
	MisfitTech is a free download and may be used, modified, evaluated and
	distributed without charge provided the user adheres to version two of the GNU
	General Public License (GPL) and does not remove the copyright notice or this
	text.  The GPL V2 text is available on the gnu.org web site

	- Commercial licensing -
	Businesses and individuals that for commercial or other reasons cannot comply
	with the terms of the GPL V2 license must obtain a low cost commercial license
	before incorporating MisfitTech code into proprietary software for distribution in
	any form.  Commercial licenses can be purchased from www.misfittech.net
	and do not require any source files to be changed.


	This code is distributed in the hope that it will be useful.  You cannot
	use MisfitTech's code unless you agree that you use the software 'as is'.
	MisfitTech's code is provided WITHOUT ANY WARRANTY; without even the implied
	warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
	PURPOSE. MisfitTech LLC disclaims all conditions and terms, be they
	implied, expressed, or statutory.


    Written by Trampas Stern for MisfitTech.

    Misfit Tech invests time and resources providing this open source code,
    please support MisfitTech and open-source hardware by purchasing
	products from MisfitTech, www.misifittech.net!
 *********************************************************************/
#include "fet_driver.h"
#include "wiring_private.h"
#include "syslog.h"
#include "angle.h"
#include "Arduino.h"
#include "sine.h"
#include "nonvolatile.h"

#pragma GCC push_options
#pragma GCC optimize ("-Ofast")

#ifdef NEMA_23_10A_HW

#define FET_DRIVER_FREQ  (46875UL) //FET PWM pin driver frequency

FetDriver *FetDriver::ptrInstance=0;

// Wait for synchronization of registers between the clock domains
static __inline__ void syncDAC() __attribute__((always_inline, unused));
static void syncDAC() {
	while (DAC->STATUS.bit.SYNCBUSY == 1)
		;
}


volatile uint32_t coilA_Value=0;
/*
 *  The discrete FETs on the NEMA 23 10A board are configured such that each H-bridge has:
 *    IN1 - Input 1
 *    IN2 - Input 2
 *    Enable - Enable driver
 *    Isense - current sense
 *
 *    The truth table for the H-Bridge is:
 *    Enable	IN1		IN2		Bridge State
 *    0			x		x		floating (FETs off)
 *    1			0		0		coil shorted to Gnd
 *    1			0		1		forward
 *    1			1		0		reverse
 *    1			1		1		coil shorted to VCC
 *
 *    For peak current control there is two state (fast decay, and slow decay)
 *
 *    Fast Decay
 *    When driving coil in forward direction and current peak is reached the fast decay turns
 *    The bridge in the reverse direction. This cause the reverse EMF from coil to charge
 *    capacitors back up and the current on the coil to drop very quickly
 *
 *    Slow Decay
 *    During this mode the current decay is slower by shorting the coil leads to ground.
 *    This in effect shorts the coil leads and reverse EMF is converted to heat.
 *
 *    In the Fast Decay mode we reverse the motor, this in effect is trying to drive coil
 *    current in the reverse direction. This in effect reduces current faster than just
 *    shorting the coil out.
 *
 *    see www.misfittech.net's blog for more information on this subject
 *
 */

/* driver code's logic
 *
 * 	This driver code needs not only to control the FETs but also handle the current limits.
 *
 * 	The way the code handles limiting current is by using two comparators internal to
 * 	the microprocessor.
 *
 * 	We first use two PWM signals to generate reference voltage for each comparator.
 * 	Then when the current sense voltage exceeds this reference voltage an interrupt is
 * 	generated. In the interrupt handler we will then set the decay mode as needed.
 *
 * 	It will have to be determined if we will use a fixed time decay mode like the A4954,
 * 	or use current as the threshold. There is a lot to do here to maintain quite operation,
 * 	that is we need this current control to be running at more than 20khz to be quite.
 *
 * 	Additionally we can use ADC on the current sense for detecting the flyback and
 * 	get some idea of the inductance. This can be used for stall dection as well as
 * 	auto tuning of some of the driver parameters.
 */



#pragma GCC push_options
#pragma GCC optimize ("-Ofast")

#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);

typedef enum {
	CURRENT_ON = 0,
	CURRENT_FAST_DECAY = 1,
	CURRENT_SLOW_DECAY = 2,
} CurrentMode_t;

typedef enum {
	COIL_FORWARD =0,
	COIL_REVERSE =1,
	COIL_BRAKE =2
} CoilState_t;

typedef struct {
	bool currentIncreasing; //true when we are increasing current
	CurrentMode_t currentState; //how is bridge driven
} BridgeState_t;

volatile BridgeState_t BridgeA, BridgeB;


#define DAC_MAX (0x01FFL)
// Wait for synchronization of registers between the clock domains
static __inline__ void syncTCC(Tcc* TCCx) __attribute__((always_inline, unused));
static void syncTCC(Tcc* TCCx) {
	//int32_t t0=1000;
	while (TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK)
	{
		//		t0--;
		//		if (t0==0)
		//		{
		//			break;
		//		}

	}
}





static inline void coilA(CoilState_t state)
{
	PIN_GPIO_OUTPUT(PIN_FET_IN1);
	PIN_GPIO_OUTPUT(PIN_FET_IN2);
	switch(state){

		case COIL_FORWARD:
			GPIO_HIGH(PIN_FET_IN1);
			GPIO_LOW(PIN_FET_IN2);
			break;

		case COIL_REVERSE:
			GPIO_HIGH(PIN_FET_IN2);
			GPIO_LOW(PIN_FET_IN1);
			break;

		case COIL_BRAKE:
			GPIO_LOW(PIN_FET_IN2);
			GPIO_LOW(PIN_FET_IN1);
			break;

		default:
			ERROR("Not a known state");
			break;
	}

}

static inline void coilB(CoilState_t state)
{
	PIN_GPIO_OUTPUT(PIN_FET_IN3);
	PIN_GPIO_OUTPUT(PIN_FET_IN4);
	switch(state){
		case COIL_FORWARD:
			GPIO_HIGH(PIN_FET_IN3);
			GPIO_LOW(PIN_FET_IN4);
			break;

		case COIL_REVERSE:
			GPIO_HIGH(PIN_FET_IN4);
			GPIO_LOW(PIN_FET_IN3);
			break;

		case COIL_BRAKE:
			GPIO_LOW(PIN_FET_IN3);
			GPIO_LOW(PIN_FET_IN4);
			break;

		default:
			ERROR("Not a known state");
			break;
	}
}


int FetDriver::coilA_PWM(int32_t value)
{
	int32_t x;
	// PIN_FET_IN1	 (PA15)		(5)  (TCC0 WO[5], aka ch1)
	//PIN_FET_IN2    (PA20)		(6)  (TCC0 WO[6], aka ch2)
	Tcc* TCCx = TCC0 ;

//
//	if (value==0)
//	{
//		GPIO_LOW(PIN_FET_IN1);
//		GPIO_LOW(PIN_FET_IN2);
//		PIN_GPIO(PIN_FET_IN1);
//		PIN_GPIO(PIN_FET_IN2);
//		return;
//	}

	if (value<0)
	{
		GPIO_LOW(PIN_FET_IN1);
		PIN_GPIO(PIN_FET_IN1);
		PIN_PERIPH(PIN_FET_IN2);
		//pinPeripheral(PIN_FET_IN2, PIO_TIMER_ALT); //TCC0 WO[7]
		value=-value;
	}else
	{
		GPIO_LOW(PIN_FET_IN2);
		PIN_GPIO(PIN_FET_IN2);
		PIN_PERIPH(PIN_FET_IN1);
		//pinPeripheral(PIN_FET_IN1, PIO_TIMER_ALT);
	}


#if (F_CPU/FET_DRIVER_FREQ)==1024
	x=value & 0x3FF;
#else
	x=MIN(value, (int32_t)(F_CPU/FET_DRIVER_FREQ));
#endif

	syncTCC(TCCx);
	TCCx->CC[1].reg = (uint32_t)x; //ch1 == ch5 //IN3
	//syncTCC(TCCx);
	TCCx->CC[2].reg = (uint32_t)x; //ch2 == ch6 //IN4
	if (x!=value)
	{
		return 1;
	}
	return 0;

}

void FetDriver::coilB_PWM(int32_t value)
{

	//PIN_FET_IN3	 (PA21)		(7)	 (TCC0 WO[7], aka ch3)
	//PIN_FET_IN4    (PA14)		(2)  (TCC0 WO[4], aka ch0)
	Tcc* TCCx = TCC0 ;


//
//	if (value==0)
//	{
//		GPIO_LOW(PIN_FET_IN3);
//		GPIO_LOW(PIN_FET_IN4);
//		PIN_GPIO(PIN_FET_IN3);
//		PIN_GPIO(PIN_FET_IN4);
//		return;
//	}


	if (value<=0)
	{
		GPIO_LOW(PIN_FET_IN3);
		PIN_GPIO(PIN_FET_IN3);
		PIN_PERIPH(PIN_FET_IN4);
		//SET_PIN_PERHERIAL(PIN_FET_IN4, PIO_TIMER_ALT); //TCC0 WO[7]
		value=-value;
	}else
	{
		GPIO_LOW(PIN_FET_IN4);
		PIN_GPIO(PIN_FET_IN4);
		PIN_PERIPH(PIN_FET_IN3);
		//SET_PIN_PERHERIAL(PIN_FET_IN3, PIO_TIMER_ALT);
	}


#if (F_CPU/FET_DRIVER_FREQ)==1024
	value=value & 0x3FF;
#else
	value=MIN(value, (int32_t)(F_CPU/FET_DRIVER_FREQ));
#endif

	//LOG("value is %d",value);
	//	if (value> 300) //(F_CPU/FET_DRIVER_FREQ))
	//	{
	//		value= 300; //F_CPU/FET_DRIVER_FREQ;
	//	}
	syncTCC(TCCx);
	TCCx->CC[0].reg = (uint32_t)value; //ch0 == ch4 //IN4
	//syncTCC(TCCx);
	TCCx->CC[3].reg = (uint32_t)value; //ch3 == ch7  //IN3


}

static void enableTCC0(void)
{
	Tcc* TCCx = TCC0 ;

	GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC0_TCC1 )) ;

	while ( GCLK->STATUS.bit.SYNCBUSY == 1 ) ;

	//ERROR("Setting TCC %d %d",ulValue,ulPin);
	TCCx->CTRLA.reg &= ~TCC_CTRLA_ENABLE;
	syncTCC(TCCx);

	// Set TCx as normal PWM
	TCCx->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;
	syncTCC(TCCx);

	// Set PER to maximum counter value (resolution : 0xFF)
	TCCx->PER.reg = F_CPU/FET_DRIVER_FREQ; //set frequency to 100Khz
	syncTCC(TCCx);

	// Enable TCCx
	TCCx->CTRLA.reg |= TCC_CTRLA_ENABLE ;
	syncTCC(TCCx);
	//ERROR("Enable TCC0 DONE");

}

static void setDAC(uint32_t DAC1, uint32_t DAC2)
{
	TCC1->CC[1].reg = (uint32_t)DAC1; //D9 PA07 - VREF12
	syncTCC(TCC1);
	TCC1->CC[0].reg = (uint32_t)DAC2; //D4 - VREF34
	syncTCC(TCC1);
}



static void setupDAC(void)
{
	Tcc* TCCx = TCC1 ;


	pinPeripheral(PIN_FET_VREF1, PIO_TIMER_ALT);
	pinPeripheral(PIN_FET_VREF2, PIO_TIMER_ALT);

	GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC0_TCC1 )) ;

	while ( GCLK->STATUS.bit.SYNCBUSY == 1 ) ;

	//ERROR("Setting TCC %d %d",ulValue,ulPin);
	TCCx->CTRLA.reg &= ~TCC_CTRLA_ENABLE;
	syncTCC(TCCx);

	// Set TCx as normal PWM
	TCCx->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;
	syncTCC(TCCx);

	// Set TCx in waveform mode Normal PWM
	TCCx->CC[1].reg = (uint32_t)0;
	syncTCC(TCCx);

	TCCx->CC[0].reg = (uint32_t)0;
	syncTCC(TCCx);

	// Set PER to maximum counter value (resolution : 0xFFF = 12 bits)
	// =48e6/2^12=11kHz frequency
	TCCx->PER.reg = DAC_MAX;
	syncTCC(TCCx);

	// Enable TCCx
	TCCx->CTRLA.reg |= TCC_CTRLA_ENABLE ;
	syncTCC(TCCx);

}


/*
 * The SAMD21 has two analog comparators
 *  COMP_FET_A(A4/PA05) and COMP_FET_B(D9/PA07) are the reference voltages
 *
 *  ISENSE_FET_A(A3/PA04) and ISENSE_FET_B(D8/PA06) are the current sense
 *
 */
/*
static void setupComparators(void)
{
	//setup the pins as analog inputs
	pinPeripheral(COMP_FET_A, PIO_ANALOG); //AIN[1]
	pinPeripheral(COMP_FET_B, PIO_ANALOG); 	//AIN[3]
	pinPeripheral(ISENSE_FET_A, PIO_ANALOG);  //AIN[0]
	pinPeripheral(ISENSE_FET_B, PIO_ANALOG);  //AIN[2]

	//enable the clock for the Analog comparator
	PM->APBCMASK.reg |= PM_APBCMASK_AC; //enable clock in the power manager

	//setup the GCLK for the analog and digital clock to the AC
	GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_AC_ANA )) ;
	 while ( GCLK->STATUS.bit.SYNCBUSY == 1 ) ;
	GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_AC_DIG )) ;
	 while ( GCLK->STATUS.bit.SYNCBUSY == 1 ) ;


	//we will drive the CMP0 and CMP1 high when our current is exceeded.
	// To do this we will set ISense Pins as the non-inverting input
	AC->CTRLA.reg=0x01; //disable AC_COMPCTRL_ENABLE and reset
	while ( AC->STATUSB.bit.SYNCBUSY == 1 ) ;
	AC->CTRLB.reg=0x0; // set start bits low (will not be used)
	while ( AC->STATUSB.bit.SYNCBUSY == 1 ) ;
	AC->COMPCTRL[0].reg = 	AC_COMPCTRL_FLEN_MAJ3_Val | //add a 3 bit majority digital filter
							AC_COMPCTRL_HYST | //enable hysterisis
							AC_COMPCTRL_MUXPOS_PIN0 | //non-inverting is AIN[0]
							AC_COMPCTRL_MUXNEG_PIN1 | //inverting pin is AIN[1]
							AC_COMPCTRL_INTSEL_RISING | //interrupt on the rising edge (TODO we might want on both edges)
							AC_COMPCTRL_SPEED_HIGH |
							AC_COMPCTRL_ENABLE;  //set to high speed mode, we don't care about power consumption
	while ( AC->STATUSB.bit.SYNCBUSY == 1 ) ;
	AC->COMPCTRL[1].reg = 	//AC_COMPCTRL_FLEN_MAJ3_Val | //add a 3 bit majority digital filter
							//AC_COMPCTRL_HYST | //enable hysterisis
							AC_COMPCTRL_MUXPOS_PIN2 | //non-inverting is AIN[2]
							AC_COMPCTRL_MUXNEG_PIN3 | //inverting pin is AIN[3]
							AC_COMPCTRL_INTSEL_RISING | //interrupt on the rising edge (TODO we might want on both edges)
							AC_COMPCTRL_SPEED_HIGH |
							//AC_COMPCTRL_SWAP |
							AC_COMPCTRL_ENABLE;  //set to high speed mode, we don't care about power consumption
	while ( AC->STATUSB.bit.SYNCBUSY == 1 ) ;

	//enable the comparator
	AC->CTRLA.reg=AC_CTRLA_ENABLE;
	while ( AC->STATUSB.bit.SYNCBUSY == 1 );



	AC->INTENSET.bit.COMP0=1;
	AC->INTENSET.bit.COMP1=1;
	NVIC_EnableIRQ(AC_IRQn); //enable the comparator interrupt
}
 */

static __inline__ void syncADC() __attribute__((always_inline, unused));
static void syncADC() {
	volatile int32_t t0=100;
	while ((ADC->STATUS.bit.SYNCBUSY == 1))// && t0>0)
	{
		t0--;
		if (t0>0)
		{
			break;
		}
	}
	if (t0<=0)
	{
		ERROR("sync ADC timeout");
	}
}




static uint32_t ADCRead(uint32_t ulPin)
{
	uint32_t valueRead = 0;
	uint32_t gainValue=0;

	if ( ulPin <= 5 ) // turn '0' -> 'A0'
	{
		ulPin += A0 ;
	}
	if (ulPin == 6) ulPin = PIN_A6;
	if (ulPin == 7) ulPin = PIN_A7;

	pinPeripheral(PIN_A4, PIO_ANALOG);

	pinPeripheral(ulPin, PIO_ANALOG);

	syncADC();
	ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV32 |    // Divide Clock by 512.
			ADC_CTRLB_RESSEL_12BIT;         // 10 bits resolution as default
	//  syncADC();
	// ADC->INPUTCTRL.reg = 0;

	//  syncADC();
	//  ADC->INPUTCTRL.bit.MUXNEG=  ADC_INPUTCTRL_MUXNEG_GND;//g_APinDescription[ulPin].ulADCChannelNumber; //ADC_INPUTCTRL_MUXNEG_GND;
	//ADC_INPUTCTRL_MUXNEG_IOGND; //ADC_INPUTCTRL_MUXNEG_PIN5;   // No Negative input (Internal Ground)


	syncADC();
	ADC->INPUTCTRL.bit.MUXPOS =  g_APinDescription[ulPin].ulADCChannelNumber;//ADC_INPUTCTRL_MUXPOS_DAC;// g_APinDescription[ulPin].ulADCChannelNumber; // Selection for the positive ADC input


	syncADC();
	ADC->INPUTCTRL.bit.GAIN = 0xF; //0x0F == gain of 1/2

	syncADC();
	ADC->REFCTRL.reg=ADC_REFCTRL_REFSEL_INTVCC1; //set the ADC reference to 1/2VDDANA

	syncADC();
	ADC->SAMPCTRL.reg=0x02;
	/*
	 * Bit 1 ENABLE: Enable
	 *   0: The ADC is disabled.
	 *   1: The ADC is enabled.
	 * Due to synchronization, there is a delay from writing CTRLA.ENABLE until the peripheral is enabled/disabled. The
	 * value written to CTRL.ENABLE will read back immediately and the Synchronization Busy bit in the Status register
	 * (STATUS.SYNCBUSY) will be set. STATUS.SYNCBUSY will be cleared when the operation is complete.
	 *
	 * Before enabling the ADC, the asynchronous clock source must be selected and enabled, and the ADC reference must be
	 * configured. The first conversion after the reference is changed must not be used.
	 */

	syncADC();
	ADC->CTRLA.bit.ENABLE = 0x01;             // Enable ADC


	// Clear the Data Ready flag
	syncADC();
	ADC->INTFLAG.bit.RESRDY = 1;
	// Start conversion
	syncADC();
	ADC->SWTRIG.bit.START = 1;


	// wait for conversion to be done
	while ( ADC->INTFLAG.bit.RESRDY == 0 );   // Waiting for conversion to complete

	// Clear the Data Ready flag
	syncADC();
	ADC->INTFLAG.bit.RESRDY = 1;
	// Start conversion again, since The first conversion after the reference is changed must not be used.
	syncADC();
	ADC->SWTRIG.bit.START = 1;

	while ( ADC->INTFLAG.bit.RESRDY == 0 );   // Waiting for conversion to complete
	valueRead = ADC->RESULT.reg;

	//	syncADC();
	//	ADC->CTRLA.bit.ENABLE = 0x00;             // Disable ADC
	//	syncADC();

	return valueRead; //mapResolution(valueRead, _ADCResolution, _readResolution);

}

int32_t fastADCRead(uint32_t ulPin)
{
	int32_t valueRead;
	if ( ulPin <= 5 ) // turn '0' -> 'A0'
	{
		ulPin += A0 ;
	}
	if (ulPin == 6) ulPin = PIN_A6;
	if (ulPin == 7) ulPin = PIN_A7;
	syncADC();
	ADC->INPUTCTRL.bit.MUXPOS =  g_APinDescription[ulPin].ulADCChannelNumber;//ADC_INPUTCTRL_MUXPOS_DAC;// g_APinDescription[ulPin].ulADCChannelNumber; // Selection for the positive ADC input
	// Clear the Data Ready flag
	syncADC();
	ADC->INTFLAG.bit.RESRDY = 1;
	// Start conversion again, since The first conversion after the reference is changed must not be used.
	syncADC();
	ADC->SWTRIG.bit.START = 1;

	while ( ADC->INTFLAG.bit.RESRDY == 0 );   // Waiting for conversion to complete
	valueRead = ADC->RESULT.reg;
	return valueRead;
}

int32_t GetMeanAdc(uint16_t pin, uint16_t samples)
{
	int32_t i=0;
	int32_t mean=0;
	int32_t adc;
	while (i<samples)
	{
		adc=ADCRead(pin);
		mean+=adc;
		i++;
	}
	return mean/i;
}

static uint32_t ADCStart(uint32_t ulPin)
{
	uint32_t valueRead = 0;
	uint32_t gainValue=0;

	if ( ulPin <= 5 ) // turn '0' -> 'A0'
	{
		ulPin += A0 ;
	}
	if (ulPin == 6) ulPin = PIN_A6;
	if (ulPin == 7) ulPin = PIN_A7;

	pinPeripheral(PIN_A4, PIO_ANALOG);

	pinPeripheral(ulPin, PIO_ANALOG);

	syncADC();
	ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV64 |    // Divide Clock by 512.
			ADC_CTRLB_RESSEL_12BIT;         // 10 bits resolution as default
	//  syncADC();
	// ADC->INPUTCTRL.reg = 0;

	//  syncADC();
	//  ADC->INPUTCTRL.bit.MUXNEG=  ADC_INPUTCTRL_MUXNEG_GND;//g_APinDescription[ulPin].ulADCChannelNumber; //ADC_INPUTCTRL_MUXNEG_GND;
	//ADC_INPUTCTRL_MUXNEG_IOGND; //ADC_INPUTCTRL_MUXNEG_PIN5;   // No Negative input (Internal Ground)

	syncADC();
	ADC->INPUTCTRL.bit.MUXPOS =  g_APinDescription[ulPin].ulADCChannelNumber;//ADC_INPUTCTRL_MUXPOS_DAC;// g_APinDescription[ulPin].ulADCChannelNumber; // Selection for the positive ADC input

	syncADC();
	ADC->INPUTCTRL.bit.INPUTSCAN=0;
	//
	//	switch (gain)
	//	{
	//		case 1:
	//			gainValue=ADC_INPUTCTRL_GAIN_1X_Val;
	//			break;
	//		case 2:
	//			gainValue=ADC_INPUTCTRL_GAIN_2X_Val;
	//			break;
	//		case 4:
	//			gainValue=ADC_INPUTCTRL_GAIN_4X_Val;
	//			break;
	//		case 8:
	//			gainValue=ADC_INPUTCTRL_GAIN_8X_Val;
	//			break;
	//		case 16:
	//			gainValue=ADC_INPUTCTRL_GAIN_16X_Val;
	//			break;
	//		default:
	//			gainValue=ADC_INPUTCTRL_GAIN_1X_Val;
	//			break;
	//	}

	//  syncADC();
	// ADC->CTRLB.bit.DIFFMODE = 0; //set to differential mode

	syncADC();
	ADC->INPUTCTRL.bit.GAIN = 0xF; //0x0F == gain of 1/2

	// syncADC();
	// ADC->AVGCTRL.reg=5;

	syncADC();
	ADC->REFCTRL.reg=ADC_REFCTRL_REFSEL_INTVCC1; //set the ADC reference to 1/2VDDANA

	syncADC();
	ADC->SAMPCTRL.reg=0x0F;
	/*
	 * Bit 1 ENABLE: Enable
	 *   0: The ADC is disabled.
	 *   1: The ADC is enabled.
	 * Due to synchronization, there is a delay from writing CTRLA.ENABLE until the peripheral is enabled/disabled. The
	 * value written to CTRL.ENABLE will read back immediately and the Synchronization Busy bit in the Status register
	 * (STATUS.SYNCBUSY) will be set. STATUS.SYNCBUSY will be cleared when the operation is complete.
	 *
	 * Before enabling the ADC, the asynchronous clock source must be selected and enabled, and the ADC reference must be
	 * configured. The first conversion after the reference is changed must not be used.
	 */
	syncADC();
	ADC->CTRLA.bit.ENABLE = 0x01;             // Enable ADC


	//Setup up for ISR
	ADC->INTENCLR.reg=0x0F;
	ADC->INTENSET.bit.RESRDY=1;

	NVIC_SetPriority(ADC_IRQn, 3);


	// Clear the Data Ready flag
	ADC->INTFLAG.bit.RESRDY = 1;

	// Start conversion
	syncADC();
	ADC->SWTRIG.bit.START = 1;



	// Start conversion again, since The first conversion after the reference is changed must not be used.
	//syncADC();
	//ADC->SWTRIG.bit.START = 1;

	//ADC->INTENSET.bit.RESRDY=1;

	//  // Store the value
	while ( ADC->INTFLAG.bit.RESRDY == 0 );   // Waiting for conversion to complete
	//  valueRead = ADC->RESULT.reg;
	//
	//  syncADC();
	//  ADC->CTRLA.bit.ENABLE = 0x00;             // Disable ADC
	//  syncADC();

	uint32_t reg;

	syncADC();
	reg=ADC->CTRLA.reg;
	LOG("ADC CTRLA 0x%04X",reg);

	syncADC();
	reg=ADC->REFCTRL.reg;
	LOG("ADC REFCTRL 0x%04X",reg);

	syncADC();
	reg=ADC->AVGCTRL.reg;
	LOG("ADC AVGCTRL 0x%04X",reg);

	syncADC();
	reg=ADC->SAMPCTRL.reg;
	LOG("ADC SAMPCTRL 0x%04X",reg);

	syncADC();
	reg=ADC->CTRLB.reg;
	LOG("ADC CTRLB 0x%04X",reg);

	syncADC();
	reg=ADC->INPUTCTRL.reg;
	LOG("ADC INPUTCTRL 0x%04X",reg);

	syncADC();
	reg=ADC->GAINCORR.reg;
	LOG("ADC GAINCORR 0x%04X",reg);

	syncADC();
	reg=ADC->OFFSETCORR.reg;
	LOG("ADC OFFSETCORR 0x%04X",reg);

	syncADC();
	reg=ADC->CALIB.reg;
	LOG("ADC CALIB 0x%04X",reg);


	// Enable InterruptVector
	NVIC_EnableIRQ(ADC_IRQn);

	// Clear the Data Ready flag
	ADC->INTFLAG.bit.RESRDY = 1;


	// Start conversion
	syncADC();
	ADC->SWTRIG.bit.START = 1;

	return 0;//valueRead; //mapResolution(valueRead, _ADCResolution, _readResolution);
}
void ADC_Handler(void)
{

	uint16_t channel;
	uint16_t value;
	static uint16_t lastChannel=0;

	//static int state=0;
	YELLOW_LED(1);
	//state=(state+1)&0x01;

	value=ADC->RESULT.reg;
	channel=ADC->INPUTCTRL.bit.MUXPOS;// + ADC->INPUTCTRL.bit.INPUTOFFSET;

	//LOG("channel is %d %d", lastChannel,value);

	FetDriver::ADC_Callback(lastChannel,value);
	lastChannel=channel;

	if (channel == g_APinDescription[ISENSE_FET_B].ulADCChannelNumber)
	{
		syncADC();
		ADC->INPUTCTRL.bit.MUXPOS =  g_APinDescription[ISENSE_FET_A].ulADCChannelNumber;
	} else
	{
		syncADC();
		ADC->INPUTCTRL.bit.MUXPOS =  g_APinDescription[ISENSE_FET_B].ulADCChannelNumber;
	}

	//LOG("channel is %d %d", ADC->INPUTCTRL.bit.MUXPOS ,value);
	//syncADC();
	ADC->INTFLAG.bit.RESRDY = 1;
	//syncADC();
	ADC->SWTRIG.bit.START = 1;
	YELLOW_LED(0);
	//state=(state+1)&0x01;

}



void FetDriver::ADC_Callback(uint16_t channel, uint16_t value)
{

	//ptrInstance->begin();
	if (ptrInstance==NULL)
	{
		return;
	}
	ptrInstance->ctrl_update(channel,value);

}

void FetDriver::ctrl_update(uint16_t channel, uint16_t value)
{
	int32_t x,error;

	if (channel ==  g_APinDescription[ISENSE_FET_A].ulADCChannelNumber)
	{
		static int32_t iterm;

		x=value-coilA_Zero;
		error=coilA_SetPoint-x;
		coilA_error=x;
		iterm+=error;

		x=error*15;//+iterm/10;
		x=x/1024;
		coilA_value+=x;

//		if (error>0)
//			coilA_value++;
//			else
//				coilA_value--;
//
//		coilA_value+= iterm/1024;
		coilA_PWM(coilA_value);
//		if (error>0)
//		{
//			coilA(COIL_FORWARD);
//		}else
//		{
//			coilA(COIL_BRAKE);
//		}

	}

	if (channel ==  g_APinDescription[ISENSE_FET_B].ulADCChannelNumber)
	{
		static int32_t itermB;
		x=value-coilB_Zero;
		error=coilB_SetPoint-x;
		coilB_error=error;


		x=error*15+itermB/10;
		x=x/1024;
		coilB_value+=x;

		//coilB_PWM(coilB_value);
//		if (error>0)
//		{
//			coilB(COIL_FORWARD);
//		}else
//		{
//			coilB(COIL_BRAKE);
//		}

	}
	return;

	//LOG("channel is %d %d", channel,value);
	if (channel ==  g_APinDescription[ISENSE_FET_B].ulADCChannelNumber)
	{
		static int32_t ib=0;
		static int32_t meanb=0;
		int32_t error,u,de;
		static int32_t itermb=0;;
		static int32_t lastErrorb=0;

		adc=value;
		x=value-coilB_Zero;
		if (coilB_Zero==-1)
		{
			if(ib<FET_DRIVER_NUM_ZERO_AVG)
			{
				meanb=meanb+x;
				ib++;
			}else
			{
				coilB_Zero=meanb/ib;
			}
			return;
		}

		error=coilB_SetPoint-x;

		//		if (error>0)
		//			u=1;
		//		else
		//			u=-1;

		de=error-lastErrorb;
		lastErrorb=error;

		if (ABS(error)<50)
		{
			itermb=itermb+1*error;
		}else
		{
			itermb=0;
		}
		u=error*320 + itermb +100*de;
		u=u/16382;
		if (u>10) u=10;
		if (u<-10) u=-10;

		coilB_value+=u;;
		//LOG("coil value %d, %d",coilB_value,u);
		coilB_value=MIN(coilB_value,(int32_t)(F_CPU/FET_DRIVER_FREQ));
		coilB_value=MAX(coilB_value,(int32_t)(-(F_CPU/FET_DRIVER_FREQ)));

		coilB_PWM(coilB_value);

		return;
	}

	if (channel ==  g_APinDescription[ISENSE_FET_A].ulADCChannelNumber)
	{
		static int32_t i=0;
		static int32_t mean=0;
		int32_t error,u,de;
		static int32_t iterm=0;;
		static int32_t lastError=0;


		x=value-coilA_Zero;
		if (coilA_Zero==-1)
		{
			if(i<FET_DRIVER_NUM_ZERO_AVG)
			{
				mean=mean+x;
				i++;
			}else
			{
				coilA_Zero=mean/i;
			}

			return;
		}

		error=coilA_SetPoint-x;
		de=error-lastError;
		lastError=error;

		if (ABS(error)<50)
		{
			iterm=iterm+1*error;
		}else
		{
			iterm=0;
		}
		u=error*320 + iterm +100*de;
		u=u/16382;
		if (u>10) u=10;
		if (u<-10) u=-10;

		coilA_value+=u;
		//LOG("coil value %d, %d",coilB_value,u);
		coilA_value=MIN(coilA_value,(int32_t)(F_CPU/FET_DRIVER_FREQ));
		coilA_value=MAX(coilA_value,(int32_t)(-(F_CPU/FET_DRIVER_FREQ)));

		coilA_PWM(coilA_value);
		return;
	}

}


void FetDriver::measureCoilB_zero(void)
{
	coilB_Zero=GetMeanAdc(ISENSE_FET_B,FET_DRIVER_NUM_ZERO_AVG);
	LOG("Coil B Zero is %d",coilB_Zero);
	return;
}

void FetDriver::measureCoilA_zero(void)
{
	coilA_Zero=GetMeanAdc(ISENSE_FET_A,FET_DRIVER_NUM_ZERO_AVG);
	LOG("Coil A Zero is %d",coilA_Zero);
	return;
}


void FetDriver::CalTableA(int32_t maxMA)
{

	int16_t table2[512]={0};
	int32_t pwm=0;
	int32_t mA=0;
	int i;


	while (mA>-maxMA)
	{
		int32_t adc;
		//LOG("Running %d",pwm);
		adc=GetMeanAdc(ISENSE_FET_A,10)-coilA_Zero;
		//LOG("ADC is %d",adc);
		mA=FET_ADC_TO_MA(adc);
		//LOG("mA is %d, ADC %d",mA,ADC);
		pwm=pwm-1;

		if (coilA_PWM(pwm)==1)
		{
			ERROR("CoilA PWM maxed");
			break;
		}
		//delay(5);
	}

	//LOG("First PWM is %d %d",pwm, mA);
	PWM_Table_A[0]=pwm;
	table2[0]=mA;
	i=1;
	while (i<512)
	{
		int32_t adc;
		adc=GetMeanAdc(ISENSE_FET_A,10)-coilA_Zero;
		mA=FET_ADC_TO_MA(adc);

		//LOG("PWM %d, %d %d",i,mA,pwm);
		if (mA>((i-255)*maxMA/256))
		{
			PWM_Table_A[i]=pwm;
			table2[i]=mA;
			i++;
		}else
		{
			pwm=pwm+1;
			coilA_PWM(pwm);
			//delay(5);
		}
	}
	coilA_PWM(0);

	Serial.print("\n\r TABLE A \n\r");;
	for (i=0; i<512; i++)
	{
		Serial.print(PWM_Table_A[i]);
		Serial.print(",");
	}
	Serial.print("\n\r");

	Serial.print("\n\r");
	for (i=0; i<512; i++)
	{
		Serial.print(table2[i]);
		Serial.print(",");
	}
	Serial.print("\n\r");
}

void FetDriver::CalTableB(int32_t maxMA)
{

	int16_t table2[512]={0};
	int32_t pwm=0;
	int32_t mA=0;
	int i;

	while (mA>-maxMA)
	{
		int32_t adc;
		adc=GetMeanAdc(ISENSE_FET_B,10)-coilB_Zero;
		mA=FET_ADC_TO_MA(adc);
		pwm=pwm-1;
		coilB_PWM(pwm);
		//delay(5);
	}

	//LOG("First PWM is %d %d",pwm, mA);
	PWM_Table_B[0]=pwm;
	table2[0]=mA;
	i=1;
	while (i<512)
	{
		int32_t adc;
		adc=GetMeanAdc(ISENSE_FET_B,10)-coilB_Zero;
		mA=FET_ADC_TO_MA(adc);

		//LOG("PWM %d, %d %d",i,mA,pwm);
		if (mA>((i-255)*maxMA/256))
		{
			PWM_Table_B[i]=pwm;
			table2[i]=mA;
			i++;
		}else
		{
			pwm=pwm+1;
			coilB_PWM(pwm);
			//delay(5);
		}
	}

	coilB_PWM(0);
	Serial.print("\n\r TABLE B \n\r");
	for (i=0; i<512; i++)
	{
		Serial.print(PWM_Table_B[i]);
		Serial.print(",");
	}
	Serial.print("\n\r");

	Serial.print("\n\r");
	for (i=0; i<512; i++)
	{
		Serial.print(table2[i]);
		Serial.print(",");
	}
	Serial.print("\n\r");
}


void FetDriver::begin()
{
	int16_t i;
	uint32_t t0;
	int32_t i0=0;
	uint32_t zero,x,k;
	int32_t max_mA;


	ptrInstance=(FetDriver *)this;
	//enable 1V reference
	SYSCTRL->VREF.reg |= SYSCTRL_VREF_BGOUTEN;
	ADCRead(ISENSE_FET_A); //setup the adc with fast timing
	//nt32_t min,max,avg;
	//Setup the FET inputs
	GPIO_OUTPUT(PIN_FET_IN1);
	GPIO_OUTPUT(PIN_FET_IN2);
	GPIO_OUTPUT(PIN_FET_IN3);
	GPIO_OUTPUT(PIN_FET_IN4);
	GPIO_OUTPUT(PIN_FET_ENABLE);
	GPIO_HIGH(PIN_FET_ENABLE);

	//setup the Pin peripheral setting correct
	pinPeripheral(PIN_FET_IN2, PIO_TIMER_ALT); //TCC0 WO[7]
	pinPeripheral(PIN_FET_IN1, PIO_TIMER_ALT);
	SET_PIN_PERHERIAL(PIN_FET_IN4, PIO_TIMER_ALT); //TCC0 WO[7]
	SET_PIN_PERHERIAL(PIN_FET_IN3, PIO_TIMER_ALT);

	pinPeripheral(ISENSE_FET_A, PIO_ANALOG);  //AIN[0]
	pinPeripheral(ISENSE_FET_B, PIO_ANALOG);  //AIN[2]

	enableTCC0();
	coilB_PWM(0);
	coilA_PWM(0);
	delay(100);
	measureCoilA_zero();
	measureCoilB_zero();


//	ADCStart(ISENSE_FET_A);


	//return;
//	while(1)
//	{
//		LOG("tick %d %d", TCC0->CC[1].reg,TCC0->CC[0].reg);
//		LOG("%d %d",coilA_error,coilB_error);
//	}

//	uint16_t data[1000];
//		ADCRead(ISENSE_FET_A);
//
//		t0=micros();
//		GPIO_LOW(PIN_FET_IN2);
//				GPIO_GPIO_OUTPUT(PIN_FET_IN2);
//				GPIO_HIGH(PIN_FET_IN1);
//				GPIO_GPIO_OUTPUT(PIN_FET_IN1);
//
//		for (i=0; i<1000; i++)
//		{
//			data[i]=fastADCRead(ISENSE_FET_A);
//		}
//		coilA_PWM(0);
//
//		t0=micros()-t0;
//
//		Serial.print("\n\r Step response \n\r");
//		Serial.print(t0);
//
//		Serial.print("\n\r Step response \n\r");
//		for (i=0; i<1000; i++)
//		{
//			Serial.print(data[i]);
//			Serial.print(",");
//		}
//		Serial.print("\n\r");
//
//		while(1)
//		{
//
//		}
	max_mA=NVM->motorParams.currentMa;
	WARNING("Maximum current is %d",max_mA);


	if (NVM->motorParams.parametersVaild && max_mA!=0)
	{
		CalTableA(max_mA);
		CalTableB(max_mA);

	}else
	{
		WARNING("NVM is not correct default to 1500mA");
		max_mA=1500;
		WARNING("calibrating phase A %dmA",max_mA);
		CalTableA(max_mA);
		WARNING("calibrating phase B %dmA",max_mA);
		CalTableB(max_mA);

	}
	return;

	//coilA_PWM(100);

	x=0;
	while(1)
	{
		//LOG("Trying to move motor %d",x);
		delay(1);
		move(x, 1000);
		x=x+256;

	}


	return; // all done

	//	//set DAC to mid level
	//	syncDAC();
	//	DAC->DATA.reg = 0x2FF;  // DAC on 10 bits.
	//	syncDAC();
	//	DAC->CTRLA.bit.ENABLE = 0x01;     // Enable DAC
	//	syncDAC();

	//	WARNING("Running ADC ISR test");
	//	ADCRead(3);

	//LOG("coil value %d %d",coilB_value,coilB_Zero);
	i=47;
	x=0;
	while(1)
	{
		int32_t adc,value;
		int32_t mA;

		if (0)
		{

			coilB_PWM(i);
			delayMicroseconds(1000);
			//LOG("%d",i);
			//if (i==47 ) delay(50);

			if (x==0)
			{
				i=i+1;
				if (i>200)
				{
					x=1;
					//i=47;

				}
			}

			if (x == 1)
			{
				i=i-1;
				if (i<47)
				{
					x=2;
					i=-47;
				}

			}

			if (x == 2)
			{
				i=i-1;
				if (i<-200)
				{
					x=3;
				}
			}

			if (x == 3)
			{
				i=i+1;
				if (i>-47)
				{
					x=0;
					i=47;
				}
			}
		}else
		{

			adc=ADCRead(ISENSE_FET_A);
			value=adc-coilA_Zero;

			mA=(value*2206)/1000;



			//
			//delay(500);
			//NVIC_DisableIRQ(ADC_IRQn);

			LOG("coil A %d %d, %d ",coilA_Zero, value, mA );

		}
		//			NVIC_DisableIRQ(ADC_IRQn);
		//
		//			NVIC_EnableIRQ(ADC_IRQn);
	}

	x=0;
	for (k=0; k<128; k++)
	{
		x=x+ADCRead(8);
	}
	zero=x/32;

	//setupDAC();
	//setDAC(5,5);
	enableTCC0();
	//setupComparators();


	ERROR("Enable PWM");
	pinPeripheral(PIN_FET_IN4, PIO_TIMER_ALT); //TCC0 WO[7]

	//
	//	for (i=40; i<55; i++)
	//	{
	//		coilB_PWM(i);
	//		delay(200);
	//		ADCRead(8,16);
	//		LOG("COMP is 0x%04X ", AC->STATUSA.reg);
	//		LOG("%d ADC is %d ",i, ADCRead(8,16));
	//		YELLOW_LED(0);
	//	}

	//ADCRead(8,16);
	//AC->INTENCLR.bit.COMP1=1;
	//coilA_Value=0;

	coilB_PWM(0);

	i=47;
	coilB_PWM(i);
	while(1)
	{
		int32_t x=0,k;
		coilB_PWM(i);
		delay(3000);
		for (k=0; k<128; k++)
		{
			x=x+ADCRead(8);
		}
		x=x/32;
		LOG("%d %d %d",i,x-zero,(x*3300)/(4096*4));
		LOG("%d",((x-zero)*5517)/10000);

		i=i+20;
		if (i>140)
		{
			i=47;
		}

	}
	/*	AC->INTENSET.bit.COMP1=1;
	while(1)
	{
		AC->INTENCLR.bit.COMP1=1;
		YELLOW_LED(0);
		AC->INTENSET.bit.COMP1=1;
		if ((millis()-t0)>10000)
		{
			int j;
			min=0xFFFFFF;
			max=(int16_t)ADCRead(8,16);
			avg=0;
			j=0;
			t0=micros();
			while( (micros()-t0)<1000)
			{
				 int16_t valueRead;

				  valueRead = ADCRead(8,16);

				  if (valueRead<min)
				  {
					  min=valueRead;
				  }
				  if (valueRead>max)
				  {
					  max=valueRead;
				  }
				  avg+=valueRead;
				  j++;
			}


			int32_t ma,x,duty;
			duty=i-45;
			duty=(1000*duty)/(F_CPU/FET_DRIVER_FREQ);

			LOG("min %d max %d, avg %d j %d, %d", min, max, (avg*10)/j, j,(avg*10)/j*(1000-duty)/1000);

			x=(avg*10)/j*(1000-duty)/1000;
			x=(x*600)/1000+200;

			LOG("mA %d\n\r",x);

			if (i<150)
			{
				i=100;
			}else
			{
				i=45;
			}
			LOG("COMP is 0x%04X ", AC->STATUSA.reg);
			LOG("%d ADC is %d %d",i, ADCRead(8,16),coilA_Value);
			t0=millis();
			AC->INTENCLR.bit.COMP1=1;
			coilA_Value=0;
			coilB_PWM(i);
			AC->INTENSET.bit.COMP1=1;
		}
	}
	 */
	return;

	//setup the PWM for current on the A4954, set for low current
	digitalWrite(PIN_A4954_VREF12,LOW);
	digitalWrite(PIN_A4954_VREF34,LOW);
	pinMode(PIN_A4954_VREF34, OUTPUT);
	pinMode(PIN_A4954_VREF12, OUTPUT);

	enabled=true;
	lastStepMicros=0;
	forwardRotation=true;

	enableTCC0();
	setupDAC();
	//
	//	WARNING("Setting DAC for 500mA output");
	//	setDAC((int32_t)((int64_t)1000*(DAC_MAX))/3300,(int32_t)((int64_t)1000*(DAC_MAX))/3300);
	//	bridge1(0);
	//	bridge2(0);
	//	while(1)
	//	{
	//
	//	}
	return;
}








int32_t FetDriver::getCoilB_mA(void)
{
	int32_t adc,ret;
	//fastADCRead(ISENSE_FET_B);
	adc=(int32_t)fastADCRead(ISENSE_FET_B);
	ret=FET_ADC_TO_MA(adc-coilB_Zero);
	//LOG("coilb %d %d",adc,ret);
	return ret;
}
int32_t FetDriver::getCoilA_mA(void)
{
	int32_t adc,ret;
	//fastADCRead(ISENSE_FET_A);
	adc=(int32_t)fastADCRead(ISENSE_FET_A);
	ret=FET_ADC_TO_MA(adc-coilA_Zero);
	//LOG("coila %d %d",adc,ret);
	return ret;
}


//this is precise move and modulo of A4954_NUM_MICROSTEPS is a full step.
// stepAngle is in A4954_NUM_MICROSTEPS units..
// The A4954 has no idea where the motor is, so the calling function has to
// to tell the A4954 what phase to drive motor coils.
// A4954_NUM_MICROSTEPS is 256 by default so stepAngle of 1024 is 360 degrees
// Note you can only move up to +/-A4954_NUM_MICROSTEPS from where you
// currently are.
int32_t FetDriver::move(int32_t stepAngle, uint32_t mA)
{
	uint16_t angle;
	int32_t cos,sin;
	int32_t dacSin,dacCos;
	int32_t dacSin_mA,dacCos_mA;
	int32_t maxMa;
	static int32_t last_dacSin_mA=0,last_dacCos_mA=0;;
	if (enabled == false)
	{
		WARNING("FET Driver disabled");

		//turn the current off to FETs
		coilA_PWM(0);
		coilB_PWM(0);

		//float the FET outputs by disabling FET driver.
		GPIO_LOW(PIN_FET_ENABLE);
		return stepAngle;
	}
	GPIO_HIGH(PIN_FET_ENABLE);


	maxMa=NVM->motorParams.currentMa;
	if (maxMa==0)
	{
		maxMa=2200;
	}

	//WARNING("move %d %d",stepAngle,mA);
	//handle roll overs, could do with modulo operator
	//stepAngle=stepAngle%SINE_STEPS;
	//	while (stepAngle<0)
	//	{
	//		stepAngle=stepAngle+SINE_STEPS;
	//	}
	//	while (stepAngle>=SINE_STEPS)
	//	{
	//		stepAngle=stepAngle-SINE_STEPS;
	//	}

	//figure out our sine Angle
	// note our SINE_STEPS is 4x of microsteps for a reason
	//angle=(stepAngle+(SINE_STEPS/8)) % SINE_STEPS;
	angle=(stepAngle) % SINE_STEPS;
	//calculate the sine and cosine of our angle
	sin=sine(angle);
	cos=cosine(angle);

	//if we are reverse swap the sign of one of the angels
	if (false == forwardRotation)
	{
		cos=-cos;
	}

	//LOG("sin/cos %d %d %d", sin,cos,angle);
	//scale sine result by current(mA)
	dacSin_mA=((int32_t)mA*(int32_t)(sin))/SINE_MAX;

	//scale cosine result by current(mA)
	dacCos_mA=((int32_t)mA*(int32_t)(cos))/SINE_MAX;

	coilA_SetPoint=FET_MA_TO_ADC(dacSin_mA);
	coilB_SetPoint=FET_MA_TO_ADC(dacCos_mA);
	//LOG("sin/cos %d %d", dacSin,dacCos);

	//convert value into 12bit DAC scaled to 3300mA max
	dacSin=(int32_t)((int64_t)dacSin_mA*(255))/maxMa;

	//convert value into 12bit DAC scaled to 3300mA max
	dacCos=(int32_t)((int64_t)dacCos_mA*(255))/maxMa;

	//LOG("sin/cos %d %d", dacSin,dacCos);
	//limit the table index to +/-255
	dacCos=MIN(dacCos,(int32_t)255);
	dacCos=MAX(dacCos,(int32_t)-255);
	dacSin=MIN(dacSin,(int32_t)255);
	dacSin=MAX(dacSin,(int32_t)-255);


	if ((dacSin_mA-last_dacSin_mA)>200)
	{
		GPIO_LOW(PIN_FET_IN2);
		PIN_GPIO_OUTPUT(PIN_FET_IN2);
		GPIO_HIGH(PIN_FET_IN1);
		PIN_GPIO_OUTPUT(PIN_FET_IN1);
	}else if ((dacSin_mA-last_dacSin_mA)<-200)
	{
		GPIO_HIGH(PIN_FET_IN2);
		PIN_GPIO_OUTPUT(PIN_FET_IN2);
		GPIO_LOW(PIN_FET_IN1);
		PIN_GPIO_OUTPUT(PIN_FET_IN1);
	}

	if ((dacCos_mA-last_dacCos_mA)>200)
	{
		GPIO_LOW(PIN_FET_IN4);
		PIN_GPIO_OUTPUT(PIN_FET_IN4);
		GPIO_HIGH(PIN_FET_IN3);
		PIN_GPIO_OUTPUT(PIN_FET_IN3);
	}else if ((dacCos_mA-last_dacCos_mA)<-200)
	{
		GPIO_HIGH(PIN_FET_IN4);
		PIN_GPIO_OUTPUT(PIN_FET_IN4);
		GPIO_LOW(PIN_FET_IN3);
		PIN_GPIO_OUTPUT(PIN_FET_IN3);
		}
	delayMicroseconds(20);
	last_dacSin_mA=dacSin_mA;
	last_dacCos_mA=dacCos_mA;

//	YELLOW_LED(1);
//	uint32_t t0=micros();
//	int done=0;
//	int32_t a,b;
//	a=FET_MA_TO_ADC(dacSin_mA);
//	b=FET_MA_TO_ADC(dacCos_mA);
//	while ((micros()-t0)<20 && done!=0x03)
//	{
//		if ( (fastADCRead(ISENSE_FET_A)-a)<FET_MA_TO_ADC(200))
//		{
//			GPIO_LOW(PIN_FET_IN2);
//			PIN_GPIO_OUTPUT(PIN_FET_IN2);
//			GPIO_HIGH(PIN_FET_IN1);
//			PIN_GPIO_OUTPUT(PIN_FET_IN1);
//			//coilA_PWM(PWM_Table_A[dacSin+255]);
//			done |=0x01;
//		}
//
//		if ((fastADCRead(ISENSE_FET_A)-a)>FET_MA_TO_ADC(200))
//		{
//			GPIO_HIGH(PIN_FET_IN2);
//			PIN_GPIO_OUTPUT(PIN_FET_IN2);
//			GPIO_LOW(PIN_FET_IN1);
//			PIN_GPIO_OUTPUT(PIN_FET_IN1);
//			done |=0x01;
//		}
//		if  ((fastADCRead(ISENSE_FET_B)-b)<FET_MA_TO_ADC(200))
//		{
//			GPIO_LOW(PIN_FET_IN4);
//			PIN_GPIO_OUTPUT(PIN_FET_IN4);
//			GPIO_HIGH(PIN_FET_IN3);
//			PIN_GPIO_OUTPUT(PIN_FET_IN3);
//			done |=0x02;
//		}
//		if  ((fastADCRead(ISENSE_FET_B)-b)>FET_MA_TO_ADC(200))
//		{
//			GPIO_HIGH(PIN_FET_IN4);
//			PIN_GPIO_OUTPUT(PIN_FET_IN4);
//			GPIO_LOW(PIN_FET_IN3);
//			PIN_GPIO_OUTPUT(PIN_FET_IN3);
//			done |=0x02;
//		}
//
//	}
//
//	YELLOW_LED(0);


	//LOG("sin/cos %d %d", dacSin,dacCos);
	//loop up the current from table and set the PWM
	coilA_PWM(PWM_Table_A[dacSin+255]);
	coilB_PWM(PWM_Table_B[dacCos+255]);

	lastStepMicros=micros();
	return stepAngle;
}
#pragma GCC pop_options //fast optimization

#endif //NEMA_23_10A_HW

#pragma GCC pop_options
