/*
 * gram_pwm_adc_sampling.c
 *
 *  Created on: 20. 4. 2014
 *      Author: tomas
 */

//This module is using TMR0, TMR1 and ADC peripherals

#include "gram_pwm_adc_sampling.h"

#define ADC_CH_MOTOR				ADC_CH0
#define ADC_CH_PIEZO				ADC_CH1
#define GPIO_MOTOR_PWM_PORT_NUM		(1)
#define GPIO_MOTOR_PWM_PIN_NUM		(21)
#define LPC_TIMER_PWM				LPC_TIMER1

// ADC sampling buffers
static uint16_t ADC_Motor_Piezo_Buffer_Data[ADC_PIEZO_SAMPLES_N];
static uint16_t ADC_Motor_Current_Buffer_Data[ADC_CURRENT_SAMPLES_N];

// Flags
//#define ADC_SAMPLING_DONE	(0)
//#define ADC_SAMPLING_GO		(1)
//#define SAMPLIG_FLAG(flag)	( SampligFlags & (1UL << flag))
static uint8_t ADC_SamplingDone = 0;
static uint8_t ADC_SamplingGo = 0;
static volatile MotorPWM_OutHigh = 0;

//Variables
static uint8_t MotorPWM_Duty = 0;

static ADC_CLOCK_SETUP_T ADCSetup;


void TIMER0_IRQHandler(void) {	//20 KHz periodic interrupt
	static uint8_t n = 0;
	static uint16_t i = 0;
	if (Chip_TIMER_MatchPending(LPC_TIMER0, 0)) {
		Chip_TIMER_ClearMatch(LPC_TIMER0, 0);

		// PWM routine
		if (MotorPWM_Duty > 0) {
			Chip_TIMER_SetMatch(LPC_TIMER_PWM, 0, MotorPWM_Duty);	//set tmr1 match to correspond with pwm on time
			if ( MotorPWM_OutHigh ){
				//duty is near 100%, tmr0 interrupt did not occure
				Chip_TIMER_Reset(LPC_TIMER_PWM);		// reset pwm timer
				Chip_TIMER_ClearMatch(LPC_TIMER1, 0);	//clear possible match
				NVIC_ClearPendingIRQ(TIMER1_IRQn);		//clear possible interrupt
			}
			else{
				//tmr1 interrupt already has happened -> start new cycle
				LPC_GPIO[GPIO_MOTOR_PWM_PORT_NUM].SET |= 1UL << GPIO_MOTOR_PWM_PIN_NUM;	//set output high
				MotorPWM_OutHigh = 1;
			}
			Chip_TIMER_Enable(LPC_TIMER_PWM);
		}

		// Sampling routine
		if (ADC_SamplingGo){
			n++;
			if (n == 10) {
				n = 0;
				// sample motor current by 2KHz
				Chip_ADC_EnableChannel(LPC_ADC, ADC_CH_MOTOR, ENABLE);
				Chip_ADC_SetStartMode(LPC_ADC, ADC_START_NOW, ADC_TRIGGERMODE_RISING);
			}
		}
	}
	NVIC_ClearPendingIRQ(TIMER0_IRQn);
}

void TIMER1_IRQHandler(void){
	if (Chip_TIMER_MatchPending(LPC_TIMER_PWM, 0)) {
		Chip_TIMER_ClearMatch(LPC_TIMER_PWM, 0);

		//End of duty cycle reached
		LPC_GPIO[GPIO_MOTOR_PWM_PORT_NUM].CLR |= 1UL << GPIO_MOTOR_PWM_PIN_NUM;	//set output low
		MotorPWM_OutHigh = 0;
	}
	NVIC_ClearPendingIRQ(TIMER1_IRQn);
}

void ADC_IRQHandler(void) {
	uint16_t adc_data;
	static uint8_t n = 0;
	NVIC_ClearPendingIRQ(ADC_IRQn);

	// Process motor current channel
	if ( LPC_ADC->DR[0] >> 31) {
		//Motor current channel conversion is done
		Chip_ADC_EnableChannel(LPC_ADC, ADC_CH_MOTOR, DISABLE);
		n++;
		if (n >= 4) {	//sample piezo by 500 Hz
			//Start conversion on piezo channel
			Chip_ADC_EnableChannel(LPC_ADC, ADC_CH_PIEZO, ENABLE);
			Chip_ADC_SetStartMode(LPC_ADC, ADC_START_NOW, ADC_TRIGGERMODE_RISING);
			n = 0;
		}
		adc_data = (uint16_t) ((LPC_ADC->DR[0] >> 4) & 0xfff);
		// save result to buffer if not full
		if ( BufferLinGetFreeCount(&ADC_Motor_Current_Buffer) ) {
			BufferLinWrite( &ADC_Motor_Current_Buffer, adc_data);
		}
	}
	// Process piezo channel
	if ( LPC_ADC->DR[1] >> 31) {
		Chip_ADC_EnableChannel(LPC_ADC, ADC_CH_PIEZO, DISABLE);
		// save result to buffer if not full
		adc_data = (uint16_t) ((LPC_ADC->DR[1] >> 4) & 0xfff);
		if ( BufferLinGetFreeCount(&ADC_Motor_Piezo_Buffer) ) {
			BufferLinWrite(&ADC_Motor_Piezo_Buffer, adc_data);
		}
	}
	if (ADC_SamplingGo){	// if sampling is in progress
		// If both buffers are full
		if (!BufferLinGetFreeCount(&ADC_Motor_Current_Buffer)
			&& !BufferLinGetFreeCount(&ADC_Motor_Current_Buffer)){
			// sampling complete
			ADC_SamplingDone = 1;
			ADC_SamplingGo = 0;
		}
	}


}

// Initialize motor pwm and start adc sampling
inline void PWM_SamplingInit(void){

	ADC_Motor_Piezo_Buffer.p_data = ADC_Motor_Piezo_Buffer_Data;
	ADC_Motor_Piezo_Buffer.buffer_size = ADC_PIEZO_SAMPLES_N;
	ADC_Motor_Current_Buffer.p_data = ADC_Motor_Current_Buffer_Data;
	ADC_Motor_Current_Buffer.buffer_size = ADC_CURRENT_SAMPLES_N;

	/*ADC Init */
	Chip_ADC_Init(LPC_ADC, &ADCSetup);
	Chip_ADC_Int_SetChannelCmd(LPC_ADC, ADC_CH_MOTOR, ENABLE);
	Chip_ADC_Int_SetChannelCmd(LPC_ADC, ADC_CH_PIEZO, ENABLE);
	NVIC_SetPriority(ADC_IRQn, 1);
	NVIC_EnableIRQ(ADC_IRQn);

	/*TMR0 init */
	Chip_Clock_SetPCLKDiv(SYSCTL_PCLK_TIMER0, SYSCTL_CLKDIV_1);	//96MHz
	Chip_TIMER_Init(LPC_TIMER0);
	Chip_TIMER_PrescaleSet(LPC_TIMER0, 0);	//96 MHz
	Chip_TIMER_SetMatch(LPC_TIMER0, 0, 4800);
	Chip_TIMER_ResetOnMatchEnable(LPC_TIMER0, 0);
	Chip_TIMER_MatchEnableInt(LPC_TIMER0, 0);
	Chip_TIMER_Enable(LPC_TIMER0);
	NVIC_SetPriority(TIMER0_IRQn, 2);
	NVIC_EnableIRQ(TIMER0_IRQn);

	// TMR1 init
	MotorPWM_Duty = 50;
	Chip_GPIO_WriteDirBit(LPC_GPIO, GPIO_MOTOR_PWM_PORT_NUM, GPIO_MOTOR_PWM_PIN_NUM, true);
	Chip_Clock_SetPCLKDiv(SYSCTL_PCLK_TIMER1, SYSCTL_CLKDIV_1);	//96MHz
	Chip_TIMER_Init(LPC_TIMER_PWM);
	Chip_TIMER_PrescaleSet(LPC_TIMER_PWM, 47);	// 47 -> 2MHz ~ 0.5us = 1/100 of max motor pwm duty
	Chip_TIMER_SetMatch(LPC_TIMER_PWM, 0, 4700);
	Chip_TIMER_ResetOnMatchEnable(LPC_TIMER_PWM, 0);
	Chip_TIMER_StopOnMatchEnable(LPC_TIMER_PWM, 0);
	Chip_TIMER_MatchEnableInt(LPC_TIMER_PWM, 0);
	NVIC_SetPriority(TIMER1_IRQn, 1);
	NVIC_EnableIRQ(TIMER1_IRQn);
}

void SamplingStart(void){
	ADC_Motor_Piezo_Buffer.data_start_ind = 0;
	ADC_Motor_Piezo_Buffer.data_end_ind = 0;
	ADC_Motor_Current_Buffer.data_start_ind = 0;
	ADC_Motor_Current_Buffer.data_end_ind = 0;
	ADC_SamplingDone = 0;
	ADC_SamplingGo = 1;
}

// Used to stop running sampling
inline void SamplingReset(void){
	ADC_SamplingGo = 0;
}

inline uint8_t SamplingIsDone(void){
	return ADC_SamplingDone;
}

inline void MotorPWM_DutySet(uint8_t arg_duty){
	if (arg_duty > 100){
		MotorPWM_Duty = 100;
	}
	else{
		MotorPWM_Duty = arg_duty;
	}
}

inline uint8_t MotorPWM_DutyGet(void){
	return MotorPWM_Duty;
}
